当前位置:   article > 正文

数据结构之栈与队列

数据结构之栈与队列

一.栈

1.定义:一种线性表,只允许在固定的一端进行删除和插入数据的操作,该端叫栈底,另一端叫栈顶

2.特点:先进后出

注:栈中元素出栈是一对多的(他虽然满足先进后出但是我们可以在pop数据前先获取栈顶元素并打印再pop,这样就可能改变数据在屏幕上出现的顺序)

3.思路:可以用数组实现,也可以用链表实现

数组实现更简单,链表实现较为复杂,下面小编将从两方面解释不选择链表实现的原因

1>若选择单链表,在进行删除操作时,我们不好解决栈顶先前移一位的问题

2>若选择双向链表,将占用过多的内存

4.代码实现(以数组实现栈为例):

(1)栈的结构体定义:包含数组指针,栈顶的位置,有效空间容量

  1. typedef int STDatatype;
  2. typedef struct Stack
  3. {
  4. STDatatype* arr;
  5. int top;//栈顶
  6. int capacity;//有效空间容量
  7. }ST;

(2)栈的初始化:将栈顶初始化为0:表示栈顶指向最后一个数据的下一个

                          将栈顶初始化为-1:表示栈顶指向最后一个数据所在位置

  1. //栈的初始化
  2. void STInit(ST* ps)
  3. {
  4. assert(ps);
  5. ps->arr = NULL;
  6. //top指向栈顶后的一个元素,此时top等值于有效数据个数
  7. ps->top = 0;
  8. ps->capacity = 0;
  9. }

(3)栈的销毁

  1. //栈的销毁
  2. void STDestory(ST* ps)
  3. {
  4. assert(ps);
  5. free(ps->arr);
  6. ps->arr = NULL;
  7. ps->capacity = ps->top = 0;
  8. }

(4)插入数据/入栈:先判断是否需要增容,再插入数据

  1. //入栈
  2. void STPush(ST* ps, STDatatype x)
  3. {
  4. assert(ps);
  5. //判断是否需要增容
  6. if (ps->top == ps->capacity)
  7. {
  8. int newcapacity = ps->capacity * 2==0 ? 4 : ps->capacity*2;
  9. STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * newcapacity);
  10. if (tmp == NULL)
  11. {
  12. perror("realloc fail");
  13. return;
  14. }
  15. ps->arr = tmp;
  16. ps->capacity = newcapacity;
  17. }
  18. ps->arr[ps->top] = x;
  19. ps->top++;
  20. }

(5)删除数据/出栈:先判断数组是否为空,再将top--即完成删除

  1. //出栈
  2. void STPop(ST* ps)
  3. {
  4. assert(ps);
  5. assert(ps->top > 0);
  6. ps->top--;
  7. }

(6)获取栈顶元素

  1. //获取栈顶数据
  2. STDatatype STTop(ST* ps)
  3. {
  4. assert(ps);
  5. return ps->arr[ps->top-1];
  6. }

(7)判断栈是否为空:若top==0则为空

  1. //判断栈是否为空
  2. bool STEmpty(ST* ps)
  3. {
  4. assert(ps);
  5. return ps->top == 0;
  6. }

(8)栈中有效数据个数

  1. //栈的有效数据个数
  2. int STSize(ST* ps)
  3. {
  4. assert(ps);
  5. return ps->top;
  6. }

二.队列

1.定义:一种线性表,只允许在一端(队尾)进行数据插入,在另一端(队头)进行数据删除

2.特点:先进先出

注:队列的数据出队列时是一对一的

3.思路:可以用数组实现,也可以用链表实现 

单链表实现更简单,数组实现较为复杂,是因为在使用数组实现时,进行删除数据不好记录下一次该删除数据的位置

4.代码实现(以单链表实现队列为例):

(1)队列的节点定义:指向下一个节点的指针,存储数据的数值

    队列的结构体定义:队列的头指针,尾指针,队列有效数据的个数

  1. typedef int QueueDataType;
  2. typedef struct QueueNode
  3. {
  4. QueueDataType val;
  5. struct QueueNode* next;
  6. }QNode;
  7. typedef struct Queue
  8. {
  9. QNode* head;
  10. QNode* tail;
  11. int size;
  12. }Queue;

