Hello everybody!今天给大家讲讲队列的相关知识。队列,属于一种数据结构。从字面意思上理解,就像是排队一样,在食堂中,先排队的人自然就先买到饭。队列也是如此,先入队列的数据自然就先出队列。希望大家可以通过这篇文章解决自己心中的疑惑!那废话不多说,让我们开始叭!

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列遵循先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

队列可以以数组和链表的结构实现,使用链表的结构实现更优,因为如果使用数组的结构,出队列是需要移动数据,时间复杂度是O(n),效率比较低。



由于实现队列需要较长的代码,因此我们要像公司中做大型项目一样创建一个头文件:Queue.h和两个源文件:Queue.c Test.c。
头文件中需要包含所需要的库文件,接口的声明,队列的声明。总之就是要我们清晰的看到队列的结构和功能。
而源文件Queue.c需要实现在头文件中声明的接口。
源文件Test.c用于测试队列的功能是否正常。
当然源文件需要包含头文件,这样才能把它们关联起来。
创建三个文件使得代码逻辑更加清晰,有利于后期代码的维护。

这是队列的结构,结构体QNode是队列的链式结构,它可以存放当前结点的值和下一个结点的地址。但是当队列为空,插入第一个数据时,需要改变头指针。因此需要传递二级指针,这样显然是有些麻烦的。所以我们再创建一个结构体Queue,用于存放队列的头指针和尾指针,更方便入队和出队。size用来记录队列中的元素个数。

以上是队列主要功能的接口,下面我们来逐个实现它们。

目前为止,我们已经实现了初始化和入队两个接口。下面测试一下它们的功能。

通过调试,我们可以看到:初始化后,在队尾成功插入1和2。目前队列中有两个数值,接口功能正常。
下面我们来实现一下其他的接口:

这是队列剩下的接口,咱们也来测试一下。

从测试结果来看:各个接口功能正常,销毁队列后,头指针和尾指针都被置为空指针,队列大小size为0。符合要求。
为了方便大家更好的学习,队列的代码我也给出来!
- #pragma once
- #include
- #include
- #include
- #include
- typedef int QDataType;
- typedef struct QueueNode {
- QDataType val;
- struct QueueNode* next;
- }QNode;
- typedef struct Queue {
- QNode* phead;
- QNode* ptail;
- int size;
- }Queue;
-
- void QueueInit(Queue* pq);//初始化
- void QueuePush(Queue* pq, QDataType x);//入队
- void QueuePop(Queue* pq);//出队
- void QueueDestroy(Queue* pq);//销毁
- QDataType QueueFront(Queue* pq);//返回队头的值
- QDataType QueueBack(Queue* pq);//返回队尾的值
- bool QueueEmpty(Queue* pq);//判断队列是否为空
- int QueueSize(Queue* pq);//返回队列的数据个数
- #include "Queue.h"
- void QueueInit(Queue* pq) {
- assert(pq);//检验pq是否为空指针
- pq->phead = pq->ptail = NULL;
- pq->size = 0;
- }
- void QueuePush(Queue* pq, QDataType x) {
- assert(pq);
- QNode* newnode = (QNode*)malloc(sizeof(QNode));//动态开辟一个新结点
- if (newnode == NULL) {//若开辟失败,终止程序
- perror("malloc fail");
- exit(-1);
- }
- newnode->val = x;
- newnode->next = NULL;
- if (pq->phead == NULL) {//如果队列中没有结点,则将新结点赋值给头指针和尾指针
- pq->phead = pq->ptail = newnode;
- }
- else {
- pq->ptail->next = newnode;//有结点的话,将新结点赋值给尾指针
- pq->ptail = newnode;
- }
- pq->size++;
- }
-
- void QueuePop(Queue* pq) {
- assert(pq);
- assert(pq->phead);//队列不能为空
- QNode* next = pq->phead->next;//记录头结点的下一个结点
- free(pq->phead);
- pq->phead = next;
- if (pq->phead == NULL) {//当phead为NULL时,ptail没有变化。但ptail指向的空间已经还给操作系统,此时ptail就是野指针,必须处理掉!
- pq->ptail = NULL;
- }
- pq->size--;
- }
- QDataType QueueFront(Queue* pq) {
- assert(pq);
- assert(pq->phead);
- return pq->phead->val;
- }
- QDataType QueueBack(Queue* pq) {
- assert(pq);
- assert(pq->ptail);
- return pq->ptail->val;
- }
- bool QueueEmpty(Queue* pq) {
- assert(pq);
- return pq->phead == NULL;
- }
- int QueueSize(Queue* pq) {
- assert(pq);
- return pq->size;
- }
- void QueueDestroy(Queue* pq) {
- assert(pq);
- while (pq->phead) {
- QNode* next = pq->phead->next;
- free(pq->phead);
- pq->phead = next;
- }
- pq->phead = pq->ptail = NULL;
- pq->size = 0;
- }
- #include "Queue.h"
- int main() {
- Queue pq;
- QueueInit(&pq);
- QueuePush(&pq, 1);
- QueuePush(&pq, 2);
- QueuePush(&pq, 3);
- QueuePush(&pq, 4);
- printf("%d\n", QueueBack(&pq));
- printf("%d\n", QueueSize(&pq));
- while (!QueueEmpty(&pq)) {
- printf("%d", QueueFront(&pq));
- QueuePop(&pq);
- }
- QueueDestroy(&pq);
- return 0;
- }
好啦,关于队列的讨论就到这里叭。不知道大家有没有收获呢?如果有疑问,可以发在评论区。
最后,希望宝子们每天都有所进步,成为自己想成为的人,做自己想做的事!\(0^◇^0)/