• 栈&队列OJ练习题(C语言版)


    目录

    一、括号匹配问题

    思路:

    完整版C语言代码:  

    讲解:

    二、用队列实现栈

    思路:

    完整版C语言代码: 

    讲解: 

    三、用栈实现队列

    思路:

    完整版C语言代码:

    讲解:

    四、 设计循环队列

    思路:

    完整版C语言代码:

    讲解:


    如果栈和队列忘了,不妨看看小生的这两篇复习一下数据结构与算法—栈     数据结构与算法—队列

    一、括号匹配问题

    20. 有效的括号 - 力扣(LeetCode)

     

    思路:

    将左括号放入栈中,通过出栈与为入栈的符号进行比较。 

    由于我们用C语言做这道题,所以代码前要加上咱们实现的的代码,同时要将数据类型STDataType改为char类型。

    完整版C语言代码:  

    1. typedef char STDataType;
    2. typedef struct Stack
    3. {
    4. STDataType* a;
    5. int top;
    6. int capacity;
    7. }ST;
    8. void STInit(ST* pst)
    9. {
    10. assert(pst);
    11. pst->a = NULL;
    12. pst->top = 0;
    13. pst->capacity = 0;
    14. }
    15. void STDestroy(ST* pst)
    16. {
    17. assert(pst);
    18. free(pst->a);
    19. pst->a = NULL;
    20. pst->top = 0;
    21. pst->capacity = 0;
    22. }
    23. void STPush(ST* pst,STDataType x)
    24. {
    25. if (pst->top == pst->capacity) {
    26. int newCapacity = pst->capacity == 0 ? 4 :pst-> capacity * 2;
    27. STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
    28. if (tmp == NULL) {
    29. perror("realloc fail");
    30. return;
    31. }
    32. pst->a = tmp;
    33. pst->capacity = newCapacity;
    34. }
    35. pst->a[pst->top] = x;
    36. pst->top++;
    37. }
    38. bool STEmpty(ST* pst)
    39. {
    40. assert(pst);
    41. return pst->top == 0;
    42. }
    43. void STPop(ST* pst)
    44. {
    45. assert(pst);
    46. assert(!STEmpty(pst));
    47. pst->top--;
    48. }
    49. STDataType STTop(ST* pst)
    50. {
    51. assert(pst);
    52. assert(!STEmpty(pst));
    53. return pst->a[pst->top - 1];
    54. }
    55. int STSize(ST* pst)
    56. {
    57. assert(pst);
    58. return pst->top;
    59. }
    60. //------以下为OJ提供-------
    61. bool isValid(char* s) {
    62. ST st;
    63. STInit(&st);
    64. while (*s) {
    65. if (*s == '(' || *s == '[' || *s == '{') {
    66. STPush(&st, *s);
    67. }
    68. else {
    69. if (STEmpty(&st)) {
    70. STDestroy(&st);
    71. return false;
    72. }
    73. char top = STTop(&st);
    74. STPop(&st);
    75. if ((top != '(' && *s == ')') ||
    76. (top != '{' && *s == '}') ||
    77. (top != '[' && *s == ']')) {
    78. STDestroy(&st);
    79. return false;
    80. }
    81. }
    82. s++;
    83. }
    84. bool ret = STEmpty(&st);
    85. STDestroy(&st);
    86. return ret;
    87. }

    讲解:

    isValid函数:

    • 创建栈结构体ST变量 st,然后进行初始化。
    • 以*s为循环进行条件
    • 首先,创建一个名为 st 的 ST 结构体实例,并使用 STInit 初始化它。

    • 然后,遍历输入字符串 s 中的每个字符。
    • 对于每个字符,如果是左括号 '(','[','{' ,则将其推入栈中。
    • 如果是右括号 ')',']','}' ,则执行以下操作:

    检查栈是否为空,如果为空,表示没有对应的左括号,则销毁栈,返回 false

    否则,弹出栈顶元素,将其与当前右括号进行匹配。如果不匹配,则销毁栈,返回 false

    • 最后,遍历完整个字符串后,检查栈是否为空。如果栈为空,表示所有括号都成功匹配,返回 true,否则返回 false
    • 最后,调用 STDestroy 销毁栈,并返回最终的匹配结果。

    二、用队列实现栈

    225. 用队列实现栈 - 力扣(LeetCode)

     

    思路:

     准备两个队列,第一个队列依次出队到只剩一个数据时停止,将已出队的数据依次入队到第二个队列,将第一个队列仅剩的一个数据出队即实现了栈的出栈。入栈时哪个队列不为空则在哪个队列入队。

    由于我们用C语言做这道题,所以代码前要加上咱们实现的队列的代码。

    完整版C语言代码: 

    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. }

    讲解: 

     1、

    1. typedef struct {
    2. Queue q1;
    3. Queue q2;
    4. } MyStack;

    首先在匿名结构体MyStack中设置两个成员 q1、q2,他们的类型为结构体Queue。

    2、

    1. MyStack* myStackCreate() {
    2. MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    3. if (obj == NULL) {
    4. perror("malloc fail");
    5. return NULL;
    6. }
    7. QueueInit(&obj->q1);
    8. QueueInit(&obj->q2);
    9. return obj;
    10. }

    myStackCreate中首先创建结构体 MyStack 指针 obj,并为其开辟空间,开辟失败则打印错误信息,然后对 obj 的两个成员 (队列) 进行初始化。

    3、

    1. void myStackPush(MyStack* obj, int x) {
    2. if (!QueueEmpty(&obj->q1)) {
    3. QueuePush(&obj->q1, x);
    4. }
    5. else {
    6. QueuePush(&obj->q2, x);
    7. }
    8. }

    myStackPush中首先判断哪个队列不为空,对不为空的队列进行入队(入栈)。

    4、

    1. int myStackPop(MyStack* obj) {
    2. Queue* pEmptyQ = &obj->q1;
    3. Queue* pNonEmptyQ = &obj->q2;
    4. if (!QueueEmpty(&obj->q1)) {
    5. pEmptyQ = &obj->q2;
    6. pNonEmptyQ = &obj->q1;
    7. }
    8. while (QueueSize(pNonEmptyQ) > 1) {
    9. QueuePush(pEmptyQ, QueueFront(pNonEmptyQ));
    10. QueuePop(pNonEmptyQ);
    11. }
    12. int top = QueueFront(pNonEmptyQ);
    13. QueuePop(pNonEmptyQ);
    14. return top;
    15. }
    • myStackPop中首先要找到为“空”和“不为空”的队列,假设队列q1为空,q2不为空,通过QueueEmpty判断如果q1为空则假设不变,否则二者互换。
    • 然后将不为空的队列的数据依次入队到为空的队列,入队结束后将不为空的队列进行出队,进行下一次循环,直到不为空的队列只剩一个元素停止循环。
    • 调用QueueFront函数获取队头节点赋值给变量top,将不为空队列仅剩的数据出队。
    • 返回top。

    5、

    1. int myStackTop(MyStack* obj) {
    2. if (!QueueEmpty(&obj->q1)) {
    3. return QueueBack(&obj->q1);
    4. }
    5. else {
    6. return QueueBack(&obj->q2);
    7. }
    8. }

    myStackTop函数找出不为空的队列,对不为空的队列调用QueueBack返回栈顶元素。

    6、

    1. bool myStackEmpty(MyStack* obj) {
    2. return QueueEmpty(&obj->q1) &&
    3. QueueEmpty(&obj->q2);
    4. }

    myStackEmpty调用QueueEmpty判断两个队列是否为空即为判断栈是否为空。

    7、

    1. void myStackFree(MyStack* obj) {
    2. QueueDestroy(&obj->q1);
    3. QueueDestroy(&obj->q2);
    4. free(obj);
    5. }

    myStackFree调用 QueueDestroy释放两个队列的内存空间,最后释放栈 obj 的内存空间。

    三、用栈实现队列

    232. 用栈实现队列 - 力扣(LeetCode)

    思路:

    一个栈用于入队,一个栈用于出队。出队栈不为空则从入队栈依次出栈,然后入栈到出队栈,这时原本入队栈的数据在出队栈中直接出栈,即可实现队列的先进先出,再次入队时数据进入入队栈,等待出队栈为空时再将数据倒过来。

     

     由于我们用C语言做这道题,所以代码前要加上咱们实现的的代码。

    完整版C语言代码:

    1. typedef int STDataType;
    2. typedef struct Stack
    3. {
    4. STDataType* a;
    5. int top;
    6. int capacity;
    7. }ST;
    8. void STInit(ST* pst)
    9. {
    10. assert(pst);
    11. pst->a = NULL;
    12. pst->top = 0;
    13. pst->capacity = 0;
    14. }
    15. void STDestroy(ST* pst)
    16. {
    17. assert(pst);
    18. free(pst->a);
    19. pst->a = NULL;
    20. pst->top = 0;
    21. pst->capacity = 0;
    22. }
    23. void STPush(ST* pst, STDataType x)
    24. {
    25. if (pst->top == pst->capacity) {
    26. int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
    27. STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
    28. if (tmp == NULL) {
    29. perror("realloc fail");
    30. return;
    31. }
    32. pst->a = tmp;
    33. pst->capacity = newCapacity;
    34. }
    35. pst->a[pst->top] = x;
    36. pst->top++;
    37. }
    38. bool STEmpty(ST* pst)
    39. {
    40. assert(pst);
    41. return pst->top == 0;
    42. }
    43. void STPop(ST* pst)
    44. {
    45. assert(pst);
    46. assert(!STEmpty(pst));
    47. pst->top--;
    48. }
    49. STDataType STTop(ST* pst)
    50. {
    51. assert(pst);
    52. assert(!STEmpty(pst));
    53. return pst->a[pst->top - 1];
    54. }
    55. int STSize(ST* pst)
    56. {
    57. assert(pst);
    58. return pst->top;
    59. }
    60. //------以下为OJ提供-------
    61. typedef struct {
    62. ST pushst;
    63. ST popst;
    64. } MyQueue;
    65. MyQueue* myQueueCreate() {
    66. MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    67. if (obj == NULL) {
    68. perror("malloc fail");
    69. return 0;
    70. }
    71. STInit(&obj->pushst);
    72. STInit(&obj->popst);
    73. return obj;
    74. }
    75. void myQueuePush(MyQueue* obj, int x) {
    76. STPush(&obj->pushst, x);
    77. }
    78. int myQueuePop(MyQueue* obj) {
    79. int front = myQueuePeek(obj);
    80. STPop(&obj->popst);
    81. return front;
    82. }
    83. int myQueuePeek(MyQueue* obj) {
    84. if (STEmpty(&obj->popst)) {
    85. while (!STEmpty(&obj->pushst)) {
    86. STPush(&obj->popst, STTop(&obj->pushst));
    87. STPop(&obj->pushst);
    88. }
    89. }
    90. return STTop(&obj->popst);
    91. }
    92. bool myQueueEmpty(MyQueue* obj) {
    93. return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
    94. }
    95. void myQueueFree(MyQueue* obj) {
    96. STDestroy(&obj->pushst);
    97. STDestroy(&obj->popst);
    98. free(obj);
    99. }

    讲解:

    1、

    1. typedef struct {
    2. ST pushst;
    3. ST popst;
    4. } MyQueue;

    在MyQueue结构体中创建两个栈 pushst 和 popst。

    2、

    1. MyQueue* myQueueCreate() {
    2. MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    3. if (obj == NULL) {
    4. perror("malloc fail");
    5. return 0;
    6. }
    7. STInit(&obj->pushst);
    8. STInit(&obj->popst);
    9. return obj;
    10. }

    创建一个 MyQueue 类型的队列 obj 。然后通过 malloc 申请内存,如果申请失败则调用perror打印错误信息,结束函数,然后分别初始化 pushst 和 popst 两个栈,返回队列obj。

     3、

    1. void myQueuePush(MyQueue* obj, int x) {
    2. STPush(&obj->pushst, x);
    3. }

    将元素 x 入队。直接调用 STPush 函数将元素 x 压入 pushst 栈。

     4、

    1. int myQueuePop(MyQueue* obj) {
    2. int front = myQueuePeek(obj);
    3. STPop(&obj->popst);
    4. return front;
    5. }

    这个函数用于将队首元素出队,并返回其值。首先调用 myQueuePeek 函数获取队首元素的值,然后调用 STPop 函数将元素从 popst 栈中弹出。

    5、 

    1. int myQueuePeek(MyQueue* obj) {
    2. if (STEmpty(&obj->popst)) {
    3. while (!STEmpty(&obj->pushst)) {
    4. STPush(&obj->popst, STTop(&obj->pushst));
    5. STPop(&obj->pushst);
    6. }
    7. }
    8. return STTop(&obj->popst);
    9. }

    这个函数用于获取队首元素的值。首先判断 popst 栈是否为空,如果为空则将 pushst 栈中的所有元素依次弹出并压入 popst 栈,最后通过 STTop 函数获取 popst 栈顶元素的值。

    6、

    1. bool myQueueEmpty(MyQueue* obj) {
    2. return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
    3. }

     这个函数用于判断队列是否为空。只有当 pushst 和 popst 两个栈都为空时,队列才为空。

    7、 

    1. void myQueueFree(MyQueue* obj) {
    2. STDestroy(&obj->pushst);
    3. STDestroy(&obj->popst);
    4. free(obj);
    5. }

    这个函数用于释放 MyQueue 类型队列所占用的内存空间。

    首先调用 STDestroy 函数销毁 pushst 和 popst 两个栈,然后调用 free 函数释放 obj 所占用的内存空间。

    四、 设计循环队列

    622. 设计循环队列

     

     思路:

    选择数组作为循环队列,为了避免队列为空和队列为满时 front 和 rear 相同的情况,将数组的容量设置为比题中要求的队列长度大一(即实际容量为k+1)。

     

    完整版C语言代码:

    1. typedef struct {
    2. int front;
    3. int rear;
    4. int k;
    5. int* a;
    6. } MyCircularQueue;
    7. MyCircularQueue* myCircularQueueCreate(int k) {
    8. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    9. obj->a = (int*)malloc((k + 1) * sizeof(int));
    10. obj->k = k;
    11. obj->front = obj->rear = 0;
    12. return obj;
    13. }
    14. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    15. return obj->front == obj->rear;
    16. }
    17. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    18. return (obj->rear + 1) % (obj->k + 1) == obj->front;
    19. }
    20. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    21. if (myCircularQueueIsFull(obj)) {
    22. return false;
    23. }
    24. obj->a[obj->rear] = value;
    25. obj->rear++;
    26. obj->rear %= (obj->k + 1);
    27. return true;
    28. }
    29. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    30. if (myCircularQueueIsEmpty(obj)) {
    31. return false;
    32. }
    33. obj->front++;
    34. obj->front %= (obj->k + 1);
    35. return true;
    36. }
    37. int myCircularQueueFront(MyCircularQueue* obj) {
    38. if (myCircularQueueIsEmpty(obj)) {
    39. return -1;
    40. }
    41. return obj->a[obj->front];
    42. }
    43. int myCircularQueueRear(MyCircularQueue* obj) {
    44. if (myCircularQueueIsEmpty(obj)) {
    45. return -1;
    46. }
    47. return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
    48. }
    49. void myCircularQueueFree(MyCircularQueue* obj) {
    50. free(obj->a);
    51. free(obj);
    52. }

    讲解:

    1、

    1. typedef struct {
    2. int front;
    3. int rear;
    4. int k;
    5. int* a;
    6. } MyCircularQueue;

    定义MyCircularQueue结构体,front指向队列第一个元素,rear指向队列最后一个元素的下一个位置,k为队列容量,指针a用于动态分配数组存储队列元素。

    2、 

    1. MyCircularQueue* myCircularQueueCreate(int k) {
    2. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    3. obj->a = (int*)malloc((k + 1) * sizeof(int));
    4. obj->k = k;
    5. obj->front = obj->rear = 0;
    6. return obj;
    7. }

    对队列obj开辟空间,为数组a分配k+1个整型元素大小的空间,多出来的一个空间用于区分空队列和满队列,将k的值存储在队列obj中,初始化 front 和 rear 为 0。

    这里将检查队列是否为空或已满的函数从后面移动到这里,方便后续函数能正常调用。 

    3、

    1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    2. return obj->front == obj->rear;
    3. }

    如果front与rear重合,则队列为空。

    4、

    1. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    2. return (obj->rear + 1) % (obj->k + 1) == obj->front;
    3. }

    通过 (obj->rear + 1) % (obj->k + 1) 计算下一个元素将要插入的位置,如果这个位置和 front 相同,说明队列已满。

    5、

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

    首先检查队列是否已满。

    • 如果满了,返回 false;不满则将数据放入数组的rear位置,然后rear向后移动一位。
    • 如果rear移动到最后一个元素的后一项位置,则通过 obj->rear %= (obj->k + 1); 更新rear的位置。

    6、 

    1. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    2. if (myCircularQueueIsEmpty(obj)) {
    3. return false;
    4. }
    5. obj->front++;
    6. obj->front %= (obj->k + 1);
    7. return true;
    8. }
    • 首先检查队列是否为空。如果为空,返回 false,
    • 如果不空,更新 front 的值,表示已经移除了一个元素,返回 true。

    7、 

    1. int myCircularQueueFront(MyCircularQueue* obj) {
    2. if (myCircularQueueIsEmpty(obj)) {
    3. return -1;
    4. }
    5. return obj->a[obj->front];
    6. }
    • 如果队列为空,返回 -1。
    • 如果不空,返回 front 指向的元素。

    8、 

    1. int myCircularQueueRear(MyCircularQueue* obj) {
    2. if (myCircularQueueIsEmpty(obj)) {
    3. return -1;
    4. }
    5. return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
    6. }
    • 如果队列为空,返回 -1。
    • 如果不空,返回 rear 指向的前一个元素。

     9、

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

    释放队列数组 a 和队列结构体的内存空间。

  • 相关阅读:
    va_list 、va_start、va_arg 和 va_end的含义和用法
    电商技术揭秘三十二:智能风控的案例研究与未来趋势
    【Python零基础入门篇 · 7】:列表、元组的相关操作(完整版)
    事件委托代理
    Windows如何ping端口
    工资总额分配方案
    Linux:centos9的本地yum仓库配置
    Mysql高级篇(逻辑架构和存储引擎)
    springmvc总结
    图片上传~
  • 原文地址:https://blog.csdn.net/m0_73800602/article/details/134074974