• C语言描述数据结构 —— 栈和队列OJ题


    1.括号匹配问题

    很显然,本篇文章的标题出卖了这道题,我们将使用栈来解决这道题。对于栈和队列,C 语言的库中并没有这两个数据结构,但在 C++ 的库中是可以直接使用这两种数据结构的。局限于目前我们只会使用 C 语言,所以在解这道题时,需要做一个前置工作,就是将我们写好的栈复制过来。这里我给出了上一篇关于栈和队列实现的代码。

    1. //可以动态开辟空间的栈
    2. typedef char StackData;//注意是字符元素
    3. typedef struct Stack
    4. {
    5. StackData* a;
    6. int top;//与顺序表的 size 一致
    7. int capacity;
    8. }Stack;
    9. void StackInit(Stack* ps);//栈初始化
    10. void StackPush(Stack* ps,StackData x);//入栈
    11. void StackPop(Stack* ps);//出栈
    12. StackData StackTop(Stack* ps);//获取栈顶元素
    13. bool StackEmpty(Stack* ps);//判断栈是否为空
    14. int StackSize(Stack* ps);//计算栈元素个数
    15. void StackDestroy(Stack* ps);//栈销毁
    16. //初始化栈
    17. void StackInit(Stack* ps)
    18. {
    19. assert(ps);
    20. //栈的初始容量置空
    21. ps->a = NULL;
    22. ps->top = ps->capacity = 0;
    23. }
    24. //入栈
    25. void StackPush(Stack* ps,StackData x)
    26. {
    27. assert(ps);
    28. //入栈只有一种方式,所以不需要将扩容独立封装
    29. if (ps->top == ps->capacity)
    30. {
    31. int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
    32. StackData* tmp = (StackData*)realloc(ps->a, NewCapacity*sizeof(StackData));
    33. assert(tmp);
    34. ps->a = tmp;
    35. ps->capacity = NewCapacity;
    36. }
    37. ps->a[ps->top] = x;
    38. ps->top++;
    39. }
    40. //出栈
    41. void StackPop(Stack* ps)
    42. {
    43. assert(ps);
    44. assert(!StackEmpty(ps));
    45. ps->top--;
    46. }
    47. //获取栈顶元素
    48. StackData StackTop(Stack* ps)
    49. {
    50. assert(ps);
    51. return ps -> a[ps->top - 1];
    52. }
    53. //判断栈是否为空
    54. bool StackEmpty(Stack* ps)
    55. {
    56. assert(ps);
    57. return ps->top == 0;//等于 0 则返回true,即栈为空
    58. }
    59. //计算栈中元素个数
    60. int StackSize(Stack* ps)
    61. {
    62. assert(ps);
    63. return ps->top;
    64. }
    65. //栈销毁
    66. void StackDestroy(Stack* ps)
    67. {
    68. assert(ps);
    69. free(ps->a);
    70. ps->a = NULL;
    71. ps->top = ps->capacity = 0;
    72. }

    做好了前置工作之后现在我们就要试着来解题。我们来分析一下思路:

     我们将它们转换为代码:

    1. bool isValid(char * s){
    2. Stack st;
    3. StackInit(&st);
    4. while(*s)
    5. {
    6. if(*s == '(' || *s == '[' || *s == '{')
    7. {
    8. StackPush(&st,*s);
    9. }
    10. else
    11. {
    12. //如果没有一个左括号入栈
    13. if(StackEmpty(&st))
    14. {
    15. StackDestroy(&st);
    16. return false;
    17. }
    18. else
    19. {
    20. char top = StackTop(&st);
    21. StackPop(&st);
    22. //如果有任何不满足匹配的情况就返回 false
    23. if(*s == ')' && top == '(' ||
    24. *s == ']' && top == '[' ||
    25. *s == '}' && top == '{')
    26. {
    27. StackDestroy(&st);
    28. return false;
    29. }
    30. }
    31. }
    32. s++;
    33. }
    34. //如果括号匹配的话,那么栈中就应该没有任何元素
    35. //这个例子见于字符串中全是左括号,没有满足任何匹配条件,就不会出栈
    36. //不会出栈就会导致栈中有元素,即不为空
    37. bool flag = StackEmpty(&st);
    38. StackDestroy(&st);
    39. return flag;
    40. }

    2.用队列实现栈

    注意看题,题目的要求是让我们用两个队列来实现栈。那么现在就要搞清楚队列转化为栈的难点是什么。队列的特征是先进先出,如果我们入队的数据是 1、2、3、4、5 ,那么出队的顺序也是 1、2、3、4、5 。而栈的特征是先进后出,即我们入栈的数据是 1、2、3、4、5,那么出栈的顺序是 5、4、3、2、1 。由此可见入队和入栈的方式一样,差别在于出队和出栈的顺序不同。我们需要解决的便是出的问题,还有就是删除的问题。

    我们知道,我们实现队列的数据结构是链表。题目的要求是让我们使用两个队列,两个队列如何实现获取队尾元素和尾删呢?

    我们拿出我们使用链表实现队列的代码,并将它复制到我们的题目上面去。

    1. //链表的结点
    2. typedef int QueueData;
    3. typedef struct QueueNode
    4. {
    5. QueueData val;
    6. struct QueueNode* next;
    7. }QueueNode;
    8. //存储头、尾结点地址的指针
    9. typedef struct HeadTail
    10. {
    11. QueueNode* head;
    12. QueueNode* tail;
    13. int size;//记录队列有几个元素
    14. }HeadTail;
    15. void QueueInit(HeadTail* p);//初始化队列
    16. void QueuePush(HeadTail* p,QueueData x);//进入队列
    17. QueueData QueueHead(HeadTail* p);//获取队头元素
    18. QueueData QueueTail(HeadTail* p);//获取队尾元素
    19. void QueuePop(HeadTail* p);//删除操作,出队
    20. bool QueueEmpty(HeadTail* p);//检查队列是否为空
    21. int QueueSize(HeadTail* p);//获取队列元素个数
    22. void QueuePopTail(HeadTail* p);
    23. void QueueDestroy(HeadTail* p);//销毁队列
    24. //初始化
    25. void QueueInit(HeadTail* p)
    26. {
    27. assert(p);
    28. p->head = p->tail = NULL;
    29. p->size = 0;
    30. }
    31. //队列尾插
    32. void QueuePush(HeadTail* p, QueueData x)
    33. {
    34. assert(p);
    35. //创建一个新结点
    36. QueueNode* newnode = (QueueNode*)calloc(1, sizeof(QueueNode));
    37. assert(newnode);
    38. newnode->val = x;
    39. newnode->next = NULL;
    40. //如果链表内没有结点,头尾指针指向同一个结点
    41. if (p->head == NULL)
    42. {
    43. p->head = newnode;
    44. p->tail = newnode;
    45. }
    46. //否则尾指针需要变化
    47. else
    48. {
    49. p->tail->next = newnode;
    50. p->tail = newnode;
    51. }
    52. p->size++;
    53. }
    54. //获取队头元素
    55. QueueData QueueHead(HeadTail* p)
    56. {
    57. assert(p);
    58. assert(!QueueEmpty(p));
    59. return p->head->val;
    60. }
    61. //获取队尾元素
    62. QueueData QueueTail(HeadTail* p)
    63. {
    64. assert(p);
    65. assert(!QueueEmpty(p));
    66. return p->tail->val;
    67. }
    68. //删除、出队
    69. void QueuePop(HeadTail* p)
    70. {
    71. assert(p);
    72. assert(!QueueEmpty(p));
    73. //如果链表只有一个结点
    74. if (p->head == p->tail)
    75. {
    76. free(p->head);
    77. p->head = p->tail = NULL;
    78. }
    79. //否则进行头删
    80. else
    81. {
    82. QueueNode* cur = p->head;
    83. p->head = p->head->next;
    84. free(cur);
    85. cur = NULL;
    86. }
    87. p->size--;
    88. }
    89. //检测队列是否为空
    90. bool QueueEmpty(HeadTail* p)
    91. {
    92. assert(p);
    93. return p->head == NULL && p->tail == NULL;
    94. }
    95. //获取队列元素个数
    96. int QueueSize(HeadTail* p)
    97. {
    98. assert(p);
    99. return p->size;
    100. }
    101. //销毁队列
    102. void QueueDestroy(HeadTail* p)
    103. {
    104. assert(p);
    105. QueueNode* cur = p->head;
    106. while (cur)
    107. {
    108. QueueNode* del = cur;
    109. cur = cur->next;
    110. free(del);
    111. }
    112. }
    1. typedef struct {
    2. HeadTail q1;
    3. HeadTail q2;
    4. } MyStack;
    5. MyStack* myStackCreate() {
    6. MyStack* obj = (MyStack*)calloc(1,sizeof(MyStack));
    7. //取 q1、q2 的地址传入函数
    8. QueueInit(&obj->q1);
    9. QueueInit(&obj->q2);
    10. return obj;
    11. }
    12. void myStackPush(MyStack* obj, int x) {
    13. //一定是向非空的队列插入数据
    14. if(!QueueEmpty(&obj->q1))
    15. {
    16. QueuePush(&obj->q1,x);
    17. }
    18. else
    19. {
    20. QueuePush(&obj->q2,x);
    21. }
    22. }
    23. int myStackPop(MyStack* obj) {
    24. //如果q1为空,将q2的size-1个倒入q1
    25. //并将q2剩下的最后一个当成栈顶元素返回
    26. if(QueueEmpty(&obj->q1))
    27. {
    28. while(QueueSize(&obj->q2) > 1)
    29. {
    30. QueuePush(&obj->q1,QueueHead(&obj->q2));
    31. QueuePop(&obj->q2);
    32. }
    33. int ret = QueueTail(&obj->q2);
    34. QueuePop(&obj->q2);
    35. return ret;
    36. }
    37. //如果q2为空,将q1的size-1个倒入q2
    38. //并将q1剩下的最后一个当成栈顶元素返回
    39. else
    40. {
    41. while(QueueSize(&obj->q1) > 1)
    42. {
    43. QueuePush(&obj->q2,QueueHead(&obj->q1));
    44. QueuePop(&obj->q1);
    45. }
    46. int ret = QueueTail(&obj->q1);
    47. QueuePop(&obj->q1);
    48. return ret;
    49. }
    50. }
    51. int myStackTop(MyStack* obj) {
    52. //这个函数并没有要求要删除数据
    53. //所以直接返回非空队列的队尾元素
    54. if(!QueueEmpty(&obj->q1))
    55. {
    56. return QueueTail(&obj->q1);
    57. }
    58. else
    59. {
    60. return QueueTail(&obj->q2);
    61. }
    62. }
    63. bool myStackEmpty(MyStack* obj) {
    64. return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
    65. }
    66. void myStackFree(MyStack* obj) {
    67. //obj里面存放的只是q1、q2空间的地址
    68. //所以需要独立释放q1、q2空间
    69. QueueDestroy(&obj->q1);
    70. QueueDestroy(&obj->q2);
    71. free(obj);
    72. }

    3.用栈实现队列

    这里的题目要求是用两个栈实现队列,那么肯定是要涉及倒数据的。我们现在来具体分析如何倒数据。

     我们将我们实现栈的数据结构的代码复制到题目中去:

    1. //可以动态开辟空间的栈
    2. typedef int StackData;
    3. typedef struct Stack
    4. {
    5. StackData* a;
    6. int top;//与顺序表的 size 一致
    7. int capacity;
    8. }Stack;
    9. void StackInit(Stack* ps);//栈初始化
    10. void StackPush(Stack* ps,StackData x);//入栈
    11. void StackPop(Stack* ps);//出栈
    12. StackData StackTop(Stack* ps);//获取栈顶元素
    13. bool StackEmpty(Stack* ps);//判断栈是否为空
    14. int StackSize(Stack* ps);//计算栈元素个数
    15. void StackDestroy(Stack* ps);//栈销毁
    16. //初始化栈
    17. void StackInit(Stack* ps)
    18. {
    19. assert(ps);
    20. //栈的初始容量置空
    21. ps->a = NULL;
    22. ps->top = ps->capacity = 0;
    23. }
    24. //入栈
    25. void StackPush(Stack* ps,StackData x)
    26. {
    27. assert(ps);
    28. //入栈只有一种方式,所以不需要将扩容独立封装
    29. if (ps->top == ps->capacity)
    30. {
    31. int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
    32. StackData* tmp = (StackData*)realloc(ps->a, NewCapacity*sizeof(StackData));
    33. assert(tmp);
    34. ps->a = tmp;
    35. ps->capacity = NewCapacity;
    36. }
    37. ps->a[ps->top] = x;
    38. ps->top++;
    39. }
    40. //出栈
    41. void StackPop(Stack* ps)
    42. {
    43. assert(ps);
    44. assert(!StackEmpty(ps));
    45. ps->top--;
    46. }
    47. //获取栈顶元素
    48. StackData StackTop(Stack* ps)
    49. {
    50. assert(ps);
    51. return ps -> a[ps->top - 1];
    52. }
    53. //判断栈是否为空
    54. bool StackEmpty(Stack* ps)
    55. {
    56. assert(ps);
    57. return ps->top == 0;//等于 0 则返回true,即栈为空
    58. }
    59. //计算栈中元素个数
    60. int StackSize(Stack* ps)
    61. {
    62. assert(ps);
    63. return ps->top;
    64. }
    65. //栈销毁
    66. void StackDestroy(Stack* ps)
    67. {
    68. assert(ps);
    69. free(ps->a);
    70. ps->a = NULL;
    71. ps->top = ps->capacity = 0;
    72. }
    1. typedef struct {
    2. Stack push;
    3. Stack pop;
    4. } MyQueue;
    5. MyQueue* myQueueCreate() {
    6. MyQueue* obj = (MyQueue*)calloc(1,sizeof(MyQueue));
    7. StackInit(&obj->push);
    8. StackInit(&obj->pop);
    9. return obj;
    10. }
    11. void myQueuePush(MyQueue* obj, int x) {
    12. //只向push栈插入数据
    13. StackPush(&obj->push,x);
    14. }
    15. int myQueuePop(MyQueue* obj) {
    16. //要向pop栈中倒入数据
    17. if(StackEmpty(&obj->pop))
    18. {
    19. while(!StackEmpty(&obj->push))
    20. {
    21. StackPush(&obj->pop,StackTop(&obj->push));
    22. StackPop(&obj->push);
    23. }
    24. }
    25. int ret = StackTop(&obj->pop);
    26. StackPop(&obj->pop);
    27. return ret;
    28. }
    29. int myQueuePeek(MyQueue* obj) {
    30. //只有向pop栈倒入数据才能取到最先进去的那个元素
    31. if(StackEmpty(&obj->pop))
    32. {
    33. while(!StackEmpty(&obj->push))
    34. {
    35. StackPush(&obj->pop,StackTop(&obj->push));
    36. StackPop(&obj->push);
    37. }
    38. }
    39. return StackTop(&obj->pop);
    40. }
    41. bool myQueueEmpty(MyQueue* obj) {
    42. return StackEmpty(&obj->push) && StackEmpty(&obj->pop);
    43. }
    44. void myQueueFree(MyQueue* obj) {
    45. StackDestroy(&obj->push);
    46. StackDestroy(&obj->pop);
    47. free(obj);
    48. }

    4.设计循环队列

     我们先不关注题目的要求,就单单循环队列而言,我们先选择合适的数据结构。

    我们有两种数据结构可选,一是链表,而是顺序表。那么那种数据结构优势较大呢?首先我们来看看循环队列是什么。

    那么其次,我们把这个逻辑结构套到两种数据结构上面去。

     

     有了上面的分析基础,现在就可以尝试去解题了。

    1. typedef struct {
    2. int* a;
    3. int head;//头
    4. int tail;//尾
    5. int size;//长度
    6. } MyCircularQueue;
    7. MyCircularQueue* myCircularQueueCreate(int k) {
    8. MyCircularQueue* obj = (MyCircularQueue*)calloc(1,sizeof(MyCircularQueue));
    9. obj->a = (int*)calloc(1,sizeof(int)*(k+1));//顺序表的空间多开一个
    10. obj->head = obj->tail = 0;
    11. obj->size=k+1;
    12. return obj;
    13. }
    14. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    15. return obj->head == obj->tail;
    16. }
    17. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    18. return (obj->tail+1)%obj->size == obj->head;
    19. }
    20. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    21. if(myCircularQueueIsFull(obj))
    22. return false;
    23. obj->a[obj->tail]=value;
    24. obj->tail++;
    25. obj->tail %= obj->size;//确保tail的值不会超过长度范围
    26. return true;
    27. }
    28. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    29. if(myCircularQueueIsEmpty(obj))
    30. return false;
    31. obj->head++;
    32. obj->head %= obj->size;//确保head的值不会超过长度范围
    33. return true;
    34. }
    35. int myCircularQueueFront(MyCircularQueue* obj) {
    36. if(myCircularQueueIsEmpty(obj))
    37. return -1;
    38. return obj->a[obj->head];
    39. }
    40. int myCircularQueueRear(MyCircularQueue* obj) {
    41. if(myCircularQueueIsEmpty(obj))
    42. return -1;
    43. //推导的公式,以防tail的值为 0
    44. return obj->a[(obj->tail-1+obj->size)%obj->size];
    45. }
    46. void myCircularQueueFree(MyCircularQueue* obj) {
    47. free(obj->a);
    48. free(obj);
    49. }

  • 相关阅读:
    AVL树 c语言版本 插入部分
    CentOS部署MySQL 5.7(详细)
    MKL稀疏矩阵运算示例及函数封装
    『现学现忘』Docker相关概念 — 6、虚拟化技术分类
    l8-d7 实现TCP通信
    机器学习笔记:seq2seq & attentioned seq2seq
    Linux下NFS共享存储安装详细步骤
    软件工程国考总结——选择题
    java 阿里云上传照片
    单点登录的三种方式
  • 原文地址:https://blog.csdn.net/weixin_59913110/article/details/126383545