• 【LeetCode刷题日志】225.用队列实现栈


    • 🎈个人主页:库库的里昂
    •  🎐C/C++领域新星创作者
    •  🎉欢迎 👍点赞✍评论⭐收藏
    • ✨收录专栏:LeetCode 刷题日志
    • 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗

    目录

    1.题目描述

    2.解题思路+代码实现

    方法一:两个队列

    思路及算法:

    代码实现:

    方法二:一个队列

    思路及算法:

    代码实现:


    1.题目描述

    OJ链接 【leetcode 题号:225.用队列实现栈】【难度:简单】

    请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

    实现 MyStack 类:

    • void push(int x) 将元素 x 压入栈顶。
    • int pop() 移除并返回栈顶元素。
    • int top() 返回栈顶元素。
    • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

    注意:

    • 你只能使用队列的基本操作 —— 也就是 push to backpeek/pop from frontsize 和 is empty 这些操作。
    • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

    示例:

    输入:
    ["MyStack", "push", "push", "top", "pop", "empty"]
    [[], [1], [2], [], [], []]
    输出:
    [null, null, null, 2, 2, false]
    
    解释:
    MyStack myStack = new MyStack();
    myStack.push(1);
    myStack.push(2);
    myStack.top(); // 返回 2
    myStack.pop(); // 返回 2
    myStack.empty(); // 返回 False
    

    提示:

    • 1 <= x <= 9
    • 最多调用100 次 pushpoptop 和 empty
    • 每次调用 pop 和 top 都保证栈不为空

    进阶:你能否仅用一个队列来实现栈。

    2.解题思路+代码实现

    方法一:两个队列
    思路及算法:

    为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列实现栈的操作,其中q1用于存储栈内的元素,q2作为入栈操作的辅助队列。

    入栈操作时,首先将元素入队到q2,然后将q1的全部元素依次出队并入队列q2 ,此时q2的前端的元素即为新入栈的元素,再将q1和q2互换,则q1的元素即为栈内的元素,q1的前端和后端分别对应栈顶和栈底。

    由于每次入栈操作都确保q1的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除q1的前端元素并返回即可,获得栈顶元素操作只需要获得q1的前端元素并返回即可(不移除元素)。

    由于q1用于存储栈内的元素,判断栈是否为空时,只需要判断q1是否为空即可。

    代码实现:
    1. typedef int QDataType;
    2. typedef struct QueueNode
    3. {
    4. struct QueueNode* next;
    5. QDataType data;
    6. }QNode;
    7. typedef struct Queue
    8. {
    9. QNode* phead;
    10. QNode* ptail;
    11. int size;
    12. }Queue;
    13. void QueueInit(Queue* pq)
    14. {
    15. assert(pq);
    16. pq->phead = NULL;
    17. pq->ptail = NULL;
    18. pq->size = 0;
    19. }
    20. void QueueDestroy(Queue* pq)
    21. {
    22. assert(pq);
    23. QNode* cur = pq->phead;
    24. while (cur) {
    25. QNode* next = cur->next;
    26. free(cur);
    27. cur = next;
    28. }
    29. pq->phead = pq->ptail = NULL;
    30. pq->size = 0;
    31. }
    32. void QueuePush(Queue* pq, QDataType x)
    33. {
    34. assert(pq);
    35. QNode* newnode = (QNode*)malloc(sizeof(QNode));
    36. if (newnode == NULL) {
    37. perror("mallloc fail\n");
    38. return;
    39. }
    40. newnode->data = x;
    41. newnode->next = NULL;
    42. if (pq->ptail == NULL) {
    43. assert(pq->phead == NULL);
    44. pq->phead = pq->ptail = newnode;
    45. }
    46. else {
    47. pq->ptail->next = newnode;
    48. pq->ptail = newnode;
    49. }
    50. pq->size++;
    51. }
    52. bool QueueEmpty(Queue* pq)
    53. {
    54. assert(pq);
    55. return pq->size == 0;
    56. }
    57. void QueuePop(Queue* pq)
    58. {
    59. assert(pq);
    60. assert(!QueueEmpty(pq));
    61. if (pq->phead->next == NULL) {
    62. free(pq->phead);
    63. pq->phead = pq->ptail = NULL;
    64. }
    65. else {
    66. QNode* next = pq->phead->next;
    67. free(pq->phead);
    68. pq->phead = next;
    69. }
    70. pq->size--;
    71. }
    72. QDataType QueueFront(Queue* pq)
    73. {
    74. assert(pq);
    75. assert(!QueueEmpty(pq));
    76. return pq->phead->data;
    77. }
    78. QDataType QueueBack(Queue* pq)
    79. {
    80. assert(pq);
    81. assert(!QueueEmpty(pq));
    82. return pq->ptail->data;
    83. }
    84. int QueueSize(Queue* pq)
    85. {
    86. assert(pq);
    87. return pq->size;
    88. }
    89. //------以下为OJ提供-------
    90. typedef struct {
    91. Queue q1;
    92. Queue q2;
    93. } MyStack;
    94. MyStack* myStackCreate() {
    95. MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    96. if (obj == NULL) {
    97. perror("malloc fail");
    98. return NULL;
    99. }
    100. QueueInit(&obj->q1);
    101. QueueInit(&obj->q2);
    102. return obj;
    103. }
    104. void myStackPush(MyStack* obj, int x) {
    105. if (!QueueEmpty(&obj->q1)) {
    106. QueuePush(&obj->q1, x);
    107. }
    108. else {
    109. QueuePush(&obj->q2, x);
    110. }
    111. }
    112. int myStackPop(MyStack* obj) {
    113. Queue* pEmptyQ = &obj->q1;
    114. Queue* pNonEmptyQ = &obj->q2;
    115. if (!QueueEmpty(&obj->q1)) {
    116. pEmptyQ = &obj->q2;
    117. pNonEmptyQ = &obj->q1;
    118. }
    119. while (QueueSize(pNonEmptyQ) > 1) {
    120. QueuePush(pEmptyQ, QueueFront(pNonEmptyQ));
    121. QueuePop(pNonEmptyQ);
    122. }
    123. int top = QueueFront(pNonEmptyQ);
    124. QueuePop(pNonEmptyQ);
    125. return top;
    126. }
    127. int myStackTop(MyStack* obj) {
    128. if (!QueueEmpty(&obj->q1)) {
    129. return QueueBack(&obj->q1);
    130. }
    131. else {
    132. return QueueBack(&obj->q2);
    133. }
    134. }
    135. bool myStackEmpty(MyStack* obj) {
    136. return QueueEmpty(&obj->q1) &&
    137. QueueEmpty(&obj->q2);
    138. }
    139. void myStackFree(MyStack* obj) {
    140. QueueDestroy(&obj->q1);
    141. QueueDestroy(&obj->q2);
    142. free(obj);
    143. }

    复杂度分析

    • 时间复杂度:入栈操作O(n),其余操作都是O(1),其中n是栈内的元素个数。 入栈操作需要将q1中的n个元素出队,并入队n+1个元素到q2,共有2n+1次操作,每次出队和入队操作的时间复杂度都是O(1),因此入栈操作的时间复杂度是 O(n)。 出栈操作对应将q1的前端元素出队,时间复杂度是 O(1)。 获得栈顶元素操作对应获得q1的前端元素,时间复杂度是O(1)。 判断栈是否为空操作只需要判断q1是否为空,时间复杂度是 O(1)。
    • 空间复杂度:O(n),其中n是栈内的元素个数。需要使用两个队列存储栈内的元素。
    方法二:一个队列
    思路及算法:

    方法一使用了两个队列实现栈的操作,也可以使用一个队列实现栈的操作。

    使用一个队列时,为了满足栈的特性,即最后入栈的元素最先出栈,同样需要满足队列前端的元素是最后入栈的元素。

    入栈操作时,首先获得入栈前的元素个数 nnn,然后将元素入队到队列,再将队列中的前n个元素(即除了新入栈的元素之外的全部元素)依次出队并入队到队列,此时队列的前端的元素即为新入栈的元素,且队列的前端和后端分别对应栈顶和栈底。

    由于每次入栈操作都确保队列的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除队列的前端元素并返回即可,获得栈顶元素操作只需要获得队列的前端元素并返回即可(不移除元素)。

    由于队列用于存储栈内的元素,判断栈是否为空时,只需要判断队列是否为空即可。

    代码实现:
    1. typedef struct tagListNode {
    2. struct tagListNode* next;
    3. int val;
    4. } ListNode;
    5. typedef struct {
    6. ListNode* top;
    7. } MyStack;
    8. MyStack* myStackCreate() {
    9. MyStack* stk = calloc(1, sizeof(MyStack));
    10. return stk;
    11. }
    12. void myStackPush(MyStack* obj, int x) {
    13. ListNode* node = malloc(sizeof(ListNode));
    14. node->val = x;
    15. node->next = obj->top;
    16. obj->top = node;
    17. }
    18. int myStackPop(MyStack* obj) {
    19. ListNode* node = obj->top;
    20. int val = node->val;
    21. obj->top = node->next;
    22. free(node);
    23. return val;
    24. }
    25. int myStackTop(MyStack* obj) {
    26. return obj->top->val;
    27. }
    28. bool myStackEmpty(MyStack* obj) {
    29. return (obj->top == NULL);
    30. }
    31. void myStackFree(MyStack* obj) {
    32. while (obj->top != NULL) {
    33. ListNode* node = obj->top;
    34. obj->top = obj->top->next;
    35. free(node);
    36. }
    37. free(obj);
    38. }

    复杂度分析

    • 时间复杂度:入栈操作O(n),其余操作都是O(1),其中n是栈内的元素个数。 入栈操作需要将队列中的n个元素出队,并入队n+1个元素到队列,共有2n+1次操作,每次出队和入队操作的时间复杂度都是O(1),因此入栈操作的时间复杂度是O(n)。 出栈操作对应将队列的前端元素出队,时间复杂度是O(1)。获得栈顶元素操作对应获得队列的前端元素,时间复杂度是O(1)。 判断栈是否为空操作只需要判断队列是否为空,时间复杂度是O(1)。
    • 空间复杂度:O(n),其中n是栈内的元素个数。需要使用一个队列存储栈内的元素。

  • 相关阅读:
    2021年12月电子学会图形化二级编程题解析含答案:绘制多边形
    如何使用 ChatGPT 指令大全
    84、Redis客户端-->可视化图形界面工具(Another Redis Desktop Manager)的下载、安装及初步使用
    Chapter 7 XGBoost
    Word控件Spire.Doc 【段落处理】教程(二):C#/VB.NET:在 Word 中设置段落缩进
    RTC 时间、闹钟
    案例实践 | 韩国 AI 金融公司 Qraft 借助 Pulsar 打造超低延迟交易系统
    新一轮智能汽车“硬核”战,头部企业如何决胜下半场?
    消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!
    Kafka中的acks机制——一次由错误资料引发的源码学习
  • 原文地址:https://blog.csdn.net/m0_68662723/article/details/134476228