• 【栈和队列面试题】用栈实现队列(动图解析更清晰)


    leetcode 232.用栈实现队列

    前言:用两个栈实现一个队列,模拟实现队列的功能

    💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥

    ✨✨刷题专栏:http://t.csdn.cn/UlvTc

    💨💨本篇内容:力扣上栈与队列面试题目

    目录

    leetcode 232.用栈实现队列

    📌结构体类型的声明(MyQueue)

    1️⃣队列的初始化(myQueueCreate)

    2️⃣元素入队列(myQueuePush)

    3️⃣获得队首元素(myQueuePeek)

    4️⃣元素出队列(myQueuePop)

    5️⃣判空(myQueueEmpty)

    6️⃣销毁队列(myQueueFree)

    总代码:


    来源:232. 用栈实现队列 - 力扣(LeetCode)

    📌结构体类型的声明(MyQueue)

            使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈(pushst),一个输出栈(popst)

    1. typedef struct {
    2. ST pushst;//输入栈
    3. ST popst;//输出栈
    4. } MyQueue;//自定义队列结构体

    1️⃣队列的初始化(myQueueCreate)

    图解:

    代码实现: 

    1. MyQueue* myQueueCreate() {
    2. //使用malloc函数为MyQueue结构体分配内存空间,定义一个MyQueue* 的指针指向这块空间
    3. MyQueue* obj =(MyQueue*)malloc(sizeof(MyQueue));
    4. // 若分配失败
    5. if(obj == NULL)
    6. {//则打印错误信息
    7. perror("malloc fail");
    8. return NULL;//返回NULL
    9. }
    10. STInit(&obj->pushst);//初始化输入栈
    11. STInit(&obj->popst);//初始化输出栈
    12. return obj;//返回指向这个结构体的指针
    13. }

    2️⃣元素入队列(myQueuePush)

    图解:在push数据的时候,只要数据放进输入栈就好

    1. void myQueuePush(MyQueue* obj, int x) {
    2. STPush(&obj->pushst , x);//往队列导入元素
    3. }

    3️⃣获得队首元素(myQueuePeek)

            假设我们需要在正常的队列中获取头部元素,那么应该怎么在这个自定义的队列中获得呢?

            那么就应该想清楚(Output stack)输出栈出栈顺序,由下图可以看出,输入栈(Input stack)出栈顺序是:3 2 1,那么(Output stack)输出栈入栈顺序是: 3 2 1,自然的,(Output stack)输出栈出栈顺序就应该为1 2 3,那么(Output stack)输出栈的这个元素1就是队列的队首元素

            这个函数的功能,先判断输出栈为空的前提下,循环条件是输入栈不为空,STPush()的功能就是把输入栈的元素,一个一个导入到输出栈再一个一个把输入栈的元素弹出直到输入栈为空栈时结束循环,返回此时的输出栈栈顶元素

    1. int myQueuePeek(MyQueue* obj) {
    2. if(STEmpty(&obj->popst))//若输出栈为空
    3. {
    4. //结束条件是把输入栈元素全部挪动到输出栈为止
    5. while(!STEmpty(&obj->pushst))//输入栈不为空
    6. { //获得输入栈头部元素,挪动到输出栈
    7. STPush(&obj->popst,STTop(&obj->pushst));
    8. STPop(&obj->pushst);//弹出输入栈元素
    9. }
    10. }
    11. //返回此时的输出栈栈顶元素
    12. return STTop(&obj->popst);
    13. }

    4️⃣元素出队列(myQueuePop)

     错误操作:假设输入栈数据没有全部导入到输出栈里,那么最终的出栈顺序会混乱的。

    正确操作:        

            所以在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。

    代码实现的部分需要注意的: 

            可以看出myQueuePop的实现,直接复用了myQueuePeek() ,要不然,对stEmpty判空的逻辑又要重写一遍。

    代码实现: 

    1. int myQueuePop(MyQueue* obj) {
    2. int front = myQueuePeek(obj);//代码复用,获得队列头部元素
    3. STPop(&obj->popst);//弹出元素
    4. return front;//获得队列的头部元素
    5. }

    5️⃣判空(myQueueEmpty)

    如果进栈和出栈都为空的话,说明模拟的队列为空了。

    1. bool myQueueEmpty(MyQueue* obj) {
    2. return STEmpty(&obj->pushst)
    3. && STEmpty(&obj->popst);
    4. }//判断队列是否为空,需要输入栈和输出栈两个同时为空,才会返回true

    6️⃣销毁队列(myQueueFree)

    和队列实现栈同样道理,需要先释放两个栈的空间,再释放结构体的空间,这样才确保不会内存泄露

    1. //队列空间的释放
    2. void myQueueFree(MyQueue* obj) {
    3. STDestroy(&obj->pushst);//释放输入栈
    4. STDestroy(&obj->popst);//释放输出栈
    5. free(obj);//释放自定义的队列
    6. }

    总代码:

    1. #include<stdlib.h>
    2. #include<assert.h>
    3. #include<stdbool.h>
    4. #include<stdio.h>
    5. typedef int STDataType;
    6. typedef struct Stack
    7. {
    8. STDataType* a;
    9. int top;//栈顶的位置
    10. int capacity;//栈的容量
    11. }ST;
    12. void STInit(ST* pst);
    13. void STDestroy(ST* pst);
    14. void STPush(ST* pst,STDataType x);
    15. void STPop(ST* pst);
    16. STDataType STTop(ST* pst);
    17. bool STEmpty(ST* pst);
    18. int STSize(ST*pst);
    19. void STInit(ST* pst)
    20. {
    21. assert(pst);
    22. pst->a = NULL;//栈底
    23. //top不是下标
    24. //pst->top=-1;//指向栈顶元素
    25. pst->top = 0;//指向栈顶元素的下一个位置
    26. pst->capacity = 0;
    27. }
    28. void STDestroy(ST* pst)
    29. {
    30. assert(pst);
    31. free(pst->a);
    32. pst->a = NULL;
    33. }
    34. void STPush(ST* pst,STDataType x)
    35. {
    36. if (pst->top == pst->capacity)
    37. {
    38. int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;//true,4.false,括2
    39. STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));//返回值地址相等就是原地扩容,不同就是异地扩
    40. if (tmp == NULL)
    41. {
    42. perror("realloc fail");
    43. return;
    44. }
    45. pst->a = tmp;//返回的是realloc出来的内存块的地址
    46. pst->capacity = newCapacity;//把扩容后的空间大小赋值给栈容量
    47. }
    48. pst->a[pst->top] = x;//先放值
    49. pst->top++;//++
    50. }
    51. void STPop(ST* pst)
    52. {
    53. assert(pst);
    54. assert(!STEmpty(pst));
    55. pst->top--;
    56. }
    57. STDataType STTop(ST* pst)
    58. {
    59. assert(pst);
    60. assert(!STEmpty(pst));
    61. return pst->a[pst->top - 1];
    62. }
    63. bool STEmpty(ST* pst)//栈为空返回true,不为空返回false
    64. {
    65. //assert(pst);
    66. //if (pst->top == 0)
    67. //{
    68. // return true;
    69. //}
    70. //else
    71. //{
    72. // return false;
    73. //}
    74. return pst->top == 0;
    75. }
    76. int STSize(ST* pst)
    77. {
    78. assert(pst);
    79. return pst->top;
    80. }
    81. typedef struct {
    82. ST pushst;//输入栈
    83. ST popst;//输出栈
    84. } MyQueue;//自定义队列
    85. MyQueue* myQueueCreate() {
    86. //使用malloc函数为MyQueue结构体分配内存空间,定义一个MyQueue* 的指针指向这块空间
    87. MyQueue* obj =(MyQueue*)malloc(sizeof(MyQueue));
    88. // 若分配失败
    89. if(obj == NULL)
    90. {//则打印错误信息
    91. perror("malloc fail");
    92. return NULL;//返回NULL
    93. }
    94. STInit(&obj->pushst);//初始化输入栈
    95. STInit(&obj->popst);//初始化输出栈
    96. return obj;//返回指向这个结构体的指针
    97. }
    98. void myQueuePush(MyQueue* obj, int x) {
    99. STPush(&obj->pushst , x);//往队列导入元素
    100. }
    101. int myQueuePop(MyQueue* obj) {
    102. int front = myQueuePeek(obj);//代码复用,获得队列头部元素
    103. STPop(&obj->popst);//弹出元素
    104. return front;//获得队列的头部元素
    105. }
    106. int myQueuePop(MyQueue* obj) {
    107. if(STEmpty(&obj->popst))//若输出栈为空
    108. {
    109. //结束条件是把输入栈元素全部挪动到输出栈为止
    110. while(!STEmpty(&obj->pushst))//输入栈不为空
    111. { //获得输入栈头部元素,挪动到输出栈
    112. STPush(&obj->popst,STTop(&obj->pushst));
    113. STPop(&obj->pushst);//弹出输入栈元素
    114. }
    115. }
    116. //返回此时的输出栈头部元素
    117. int front= STTop(&obj->popst);
    118. STPop(&obj->popst);//弹出元素
    119. return front;//获得队列的头部元素
    120. }
    121. int myQueuePeek(MyQueue* obj) {
    122. if(STEmpty(&obj->popst))//若输出栈为空
    123. {
    124. //结束条件是把输入栈元素全部挪动到输出栈为止
    125. while(!STEmpty(&obj->pushst))//输入栈不为空
    126. { //获得输入栈头部元素,挪动到输出栈
    127. STPush(&obj->popst,STTop(&obj->pushst));
    128. STPop(&obj->pushst);//弹出输入栈元素
    129. }
    130. }
    131. //返回此时的输出栈头部元素
    132. return STTop(&obj->popst);
    133. }
    134. bool myQueueEmpty(MyQueue* obj) {
    135. return STEmpty(&obj->pushst)
    136. && STEmpty(&obj->popst);
    137. }//判断队列是否为空,需要输入栈和输出栈两个同时为空,才会返回true
    138. //队列空间的释放
    139. void myQueueFree(MyQueue* obj) {
    140. //和队列实现栈同样道理,需要先释放两个栈的空间,再释放结构体的空间,这样才确保不会内存泄露
    141. STDestroy(&obj->pushst);//释放输入栈
    142. STDestroy(&obj->popst);//释放输出栈
    143. free(obj);//释放自定义的队列
    144. }

    代码执行: 

            本文结束,如有错误,欢迎指正,感谢你的来访!🚩

  • 相关阅读:
    机器学习教程
    8.11学习日志 后缀数组+DLX
    金仓数据库 KingbaseES PL/SQL 过程语言参考手册(16. A PL/SQL源文本加密)
    go语言插件平台的实现思路
    @Validated指定校验顺序
    Unity ML-Agents默认接口参数含义
    Leetcode6233-温度转换
    Node.js搭建WEB服务器
    ctfshow web入门 php特性 web126-web130
    vulnhub_driftingblues7靶机渗透测试
  • 原文地址:https://blog.csdn.net/weixin_65186652/article/details/133046888