当前位置:   article > 正文

【LeetCode】栈和队列oj专题

【LeetCode】栈和队列oj专题

有不懂的地方一定要看看我往期文章哦!

个人主页小八哥向前冲~_博客

所属专栏:数据结构【c语言】

注意:这些题目实现全部由c语言实现,解决这些问题需要用到栈和队列,如果对c语言实现栈和队列有问题的同学,可以看:队列的实现 和 栈的实现 俩篇文章哦!

目录

配括号问题

循环队列问题

用栈实现队列

用队列实现栈


配括号问题

题目:

详情:【LeetCode】配括号问题

思路:

我们将 '('     '['   '{'统称为左括号,将  ']'   ')'   '}'统称为右括号。

经过观察,这个配括号问题就是一个简单的配对问题。

我们遍历数组,将左括号全部入栈,然后一一出栈和数组中右括号进行配对。

实现代码的时候需要注意几点:

  • 如果数组里面没有左括号,只有右括号,则永远配不成功。
  • 如果出栈配对时,栈里最终还有左括号,也就是说右括号比左括号多,也配不成功。

代码:

  1. typedef char STDateType;
  2. //栈
  3. typedef struct stack
  4. {
  5. STDateType* a;
  6. int top;
  7. int capacity;
  8. }ST;
  9. //栈的初始化和销毁
  10. void STInit(ST* p);
  11. void STDestroy(ST* p);
  12. //入栈,出栈
  13. void STpush(ST* p,STDateType x);
  14. void STpop(ST* p);
  15. //栈顶的数据
  16. STDateType STtop(ST* p);
  17. //栈的数据个数
  18. int STsize(ST* p);
  19. //判空
  20. bool STEmpty(ST* p);
  21. //栈的初始化和销毁
  22. void STInit(ST* p)
  23. {
  24. assert(p);
  25. p->a = NULL;
  26. p->capacity = 0;
  27. p->top = 0;
  28. }
  29. void STDestroy(ST* p)
  30. {
  31. assert(p);
  32. free(p->a);
  33. p->capacity = p->top = 0;
  34. }
  35. //入栈,出栈
  36. void STpush(ST* p, STDateType x)
  37. {
  38. assert(p);
  39. if (p->top == p->capacity)
  40. {
  41. int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
  42. STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
  43. if (tmp == NULL)
  44. {
  45. perror("realloc failed!");
  46. return;
  47. }
  48. p->a = tmp;
  49. p->capacity = newcapacity;
  50. }
  51. p->a[p->top++] = x;
  52. }
  53. void STpop(ST* p)
  54. {
  55. assert(p);
  56. //出栈的话要判断一下空的情况
  57. assert(!STEmpty(p));
  58. p->top--;
  59. }
  60. //栈顶的数据
  61. STDateType STtop(ST* p)
  62. {
  63. assert(p);
  64. //出栈的话要判断一下空的情况
  65. assert(!STEmpty(p));
  66. return p->a[p->top-1];
  67. }
  68. //栈的数据个数
  69. int STsize(ST* p)
  70. {
  71. assert(p);
  72. return p->top;
  73. }
  74. //判空
  75. bool STEmpty(ST* p)
  76. {
  77. assert(p);
  78. return p->top == 0;
  79. }
  80. bool isValid(char* s) {
  81. ST st;
  82. STInit(&st);
  83. while(*s)
  84. {
  85. //左括号入栈
  86. if(*s=='('||*s=='['||*s=='{')
  87. {
  88. STpush(&st,*s);
  89. }
  90. else
  91. {
  92. //如果一个左括号也没有,栈里面就没有数据,那么永远配不成功
  93. if(STEmpty(&st))
  94. {
  95. STDestroy(&st);
  96. return false;
  97. }
  98. char top=STtop(&st);
  99. STpop(&st);
  100. if(top=='('&&*s!=')'
  101. ||top=='['&&*s!=']'
  102. ||top=='{'&&*s!='}')
  103. {
  104. STDestroy(&st);
  105. return false;
  106. }
  107. }
  108. s++;
  109. }
  110. //如果出栈配对后,栈里还有左括号,那么也永远配不成功
  111. bool ret=STEmpty(&st);
  112. STDestroy(&st);
  113. return ret;
  114. }

