史上最强数据结构----栈和队列相关笔试面试题

x33g5p2x  于2022-04-12 转载在 其他  
字(9.7k)|赞(0)|评价(0)|浏览(450)

1. 栈和队列面试题

1.1 括号匹配问题

题目:

思路:

首先将所给的字符串进行遍历,如果是左括号就将它压入栈中,根据栈后进先出的特性,然后逐个取出栈中的左括号与后面剩下的右括号进行逐对进行匹配,如果不匹配就返回false,如果都匹配了就返回true。

例如:

代码:

  1. typedef char STDataType;
  2. typedef struct Stack
  3. {
  4. STDataType* a;
  5. int top;//栈顶的位置
  6. int capacity;//容量
  7. }ST;
  8. void StackInit(ST* ps)
  9. {
  10. assert(ps);
  11. ps->a = NULL;
  12. ps->capacity = 0;
  13. ps->top = 0;
  14. }
  15. void StackDestory(ST*ps)
  16. {
  17. assert(ps);
  18. free(ps->a);
  19. ps->a = NULL;
  20. ps->capacity =ps->top = 0;
  21. }
  22. void StackPush(ST* ps, STDataType x)
  23. {
  24. assert(ps);
  25. if (ps->top == ps->capacity)//满了进行扩容
  26. {
  27. int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
  28. ps->a = (STDataType*)realloc(ps->a,sizeof(STDataType)*newCapacity);
  29. if (ps->a == NULL)
  30. {
  31. printf("fail malloc\n");
  32. exit(-1);
  33. }
  34. ps->capacity = newCapacity;
  35. }
  36. ps->a[ps->top++] = x;
  37. }
  38. void StackPop(ST* ps)
  39. {
  40. assert(ps);
  41. assert(ps->top > 0);
  42. --ps->top;
  43. }
  44. bool StackEmpty(ST* ps)
  45. {
  46. assert(ps);
  47. return ps->top == 0;
  48. }
  49. STDataType StackTop(ST* ps)
  50. {
  51. assert(ps);
  52. assert(ps->top > 0);
  53. return ps->a[ps->top - 1];
  54. }
  55. int SizeStack(ST* ps)
  56. {
  57. assert(ps);
  58. return ps->top;
  59. }
  60. //前面的所有代码就是定义一个栈以及栈的相关函数
  61. bool isValid(char * s){
  62. ST st;
  63. StackInit(&st);
  64. while(*s)
  65. {
  66. if(*s=='['||*s=='('||*s=='{')
  67. {
  68. StackPush(&st,*s);
  69. ++s;
  70. }
  71. else
  72. {
  73. if(StackEmpty(&st))//当所给的字符串中只有右括号时直接返回false
  74. {
  75. StackDestory(&st);
  76. return false;
  77. }
  78. char top = StackTop(&st);//从栈中取一个左括号
  79. StackPop(&st);//进行出栈操作,让刚才取的字符出栈
  80. if((*s==']'&&top!='[')
  81. ||(*s=='}'&&top!='{')
  82. ||(*s==')'&&top!='('))//两个字符不相匹配的情况下直接返回false
  83. {
  84. StackDestory(&st);
  85. return false;
  86. }
  87. else
  88. {
  89. ++s;
  90. }
  91. }
  92. }
  93. //栈为空,说明所有左括号都匹配了
  94. bool ret = StackEmpty(&st);//判断是否只有左括号,如果只有左括号此时ret就为false,如果前面都已经匹配完了ret就等于true
  95. StackDestory(&st);
  96. return ret;
  97. }

1.2 用队列实现栈

解析上面的实例:

输入的第一行是执行的操作。第二行是执行操作的数据。

输出是对应的返回值。

思路:用两个队列实现栈。

栈的基本结构:

1、入栈,push数据到不为空的队列。

2、出栈,把不为空的队列的数据前N-1导入到另一个空队列,最后剩下的一个删掉。

