队列的实现与讲授

[复制链接]
发表于 2026-1-14 06:52:04 | 显示全部楼层 |阅读模式
一.概念与结构

1.概念

只允许在⼀端举行插⼊数据操纵,在另⼀端举行删除数据操纵的特殊线性表,队列具有先辈先出FIFO(First In First Out)
入队列:进⾏插⼊操纵的⼀端称为队尾
​ 出队列:进⾏删除操纵的⼀端称为队头
注意:与上文讲到的栈对比,栈是先辈后出,队列是先辈先出。
2.结构


队列也可以数组和链表的结构实现,使⽤链表的结构实现更优⼀些,由于假如使⽤数组的结构,出队列在数组头上出数据,服从会⽐较低。
数组头删时间复杂度:O(N) ** 数组尾插时间复杂度:O(1)
单链表头删时间复杂度:O(1) 单链表尾插时间复杂度:O(N)
 因此下战书队列的实现中,我们接纳链表的结构来举行。
二.队列的实现

queue.h

完备头文件如下。
  1. #pragma once
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. #include <stdbool.h>
  6. typedef int QDataType;
  7. typedef struct QueueNode//队列节点的结构,即单链表节点的结构
  8. {
  9.         QDataType data;
  10.         struct QueueNode* next;
  11. }QNode;
  12. typedef struct Queue//队列的结构,定义指向队列头尾的指针,以及队列节点的个数
  13. {
  14.         QNode* phead;
  15.         QNode* ptail;
  16.         QDataType size;
  17. }Q;
  18. void QueueInit(Q*);
  19. //入队列,队尾
  20. void QueuePush(Q*, QDataType);
  21. //出队列,队头
  22. void QueuePop(Q*);
  23. //队列判空
  24. bool QueueEmpty(Q*);
  25. //取队头数据
  26. QDataType QueueFront(Q*);
  27. //取队尾数据
  28. QDataType QueueBack(Q*);
  29. //队列有效元素个数
  30. int QueueSize(Q*);
  31. void QueueDestroy(Q*);
复制代码
分析:
1.此处我们界说了两个结构体,一个是队列的根本结构QNode,一个是用来表现队列的队头,队尾和元素个数的Q。
2.Q存在的意义紧张是为了便于后续表现队列的队头,队尾时可以简化二级指针的个数,且更加清晰。

test.c

相干测试代码如下。
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "Queue.h"
  3. void QueueTest01()
  4. {
  5.         Q q;//定义队列
  6.         QueueInit(&q);
  7.         QueuePush(&q, 1);
  8.         QueuePush(&q, 2);
  9.         QueuePush(&q, 3);
  10.         QueuePush(&q, 4);
  11.         ///
  12.         printf("head:%d\n", QueueFront(&q));
  13.         printf("tail:%d\n", QueueBack(&q));
  14.         printf("size:%d\n", QueueSize(&q));
  15.         QueuePop(&q);
  16.         QueueDestroy(&q);
  17. }
  18. int main()
  19. {
  20.         QueueTest01();
  21.         return 0;
  22. }
复制代码
队列的初始化
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "Queue.h"
  3. void QueueInit(Q* pq)
  4. {
  5.         assert(pq);
  6.         pq->phead = pq->ptail = NULL;
  7.         pq->size = 0;
  8. }
复制代码
分析:
1.需注意,我们此处传入的是指针Q*而非QNode*。
2.别的置空断言操纵如常。
队列的烧毁
  1. void QueueDestroy(Q* pq)
  2. {
  3.         assert(pq);
  4.         assert(!QueueEmpty(pq));
  5.         QNode* pcur = pq->phead;
  6.         while (pcur)
  7.         {
  8.                 QNode* next = pcur->next;
  9.                 free(pcur);
  10.                 pcur = next;
  11.         }
  12.         pq->phead = pq->ptail = NULL;
  13.         pq->size = 0;
  14. }
复制代码
分析:
1.由于每个节点与之前的链表类似,都是动态开发的,因此我们通过Q*找到队列头phead,之后逐个开释节点即可。
2.依次开释之后置空并将元素个数置为0即可。
节点的创建

起首创建一个专门用来生产节点的函数,克制后续插入期间码的冗余。
  1. QNode* BuyNode(QDataType x)
  2. {
  3.         QNode* newnode = (QNode*)malloc(sizeof(QNode));
  4.         if (newnode == NULL)
  5.         {
  6.                 perror("malloc fail!");
  7.                 exit(1);
  8.         }
  9.         newnode->data = x;
  10.         newnode->next = NULL;
  11.         return newnode;
  12. }
复制代码
注意:在创建节点跋文得把size++。

队尾插入数据——入队列
  1. void QueuePush(Q* pq, QDataType x)
  2. {
  3.         assert(pq);
  4.         if (pq->phead == NULL)
  5.                 pq->phead = pq->ptail = BuyNode(x);
  6.         else
  7.         {
  8.                 pq->ptail->next = BuyNode(x);
  9.                 pq->ptail = pq->ptail->next;
  10.         }
  11.         pq->size++;
  12. }
复制代码
分析:与链表的尾插原理根本类似。
队列判空

在进入删除之前,起首必要判断队列数据个数是否为空。
  1. bool QueueEmpty(Q* pq)
  2. {
  3.         assert(pq);
  4.         return pq->phead == NULL && pq->ptail == NULL;
  5. }