循环队列问题

题目:

详情:【LeetCode】循环队列的实现

思路:两种方法

方法一:

将这个循环队列看成循环数组。

由于队列的先进先出原则,我们可以定义三个变量:头变量,尾变量,数据个数变量。

我们简单推理知道:我们动态开辟k个数据,进队列,从尾插入数据,入队列,从头删除数据,出队列。

可是又有一个新的问题:如何判断这个循环数组里面是空还是满呢?

上图:

由此可知,我们判断不了满的情况和空的情况。

我们可以这样来解决:

  1. 定义一个size变量,记录数组数据个数。
  2. 多开辟一个数据空间。

我们主要介绍第二种解决方案。

那么如何判断空和满呢?

另外需要注意的是:

在入队列(tail++)时,tail可能会越界,也要取模运算,出队列(head++)时,head也可能越界,也要取模运算。

思路到了这里,还要最后一个问题:怎么访问尾队列数据和头队列数据呢?

我们访问头队列数据很容易,直接访问头变量就行,可是尾呢?

上图:

代码:

  1. typedef struct {
  2. int*a;
  3. int head;
  4. int tail;
  5. int k;
  6. } MyCircularQueue;
  7. MyCircularQueue* myCircularQueueCreate(int k) {
  8. MyCircularQueue*p=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  9. //多开一个空间,解决判满判空冲突问题
  10. p->a=(int*)malloc(sizeof(int)*(k+1));
  11. p->head=p->tail=0;
  12. p->k=k;
  13. return p;
  14. }
  15. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
  16. return obj->head==obj->tail;
  17. }
  18. bool myCircularQueueIsFull(MyCircularQueue* obj) {
  19. return (obj->tail+1)%(obj->k+1)==obj->head;
  20. }
  21. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
  22. if(myCircularQueueIsFull(obj))
  23. {
  24. return false;
  25. }
  26. obj->a[obj->tail]=value;
  27. obj->tail++;
  28. (obj->tail)%=(obj->k+1);
  29. return true;
  30. }
  31. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
  32. if(myCircularQueueIsEmpty(obj))
  33. {
  34. return false;
  35. }
  36. obj->head++;
  37. (obj->head)%=(obj->k+1);
  38. return true;
  39. }
  40. int myCircularQueueFront(MyCircularQueue* obj) {
  41. if(myCircularQueueIsEmpty(obj))
  42. {
  43. return -1;
  44. }
  45. else
  46. return obj->a[obj->head];
  47. }
  48. int myCircularQueueRear(MyCircularQueue* obj) {
  49. if(myCircularQueueIsEmpty(obj))
  50. {
  51. return -1;
  52. }
  53. else
  54. return obj->a[(obj->tail+obj->k)%(obj->k+1)];
  55. //return obj->a[obj->tail==0?k:obj->tail-1];
  56. }
  57. void myCircularQueueFree(MyCircularQueue* obj) {
  58. free(obj->a);
  59. free(obj);
  60. }

方法二

思路:

我们可以将这个循环队列看成循环链表。

定义三个指针变量:尾指针,头指针,尾指针的前一个指针。

进队列,从尾进,尾指针指向下一个,出队列,从头出,头指针指向下一个。

有了上一部分基础,我们判空判满依旧可以多开辟一个空间

为什么要定义尾指针的前一个指针,只定义头指针和尾指针不行吗?

当我们访问尾队列数据时,可以直接用尾指针的前一个指针直接访问就行,省了一个循环找尾队列数据的遍历!

