【初阶数据结构篇】队列的实现(赋源码)

文章目录

  • 队列
    • [1 代码位置](#1 代码位置)
    • [2 概念与结构](#2 概念与结构)
    • [2 队列的实现](#2 队列的实现)
      • [2.1 队列的初始化和销毁](#2.1 队列的初始化和销毁)
        • [2.1.1 初始化](#2.1.1 初始化)
        • [2.1.2 销毁](#2.1.2 销毁)
      • [2.2 队列插入和删除数据](#2.2 队列插入和删除数据)
        • [2.2.1 队尾插入数据(入队列)](#2.2.1 队尾插入数据(入队列))
        • [2.2.2 队头删除数据(出队列)](#2.2.2 队头删除数据(出队列))
      • [2.3 返回队头/队尾数据](#2.3 返回队头/队尾数据)
      • [2.4 返回队列的有效数据个数](#2.4 返回队列的有效数据个数)

队列

1 代码位置

gitee\]([Queue/Queue · petrichor/2024-summer-c-language - 码云 - 开源中国 (gitee.com)](https://gitee.com/petrichor2024/2024-summer-c-language/tree/master/Queue/Queue)) ### 2 概念与结构 #### 1.1概念 ​ **只允许** 在⼀端进行插⼊数据操作,在另⼀端进行删除数据操作的特殊线性表,队列具有先进先出**FIFO(First In First Out)** ​ **⼊队列:进⾏插⼊操作的⼀端称为队尾** ​ **出队列:进⾏删除操作的⼀端称为队头** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/26332a6ffd564cd4b0a82efaf852339c.png) #### 1.2结构 > 队列也可以数组和链表的结构实现,使⽤链表的结构实现更优⼀些,因为如果使⽤数组的结构,出队列在数组头上出数据,效率会⽐较低。 > > \*\*数组头删时间复杂度:O(N) \*\* **数组尾插时间复杂度:O(1)** > > **单链表头删时间复杂度:O(1)** **单链表尾插时间复杂度:O(N)** * 乍一看二者难分伯仲,但如果我们在单链表的结构里再定义一个指向尾结点的指针,那么单链表就可以实现O(1)的尾插时间复杂度,而数组没有什么特别好的办法实现这种转变。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6d053adb971342a2b8f0530dddd9aad2.png) *** ** * ** *** ### 2 队列的实现 **Queue.h** ```c #pragma once #include #include #include #include typedef int QDataType; typedef struct QueueNode//队列节点的结构,即单链表节点的结构 { QDataType data; struct QueueNode* next; }QNode; typedef struct Queue//队列的结构,定义指向队列头尾的指针,以及队列节点的个数 { QNode* phead; QNode* ptail; QDataType size; }Q; void QueueInit(Q*); //入队列,队尾 void QueuePush(Q*, QDataType); //出队列,队头 void QueuePop(Q*); //队列判空 bool QueueEmpty(Q*); //取队头数据 QDataType QueueFront(Q*); //取队尾数据 QDataType QueueBack(Q*); //队列有效元素个数 int QueueSize(Q*); void QueueDestroy(Q*); ``` *** ** * ** *** **test.c** * 用来测试我们写的函数(函数的调用) * 这一部分就是自己写的时候用的测试用例,随便什么都行 最好是写一个方法测试一次,不然找错误的时候会很痛苦😜 ```c #define _CRT_SECURE_NO_WARNINGS 1 #include "Queue.h" void QueueTest01() { Q q;//定义队列 QueueInit(&q); QueuePush(&q, 1); QueuePush(&q, 2); QueuePush(&q, 3); QueuePush(&q, 4); /// printf("head:%d\n", QueueFront(&q)); printf("tail:%d\n", QueueBack(&q)); printf("size:%d\n", QueueSize(&q)); QueuePop(&q); QueueDestroy(&q); } int main() { QueueTest01(); return 0; } ``` *** ** * ** *** Queue.c **函数方法的实现,重点重点!!!** 在每一个方法的第一排都使用assert宏来判断形参是否为空(避免使用时传入空指针,后续解引用都会报错) #### 2.1 队列的初始化和销毁 ##### 2.1.1 初始化 * 前面提到,我们在定义队列时,一个结构体用来定义队列,其中有指向队列头尾的指针(其中还有一个size,用来保存链表长度,后面会讲到为什么),另一个就是队列结点的结构,和单链表一样 ```c #define _CRT_SECURE_NO_WARNINGS 1 #include "Queue.h" void QueueInit(Q* pq) { assert(pq); pq->phead = pq->ptail = NULL; pq->size = 0; } ``` * 和单链表一样,初始化只需让指向队列的指针置为空即可,只不过这里多了一个尾指针,同时队列为空,size=0 * 链表空间都是按需申请,所以入数据都是通过之后的插入方法一个一个实现的 ##### 2.1.2 销毁 ```c void QueueDestroy(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); QNode* pcur = pq->phead; while (pcur) { QNode* next = pcur->next; free(pcur); pcur = next; } pq->phead = pq->ptail = NULL; pq->size = 0; } ``` 同单链表 *** ** * ** *** #### 2.2 队列插入和删除数据 ##### 2.2.1 队尾插入数据(入队列) 同样的我们先写一个申请结点空间的函数 ```c QNode* BuyNode(QDataType x) { QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { perror("malloc fail!"); exit(1); } newnode->data = x; newnode->next = NULL; return newnode; } ``` * 根据链表是否为空分两种情况 * 别忘了size++ ```c void QueuePush(Q* pq, QDataType x) { assert(pq); if (pq->phead == NULL) pq->phead = pq->ptail = BuyNode(x); else { pq->ptail->next = BuyNode(x); pq->ptail = pq->ptail->next; } pq->size++; } ``` ##### 2.2.2 队头删除数据(出队列) 删除数据的时候一定要判断队列是否为空!(也可以通过size来判断) ```c bool QueueEmpty(Q* pq) { assert(pq); return pq->phead == NULL && pq->ptail == NULL; } ``` * 根据队列结点个数是否大于一个分两种情况讨论 * 别忘了size-- ```c void QueuePop(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); //只有一个节点的情况,避免ptail变成野指针 if (pq->ptail == pq->phead) { free(pq->phead); pq->phead = pq->ptail = NULL; } else { QNode* next = pq->phead->next; free(pq->phead); pq->phead = next; } pq->size--; } ``` *** ** * ** *** #### 2.3 返回队头/队尾数据 ```c QDataType QueueFront(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); return pq->phead->data; } QDataType QueueBack(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); return pq->ptail->data; } ``` *** ** * ** *** #### 2.4 返回队列的有效数据个数 * **size作用** * 首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的 * 其次时间复杂度O(N),程序效率低 所以我们在队列结构里多定义了一个size,很好地解决了这个问题 ```c int QueueSize(Q* pq) { assert(pq); //不规范且时间复杂度O(n) //int size = 0; //QNode* pcur = pq->phead; //while (pcur) //{ // size++; // pcur = pcur->next; //} //return size; return pq->size; } ``` *** ** * ** *** Queue.c(完整版) ```c #define _CRT_SECURE_NO_WARNINGS 1 #include "Queue.h" void QueueInit(Q* pq) { assert(pq); pq->phead = pq->ptail = NULL; pq->size = 0; } QNode* BuyNode(QDataType x) { QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { perror("malloc fail!"); exit(1); } newnode->data = x; newnode->next = NULL; return newnode; } void QueuePush(Q* pq, QDataType x) { assert(pq); if (pq->phead == NULL) pq->phead = pq->ptail = BuyNode(x); else { pq->ptail->next = BuyNode(x); pq->ptail = pq->ptail->next; } pq->size++; } bool QueueEmpty(Q* pq) { assert(pq); return pq->phead == NULL && pq->ptail == NULL; } void QueuePop(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); //只有一个节点的情况,避免ptail变成野指针 if (pq->ptail == pq->phead) { free(pq->phead); pq->phead = pq->ptail = NULL; } else { QNode* next = pq->phead->next; free(pq->phead); pq->phead = next; } pq->size--; } QDataType QueueFront(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); return pq->phead->data; } QDataType QueueBack(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); return pq->ptail->data; } int QueueSize(Q* pq) { assert(pq); //不规范且时间复杂度O(n) //int size = 0; //QNode* pcur = pq->phead; //while (pcur) //{ // size++; // pcur = pcur->next; //} //return size; return pq->size; } void QueueDestroy(Q* pq) { assert(pq); assert(!QueueEmpty(pq)); QNode* pcur = pq->phead; while (pcur) { QNode* next = pcur->next; free(pcur); pcur = next; } pq->phead = pq->ptail = NULL; pq->size = 0; } ``` **对于队列这一种结构的实现,因为和单链表差异也不大,更细节的内容就没有过多赘述,对以单链表的实现方法有什么疑问的话,推荐先去看这一篇哦-\>** [单链表的实现方法](http://t.csdnimg.cn/pnBt7) 以上就是队列的实现方法啦,各位大佬有什么问题欢迎在评论区指正,您的支持是我创作的最大动力!❤️ ![请添加图片描述](https://i-blog.csdnimg.cn/direct/721ad0d1623c47ca8ad5ede1c31491b1.gif)

相关推荐
代码AC不AC14 分钟前
【数据结构】队列
c语言·数据结构·学习·队列·深度讲解
小林熬夜学编程16 分钟前
【高并发内存池】第八弹---脱离new的定长内存池与多线程malloc测试
c语言·开发语言·数据结构·c++·算法·哈希算法
ゞ 正在缓冲99%…1 小时前
leetcode152.乘积最大子数组
数据结构·算法·leetcode
CYRUS_STUDIO1 小时前
Frida Hook Android Native 函数
android·c语言·逆向
W说编程1 小时前
《UNIX网络编程卷1:套接字联网API》第5章 TCP客户服务器程序示例
c语言·网络·网络协议·tcp/ip·unix·tcp
闯闯爱编程2 小时前
数组与特殊压缩矩阵
数据结构·算法·矩阵
laimaxgg2 小时前
数据结构B树的实现
开发语言·数据结构·c++·b树·算法
灋✘逞_兇4 小时前
链表的操作-反转链表
数据结构·链表
LuckyAnJo7 小时前
Leetcode-100 链表常见操作
算法·leetcode·链表
双叶8369 小时前
(C语言)虚数运算(结构体教程)(指针解法)(C语言教程)
c语言·开发语言·数据结构·c++·算法·microsoft