队列的定义
队列是限制结点插入操作固定在一端(队尾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;
}
入列
入列这里需要分成俩种情况去处理
- 第一次插入数据
- 不是第一次插入数据
原因:
如果我们是第一次插入数据,我们的头尾指针此时是指向空的,需将头尾俩个指针同时指向第一个节点,而往后的数据入列,只需要改变尾指针就好了。
代码实现如下:
//入列函数
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;
}
}
注:此处的创建出新的节点可优化成单个函数。
出列
出列也需要分成俩种情况去处理
- 队列中只剩一个节点时
- 队列中非只剩一个节点时
原因:
当队列中不是进行最后一次删除时,我们每次删除的操作,只需要将头指针指向的节点释放掉并置空就好,然后将头指针更新到指向原来的头指针指向的节点的下一个节点;但是当我们对队列进行的是最后一次删除时,如果还是只是进行这样的操作就会出问题,这时候我们的头指针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;
}
}
获取元素个数
思路:
- 遍历(对指针进行迭代更新),当此指针指向空时,停止计数,
- 在一开始的第二个结构体中定义一个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;
}
销毁队列
步骤:
- 先释放掉每个节点,这里释放使用的也是迭代
- 再将头尾指针置空
//销毁函数
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;
}
本篇文章到此就结束啦,感谢各位大佬的观看,欢迎讨论学习 😐 😐 😐。