(2)队列的初始化

  1. //队列的初始化
  2. void QueueInit(Queue* pq)
  3. {
  4. assert(pq);
  5. pq->head = NULL;
  6. pq->tail = NULL;
  7. pq->size = 0;
  8. }

(3)队列的销毁:先逐个销毁队列的节点,再销毁队列

  1. //队列的销毁
  2. void QueueDestory(Queue* pq)
  3. {
  4. assert(pq);
  5. QNode* cur = pq->head;
  6. while (cur)
  7. {
  8. QNode* Next = cur->next;
  9. free(cur);
  10. cur = Next;
  11. }
  12. pq->head = NULL;
  13. pq->tail = NULL;
  14. pq->size = 0;
  15. }

(4)插入数据:为该数据开辟一个新节点,若原队列中有数据则让尾指针向后走一步,若无则头尾    指针均指向该新节点

  1. //队尾入数据
  2. void QueuePush(Queue* pq, QueueDataType x)
  3. {
  4. //开辟一个新节点
  5. QNode* node = (QNode*)malloc(sizeof(QNode));
  6. if (node == NULL)
  7. {
  8. perror("malloc fail");
  9. return;
  10. }
  11. node->val = x;
  12. node->next = NULL;
  13. //队列中没有节点
  14. if (pq->tail == NULL)
  15. {
  16. pq->head = node;
  17. pq->tail = node;
  18. }
  19. else
  20. {
  21. pq->tail->next = node;
  22. pq->tail = node;
  23. }
  24. pq->size++;
  25. }

(5)删除数据:删除头指针指向的节点,并让头指针向后走一步,若只有一个节点删除后需将尾指针置空

  1. //队头出数据
  2. void QueuePop(Queue* pq)
  3. {
  4. assert(pq);
  5. assert(pq->size != 0);
  6. QNode* next = pq->head->next;
  7. free(pq->head);
  8. pq->head = next;
  9. if (pq->head == NULL)
  10. {
  11. pq->tail = NULL;
  12. }
  13. pq->size--;
  14. }

(6)获取队头元素

  1. //获取队头数据
  2. QueueDataType QueueFront(Queue* pq)
  3. {
  4. assert(pq);
  5. return pq->head->val;
  6. }

(7)获取队尾元素

  1. //获取队尾数据
  2. QueueDataType QueueBack(Queue* pq)
  3. {
  4. assert(pq);
  5. return pq->tail->val;
  6. }

(8)判断队列是否为空

  1. //判断队列是否为空
  2. bool QueueEmpty(Queue* pq)
  3. {
  4. assert(pq);
  5. return pq->size == 0;
  6. }

(9)队列的大小

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

三.栈与队列的相互转化

1.用栈实现队列

. - 力扣(LeetCode)

(1)思路:栈:先进后出,队列:先进先出

               创建两个栈实现队列中数据的删除

(2)解题方法:

1>自己实现栈

2>定义一个队列结构体包含两个栈

3>队列的创建:为队列结构体malloc一块空间,防止出函数时结构体被销毁

4>队列的销毁:先销毁两个栈,再销毁队列

5>队尾插数据:当两栈均为空栈时:随便在哪个空栈的栈顶插入数据均可

                          当有一个栈不为空时:在非空栈的栈顶插入数据

6>队头删数据:将非空栈的前size-1个数据导入空栈中,再删除队头的数据。此时对下次删除来说栈中数据顺序是错乱的,故还需要将原空栈的数据导回原非空栈中。

7>判断队列为空:当两个栈均为空时,队列为空

8>获取队尾数据:即非空栈的栈顶数据