代码:

  1. typedef struct Node
  2. {
  3. struct Node*next;
  4. int val;
  5. }Node;
  6. typedef struct {
  7. Node*phead;
  8. Node*ptail;
  9. Node*preptail;
  10. } MyCircularQueue;
  11. MyCircularQueue* myCircularQueueCreate(int k) {
  12. MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  13. obj->phead=obj->ptail=NULL;
  14. for(int i=0;i<k+1;i++)
  15. {
  16. Node*cur=(Node*)malloc(sizeof(Node));
  17. cur->next=NULL;
  18. if(obj->phead==NULL)
  19. {
  20. obj->phead=obj->ptail=cur;
  21. }
  22. else
  23. {
  24. obj->ptail->next=cur;
  25. obj->ptail=cur;
  26. }
  27. }
  28. obj->ptail->next=obj->phead;
  29. obj->preptail=obj->ptail;
  30. obj->ptail=obj->phead;
  31. return obj;
  32. }
  33. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
  34. return obj->phead==obj->ptail;
  35. }
  36. bool myCircularQueueIsFull(MyCircularQueue* obj) {
  37. return obj->ptail->next==obj->phead;
  38. }
  39. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
  40. if(myCircularQueueIsFull(obj))
  41. {
  42. return false;
  43. }
  44. obj->ptail->val=value;
  45. obj->ptail=obj->ptail->next;
  46. obj->preptail=obj->preptail->next;
  47. return true;
  48. }
  49. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
  50. if(myCircularQueueIsEmpty(obj))
  51. {
  52. return false;
  53. }
  54. obj->phead=obj->phead->next;
  55. return true;
  56. }
  57. int myCircularQueueFront(MyCircularQueue* obj) {
  58. if(myCircularQueueIsEmpty(obj))
  59. {
  60. return -1;
  61. }
  62. return obj->phead->val;
  63. }
  64. int myCircularQueueRear(MyCircularQueue* obj) {
  65. if(myCircularQueueIsEmpty(obj))
  66. {
  67. return -1;
  68. }
  69. return obj->preptail->val;
  70. }
  71. void myCircularQueueFree(MyCircularQueue* obj) {
  72. while(obj->phead!=obj->ptail)
  73. {
  74. Node*next=obj->phead->next;
  75. free(obj->phead);
  76. obj->phead=next;
  77. }
  78. free(obj);
  79. }

用栈实现队列

题目:

详情:【LeetCode】栈实现队列

思路:

我们可以将一个栈专门用来入数据的,另一个专门用来出数据的。

当出数据的栈为空时,还想出数据,将入数据的栈的数据全部导入出数据的栈,再继续出数据即可!