代码:

  1. typedef int QDataType;
  2. typedef struct QueueNode
  3. {
  4. QDataType data;
  5. struct QueueNode* next;
  6. }QNode;
  7. typedef struct Queue
  8. {
  9. QNode* head;
  10. QNode* tail;
  11. }Queue;
  12. void QueueInit(Queue* pq);
  13. void QueueDestory(Queue* pq);
  14. void QueuePush(Queue*pq,QDataType x);
  15. void QueuePop(Queue*pq);
  16. size_t QueueSize(Queue* pq);
  17. QDataType QueueFront(Queue* pq);
  18. QDataType QueueBack(Queue* pq);
  19. bool QueueEmpty(Queue* pq);
  20. void QueueInit(Queue* pq)
  21. {
  22. assert(pq);
  23. pq->head =pq->tail = NULL;
  24. }
  25. QNode* BuyQNode(QDataType x)
  26. {
  27. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  28. if (newnode == NULL)
  29. {
  30. printf("fail malloc\n");
  31. exit(-1);
  32. }
  33. newnode->data = x;
  34. newnode->next = NULL;
  35. return newnode;
  36. }
  37. void QueuePush(Queue* pq, QDataType x)
  38. {
  39. assert(pq);
  40. QNode* newnode = BuyQNode(x);
  41. if (pq->tail == NULL)
  42. {
  43. assert(pq->head == NULL);
  44. pq->head= pq->tail = newnode;
  45. }
  46. else
  47. {
  48. pq->tail->next = newnode;
  49. pq->tail = newnode;
  50. }
  51. }
  52. void QueuePop(Queue* pq)
  53. {
  54. assert(pq);
  55. assert(pq->tail&&pq->head);
  56. if (pq->head->next==NULL)//处理只有一个节点的时候
  57. {
  58. free(pq->tail);
  59. pq->tail = pq->head = NULL;
  60. }
  61. else//有多个节点
  62. {
  63. QNode* next = pq->head->next;
  64. free(pq->head);
  65. pq->head = next;
  66. }
  67. }
  68. size_t QueueSize(Queue* pq)
  69. {
  70. assert(pq);
  71. size_t size = 0;
  72. QNode* cur = pq->head;
  73. while (cur!= pq->tail->next)
  74. {
  75. size++;
  76. cur = cur->next;
  77. }
  78. return size;
  79. }
  80. QDataType QueueFront(Queue* pq)
  81. {
  82. assert(pq);
  83. assert(pq->head);
  84. return pq->head->data;
  85. }
  86. QDataType QueueBack(Queue* pq)
  87. {
  88. assert(pq);
  89. assert(pq->tail);
  90. return pq->tail->data;
  91. }
  92. void QueueDestory(Queue* pq)
  93. {
  94. assert(pq);
  95. QNode* cur = pq->head;
  96. while (cur)
  97. {
  98. QNode* next = cur->next;
  99. free(cur);
  100. cur = next;
  101. }
  102. pq->head = pq->tail = NULL;
  103. }
  104. bool QueueEmpty(Queue* pq)
  105. {
  106. assert(pq);
  107. return pq->head==NULL&&pq->tail==NULL;
  108. }
  109. typedef struct {
  110. Queue q1;
  111. Queue q2;
  112. } MyStack;
  113. MyStack* myStackCreate() {
  114. MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
  115. assert(pst);
  116. QueueInit(&pst->q1);//这一行代码和后面这一行代码等价:QueueInit(&(pst->q1));
  117. QueueInit(&pst->q2);//这一行代码和后面这一行代码等价:QueueInit(&(pst->q2));
  118. return pst;
  119. }
  120. void myStackPush(MyStack* obj, int x) {
  121. assert(obj);
  122. if(!QueueEmpty(&obj->q1))
  123. {
  124. QueuePush(&obj->q1,x);
  125. }
  126. else
  127. {
  128. QueuePush(&obj->q2,x);
  129. }
  130. }
  131. int myStackPop(MyStack* obj) {
  132. assert(obj);
  133. Queue *emptyQ = &obj->q1;
  134. Queue*nonEmptyQ = &obj->q2;
  135. if(!QueueEmpty(&obj->q1))
  136. {
  137. emptyQ = &obj->q2;
  138. nonEmptyQ = &obj->q1;
  139. }
  140. //把非空队列的前N个数据,导入空队列,剩下一个删掉
  141. //就实现了后进先出
  142. while(QueueSize(nonEmptyQ)>1)
  143. {
  144. QueuePush(emptyQ,QueueFront(nonEmptyQ));//将非空队列的队头数据push到非空队列中
  145. QueuePop(nonEmptyQ);//将非空队列的队头数据出队
  146. }
  147. QDataType top = QueueFront(nonEmptyQ);
  148. QueuePop(nonEmptyQ);
  149. return top;
  150. }
  151. int myStackTop(MyStack* obj) {
  152. assert(obj);
  153. if(!QueueEmpty(&obj->q1))
  154. {
  155. return QueueBack(&obj->q1);
  156. }
  157. else
  158. {
  159. return QueueBack(&obj->q2);
  160. }
  161. }
  162. bool myStackEmpty(MyStack* obj) {
  163. assert(obj);
  164. return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
  165. }
  166. void myStackFree(MyStack* obj) {
  167. assert(obj);
  168. QueueDestory(&obj->q1);
  169. QueueDestory(&obj->q2);
  170. free(obj);
  171. }

1.3 用栈实现队列

思路:

注意:在上面的代码中,在进行出队操作时,只要popST这个栈中有数据,那么我们就不进行倒数据的操作(即将push中的数据倒到popST这个栈中),只有当pop中的数据为空且我们要进行出队时才进行倒数据。