复制代码
此处通过利用size个数是否为0举行判空也可。
队头删除数据——出队列
  1. void QueuePop(Q* pq)
  2. {
  3.         assert(pq);
  4.         assert(!QueueEmpty(pq));
  5.         //只有一个节点的情况,避免ptail变成野指针
  6.         if (pq->ptail == pq->phead)
  7.         {
  8.                 free(pq->phead);
  9.                 pq->phead = pq->ptail = NULL;
  10.         }
  11.         else
  12.         {
  13.                 QNode* next = pq->phead->next;
  14.                 free(pq->phead);
  15.                 pq->phead = next;
  16.         }
  17.         pq->size--;
  18. }
复制代码
分析:
1.起首判断队列数据是否为空。
2.之后思量两种情况,假如队列只有一个节点,那么直接删除之后必要将队头和队尾都置为空。
3,假如队列不止存在一个节点,同链表的删除原理类似。

返回队头数据
  1. QDataType QueueFront(Q* pq)
  2. {
  3.         assert(pq);
  4.         assert(!QueueEmpty(pq));
  5.         return pq->phead->data;
  6. }
复制代码
返回队尾数据
  1. QDataType QueueBack(Q* pq)
  2. {
  3.         assert(pq);
  4.         assert(!QueueEmpty(pq));
  5.         return pq->ptail->data;
  6. }
复制代码
输出队列数据个数
  1. int QueueSize(Q* pq)
  2. {
  3.         assert(pq);
  4.         //不规范且时间复杂度O(n)
  5.         //int size = 0;
  6.         //QNode* pcur = pq->phead;
  7.         //while (pcur)
  8.         //{
  9.         //        size++;
  10.         //        pcur = pcur->next;
  11.         //}
  12.         //return size;
  13.         return pq->size;
  14. }
复制代码
分析:
1.假如我们不在最初引入变量size记载数据个数,由于队列是链表结构无法直接通过下标访问元素个数,必要逐个遍历记载,时间复杂度为O(n)。
2.直接返回size的话就可以镌汰时间斲丧。
三.完备代码
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "Queue.h"
  3. void QueueInit(Q* pq)
  4. {
  5.         assert(pq);
  6.         pq->phead = pq->ptail = NULL;
  7.         pq->size = 0;
  8. }
  9. QNode* BuyNode(QDataType x)
  10. {
  11.         QNode* newnode = (QNode*)malloc(sizeof(QNode));
  12.         if (newnode == NULL)
  13.         {
  14.                 perror("malloc fail!");
  15.                 exit(1);
  16.         }
  17.         newnode->data = x;
  18.         newnode->next = NULL;
  19.         return newnode;
  20. }
  21. void QueuePush(Q* pq, QDataType x)
  22. {
  23.         assert(pq);
  24.         if (pq->phead == NULL)
  25.                 pq->phead = pq->ptail = BuyNode(x);
  26.         else
  27.         {
  28.                 pq->ptail->next = BuyNode(x);
  29.                 pq->ptail = pq->ptail->next;
  30.         }
  31.         pq->size++;
  32. }
  33. bool QueueEmpty(Q* pq)
  34. {
  35.         assert(pq);
  36.         return pq->phead == NULL && pq->ptail == NULL;
  37. }
  38. void QueuePop(Q* pq)
  39. {
  40.         assert(pq);
  41.         assert(!QueueEmpty(pq));
  42.         //只有一个节点的情况,避免ptail变成野指针
  43.         if (pq->ptail == pq->phead)
  44.         {
  45.                 free(pq->phead);
  46.                 pq->phead = pq->ptail = NULL;
  47.         }
  48.         else
  49.         {
  50.                 QNode* next = pq->phead->next;
  51.                 free(pq->phead);
  52.                 pq->phead = next;
  53.         }
  54.         pq->size--;
  55. }
  56. QDataType QueueFront(Q* pq)
  57. {
  58.         assert(pq);
  59.         assert(!QueueEmpty(pq));
  60.         return pq->phead->data;
  61. }
  62. QDataType QueueBack(Q* pq)
  63. {
  64.         assert(pq);
  65.         assert(!QueueEmpty(pq));
  66.         return pq->ptail->data;
  67. }
  68. int QueueSize(Q* pq)
  69. {
  70.         assert(pq);
  71.         //不规范且时间复杂度O(n)
  72.         //int size = 0;
  73.         //QNode* pcur = pq->phead;
  74.         //while (pcur)
  75.         //{
  76.         //        size++;
  77.         //        pcur = pcur->next;
  78.         //}
  79.         //return size;
  80.         return pq->size;
  81. }
  82. void QueueDestroy(Q* pq)
  83. {
  84.         assert(pq);
  85.         assert(!QueueEmpty(pq));
  86.         QNode* pcur = pq->phead;
  87.         while (pcur)
  88.         {
  89.                 QNode* next = pcur->next;
  90.                 free(pcur);
  91.                 pcur = next;
  92.         }
  93.         pq->phead = pq->ptail = NULL;
  94.         pq->size = 0;
  95. }
复制代码
以上就是关于队列的概念,结构和用链表方式实现队列的讲授,欢迎各位大佬前来支持斧正!!!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表