代码:

  1. typedef int STDateType;
  2. //栈
  3. typedef struct stack
  4. {
  5. STDateType* a;
  6. int top;
  7. int capacity;
  8. }ST;
  9. //栈的初始化和销毁
  10. void STInit(ST* p);
  11. void STDestroy(ST* p);
  12. //入栈,出栈
  13. void STpush(ST* p,STDateType x);
  14. void STpop(ST* p);
  15. //栈顶的数据
  16. STDateType STtop(ST* p);
  17. //栈的数据个数
  18. int STsize(ST* p);
  19. //判空
  20. bool STEmpty(ST* p);
  21. //栈的初始化和销毁
  22. void STInit(ST* p)
  23. {
  24. assert(p);
  25. p->a = NULL;
  26. p->capacity = 0;
  27. p->top = 0;
  28. }
  29. void STDestroy(ST* p)
  30. {
  31. assert(p);
  32. free(p->a);
  33. p->capacity = p->top = 0;
  34. }
  35. //入栈,出栈
  36. void STpush(ST* p, STDateType x)
  37. {
  38. assert(p);
  39. if (p->top == p->capacity)
  40. {
  41. int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
  42. STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
  43. if (tmp == NULL)
  44. {
  45. perror("realloc failed!");
  46. return;
  47. }
  48. p->a = tmp;
  49. p->capacity = newcapacity;
  50. }
  51. p->a[p->top++] = x;
  52. }
  53. void STpop(ST* p)
  54. {
  55. assert(p);
  56. //出栈的话要判断一下空的情况
  57. assert(!STEmpty(p));
  58. p->top--;
  59. }
  60. //栈顶的数据
  61. STDateType STtop(ST* p)
  62. {
  63. assert(p);
  64. //出栈的话要判断一下空的情况
  65. assert(!STEmpty(p));
  66. return p->a[p->top-1];
  67. }
  68. //栈的数据个数
  69. int STsize(ST* p)
  70. {
  71. assert(p);
  72. return p->top;
  73. }
  74. //判空
  75. bool STEmpty(ST* p)
  76. {
  77. assert(p);
  78. return p->top == 0;
  79. }
  80. typedef struct {
  81. ST pushst;
  82. ST popst;
  83. } MyQueue;
  84. MyQueue* myQueueCreate() {
  85. MyQueue*p=(MyQueue*)malloc(sizeof(MyQueue));
  86. STInit(&(p->pushst));
  87. STInit(&(p->popst));
  88. return p;
  89. }
  90. void myQueuePush(MyQueue* obj, int x) {
  91. STpush(&(obj->pushst),x);
  92. }
  93. int myQueuePop(MyQueue* obj) {
  94. int top=myQueuePeek(obj);
  95. STpop(&(obj->popst));
  96. return top;
  97. }
  98. int myQueuePeek(MyQueue* obj) {
  99. if(STEmpty(&(obj->popst)))
  100. {
  101. while(!STEmpty(&(obj->pushst)))
  102. {
  103. STpush(&(obj->popst),STtop(&(obj->pushst)));
  104. STpop(&(obj->pushst));
  105. }
  106. }
  107. return STtop(&(obj->popst));
  108. }
  109. bool myQueueEmpty(MyQueue* obj) {
  110. return STEmpty(&(obj->pushst))&&STEmpty(&(obj->popst));
  111. }
  112. void myQueueFree(MyQueue* obj) {
  113. STDestroy(&(obj->pushst));
  114. STDestroy(&(obj->popst));
  115. free(obj);
  116. }

用队列实现栈

题目:

详情:【LeetCode】队列实现栈

思路:

我们可以将判断俩队列哪个为空队列,哪个不为空队列。

进栈时,入数据在不为空的队列里,出栈时,出数据时,需要将不为空队列的数据导入为空队列中去,然后再去出栈!

而判断哪个为空队列,我们可以用假设法!