队列结构:

代码:

  1. typedef int STDataType;
  2. //数组栈的实现
  3. typedef struct Stack
  4. {
  5. STDataType* a;
  6. int top;//栈顶的位置
  7. int capacity;//容量
  8. }ST;
  9. void StackInit(ST* ps)
  10. {
  11. assert(ps);
  12. ps->a = NULL;
  13. ps->capacity = 0;
  14. ps->top = 0;
  15. }
  16. void StackDestory(ST*ps)
  17. {
  18. assert(ps);
  19. free(ps->a);
  20. ps->a = NULL;
  21. ps->capacity =ps->top = 0;
  22. }
  23. void StackPush(ST* ps, STDataType x)
  24. {
  25. assert(ps);
  26. if (ps->top == ps->capacity)//满了进行扩容
  27. {
  28. int newCapacity = ps->capacity == 0 ? 2 : 2 * ps->capacity;
  29. STDataType*new = (STDataType*)realloc(ps->a,sizeof(STDataType)*newCapacity);
  30. if (new == NULL)
  31. {
  32. printf("fail malloc\n");
  33. exit(-1);
  34. }
  35. ps->a = new;
  36. ps->capacity = newCapacity;
  37. }
  38. ps->a[ps->top++] = x;
  39. }
  40. void StackPop(ST* ps)
  41. {
  42. assert(ps);
  43. assert(ps->top > 0);
  44. --ps->top;
  45. }
  46. bool StackEmpty(ST* ps)
  47. {
  48. assert(ps);
  49. return ps->top == 0;
  50. }
  51. STDataType StackTop(ST* ps)
  52. {
  53. assert(ps);
  54. assert(ps->top > 0);
  55. return ps->a[ps->top - 1];
  56. }
  57. int SizeStack(ST* ps)
  58. {
  59. assert(ps);
  60. return ps->top;
  61. }
  62. void StackInit(ST*ps);
  63. void StackDestory(ST* ps);
  64. void StackPush(ST* ps,STDataType x);
  65. void StackPop(ST* ps);
  66. bool StackEmpty(ST* ps);
  67. int SizeStack(ST* ps);
  68. STDataType StackTop(ST* ps);
  69. typedef struct {
  70. ST pushST;
  71. ST popST;
  72. } MyQueue;
  73. MyQueue* myQueueCreate() {
  74. MyQueue * myQueue = (MyQueue*)malloc(sizeof(MyQueue));
  75. assert(myQueue);
  76. StackInit(&myQueue->pushST);
  77. StackInit(&myQueue->popST);
  78. return myQueue;
  79. }
  80. void myQueuePush(MyQueue* obj, int x) {
  81. assert(obj);
  82. StackPush(&obj->pushST,x);//入队直接向pushST插入即可
  83. }
  84. int myQueuePop(MyQueue* obj){
  85. assert(obj);
  86. if(StackEmpty(&obj->popST))//push为空,就进行倒数据,就符合先进先出的顺序了
  87. {
  88. while(!StackEmpty(&obj->pushST))
  89. {
  90. StackPush(&obj->popST,StackTop(&obj->pushST));
  91. StackPop(&obj->pushST);
  92. }
  93. }
  94. STDataType ret = StackTop(&obj->popST);//临时保存返回的数据
  95. StackPop(&obj->popST);
  96. return ret;
  97. }
  98. int myQueuePeek(MyQueue* obj) {
  99. assert(obj);
  100. if(StackEmpty(&obj->popST))
  101. {
  102. while(!StackEmpty(&obj->pushST))
  103. {
  104. StackPush(&obj->popST,StackTop(&obj->pushST));
  105. StackPop(&obj->pushST);
  106. }
  107. }
  108. return StackTop(&obj->popST);
  109. }
  110. bool myQueueEmpty(MyQueue* obj) {
  111. assert(obj);
  112. return StackEmpty(&obj->pushST)&&StackEmpty(&obj->popST);
  113. }
  114. void myQueueFree(MyQueue* obj) {
  115. assert(obj);
  116. StackDestory(&obj->pushST);
  117. StackDestory(&obj->popST);
  118. free(obj);
  119. }

1.4 设计循环队列

为了避免空和满混淆,无法区分,多开一个空间。

1. front = tail时是空

2. tail下一个位置是front时是满

满的两种情况:

1、obj->tail = k && obj->head = 0

2、obj->tail+1 = obj->head

