• 【数据结构与算法】<==>栈和队列的OJ题


    作者:旧梦拾遗186

    专栏:数据结构成长日记

     

    目录

    用队列实现栈

    用栈实现队列

    循环队列设计

    循环队列的概念选择题


    用队列实现栈

     

    用队列实现栈https://leetcode.cn/problems/implement-stack-using-queues/

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

    实现 MyStack 类:

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

    注意:

    你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 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 次 push、pop、top 和 empty
    每次调用 pop 和 top 都保证栈不为空
     

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

    解答

    题目的意思其实很简单:就是让你用两个队列去模拟实现一个栈

    思路:思路其实很简单,我们知道队列是先进先出,而栈是后进先出,两个最大的不同在于顺序:

    我们假设在队列入了1,2,3,4那么现在要在队列中出一个元素,那就是1
    如果在栈中入了1,2,3,4那么现在要在栈中出一个元素,那就是4

    我们该怎么改变这种顺序呢?很简单,让有元素队列把前n-1个导入到空的队列,此时取出剩下的元素就是符合栈顺序的元素,我们不妨来画个图:

    1. #include
    2. #include
    3. #include
    4. typedef int QDataType;
    5. typedef struct QueueNode
    6. {
    7. QDataType data;
    8. struct QueueNode*next;
    9. }QNode;
    10. typedef struct Queue{
    11. QNode*head;
    12. QNode*tail;
    13. int size;
    14. }Queue;
    15. void QueueInit(Queue*pq);
    16. void QueueDestroy(Queue*pq);
    17. void QueuePush(Queue*pq,QDataType x);
    18. void QueuePop(Queue*pq);
    19. QDataType QueueFront(Queue*pq);
    20. QDataType QueueBack(Queue*pq);
    21. bool QueueEmpty(Queue*pq);
    22. QDataType QueueSize(Queue*pq);
    23. void QueueInit(Queue*pq)
    24. {
    25. assert(pq);
    26. pq->head = pq->tail = NULL;
    27. pq->size = 0;
    28. }
    29. void QueueDestroy(Queue*pq)
    30. {
    31. assert(pq);
    32. QNode*cur = pq->head;
    33. while(cur)
    34. {
    35. QNode*del = cur;
    36. cur = cur->next;
    37. free(del);
    38. }
    39. pq->head = pq->tail = NULL;
    40. }
    41. void QueuePush(Queue*pq,QDataType x)
    42. {
    43. assert(pq);
    44. QNode*newnode = (QNode*)malloc(sizeof(QNode));
    45. if(NULL == newnode)
    46. {
    47. exit(-1);
    48. }
    49. else
    50. {
    51. newnode->data = x;
    52. newnode->next = NULL;
    53. }
    54. if(pq->tail == NULL)
    55. {
    56. pq->head = pq->tail = newnode;
    57. }
    58. else
    59. {
    60. pq->tail->next = newnode;
    61. pq->tail = newnode;
    62. }
    63. pq->size++;
    64. }
    65. void QueuePop(Queue*pq)
    66. {
    67. assert(pq);
    68. assert(!QueueEmpty(pq));
    69. if(pq->head->next == NULL)
    70. {
    71. free(pq->head);
    72. pq->head = pq->tail = NULL;
    73. }
    74. else
    75. {
    76. QNode*del = pq->head;
    77. pq->head = pq->head->next;
    78. free(del);
    79. del = NULL;
    80. }
    81. pq->size--;
    82. }
    83. QDataType QueueFront(Queue*pq)
    84. {
    85. assert(pq);
    86. assert(!QueueEmpty(pq));
    87. return pq->head->data;
    88. }
    89. QDataType QueueBack(Queue*pq)
    90. {
    91. assert(pq);
    92. assert(!QueueEmpty(pq));
    93. return pq->tail->data;
    94. }
    95. bool QueueEmpty(Queue*pq)
    96. {
    97. assert(pq);
    98. return pq->head == NULL&&pq->tail == NULL;
    99. }
    100. QDataType QueueSize(Queue*pq)
    101. {
    102. return pq->size;
    103. }
    104. typedef struct {
    105. Queue q1;
    106. Queue q2;
    107. } MyStack;
    108. MyStack* myStackCreate() {
    109. MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    110. if(NULL==obj)
    111. {
    112. exit(-1);
    113. }
    114. QueueInit(&obj->q1);
    115. QueueInit(&obj->q2);
    116. return obj;
    117. }
    118. void myStackPush(MyStack* obj, int x) {
    119. if(!QueueEmpty(&obj->q1))
    120. {
    121. QueuePush(&obj->q1,x);
    122. }
    123. else
    124. {
    125. QueuePush(&obj->q2,x);
    126. }
    127. }
    128. int myStackPop(MyStack* obj) {
    129. Queue* empty=&obj->q1;
    130. Queue* nonEmpty=&obj->q2;
    131. if(!QueueEmpty(&obj->q1))
    132. {
    133. empty=&obj->q2;
    134. nonEmpty=&obj->q1;
    135. }
    136. while(QueueSize(nonEmpty)>1)
    137. {
    138. QueuePush(empty,QueueFront(nonEmpty));
    139. QueuePop(nonEmpty);
    140. }
    141. int top=QueueFront(nonEmpty);
    142. QueuePop(nonEmpty);
    143. return top;
    144. }
    145. int myStackTop(MyStack* obj) {
    146. if(!QueueEmpty(&obj->q1))
    147. {
    148. return QueueBack(&obj->q1);
    149. }
    150. else
    151. {
    152. return QueueBack(&obj->q2);
    153. }
    154. }
    155. bool myStackEmpty(MyStack* obj) {
    156. return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
    157. }
    158. void myStackFree(MyStack* obj) {
    159. QueueDestroy(&obj->q1);
    160. QueueDestroy(&obj->q2);
    161. }
    162. /**
    163. * Your MyStack struct will be instantiated and called as such:
    164. * MyStack* obj = myStackCreate();
    165. * myStackPush(obj, x);
    166. * int param_2 = myStackPop(obj);
    167. * int param_3 = myStackTop(obj);
    168. * bool param_4 = myStackEmpty(obj);
    169. * myStackFree(obj);
    170. */

     

    用栈实现队列

    用栈实现队列https://leetcode.cn/problems/implement-queue-using-stacks/

    请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

    实现 MyQueue 类:

    void push(int x) 将元素 x 推到队列的末尾
    int pop() 从队列的开头移除并返回元素
    int peek() 返回队列开头的元素
    boolean empty() 如果队列为空,返回 true ;否则,返回 false
    说明:

    你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
    你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
     

    示例 1:

    输入:
    ["MyQueue", "push", "push", "peek", "pop", "empty"]
    [[], [1], [2], [], [], []]
    输出:
    [null, null, null, 1, 1, false]

    解释:
    MyQueue myQueue = new MyQueue();
    myQueue.push(1); // queue is: [1]
    myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
    myQueue.peek(); // return 1
    myQueue.pop(); // return 1, queue is [2]
    myQueue.empty(); // return false
     

    提示:

    1 <= x <= 9
    最多调用 100 次 push、pop、peek 和 empty
    假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)
     

    进阶:

    你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

    题解

    这道题和上面的那道题是相反的,这里要让你用两个栈去实现队列,思路也是差不多的,但是对于两个栈来说,就存在一些差别了:我们还是以入栈1、2、3、4来举例子:

    只要导一次就可以了,这跟上边的题不一样,倒过去后顺序的相反的。 

     所以两个栈我们可以一个为PopST进行出数据的操作,出空了,再去另一个栈PushST将数据倒过来,仔细琢磨琢磨

    1. #include
    2. #include
    3. #include
    4. #include
    5. //手撸一个栈
    6. typedef int STDataType;
    7. typedef struct Stack
    8. {
    9. STDataType*a;
    10. int top;
    11. int capacity;
    12. }ST;
    13. void StackInit(ST* ps);
    14. void StackDestory(ST* ps);
    15. void StackPush(ST* ps,STDataType x);
    16. void StackPop(ST* ps);
    17. STDataType StackTop(ST* ps);
    18. bool StackEmpty(ST*ps);
    19. int StackSize(ST* ps);
    20. void StackInit(ST* ps)
    21. {
    22. assert(ps);
    23. ps->a = NULL;
    24. ps->capacity = ps->top = 0;
    25. }
    26. void StackDestory(ST* ps)
    27. {
    28. assert(ps);
    29. free(ps->a);
    30. ps->a = NULL;
    31. ps->capacity = ps->top = 0;
    32. }
    33. void StackPush(ST* ps, STDataType x)
    34. {
    35. assert(ps);
    36. if (ps->top == ps->capacity)
    37. {
    38. int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    39. STDataType* tmp = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
    40. if (NULL == tmp)
    41. {
    42. perror("malloc fail");
    43. exit(-1);
    44. }
    45. ps->a = tmp;
    46. ps->capacity = newCapacity;
    47. }
    48. ps->a[ps->top] = x;
    49. ps->top++;
    50. }
    51. void StackPop(ST* ps)
    52. {
    53. assert(ps);
    54. assert(!StackEmpty(ps));
    55. ps->top--;
    56. }
    57. STDataType StackTop(ST* ps)
    58. {
    59. assert(ps);
    60. assert(!StackEmpty(ps));
    61. return ps->a[ps->top - 1];
    62. }
    63. bool StackEmpty(ST* ps)
    64. {
    65. assert(ps);
    66. return ps->top == 0;
    67. }
    68. int StackSize(ST* ps)
    69. {
    70. assert(ps);
    71. return ps->top;
    72. }
    73. typedef struct {
    74. ST pushST;
    75. ST popST;
    76. } MyQueue;
    77. MyQueue* myQueueCreate() {
    78. MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    79. StackInit(&obj->pushST);
    80. StackInit(&obj->popST);
    81. return obj;
    82. }
    83. //入队
    84. void myQueuePush(MyQueue* obj, int x) {
    85. //有数据就直接进入pushST栈
    86. StackPush(&obj->pushST,x);
    87. }
    88. void pushSTtopopST(MyQueue* obj)
    89. {
    90. //先把popST栈的数据出栈,如果没有数据就让pushST栈的数据进入
    91. if(StackEmpty(&obj->popST))
    92. {
    93. while(!StackEmpty(&obj->pushST))
    94. {
    95. StackPush(&obj->popST,StackTop(&obj->pushST));
    96. StackPop(&obj->pushST);
    97. }
    98. }
    99. }
    100. //出队
    101. int myQueuePop(MyQueue* obj) {
    102. pushSTtopopST(obj);
    103. int front=StackTop(&obj->popST);
    104. StackPop(&obj->popST);
    105. return front;
    106. }
    107. //取队头元素
    108. int myQueuePeek(MyQueue* obj) {
    109. pushSTtopopST(obj);
    110. return StackTop(&obj->popST);
    111. }
    112. bool myQueueEmpty(MyQueue* obj) {
    113. return StackEmpty(&obj->pushST)&&StackEmpty(&obj->popST);
    114. }
    115. void myQueueFree(MyQueue* obj) {
    116. StackDestory(&obj->popST);
    117. StackDestory(&obj->pushST);
    118. }
    119. /**
    120. * Your MyQueue struct will be instantiated and called as such:
    121. * MyQueue* obj = myQueueCreate();
    122. * myQueuePush(obj, x);
    123. * int param_2 = myQueuePop(obj);
    124. * int param_3 = myQueuePeek(obj);
    125. * bool param_4 = myQueueEmpty(obj);
    126. * myQueueFree(obj);
    127. */

     

    循环队列设计

    另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型
    时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

     

    设计循环队列icon-default.png?t=M666https://leetcode.cn/problems/design-circular-queue/

     

    设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

    循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

    你的实现应该支持如下操作:

    MyCircularQueue(k): 构造器,设置队列长度为 k 。
    Front: 从队首获取元素。如果队列为空,返回 -1 。
    Rear: 获取队尾元素。如果队列为空,返回 -1 。
    enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
    deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
    isEmpty(): 检查循环队列是否为空。
    isFull(): 检查循环队列是否已满。
     

    示例:

    MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
    circularQueue.enQueue(1);  // 返回 true
    circularQueue.enQueue(2);  // 返回 true
    circularQueue.enQueue(3);  // 返回 true
    circularQueue.enQueue(4);  // 返回 false,队列已满
    circularQueue.Rear();  // 返回 3
    circularQueue.isFull();  // 返回 true
    circularQueue.deQueue();  // 返回 true
    circularQueue.enQueue(4);  // 返回 true
    circularQueue.Rear();  // 返回 4
     

    提示:

    所有的值都在 0 至 1000 的范围内;
    操作数将在 1 至 1000 的范围内;
    请不要使用内置的队列库。

    题解

     

    下面我们来看一看上面的题目:

    对于结构体的定义:我们需要一个数组存放数据,同时还有队头和队尾,以及空间的大小。

    我们来区分一下如何判断是否为空和是否为满的情况

    空的时候,队头==队尾

    满的时候,(队尾+1)%空间的大小==队头。

    当判断对满是要考虑到如图的特殊情况此时,back在front前,此时还需要(back+1)%N的操作。

     

    后面一些细节的东西,我们直接来看代码:

    1. typedef struct {
    2. int* a;
    3. int front;
    4. int back;
    5. int N;
    6. } MyCircularQueue;
    7. bool myCircularQueueIsEmpty(MyCircularQueue* obj);//记得要函数声明
    8. bool myCircularQueueIsFull(MyCircularQueue* obj);//记得函数声明
    9. MyCircularQueue* myCircularQueueCreate(int k) {
    10. MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    11. obj->a=(int *)malloc(sizeof(int)*(k+1));
    12. obj->front=obj->back=0;
    13. obj->N=k+1;
    14. return obj;
    15. }
    16. //入队
    17. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    18. if(myCircularQueueIsFull(obj))
    19. {
    20. return false;
    21. }
    22. else
    23. {
    24. obj->a[obj->back]=value;
    25. obj->back++;
    26. //到尾
    27. obj->back%=obj->N;
    28. return true;
    29. }
    30. }
    31. //出头数据
    32. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    33. if(myCircularQueueIsEmpty(obj))
    34. {
    35. return false;
    36. }
    37. obj->front++;
    38. obj->front%=obj->N;
    39. return true;
    40. }
    41. //队头元素
    42. int myCircularQueueFront(MyCircularQueue* obj) {
    43. if(myCircularQueueIsEmpty(obj))
    44. {
    45. return -1;
    46. }
    47. else
    48. {
    49. return obj->a[obj->front];
    50. }
    51. }
    52. //队尾元素
    53. int myCircularQueueRear(MyCircularQueue* obj) {
    54. if(myCircularQueueIsEmpty(obj))
    55. {
    56. return -1;
    57. }
    58. else
    59. {
    60. return obj->a[(obj->back-1+obj->N)%obj->N];
    61. }
    62. }
    63. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    64. return obj->back==obj->front;
    65. }
    66. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    67. return (obj->back+1)%obj->N==obj->front;
    68. }
    69. void myCircularQueueFree(MyCircularQueue* obj) {
    70. free(obj->a);
    71. free(obj);
    72. }
    73. /**
    74. * Your MyCircularQueue struct will be instantiated and called as such:
    75. * MyCircularQueue* obj = myCircularQueueCreate(k);
    76. * bool param_1 = myCircularQueueEnQueue(obj, value);
    77. * bool param_2 = myCircularQueueDeQueue(obj);
    78. * int param_3 = myCircularQueueFront(obj);
    79. * int param_4 = myCircularQueueRear(obj);
    80. * bool param_5 = myCircularQueueIsEmpty(obj);
    81. * bool param_6 = myCircularQueueIsFull(obj);
    82. * myCircularQueueFree(obj);
    83. */

     

     

    循环队列的概念选择题

    1. 一个栈的初始状态为空。现将元素 1 2 3 4 5 A B C D E 依次入栈,然后再依次出栈,则元素出
    栈的顺序是( )。
    A 12345ABCDE
    B EDCBA54321
    C ABCDE12345
    D 54321EDCBA
    2. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
    A 1,4,3,2
    B 2,3,4,1
    C 3,1,4,2
    D 3,4,2,1
    3. 循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作
    后, front=rear=99 ,则循环队列中的元素个数为(
    A 1
    B 2
    C 99
    D 0 或者 100
    4. 以下 ( ) 不是队列的基本运算?
    A 从队尾插入一个新元素
    B 从队列中删除第 i 个元素
    C 判断一个队列是否为空
    D 读取队头元素的值
    5. 现有一循环队列,其队头指针为 front ,队尾指针为 rear ;循环队列长度为 N 。其队内有效长度为? ( 假设
    队头不存放数据 )
    A (rear - front + N) % N + 1
    B (rear - front + N) % N
    C ear - front) % (N + 1)
    D (rear - front + N) % (N - 1)
    1.B
    2.C
    3.D
    4.B
    5.B

     

  • 相关阅读:
    Process.Start() 报错:系统找不到指定文件
    小度打头阵,百度大模型能否“赋能万物”?
    C++ map容器用法
    数据链路与MAC帧
    dsu on tree模板
    如何使用ChatGPT辅助写论文、数据分析、AI绘图?【附学习资料】
    注意论文投稿风险,现投期刊会不会成为预警期刊呢?
    MYSQL:Select语句顺序
    开源B2B网站电子商务平台源码下载搭建 实现高效交易的桥梁
    应用案例 | 使用dataFEED OPC Suite将汽车零部件工厂数据集成到SAP Business Suite
  • 原文地址:https://blog.csdn.net/weixin_67900732/article/details/126299269