• [数据结构]~栈和队列(0-1)


     前言

    作者小蜗牛向前冲

    名言我可以接收失败,但我不能接收放弃

     如果觉的博主的文章还不错的话,还点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。

    目录

    一 “栈”

    二 栈的实现

    1 栈的定义

    2 栈功能的实现

     三 “队列”

    四 队列的实现

    1 队列的定义 

     五 栈和队列的区别


    栈和队列作为一种典型的线性表,都是基于线性表(顺序表)实现的,有人可能会问,我都有线性表了,为什么还要知道栈和队列呢?

    举个例子:

    有个小平大厨,他要去做一道名菜,可能要花费上百道刀工,在此期间他会换不同的刀去完成不同的工艺,我们试想一下难道我用一把刀就不能完成各种刀工,其实也是可以的,那小平大厨为什么要那么麻烦去换不同的刀呢?那大家肯定会说这样方便啊!对没错就是方便。

    其实换到数据结构上来说,由于我们会频繁的入栈出栈取栈顶元素,怎么操作都是最常用的,所以我们就定义栈和队列来完成他,省的我们去运用更麻烦的线性表,降低我们出错的概率。

    下面我们就一起去认识栈和队列吧!

    一 “栈”

    :一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶另一端称为栈底
    压栈(入栈):栈的插入操作,数据会在栈顶

     出栈:栈的删除操作叫做出栈。出数据也在栈顶

    我们可以理解为出栈就为压栈的逆过程

    栈的特点先进后出

    二 栈的实现

    对于栈来说,由于他都是尾插和尾删数据,所以我们选择用顺序表来实现他,此时间复杂度为O(1)。

    1 栈的定义

    这里我们在Stack.h的头文件中包含以下内容:

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. typedef int STDataType;
    7. //定义栈
    8. typedef struct Stack
    9. {
    10. STDataType* arr;//数据类型
    11. int pos;//数组下标
    12. int capacity;//栈的容量
    13. }ST;
    14. //初始化
    15. void StackInit(ST* ps);
    16. //销毁
    17. void StackDestroy(ST* ps);
    18. //入栈
    19. void StackPush(ST* ps, STDataType x);
    20. //出栈
    21. void StackPop(ST* ps);
    22. 显示返回栈顶数据
    23. STDataType StackTop(ST* ps);
    24. //判断栈是否为空
    25. bool StackEmpty(ST* ps);
    26. //返回栈的长度
    27. int StackSize(ST* ps);

    2 栈功能的实现

    1. #include"Stack.h"
    2. //初始化
    3. void StackInit(ST* ps)
    4. {
    5. assert(ps);
    6. ps->arr = NULL;//初始数组为空
    7. ps->pos = ps->capacity = 0;//初始为0
    8. }
    9. //销毁
    10. void StackDestroy(ST* ps)
    11. {
    12. assert(ps);
    13. free(ps->arr);//arr是整个栈的地址
    14. ps->arr = NULL;
    15. ps->capacity = ps->pos = 0;
    16. }
    17. //入栈
    18. void StackPush(ST* ps, STDataType x)
    19. {
    20. assert(ps);
    21. //判断栈的空间是否满
    22. if (ps->pos == ps->capacity)
    23. {
    24. //扩容
    25. int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//扩2倍
    26. STDataType* tmp = (STDataType*)realloc(ps->arr,newCapacity * sizeof(STDataType));
    27. if (tmp == NULL)
    28. {
    29. perror("reamlloc fail");
    30. exit(-1);
    31. }
    32. //跟新容量
    33. ps->arr = tmp;
    34. ps->capacity = newCapacity;
    35. }
    36. //入栈
    37. ps->arr[ps->pos] = x;
    38. ps->pos++;//下标++
    39. }
    40. //出栈
    41. void StackPop(ST* ps)
    42. {
    43. assert(ps);
    44. //断言栈是否为空
    45. assert(!StackEmpty(ps));
    46. --ps->pos;
    47. }
    48. //判断栈是否为空
    49. bool StackEmpty(ST* ps)
    50. {
    51. assert(ps);
    52. return ps->pos == 0;
    53. }
    54. //显示返回栈顶数据
    55. STDataType StackTop(ST* ps)
    56. {
    57. assert(ps);
    58. //断言栈是否为空
    59. assert(!StackEmpty(ps));
    60. return ps->arr[ps->pos - 1];//下标已经前移
    61. }
    62. //返回栈的长度
    63. int StackSize(ST* ps)
    64. {
    65. assert(ps);
    66. return ps->pos;
    67. }

    在这里我们重点为大家刨析压栈,出栈,取栈的写法。

    (1)压栈

    1. //入栈
    2. void StackPush(ST* ps, STDataType x)
    3. {
    4. assert(ps);
    5. //判断栈的空间是否满
    6. if (ps->pos == ps->capacity)
    7. {
    8. //扩容
    9. int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//扩2倍
    10. STDataType* tmp = (STDataType*)realloc(ps->arr,newCapacity * sizeof(STDataType));
    11. if (tmp == NULL)
    12. {
    13. perror("reamlloc fail");
    14. exit(-1);
    15. }
    16. //跟新容量
    17. ps->arr = tmp;
    18. ps->capacity = newCapacity;
    19. }
    20. //入栈
    21. ps->arr[ps->pos] = x;
    22. ps->pos++;//下标++
    23. }

    这里我们的实现思路是:

    判断栈的空间是否以满;

    在进行入栈

    (2)出栈

    1. //出栈
    2. void StackPop(ST* ps)
    3. {
    4. assert(ps);
    5. //断言栈是否为空
    6. assert(!StackEmpty(ps));
    7. --ps->pos;
    8. }

    出栈的实现是非常简单的,只要将pos的标记--即可。

    (3)取栈

    1. //显示返回栈顶数据
    2. STDataType StackTop(ST* ps)
    3. {
    4. assert(ps);
    5. //断言栈是否为空
    6. assert(!StackEmpty(ps));
    7. return ps->arr[ps->pos - 1];//下标已经前移
    8. }

    我们直接返回栈顶指针即可

    每当我们完成一个功能时候,我们都应该去测试一下:

     三 “队列”

    队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
    入队列:进行插入操作的一端称为队尾

    出队列:进行删除操作的一端称为队头


     

    四 队列的实现

    1 队列的定义 

    我们同样在Queue头文件在实现:

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. typedef int QDataType;
    7. //定义队列
    8. typedef struct QueueNode
    9. {
    10. QDataType data;//数据类型
    11. struct QueueNode* next;
    12. }QNode;
    13. //定义指向头和尾的二个指针
    14. typedef struct Queue
    15. {
    16. QNode* head;
    17. QNode* tail;
    18. int size;
    19. }Queue;
    20. //初始化
    21. void QueueInit(Queue* pq);
    22. //销毁
    23. void QueueDestroy(Queue* pq);
    24. //入队
    25. void QueuePush(Queue* pq, QDataType x);
    26. //出队
    27. void QueuePop(Queue* pq);
    28. //返回指向队头的数据的指针
    29. QDataType QueueFront(Queue* pq);
    30. //返回指向队尾的数据的指针
    31. QDataType QueueBack(Queue* pq);
    32. //判断队列是否为空
    33. bool QueueEmpty(Queue* pq);
    34. //返回队列的大小
    35. int QueueSize(Queue* pq);

    由于栈和队列中定义是差不多,这里就不在过的的说明了。

    2 队列的实现

    这里就直接上代码,里面有详细的注释,大家不懂就可以看看。

    大家在实现队列时可以对照着头文件的函数功能经行实现。

    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include"Queue.h"
    3. //初始化
    4. void QueueInit(Queue* pq)
    5. {
    6. assert(pq);
    7. pq->head = pq->tail = NULL;
    8. pq->size = 0;
    9. }
    10. //销毁
    11. void QueueDestroy(Queue* pq)
    12. {
    13. assert(pq);
    14. QNode* cur = pq->head;
    15. while (cur)
    16. {
    17. QNode* del = cur;
    18. cur = cur->next;//指向下个节点
    19. free(del);
    20. }
    21. pq->head = pq->tail = NULL;//防止出现野指针
    22. pq->size = 0;
    23. }
    24. //入队
    25. void QueuePush(Queue* pq, QDataType x)
    26. {
    27. assert(pq);
    28. //申请节点
    29. QNode* newNode = (QNode*)malloc(sizeof(QNode));
    30. if (newNode==NULL)
    31. {
    32. perror("malloc fail");
    33. exit(-1);
    34. }
    35. else
    36. {
    37. newNode->data = x;
    38. newNode->next = NULL;
    39. }
    40. //队列为空
    41. if (pq->tail == NULL)
    42. {
    43. pq->head = pq->tail = newNode;
    44. }
    45. //不为空
    46. else
    47. {
    48. pq->tail->next = newNode;
    49. pq->tail = newNode;//tail指针指向newNode
    50. }
    51. pq->size++;
    52. }
    53. //出队
    54. void QueuePop(Queue* pq)
    55. {
    56. assert(pq);
    57. //断言队列是否为空
    58. assert(!QueueEmpty(pq));
    59. //当队列中就一个数据时
    60. if (pq->head->next == NULL)
    61. {
    62. free(pq->head);
    63. pq->head = pq->tail = NULL;
    64. }
    65. else
    66. {
    67. QNode* del = pq->head;
    68. pq->head = pq->head->next;//头变为下个节点
    69. free(del);
    70. del = NULL;
    71. }
    72. pq->size--;
    73. }
    74. //判断队列是否为空
    75. bool QueueEmpty(Queue* pq)
    76. {
    77. assert(pq);
    78. return pq->tail == NULL;
    79. }
    80. //返回指向队头的数据的指针
    81. QDataType QueueFront(Queue* pq)
    82. {
    83. assert(pq);
    84. //断言队列是否为空
    85. assert(!QueueEmpty(pq));
    86. return pq->head->data;
    87. }
    88. //返回指向队尾的数据的指针
    89. QDataType QueueBack(Queue* pq)
    90. {
    91. assert(pq);
    92. //断言队列是否为空
    93. assert(!QueueEmpty(pq));
    94. return pq->tail->data;
    95. }
    96. //返回队列的大小
    97. int QueueSize(Queue* pq)
    98. {
    99. return pq->size;
    100. }

    下面我们继续测试一下队列是否能够成功实现自己的功能:

     五 栈和队列的区别

    栈和队列区别:

    (1)操作的限定不同:

    是在栈顶进栈顶出,无法对栈底进行直接操作。

    队列是在队尾入队头出,可以对二边进行操作。

    (2)操作的规则不同:

    先进后出,新来的成员从栈顶入,老成员要想离开,就得先让栈顶的成员先离开。

    队列先进先出,新来的成员总是在队尾插入,每次离开的成员都是从队头离开。

    (3)遍历数据速度不同:

    是只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,而且在遍历数据的同时需要为数据开辟临时空间,保持数据在遍历前的一致性

    队列是通过地址指针进行遍历,而且可以从头部或者尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度要快

  • 相关阅读:
    Excel-VBA 快速上手(十一、字符串常用操作)
    SSM实现一个图书销售商场项目网站
    Ansible非标记语言YAML与任务剧本Playbook
    0基础学习PyFlink——用户自定义函数之UDAF
    策略模式在社会中的应用
    PROFINET通信介绍
    深入react源码看setState究竟做了什么?
    opencv_5_图像像素的算术操作
    RabbitMQ队列持久化的重要性与意义
    Linux系统编程-进程间通信(用时一周总结,非常详细,建议收藏)
  • 原文地址:https://blog.csdn.net/qq_61552595/article/details/126698226