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

妖狐艹你老母 2024-04-22 04:37 132阅读 0赞

029e4c2ad845498ebf249bfa76c41a5f.png

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

目录

1.题目描述

2.解题思路+代码实现

方法一:两个队列

思路及算法:

代码实现:

方法二:一个队列

思路及算法:

代码实现:


1.题目描述

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

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

实现 MyStack 类:

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

注意:

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

示例:

  1. 输入:
  2. ["MyStack", "push", "push", "top", "pop", "empty"]
  3. [[], [1], [2], [], [], []]
  4. 输出:
  5. [null, null, null, 2, 2, false]
  6. 解释:
  7. MyStack myStack = new MyStack();
  8. myStack.push(1);
  9. myStack.push(2);
  10. myStack.top(); // 返回 2
  11. myStack.pop(); // 返回 2
  12. myStack.empty(); // 返回 False

提示:

  • 1 <= x <= 9
  • 最多调用100pushpoptopempty
  • 每次调用 poptop 都保证栈不为空

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

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是栈内的元素个数。需要使用一个队列存储栈内的元素。

24635f1b99a249a5b6a2488f1e379718.png

发表评论

表情:
评论列表 (有 0 条评论,132人围观)

还没有评论,来说两句吧...

相关阅读