代码:

  1. typedef struct {
  2. int *a;
  3. int head;//循环队列的头
  4. int tail;//循环队列的尾
  5. int capacity;//循环队列元素的最大数目
  6. } MyCircularQueue;
  7. bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空的声明
  8. bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满的声明
  9. MyCircularQueue* myCircularQueueCreate(int k) //循环队列的初始化
  10. {
  11. MyCircularQueue*myCircularQ = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  12. assert(myCircularQ);
  13. int *tmp = (int*)malloc(sizeof(int)*(k+1));
  14. assert(tmp);
  15. myCircularQ->a = tmp;
  16. myCircularQ->head = 0;
  17. myCircularQ->tail = 0;
  18. myCircularQ->capacity = k;
  19. return myCircularQ;
  20. }
  21. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //向循环队列插入一个元素,如果成功插入则返回真。
  22. {
  23. assert(obj);
  24. if(myCircularQueueIsFull(obj))//满了的情况
  25. {
  26. return false;
  27. }
  28. obj->a[obj->tail] = value;
  29. if(obj->tail==obj->capacity)//此时已经到达了开辟数组的最后一个位置,tail再进行++操作后会跳跃到第一个位置
  30. {
  31. obj->tail = 0;
  32. }
  33. else
  34. {
  35. ++obj->tail;
  36. }
  37. return true;
  38. }
  39. bool myCircularQueueDeQueue(MyCircularQueue* obj) //从循环队列中删除一个元素,如果成功删除则返回真。
  40. {
  41. assert(obj);
  42. if(myCircularQueueIsEmpty(obj))//循环队列中为空的情况
  43. {
  44. return false;
  45. }
  46. if(obj->head==obj->capacity)//循环队列中的head在开辟的最后的一个空间位置的情况,head要发生跳跃
  47. {
  48. obj->head = 0;
  49. }
  50. else
  51. {
  52. ++obj->head;
  53. }
  54. return true;
  55. }
  56. int myCircularQueueFront(MyCircularQueue* obj) //从队首获取元素,如果队列为空,返回 -1 。
  57. {
  58. assert(obj);
  59. if(myCircularQueueIsEmpty(obj))//队列元素为空的情况
  60. return -1;
  61. return obj->a[obj->head];
  62. }
  63. int myCircularQueueRear(MyCircularQueue* obj)//获取队尾元素,如果队列为空,返回 -1 。
  64. {
  65. assert(obj);
  66. if(myCircularQueueIsEmpty(obj))//循环队列元素为空的情况
  67. return -1;
  68. if(obj->tail==0)//尾在头的时候,就要返回最后一个空间的位置
  69. {
  70. return obj->a[obj->capacity];
  71. }
  72. else
  73. {
  74. return obj->a[obj->tail-1];//正常情况返回tail的前一个位置
  75. }
  76. }
  77. bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断循环队列是否满
  78. {
  79. assert(obj);
  80. return obj->tail==obj->head;//尾下标等于头下标的时候就是空
  81. }
  82. bool myCircularQueueIsFull(MyCircularQueue* obj) //判断循环队列是否满
  83. {
  84. assert(obj);
  85. if(obj->tail==obj->capacity && obj->head==0)//判断的是尾下标指向最后一块空间,头下标在最靠前的空间
  86. {
  87. return true;
  88. }
  89. else
  90. {
  91. return obj->tail+1 ==obj->head;
  92. }
  93. }
  94. void myCircularQueueFree(MyCircularQueue* obj) //循环队列的销毁
  95. {
  96. assert(obj);
  97. free(obj->a);
  98. free(obj);
  99. }

2. 概念选择题

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出 栈的顺序是(B)。

A 12345ABCDE

B EDCBA54321

C ABCDE12345

D 54321EDCBA

2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C

A 1,4,3,2

B 2,3,4,1

C 3,1,4,2

D 3,4,2,1

解析:C选项中,3出来之后要想出1的话,就必须先出2。

3.循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作 后, front=rear=99 ,则循环队列中的元素个数为( D)

A 1

B 2

C 99

D 0

解析:由前面循环队列的实现,很容易就得知此时元素的个数为0。

4.以下( )不是队列的基本运算? (B)

A 从队尾插入一个新元素

B 从队列中删除第i个元素

C 判断一个队列是否为空

D 读取队头元素的值

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N,实际最多存储N - 1个数据。其队内有效长度为(B)(假设队头不存放数据)

A (rear - front + N) % N + 1

B (rear - front + N) % N

C ear - front) % (N + 1)

D (rear - front + N) % (N - 1)

解析:此题要考虑两种情况:

第一种情况:front在rear的前面

第二种情况:front在rear的后面

注意:从最后绕到了最前面其实就相当于在最后面越界继续相加!如下图所示:

综合上面两种情况:最终公式为:

count = (rear - front + N ) % N (为什么第一种也加了N?因为对于front在rear前面的情况来说,是否加N对结果没有任何的影响,因为还要对N进行取模操作)

3. 栈和队列的用途

栈:用来解决括号匹配,逆波兰表达式的求解,递归改非递归等等。

队列:公平排队,广度优先遍历等等。

相关文章