代码:

  1. typedef int QDataType;
  2. //原则:先进先出
  3. //队列
  4. typedef struct QueueNode
  5. {
  6. struct QueueNode* next;
  7. QDataType val;
  8. }QNode;
  9. //管理队列的一些参数
  10. typedef struct Queue
  11. {
  12. QNode* phead;
  13. QNode* ptail;
  14. int size;//队列中数据的个数
  15. }Queue;
  16. //参数初始化
  17. void QueueInit(Queue* pq);
  18. //入队 队尾入队
  19. void Queuepush(Queue* pq, QDataType x);
  20. //出队 队头出队
  21. void Queuepop(Queue* pq);
  22. //数据个数
  23. int Queuesize(Queue* pq);
  24. //队列判空
  25. bool QueueEmpty(Queue* pq);
  26. //队尾数据
  27. QDataType QueueBack(Queue* pq);
  28. //队头数据
  29. QDataType QueueFront(Queue* pq);
  30. //队列的销毁
  31. void QueueDestroy(Queue* pq);
  32. //参数初始化
  33. void QueueInit(Queue* pq)
  34. {
  35. assert(pq);
  36. pq->phead = pq->ptail = NULL;
  37. pq->size = 0;
  38. }
  39. //入队 队尾入队
  40. void Queuepush(Queue* pq, QDataType x)
  41. {
  42. QNode* node = (QNode*)malloc(sizeof(QNode));
  43. if (node == NULL)
  44. {
  45. perror("malloc failed!");
  46. return;
  47. }
  48. node->next = NULL;
  49. node->val = x;
  50. //入队
  51. if (pq->ptail == NULL)
  52. {
  53. pq->phead = pq->ptail = node;
  54. }
  55. else
  56. {
  57. pq->ptail->next = node;
  58. pq->ptail = node;
  59. }
  60. //插入数据,数据个数增加
  61. pq->size++;
  62. }
  63. //出队 队头出队
  64. void Queuepop(Queue* pq)
  65. {
  66. assert(pq);
  67. //队列不能为空
  68. assert(pq->size > 0);
  69. //当只有一个节点时,出队后,尾指针变成野指针!
  70. if (pq->phead->next == NULL)//一个节点
  71. {
  72. free(pq->phead);
  73. pq->phead = pq->ptail = NULL;
  74. }
  75. else//多个节点
  76. {
  77. QNode* next = pq->phead->next;
  78. free(pq->phead);
  79. pq->phead = next;
  80. }
  81. pq->size--;
  82. }
  83. //队尾数据
  84. QDataType QueueBack(Queue* pq)
  85. {
  86. assert(pq->size > 0);
  87. return pq->ptail->val;
  88. }
  89. //队头数据
  90. QDataType QueueFront(Queue* pq)
  91. {
  92. assert(pq->size != 0);
  93. return pq->phead->val;
  94. }
  95. //数据个数
  96. int Queuesize(Queue* pq)
  97. {
  98. assert(pq);
  99. return pq->size;
  100. }
  101. //队列判空
  102. bool QueueEmpty(Queue* pq)
  103. {
  104. assert(pq);
  105. return pq->size == 0;
  106. }
  107. //队列的销毁
  108. void QueueDestroy(Queue* pq)
  109. {
  110. assert(pq);
  111. QNode* cur = pq->phead;
  112. while (cur)
  113. {
  114. QNode* next = cur->next;
  115. free(cur);
  116. cur = next;
  117. }
  118. pq->phead = pq->ptail = NULL;
  119. pq->size = 0;
  120. cur = NULL;
  121. }
  122. typedef struct {
  123. Queue q1;
  124. Queue q2;
  125. } MyStack;
  126. MyStack* myStackCreate() {
  127. MyStack*p=(MyStack*)malloc(sizeof(MyStack));
  128. QueueInit(&(p->q1));
  129. QueueInit(&(p->q2));
  130. return p;
  131. }
  132. void myStackPush(MyStack* obj, int x) {
  133. if(!QueueEmpty(&(obj->q1)))
  134. {
  135. Queuepush(&(obj->q1),x);
  136. }
  137. else
  138. {
  139. Queuepush(&(obj->q2),x);
  140. }
  141. }
  142. int myStackPop(MyStack* obj) {
  143. Queue*empty=&(obj->q1);
  144. Queue*noempty=&(obj->q2);
  145. if(!QueueEmpty(&(obj->q1)))
  146. {
  147. noempty=&(obj->q1);
  148. empty=&(obj->q2);
  149. }
  150. while(Queuesize(noempty)>1)
  151. {
  152. Queuepush(empty,QueueFront(noempty));
  153. Queuepop(noempty);
  154. }
  155. int front=QueueFront(noempty);
  156. Queuepop(noempty);
  157. return front;
  158. }
  159. int myStackTop(MyStack* obj) {
  160. if(!QueueEmpty(&(obj->q1)))
  161. {
  162. return QueueBack(&(obj->q1));
  163. }
  164. else
  165. {
  166. return QueueBack(&(obj->q2));
  167. }
  168. }
  169. bool myStackEmpty(MyStack* obj) {
  170. return QueueEmpty(&(obj->q1))&&QueueEmpty(&(obj->q2));
  171. }
  172. void myStackFree(MyStack* obj) {
  173. QueueDestroy(&(obj->q1));
  174. QueueDestroy(&(obj->q2));
  175. free(obj);
  176. }

这些题目你都掌握了吗?只要对链表和顺序表很熟悉,解决这种题目还是很轻松的!

我们下期见!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/604583
推荐阅读
相关标签
  

闽ICP备14008679号