• 【数据结构】— 『队列』的实现以及LeetCode队列练习题


     ꧁   各位大佬们好!很荣幸能够得到您的访问,让我们一起在编程道路上任重道远!꧂

    ☙ 博客专栏:【数据结构初阶】

    ⛅ 本篇内容简介:数据结构初阶中的队列的实现以及LeetCode练习!

    ⭐ 了解作者:励志成为一名编程大牛的学子,目前是大二的编程小白。

    励志术语:编程道路的乏味,让我们一起学习变得有趣!


    文章目录

    🌔 队列的概念及结构

    🌗 队列的实现

    🌒 队列的链式结构

     🌒 队列的初始化

     🌒 队尾入数据

     🌒 队头出数据

    🌒 获取队头的数据

    🌒 获取队尾的数据

    🌒 获取队列有效元素个数

    🌒 判断队列是否为空

    🌒 队列的销毁

    🌒 测试队列接口功能

     🌔 队列源代码

    🌗 Queue.c文件

    🌗 Queue.h文件

    🌗 test.c文件

    🌔 225.用队列实现栈

    🌗 题解思路

     🌗 题解代码

    🌔 232.用栈实现队列

     🌗 题解思路

    🌗 题解代码

    🌗 结束语


    🌔 队列的概念及结构

    队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First in First out)。

    入队列:进行插入操作的一端称为队尾。

    出队列:进行删除操作的一端称为队头。

    🌗 队列的实现

    队列可以是数组或者链表的结构实现,但是使用链表的结构实现更优一点。因为如果使用数组的结构,出队列在数组头上的数据,效率会比较低。在这里我们使用链表来实现队列。(数组不适合数组头频繁删除或者数组中插入数据)。

    🌒 队列的链式结构

    1. //队列的链式结构
    2. typedef int QDataType;
    3. typedef struct QListNode
    4. {
    5. struct QListNode* next;
    6. QDataType Data;
    7. }QNode;
    8. //队列的结构
    9. typedef struct Queue
    10. {
    11. QNode* front;
    12. QNode* back;
    13. int size;//记录队列节点的个数
    14. }Queue;

     🌒 队列的初始化

    初始化队列就是要使头指针与尾指针都置为空,将size也置为空。

    1. void QueueInit(Queue* pq)
    2. {
    3. //断言队列不能为空
    4. assert(pq);
    5. pq->front = pq->back = NULL;
    6. pq->size = 0;
    7. }

     🌒 队尾入数据

    首先我们要先创造新的要插入的节点,1.如果是第一个插入数据,将头指针与尾指针都指向newnode;2.平常的尾插,先将newnode尾插到back指针后,再将back指针指向尾节点。

    1. void QueuePush(Queue* pq, QDataType x)
    2. {
    3. assert(pq);
    4. //先创造节点,再进行链接
    5. QNode* newnode = (QNode*)malloc(sizeof(QNode));
    6. if (newnode == NULL)
    7. {
    8. perror("malloc fail");
    9. exit(-1);
    10. }
    11. //初始化节点
    12. newnode->Data = x;
    13. newnode->next = NULL;
    14. //链接,考虑尾节点是空,还是非空
    15. if (pq->back == NULL)
    16. {
    17. pq->front = pq->back = newnode;
    18. }
    19. else
    20. {
    21. pq->back->next = newnode;
    22. pq->back = newnode;
    23. }
    24. pq->size++;//队列节点++
    25. }

     🌒 队头出数据

    队头出数据时,当队头为空时就不能再出数据了,需要进行判空处理。

    1. 当队列只剩余一个数据时,如果直接free,会造成back的野指针问题,所以我们需要单独判断这种情况。

    2. 我们需要记录要删除的节点,再将front指向下一个,最后置空。

    1. void QueuePop(Queue* pq)
    2. {
    3. assert(pq);
    4. //判断只剩一个数据时
    5. if (pq->front == NULL)
    6. {
    7. free(pq->front);
    8. pq->front = pq->back = NULL;
    9. }
    10. else
    11. {
    12. QNode* del = pq->front;
    13. pq->front = pq->front->next;
    14. free(del);
    15. del = NULL;
    16. }
    17. pq->size--;//出数据需要--size
    18. }

    🌒 获取队头的数据

    1. //获取队头的数据
    2. QDataType QueueFront(Queue* pq)
    3. {
    4. assert(pq);
    5. assert(!QueueEmpty(pq));
    6. return pq->front->Data;
    7. }

    🌒 获取队尾的数据

    1. //获取队尾的数据
    2. QDataType QueueBack(Queue* pq)
    3. {
    4. assert(pq);
    5. assert(!QueueEmpty(pq));
    6. return pq->back->Data;
    7. }

    🌒 获取队列有效元素个数

    因为我们在队列的结构体中加上了size记录元素个数,所以我们在这里可以直接返回。

    1. //获取队列中有效元素的个数
    2. int QueueSize(Queue* pq)
    3. {
    4. assert(pq);
    5. return pq->size;
    6. }

    🌒 判断队列是否为空

    如果队列为空返回非0值,如果是非空返回0。

    1. //队列的判空
    2. bool QueueEmpty(Queue* pq)
    3. {
    4. assert(pq);
    5. return pq->front == NULL && pq->back == NULL;
    6. }

    🌒 队列的销毁

    因为我们这里使用链表实现,所以我们要将链表的节点,一个一个释放。

    1. //队列的销毁
    2. void QueueDestroy(Queue* pq)
    3. {
    4. assert(pq);
    5. QNode* cur = pq->front;
    6. while (cur != NULL)
    7. {
    8. QNode* del = cur;
    9. cur = cur->next;
    10. free(del);
    11. }
    12. pq->front = pq->back = NULL;
    13. }

    🌒 测试队列接口功能

    1. int main()
    2. {
    3. Queue q;
    4. QueueInit(&q);
    5. //队尾插入数据
    6. QueuePush(&q, 1);
    7. QueuePush(&q, 2);
    8. QueuePush(&q, 3);
    9. QueuePush(&q, 4);
    10. QueuePush(&q, 5);
    11. while (!QueueEmpty(&q))
    12. {
    13. //获取队头的数据
    14. printf("%d ", QueueFront(&q));
    15. QueuePop(&q);
    16. }
    17. printf("\n");
    18. QueueDestroy(&q);
    19. return 0;
    20. }


     🌔 队列源代码

    🌗 Queue.c文件

    1. #include"Queue.h"
    2. void QueueInit(Queue* pq)
    3. {
    4. //断言队列不能为空
    5. assert(pq);
    6. pq->front = pq->back = NULL;
    7. pq->size = 0;
    8. }
    9. void QueuePush(Queue* pq, QDataType x)
    10. {
    11. assert(pq);
    12. //先创造节点,再进行链接
    13. QNode* newnode = (QNode*)malloc(sizeof(QNode));
    14. if (newnode == NULL)
    15. {
    16. perror("malloc fail");
    17. exit(-1);
    18. }
    19. //初始化节点
    20. newnode->Data = x;
    21. newnode->next = NULL;
    22. //链接,考虑尾节点是空,还是非空
    23. if (pq->back == NULL)
    24. {
    25. pq->front = pq->back = newnode;
    26. }
    27. else
    28. {
    29. pq->back->next = newnode;
    30. pq->back = newnode;
    31. }
    32. pq->size++;//队列节点++
    33. }
    34. void QueuePop(Queue* pq)
    35. {
    36. assert(pq);
    37. assert(!QueueEmpty(pq));
    38. //判断只剩一个数据时
    39. if (pq->front == NULL)
    40. {
    41. free(pq->front);
    42. pq->front = pq->back = NULL;
    43. }
    44. else
    45. {
    46. QNode* del = pq->front;
    47. pq->front = pq->front->next;
    48. free(del);
    49. del = NULL;
    50. }
    51. pq->size--;//出数据需要--size
    52. }
    53. //获取队头的数据
    54. QDataType QueueFront(Queue* pq)
    55. {
    56. assert(pq);
    57. assert(!QueueEmpty(pq));
    58. return pq->front->Data;
    59. }
    60. //获取队尾的数据
    61. QDataType QueueBack(Queue* pq)
    62. {
    63. assert(pq);
    64. assert(!QueueEmpty(pq));
    65. return pq->back->Data;
    66. }
    67. //获取队列中有效元素的个数
    68. int QueueSize(Queue* pq)
    69. {
    70. assert(pq);
    71. return pq->size;
    72. }
    73. //队列的判空
    74. bool QueueEmpty(Queue* pq)
    75. {
    76. assert(pq);
    77. return pq->front == NULL && pq->back == NULL;
    78. }
    79. //队列的销毁
    80. void QueueDestroy(Queue* pq)
    81. {
    82. assert(pq);
    83. QNode* cur = pq->front;
    84. while (cur != NULL)
    85. {
    86. QNode* del = cur;
    87. cur = cur->next;
    88. free(del);
    89. }
    90. pq->front = pq->back = NULL;
    91. }

    🌗 Queue.h文件

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #pragma once
    3. #include<stdio.h>
    4. #include<assert.h>
    5. #include<stdlib.h>
    6. #include<stdbool.h>
    7. //队列的链式结构
    8. typedef int QDataType;
    9. typedef struct QListNode
    10. {
    11. struct QListNode* next;
    12. QDataType Data;
    13. }QNode;
    14. //队列的结构
    15. typedef struct Queue
    16. {
    17. QNode* front;
    18. QNode* back;
    19. int size;//记录队列节点的个数
    20. }Queue;
    21. //初始化队列
    22. void QueueInit(Queue* pq);
    23. //队尾入数据
    24. void QueuePush(Queue* pq, QDataType x);
    25. //队尾出数据
    26. void QueuePop(Queue* pq);
    27. //获取队头的数据
    28. QDataType QueueFront(Queue* pq);
    29. //获取队尾的数据
    30. QDataType QueueBack(Queue* pq);
    31. //获取队列中有效元素的个数
    32. int QueueSize(Queue* pq);
    33. //队列的判空
    34. bool QueueEmpty(Queue* pq);
    35. //队列的销毁
    36. void QueueDestroy(Queue* pq);

    🌗 test.c文件

    1. #include"Queue.h"
    2. int main()
    3. {
    4. Queue q;
    5. QueueInit(&q);
    6. //队尾插入数据
    7. QueuePush(&q, 1);
    8. QueuePush(&q, 2);
    9. QueuePush(&q, 3);
    10. QueuePush(&q, 4);
    11. QueuePush(&q, 5);
    12. while (!QueueEmpty(&q))
    13. {
    14. //获取队头的数据
    15. printf("%d ", QueueFront(&q));
    16. QueuePop(&q);
    17. }
    18. printf("\n");
    19. QueueDestroy(&q);
    20. return 0;
    21. }


    🌔 225.用队列实现栈

    LeeCode题目链接:使用两个栈实现队列。OJ

    题目描述:

    🌗 题解思路

    实现栈的结构:在此之前我们需要队列的结构,不过还好我们在前面实现过一个队列了,这里我们直接用。这里我们定义两个队列,一个q1,一个q2。

     

    创造栈:这里我们不能像创造队列一样,

    Queue q;

    return &q;

    因为局部变量出了作用域就销毁,所以我们需要malloc一个栈,并且初始化,再返回。

     

    入栈:哪个队列不为空,就往哪个队列入数据。

     

    出栈:假设一个队列为空,一个队列不为空,将不为空队列的数据导入为空队列中,至原来不为空的队列还剩一个数据。再记录最后一个数据,并且返回。

     

    获取栈顶数据:哪个队列不为空,就获取哪个队列中的队尾的数据。

     

    判断栈是否为空:当两个队列都为空时,栈就为空。

     

    栈的销毁:因为在栈中,有两个队列,所以先销毁两个队列,再释放栈。

     

     🌗 题解代码

    1. // 两个队列
    2. typedef struct {
    3. Queue q1;
    4. Queue q2;
    5. } MyStack;
    6. MyStack* myStackCreate() {
    7. // 需要malloc obj
    8. MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    9. //初始化
    10. QueueInit(&obj->q1);
    11. QueueInit(&obj->q2);
    12. return obj;
    13. }
    14. void myStackPush(MyStack* obj, int x) {
    15. //两个不为空就push哪个
    16. if(!QueueEmpty(&obj->q1))
    17. {
    18. //入数据
    19. QueuePush(&obj->q1,x);
    20. }
    21. else{
    22. QueuePush(&obj->q2,x);
    23. }
    24. }
    25. int myStackPop(MyStack* obj) { //要返回栈顶的数据
    26. //Pop不为空队列 假设q1为空
    27. MyStack* empty=&obj->q1;
    28. MyStack* nonempty=&obj->q2;
    29. if(!QueueEmpty(&obj->q1))
    30. {
    31. empty=&obj->q2;
    32. nonempty=&obj->q1;
    33. }
    34. //倒数据 还剩下一个数据
    35. while(QueueSize(nonempty)>1)
    36. {
    37. //不为空队列的头数据
    38. QueuePush(empty,QueueFront(nonempty));
    39. QueuePop(nonempty);
    40. }
    41. //返回栈顶元素
    42. int top=QueueFront(nonempty);
    43. QueuePop(nonempty);
    44. return top;
    45. }
    46. int myStackTop(MyStack* obj) {
    47. //获取不为空的队列的尾数据
    48. if(!QueueEmpty(&obj->q1))
    49. {
    50. return QueueBack(&obj->q1);
    51. }
    52. else{
    53. return QueueBack(&obj->q2);
    54. }
    55. }
    56. bool myStackEmpty(MyStack* obj) {
    57. //两个队列都为空
    58. return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
    59. }
    60. void myStackFree(MyStack* obj) {
    61. //先销毁队列,再free
    62. QueueDestroy(&obj->q1);
    63. QueueDestroy(&obj->q2);
    64. free(obj);
    65. }

    🌔 232.用栈实现队列

    LeetCode题目链接:用两个栈实现队列。OJ

    题目描述:

     🌗 题解思路

    队列结构:还是一样需要栈的所有接口实现,在这里我们定义两个栈,

    一个栈叫Push栈:专门用来队列的插入数据。

    另一个栈叫Pop栈:专门用来实现出队列的操作。

     

    创造队列:还是一样需要malloc一个队列,并且初始化,最后返回。

     

    入队列:直接往Push栈里面插入数据。 

     

    出队列:判断需不需要将数据从Push栈中全部导入Pop栈中,如果Pop栈中没有数据,就说明需要倒数据。因为是栈的性质,所以队头的数据就是栈顶的数据,就是倒过去之后栈顶的数据。(需要Pop操作)

     

    获取队头的数据:也是一样判断是否需要倒数据,再记录Pop栈顶的数据,返回。(不需要Pop操作)

     

    队列的判空:两个栈都为空时,队列就为空。 

     

    队列的销毁:先销毁两个栈,再释放队列。

     

    🌗 题解代码

    1. typedef struct {
    2. //一个栈用来Push,一个栈用来Pop
    3. Stack PushST;
    4. Stack PopST;
    5. } MyQueue;
    6. MyQueue* myQueueCreate() {
    7. MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    8. //进行初始化
    9. StackInit(&obj->PushST);
    10. StackInit(&obj->PopST);
    11. return obj;
    12. }
    13. void myQueuePush(MyQueue* obj, int x) {
    14. //直接往PushST的栈入数据
    15. StackPush(&obj->PushST,x);
    16. }
    17. void PushST_To_PopST(MyQueue* obj)
    18. {
    19. if(StackEmpty(&obj->PopST))
    20. {
    21. while(!StackEmpty(&obj->PushST))
    22. {
    23. //倒PushST栈顶的数据
    24. StackPush(&obj->PopST,StackTop(&obj->PushST));
    25. //删除这个数据
    26. StackPop(&obj->PushST);
    27. }
    28. }
    29. }
    30. int myQueuePop(MyQueue* obj) {
    31. //考虑是否倒数据
    32. PushST_To_PopST(obj);
    33. //返回队列开头的数据
    34. int front=StackTop(&obj->PopST);
    35. StackPop(&obj->PopST);
    36. return front;
    37. }
    38. int myQueuePeek(MyQueue* obj) {
    39. //直接返回队列头的数据
    40. PushST_To_PopST(obj);
    41. int front=StackTop(&obj->PopST);
    42. return front;
    43. }
    44. bool myQueueEmpty(MyQueue* obj) {
    45. return StackEmpty(&obj->PushST) && StackEmpty(&obj->PopST);
    46. }
    47. void myQueueFree(MyQueue* obj) {
    48. StackDestroy(&obj->PushST);
    49. StackDestroy(&obj->PopST);
    50. free(obj);
    51. }

    🌗 结束语

    🎈 相隔千里,不能陪各位大佬在中秋节一起赏月,但是我们还能看到同一个月亮。中秋节祝各位大佬中秋快乐。

    【写在最后·想告诉你】

    在互联网这个行业里

    任何时候都要学好技术

    永远都是 技术为王

  • 相关阅读:
    [附源码]java毕业设计健身房管理系统论文2022
    Java学习中Spring到底是什么?该如何学习?
    【AI学习笔记】jupyter notebook 默认路径修改(超简介,超详细)
    Oracle-expdp方式升级19c问题合集
    SpringCloud微服务(十二)——Seata分布式事务
    @Redis--主从复制
    centos离线安装包(https部署下需要mod_ssl)
    Unity的UnityStats: 属性详解与实用案例
    nodejs微信小程序-利康药房管理系统的设计与实现- 安卓-python-PHP-计算机毕业设计
    工控自动化方案:和利时LE系列PLC数采通讯
  • 原文地址:https://blog.csdn.net/qq_63938813/article/details/126788918