c语言模拟实现队列(动图演示)

本文详细介绍了队列的定义、常用接口,包括初始化、入列、出列、元素个数获取、队首尾元素访问及队列空判断。通过实例演示和代码实现,帮助理解如何用链表构造高效的数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

队列的定义

队列是限制结点插入操作固定在一端(队尾rear)进行,而结点的删除操作固定在另一端(队首front)进行的线性表.特点是:先进先出,后进后出

队列的常用接口

前言

由定义可知,队列同栈一样,都可分成俩种存储结构:顺序结构存储,链式结构存储。之前栈的实现我们是用顺序表来实现的,那么这个我们就用链表来实现。下面我们先以给出与队列特点相同的几个生活实例:
银行办理业务:
在这里插入图片描述
食堂打饭:
在这里插入图片描述

动图演示:
数据{4,7,8,9}分别插入队列
请添加图片描述

元素定义

由上图可知我们的链表元素中要包含什么

1.头尾指针
2.数据
3.指向下一个节点的指针

而我们发现如果我们每个节点都定义头尾指针的话,就会很浪费,所以我们将链表分成俩个结构体来定义,结构体1包含(数据和节点指针),结构体2(则管理头尾指针,掌控链表)

typedef int Datatype;

typedef struct QueueNode {
	  Datatype val;
	 struct QueueNode * next;     //指向下一个节点
}QNode;

typedef struct Queue
{
	struct QueueNode* head;//记录队首
	struct QueueNode* tail;//队尾
}Queue;

初始化

由上面我们可知,我们链表的核心其实是Queue,即里面的头尾指针,所以我们初始化只需初始化这俩个指针就好了,为什么不用初始化节点呢?因为我们初始化的时候里面还没有节点 😐,此外因为他传进来的是一个结构体指针,所以他一定不可能为空,所以进行一下断言。
代码如下:

//初始化函数
void InitQueue(Queue* plist) 
{
	assert(plist);
	// =(Queue *)malloc( sizeof(Queue) );  //如果动态开辟内间 就相当于改变了plist 的指向 再对plist解引用 拿到的就不是一开始传进来的plist的head
	/*plist->head =plist->tail= NULL;*/           //和tail了
	plist->head = plist->tail =NULL;

}

入列

入列这里需要分成俩种情况去处理

  1. 第一次插入数据
  2. 不是第一次插入数据

原因:

如果我们是第一次插入数据,我们的头尾指针此时是指向空的,需将头尾俩个指针同时指向第一个节点,而往后的数据入列,只需要改变尾指针就好了。

代码实现如下:

//入列函数
void Push(Queue* plist, Datatype x)
{
	assert(plist);
	//创建出一个新结点出来
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->val = x;
	newnode->next = NULL;

	//第一个入列
	if (!plist->head)
	{
		plist->head = plist->tail = newnode;
	}
	else//更新队尾
	{
		plist->tail->next = newnode;
		plist->tail = newnode;
	}

}

注:此处的创建出新的节点可优化成单个函数。

出列

出列也需要分成俩种情况去处理

  1. 队列中只剩一个节点时
  2. 队列中非只剩一个节点时

原因:

当队列中不是进行最后一次删除时,我们每次删除的操作,只需要将头指针指向的节点释放掉并置空就好,然后将头指针更新到指向原来的头指针指向的节点的下一个节点;但是当我们对队列进行的是最后一次删除时,如果还是只是进行这样的操作就会出问题,这时候我们的头指针head会指向NULL,但是我们的尾指针还指向我们最后一次删除的节点,也就是说还是能通过尾指针去访问那块已经被释放掉的空间,这就会造成内存泄漏。

所以我们需要做出处理,处理就是每次都判断一下,head是否为NULL,head为空了,就证明队列已经删空了,就将tail也置空。
代码实现如下:

//出列函数
void PopQueue(Queue* plist)
{
	assert(plist);

	//如果队列不为空就出列
	assert(!empty(plist));

		QNode *next= plist->head->next;
		free(plist->head);
		//plist->head = NULL;//必须置空
		plist->head = next;
	if (plist->head == NULL)//当队列被删空时 如果不把tail也置为空指针 会发生内存泄露
	{
		plist->tail = NULL;
	}
}

获取元素个数

思路:

  1. 遍历(对指针进行迭代更新),当此指针指向空时,停止计数,
  2. 在一开始的第二个结构体中定义一个size或者capacity

下面给出第一种思路的代码:

//获取大小函数
int sizeQueue(Queue* plist)
{
	assert(plist);
	QNode* cur = plist->head;
	int n = 0;
	while (cur)
	{
		++n;
		cur = cur->next;
	}
	return n;
}

获得队首元素

这里唯一需要注意的就是当队列为空时,获取不了,结合empty函数就可以实现

//获取队首元素
Datatype Queuefront(Queue* plist)
{
	assert(plist);
	assert(plist->head != NULL);

	return plist->head->val;
}

获得队尾元素

注意同上获取队首元素

//获取队尾元素
Datatype Queueback(Queue* plist)
{
	assert(plist);
	assert(plist->head != NULL);

	return plist->tail->val;
}

empty

实现起来最轻松的接口:

//检测是否为空队列函数
bool empty(Queue* plist)   //为空就放回true 为假返回false
{
	assert(plist);

	return plist->head == NULL;
}

销毁队列

步骤:

  1. 先释放掉每个节点,这里释放使用的也是迭代
  2. 再将头尾指针置空
//销毁函数
void DestroyQueue(Queue* plist)
{
	//防止空指针
	assert(plist);

	QNode* cur = plist->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	plist->head=plist->tail = NULL;
	//plist = NULL;
}

完整代码

//初始化函数
void InitQueue(Queue* plist)  
{                           
	assert(plist);
	// =(Queue *)malloc( sizeof(Queue) );  //如果动态开辟内间 就相当于改变了plist 的指向 再对plist解引用 拿到的就不是一开始传进来的plist的head
	/*plist->head =plist->tail= NULL;*/           //和tail了
	plist->head = plist->tail =NULL;

}
//销毁函数
void DestroyQueue(Queue* plist)
{
	//防止空指针
	assert(plist);

	QNode* cur = plist->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	plist->head=plist->tail = NULL;
	//plist = NULL;
}
//入列函数
void Push(Queue* plist, Datatype x)
{
	assert(plist);
	//创建出一个新结点出来
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	newnode->val = x;
	newnode->next = NULL;

	//第一个入列
	if (!plist->head)
	{
		plist->head = plist->tail = newnode;
	}
	else//更新队尾
	{
		plist->tail->next = newnode;
		plist->tail = newnode;
	}

}
//出列函数
void PopQueue(Queue* plist)
{
	assert(plist);

	//如果队列不为空就出列
	assert(!empty(plist));

		QNode *next= plist->head->next;
		free(plist->head);
		//plist->head = NULL;//必须置空
		plist->head = next;
	if (plist->head == NULL)//当队列被删空时 如果不把tail也置为空指针 会发生内存泄露
	{
		plist->tail = NULL;
	}
}
//获取大小函数
int sizeQueue(Queue* plist)
{
	assert(plist);
	QNode* cur = plist->head;
	int n = 0;
	while (cur)
	{
		++n;
		cur = cur->next;
	}
	return n;
}
//检测是否为空队列函数
bool empty(Queue* plist)   //为空就放回true 为假返回false
{
	assert(plist);

	return plist->head == NULL;
}

//获取队首元素
Datatype Queuefront(Queue* plist)
{
	assert(plist);
	assert(plist->head != NULL);

	return plist->head->val;
}
//获取队尾元素
Datatype Queueback(Queue* plist)
{
	assert(plist);
	assert(plist->head != NULL);

	return plist->tail->val;
}

本篇文章到此就结束啦,感谢各位大佬的观看,欢迎讨论学习 😐 😐 😐。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值