(3)代码实现:

  1. typedef int STDatatype;
  2. typedef struct Stack
  3. {
  4. STDatatype* arr;
  5. int top;//栈顶
  6. int capacity;//有效空间容量
  7. }ST;
  8. //栈的初始化
  9. void STInit(ST* ps);
  10. //栈的销毁
  11. void STDestory(ST* ps);
  12. //入栈
  13. void STPush(ST* ps, STDatatype x);
  14. //出栈
  15. void STPop(ST* ps);
  16. //获取栈顶数据
  17. STDatatype STTop(ST* ps);
  18. //判断栈是否为空
  19. bool STEmpty(ST* ps);
  20. //栈的有效数据个数
  21. int STSize(ST* ps);
  22. //栈的初始化
  23. void STInit(ST* ps)
  24. {
  25. assert(ps);
  26. ps->arr = NULL;
  27. //top指向栈顶后的一个元素,此时top等值于有效数据个数
  28. ps->top = 0;
  29. ps->capacity = 0;
  30. }
  31. //栈的销毁
  32. void STDestory(ST* ps)
  33. {
  34. assert(ps);
  35. free(ps->arr);
  36. ps->arr = NULL;
  37. ps->capacity = ps->top = 0;
  38. }
  39. //入栈
  40. void STPush(ST* ps, STDatatype x)
  41. {
  42. assert(ps);
  43. //判断是否需要增容
  44. if (ps->top == ps->capacity)
  45. {
  46. int newcapacity = ps->capacity * 2==0 ? 4 : ps->capacity*2;
  47. STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * newcapacity);
  48. if (tmp == NULL)
  49. {
  50. perror("realloc fail");
  51. return;
  52. }
  53. ps->arr = tmp;
  54. ps->capacity = newcapacity;
  55. }
  56. ps->arr[ps->top] = x;
  57. ps->top++;
  58. }
  59. //出栈
  60. void STPop(ST* ps)
  61. {
  62. assert(ps);
  63. assert(ps->top > 0);
  64. ps->top--;
  65. }
  66. //获取栈顶数据
  67. STDatatype STTop(ST* ps)
  68. {
  69. assert(ps);
  70. return ps->arr[ps->top-1];
  71. }
  72. //判断栈是否为空
  73. bool STEmpty(ST* ps)
  74. {
  75. assert(ps);
  76. return ps->top == 0;
  77. }
  78. //栈的有效数据个数
  79. int STSize(ST* ps)
  80. {
  81. assert(ps);
  82. return ps->top;
  83. }
  84. typedef struct {
  85. ST st1;
  86. ST st2;
  87. } MyQueue;
  88. MyQueue* myQueueCreate() {
  89. MyQueue* pq=(MyQueue*)malloc(sizeof(MyQueue));
  90. STInit(&(pq->st1));
  91. STInit(&(pq->st2));
  92. return pq;
  93. }
  94. void myQueuePush(MyQueue* obj, int x) {
  95. assert(obj);
  96. if(!STEmpty(&(obj->st1)))
  97. {
  98. STPush(&(obj->st1),x);
  99. }
  100. else
  101. {
  102. STPush(&(obj->st2),x);
  103. }
  104. }
  105. int myQueuePop(MyQueue* obj) {
  106. assert(obj);
  107. ST* empty=&(obj->st1);
  108. ST* noempty=&(obj->st2);
  109. if(!STEmpty(&(obj->st1)))
  110. {
  111. empty=&(obj->st2);
  112. noempty=&(obj->st1);
  113. }
  114. while(STSize(noempty)>1)
  115. {
  116. STPush(empty,STTop(noempty));
  117. STPop(noempty);
  118. }
  119. STDatatype ret=STTop(noempty);
  120. STPop(noempty);
  121. //将noempty的数据导回empty,保证下次pop时数据顺序
  122. while(STSize(empty)>0)
  123. {
  124. STPush(noempty,STTop(empty));
  125. STPop(empty);
  126. }
  127. return ret;
  128. }
  129. int myQueuePeek(MyQueue* obj) {
  130. if(!STEmpty(&(obj->st1)))
  131. {
  132. return obj->st1.arr[0];
  133. }
  134. else
  135. {
  136. return obj->st2.arr[0];
  137. }
  138. }
  139. bool myQueueEmpty(MyQueue* obj) {
  140. return STEmpty(&(obj->st1)) && STEmpty(&(obj->st2));
  141. }
  142. void myQueueFree(MyQueue* obj) {
  143. assert(obj);
  144. STDestory(&(obj->st1));
  145. STDestory(&(obj->st2));
  146. free(obj);
  147. obj=NULL;
  148. }

2.用队列实现栈

. - 力扣(LeetCode)

(1)思路:栈:先进后出,队列:先进先出

               创建两个队列实现栈中数据的删除

(2)解题方法:

1>自己实现队列

2>定义一个栈结构体包含两个队列

3>栈的创建:为栈结构体malloc一块空间,防止出函数时结构体被销毁

4>栈的销毁:先销毁两个队列,再销毁栈

5>入栈:当两队列均为空队列时:随便在哪个空队列的队尾插入数据均可

                          当有一个队列不为空时:在非空队列的队尾插入数据

6>出栈:将非空队列的前size-1个数据导入空队列中,再删除栈顶元素。

7>判断栈为空:当两个队列均为空时,栈为空

8>获取栈顶数据:即非空队列的队尾元素

(3)代码实现:

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

四.循环队列

. - 力扣(LeetCode)

1.定义:一种线性结构,基于先进先出原则,将队尾与队头连接起来构成循环

2.特点:有限空间,保证先进先出,从重复使用

3.思路:可以用数组实现,也可以用链表实现

用数组实现较为简单,用链表实现比较困难,下面小编将从两个角度来解释原因:

1>若使用单链表,在删除数据时不好获取待删除数据的前一个节点的位置,无法实现队尾的改变

2>若使用双向链表,占用内存太大,而且此时在判空判满时需要比较两个节点的地址比较麻烦

4.代码实现(以数组实现为例):

(1)循环队列的结构体定义:数组指针,队头的下标,队尾下一个数据的下标,空间的大小

  1. typedef struct {
  2. int* a;
  3. int head;//指向头
  4. int rear;//指向尾的下一个
  5. int k;//空间大小
  6. } MyCircularQueue;

(2)循环队列的创建:

1>为循环队列malloc一块空间,防止出函数时,结构体被销毁

2>由于rear指向队尾的下一个数据,若直接开辟指定大小的空间,在插入最后一个数据时,rear会越界访问,此时我们称该情况为假溢出现象,为了解决假溢出我们可以采取以下两种措施

【1】为数组多开一块空间

【2】

  1. MyCircularQueue* myCircularQueueCreate(int k) {
  2. MyCircularQueue* pc=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  3. //多开一块空间,解决假溢出问题
  4. pc->a=(int*)malloc(sizeof(int)*(k+1));
  5. pc->head=0;
  6. pc->rear=0;
  7. pc->k=k;
  8. return pc;
  9. }

(3)循环队列的销毁:先销毁为数组开辟的空间,再销毁循环队列

  1. void myCircularQueueFree(MyCircularQueue* obj) {
  2. free(obj->a);
  3. obj->a=NULL;
  4. free(obj);
  5. obj=NULL;
  6. }

(4)循环队列的判空:

  1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
  2. assert(obj);
  3. //头与尾的下一个指向同一位置即为空
  4. return obj->head==obj->rear;
  5. }

(5)循环队列的判满:

  1. bool myCircularQueueIsFull(MyCircularQueue* obj) {
  2. assert(obj);
  3. //尾的下一个和头指向的位置相同即为满
  4. return obj->head==(obj->rear+1)%(obj->k+1);
  5. }

(6)循环队列插入数据:在队尾插入数据,让rear向后走一步,为了避免rear++后越界访问,需要将自增后的rear模上k+1,使其在有效下标内且可以完成循环

  1. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
  2. assert(obj);
  3. if(myCircularQueueIsFull(obj))
  4. {
  5. return false;
  6. }
  7. obj->a[obj->rear]=value;
  8. obj->rear++;
  9. obj->rear%=(obj->k+1);
  10. return true;
  11. }

(7)循环队列删除数据:删除队头数据,让head向后走一步,为了避免head++后越界访问,需要将自增后的head模上k+1,使其在有效下标内且可以完成循环(原理同上,就不再赘述了)

  1. ool myCircularQueueDeQueue(MyCircularQueue* obj) {
  2. assert(obj);
  3. if(myCircularQueueIsEmpty(obj))
  4. {
  5. return false;
  6. }
  7. obj->head++;
  8. obj->head%=(obj->k+1);
  9. return true;
  10. }

(8)获取队尾元素:

  1. int myCircularQueueRear(MyCircularQueue* obj) {
  2. if(myCircularQueueIsEmpty(obj))
  3. {
  4. return -1;
  5. }
  6. else
  7. {
  8. return obj->rear==0?obj->a[obj->k]:obj->a[obj->rear-1];
  9. }
  10. }

(9)获取队头元素:

  1. int myCircularQueueFront(MyCircularQueue* obj) {
  2. if(myCircularQueueIsEmpty(obj))
  3. {
  4. return -1;
  5. }
  6. else
  7. {
  8. return obj->a[obj->head];
  9. }
  10. }

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

闽ICP备14008679号