当前位置:   article > 正文

【数据结构】详解栈

【数据结构】详解栈

今天我们主要来了解栈!如果对知识点有模糊,可翻阅以往文章哦!

个人主页小八哥向前冲~-CSDN博客

所属专栏:数据结构【c语言版】_小八哥向前冲~的博客-CSDN博客

c语言专栏:c语言_小八哥向前冲~的博客-CSDN博客

值得注意的是,如果你十分了解顺序表和链表,今天这期会很轻松哦!

哈哈哈哈!当然,这期也能检测你对顺序表和链表的理解!一起看看吧!

目录

栈的定义

顺序表和链表的比较

栈的实现--顺序表

初始化

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

题目操练:配括号问题

栈的实现--链表

栈为空的判定

入栈

出栈

销毁

栈顶数据

数据个数

码源--栈(顺序表)

码源--栈(链表)


栈的定义

  • :一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
  • 出栈:栈的删除操作叫做出栈。出数据也在栈顶

上图理解一下:

注意:遵循后进先出的原则!

知道了这个原则,我们来巩固一下:

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

显然:1.B      2.C

相信第一题不难,我们解释一下第二题:看到C选项,1,2,3进栈后,3出栈,而第二次出栈的只能是2或4,不可能是1,所以C错误!

了解了栈的概念,我们实现这个栈是使用顺序表还是链表呢?

  • 如果是顺序表的话,我们的栈顶应该要在数组末尾!如果在数组头部的话,数据进栈时还需要挪动其余数据以便数据的存入!效率很低
  • 如果是链表的话,我们的栈顶要在链表的头入栈时,头插即可!如果栈顶在链表尾部的话,虽然入栈尾插即可,但需要遍历,效率低,那么这时就需要使用双链表!

综上所述,我们栈使用顺序表较好!(两种都实现看看)

上图看看它们:

为了更好透彻了解顺序表和链表,我们将它们比较看看!

顺序表和链表的比较

图文更加直观:

这里的缓存利用率不做过多解释,详情见https://www.cnblogs.com/yungyu16/p/13054874.html

栈的实现--顺序表

既然是要在顺序表基础上实现栈,那么就要实现顺序表和栈的基本框架。

(单链表若有不懂的知识点,可见:通讯录的实现(顺序表版本)-CSDN博客

stack.h文件--包含各种需要的函数

栈里面的变量:top表示栈顶下标,capacity表示栈空间。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. #include<stdbool.h>
  5. typedef int STDateType;
  6. //栈
  7. typedef struct stack
  8. {
  9. STDateType* a;
  10. int top;
  11. int capacity;
  12. }ST;
  13. //栈的初始化和销毁
  14. void STInit(ST* p);
  15. void STDestroy(ST* p);
  16. //入栈,出栈
  17. void STpush(ST* p,STDateType x);
  18. void STpop(ST* p);
  19. //栈顶的数据
  20. STDateType STtop(ST* p);
  21. //栈的数据个数
  22. int STsize(ST* p);
  23. //判空
  24. bool STEmpty(ST* p);

接下来我们一一实现!

初始化

我们要将栈中各个变量进行初始化。

  1. void STInit(ST* p)
  2. {
  3. assert(p);
  4. p->a = NULL;
  5. p->capacity = 0;
  6. p->top = 0;
  7. }

栈为空的判定

我们在实现这个函数时,很多人会用 if语句来判断是否为空,但我们仔细一想,可以好好优化一下代码!

  1. //判空
  2. bool STEmpty(ST* p)
  3. {
  4. assert(p);
  5. return p->top == 0;
  6. }

入栈

经过我们刚刚的分析,入栈要在数组尾部!记得每次入栈需要判断空间是否够用哦!

  1. void STpush(ST* p, STDateType x)
  2. {
  3. assert(p);
  4. if (p->top == p->capacity)
  5. {
  6. int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
  7. STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
  8. if (tmp == NULL)
  9. {
  10. perror("realloc failed!");
  11. return;
  12. }
  13. p->a = tmp;
  14. p->capacity = newcapacity;
  15. }
  16. p->a[p->top++] = x;
  17. }

出栈

入栈要在尾部,出栈也要在尾部,后进先出的原则要时刻记住!

需要注意的是:当栈为空时,数据出不了栈!所以我们先需要判断是否为空!

  1. void STpop(ST* p)
  2. {
  3. assert(p);
  4. //出栈的话要判断一下空的情况
  5. assert(!STEmpty(p));
  6. p->top--;
  7. }

销毁

我们既然用了开辟内存函数,当我们不使用栈时,要将空间释放掉!

  1. void STDestroy(ST* p)
  2. {
  3. assert(p);
  4. free(p->a);
  5. p->capacity = p->top = 0;
  6. }

栈顶数据

在访问栈顶数据时,我们也要先判断栈是否为空,否则当栈为空时,访问栈顶数据便会越界访问!

  1. //栈顶的数据
  2. STDateType STtop(ST* p)
  3. {
  4. assert(p);
  5. //出栈的话要判断一下空的情况
  6. assert(!STEmpty(p));
  7. return p->a[p->top-1];
  8. }

数据个数

  1. //栈的数据个数
  2. int STsize(ST* p)
  3. {
  4. assert(p);
  5. return p->top;
  6. }

题目操练:配括号问题

既然我们已经实现的栈,我们来应用一下吧!

题目:详情--20. 有效的括号 - 力扣(LeetCode)

思路:

遍历数组,当是左括号时("(","{","】")时就入栈,当不是左括号时就出栈比较,直到遍历完成!

这样听是不是很简单呢?当然里面没有栈,我们需要将栈创建一下!

代码:

  1. typedef char STDateType;
  2. //栈
  3. typedef struct stack
  4. {
  5. STDateType* a;
  6. int top;
  7. int capacity;
  8. }ST;
  9. //栈的初始化和销毁
  10. void STInit(ST* p);
  11. void STDestroy(ST* p);
  12. //入栈,出栈
  13. void STpush(ST* p,STDateType x);
  14. void STpop(ST* p);
  15. //栈顶的数据
  16. STDateType STtop(ST* p);
  17. //栈的数据个数
  18. int STsize(ST* p);
  19. //判空
  20. bool STEmpty(ST* p);
  21. //栈的初始化和销毁
  22. void STInit(ST* p)
  23. {
  24. assert(p);
  25. p->a = NULL;
  26. p->capacity = 0;
  27. p->top = 0;
  28. }
  29. void STDestroy(ST* p)
  30. {
  31. assert(p);
  32. free(p->a);
  33. p->capacity = p->top = 0;
  34. }
  35. //入栈,出栈
  36. void STpush(ST* p, STDateType x)
  37. {
  38. assert(p);
  39. if (p->top == p->capacity)
  40. {
  41. int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
  42. STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
  43. if (tmp == NULL)
  44. {
  45. perror("realloc failed!");
  46. return;
  47. }
  48. p->a = tmp;
  49. p->capacity = newcapacity;
  50. }
  51. p->a[p->top++] = x;
  52. }
  53. void STpop(ST* p)
  54. {
  55. assert(p);
  56. //出栈的话要判断一下空的情况
  57. assert(!STEmpty(p));
  58. p->top--;
  59. }
  60. //栈顶的数据
  61. STDateType STtop(ST* p)
  62. {
  63. assert(p);
  64. //出栈的话要判断一下空的情况
  65. assert(!STEmpty(p));
  66. return p->a[p->top-1];
  67. }
  68. //栈的数据个数
  69. int STsize(ST* p)
  70. {
  71. assert(p);
  72. return p->top;
  73. }
  74. //判空
  75. bool STEmpty(ST* p)
  76. {
  77. assert(p);
  78. return p->top == 0;
  79. }
  80. bool isValid(char* s) {
  81. ST st;
  82. STInit(&st);
  83. while(*s)
  84. {
  85. //左括号入栈
  86. if(*s=='('||*s=='['||*s=='{')
  87. {
  88. STpush(&st,*s);
  89. }
  90. else
  91. //不是左括号出栈比较
  92. {
  93. if(STEmpty(&st))
  94. {
  95. STDestroy(&st);
  96. return false;
  97. }
  98. char top=STtop(&st);
  99. STpop(&st);
  100. if(top=='('&&*s!=')'
  101. ||top=='['&&*s!=']'
  102. ||top=='{'&&*s!='}')
  103. {
  104. STDestroy(&st);
  105. return false;
  106. }
  107. }
  108. s++;
  109. }
  110. //当栈中没有左括号比较完时,便匹配不成
  111. bool ret=STEmpty(&st);
  112. STDestroy(&st);
  113. return ret;
  114. }

可能有人说太麻烦了,但c语言中没有栈,只能自己创建哦!这是用目前c语言最简单的方法!

栈的实现--链表

和顺序表一样,我们首先要创建栈和链表的基本框架!

stack.h文件--包含需要的函数

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. #include<stdbool.h>
  5. typedef int STDataType;
  6. //栈
  7. typedef struct stack
  8. {
  9. struct stcak* next;
  10. STDataType data;
  11. }STNode;
  12. //栈的销毁
  13. void STDestroy(STNode* phead);
  14. //入栈
  15. void STpush(STNode** pphead,STDataType x);
  16. //出栈
  17. void STpop(STNode** pphead);
  18. //栈顶数据
  19. STDataType STtop(STNode* phead);
  20. //判空
  21. bool STEmpty(STNode* phead);
  22. //栈数据个数
  23. int STsize(STNode* phead);

栈为空的判定

有了顺序表的基础,接下来依葫芦画瓢----最简单不过!

  1. //判空
  2. bool STEmpty(STNode* phead)
  3. {
  4. return phead == NULL;
  5. }

入栈

我们分析将链表的头部为栈顶,进出都在头!(这种方案最佳!)

  1. //创建节点
  2. STNode* STBuyNode(STDataType x)
  3. {
  4. STNode* node = (STNode*)malloc(sizeof(STNode));
  5. if (node == NULL)
  6. {
  7. perror("malloc failed!");
  8. return NULL;
  9. }
  10. node->data = x;
  11. node->next = NULL;
  12. return node;
  13. }
  14. //入栈
  15. void STpush(STNode** pphead, STDataType x)
  16. {
  17. STNode* node = STBuyNode(x);
  18. node->next = *pphead;
  19. *pphead = node;
  20. }

出栈

将头节点指向下一个节点,原来的头节点释放!

  1. //出栈
  2. void STpop(STNode** pphead)
  3. {
  4. assert(!STEmpty(*pphead));
  5. STNode* cur = *pphead;
  6. *pphead = (*pphead)->next;
  7. free(cur);
  8. }

销毁

同样的,动态开辟了空间,当我们不用栈时,要将开辟的空间释放掉!

  1. //栈的销毁
  2. void STDestroy(STNode* phead)
  3. {
  4. STNode* cur = phead;
  5. while (cur)
  6. {
  7. STNode* next = cur->next;
  8. free(cur);
  9. cur = next;
  10. }
  11. }

栈顶数据

也是一样的,在访问栈顶数据时,要判断栈是否为空,防止越界访问!

  1. //栈顶数据
  2. STDataType STtop(STNode* phead)
  3. {
  4. assert(!STEmpty(phead));
  5. return phead->data;
  6. }

数据个数

遍历链表,将一个一个节点计数起来!

  1. //栈数据个数
  2. int STsize(STNode* phead)
  3. {
  4. STNode* cur = phead;
  5. int count = 0;
  6. while (cur)
  7. {
  8. count++;
  9. cur = cur->next;
  10. }
  11. return count;
  12. }

码源--栈(顺序表)

stack.h文件

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. #include<stdbool.h>
  5. typedef int STDateType;
  6. //栈
  7. typedef struct stack
  8. {
  9. STDateType* a;
  10. int top;
  11. int capacity;
  12. }ST;
  13. //栈的初始化和销毁
  14. void STInit(ST* p);
  15. void STDestroy(ST* p);
  16. //入栈,出栈
  17. void STpush(ST* p,STDateType x);
  18. void STpop(ST* p);
  19. //栈顶的数据
  20. STDateType STtop(ST* p);
  21. //栈的数据个数
  22. int STsize(ST* p);
  23. //判空
  24. bool STEmpty(ST* p);

stack.c文件

  1. #include"stack.h"
  2. //栈的初始化和销毁
  3. void STInit(ST* p)
  4. {
  5. assert(p);
  6. p->a = NULL;
  7. p->capacity = 0;
  8. p->top = 0;
  9. }
  10. void STDestroy(ST* p)
  11. {
  12. assert(p);
  13. free(p->a);
  14. p->capacity = p->top = 0;
  15. }
  16. //入栈,出栈
  17. void STpush(ST* p, STDateType x)
  18. {
  19. assert(p);
  20. if (p->top == p->capacity)
  21. {
  22. int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
  23. STDateType* tmp = (STDateType*)realloc(p->a, newcapacity * sizeof(STDateType));
  24. if (tmp == NULL)
  25. {
  26. perror("realloc failed!");
  27. return;
  28. }
  29. p->a = tmp;
  30. p->capacity = newcapacity;
  31. }
  32. p->a[p->top++] = x;
  33. }
  34. void STpop(ST* p)
  35. {
  36. assert(p);
  37. //出栈的话要判断一下空的情况
  38. assert(!STEmpty(p));
  39. p->top--;
  40. }
  41. //栈顶的数据
  42. STDateType STtop(ST* p)
  43. {
  44. assert(p);
  45. //出栈的话要判断一下空的情况
  46. assert(!STEmpty(p));
  47. return p->a[p->top-1];
  48. }
  49. //栈的数据个数
  50. int STsize(ST* p)
  51. {
  52. assert(p);
  53. return p->top;
  54. }
  55. //判空
  56. bool STEmpty(ST* p)
  57. {
  58. assert(p);
  59. return p->top == 0;
  60. }

码源--栈(链表)

stack.h文件

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. #include<stdbool.h>
  5. typedef int STDataType;
  6. //栈
  7. typedef struct stack
  8. {
  9. struct stcak* next;
  10. STDataType data;
  11. }STNode;
  12. //栈的销毁
  13. void STDestroy(STNode* phead);
  14. //入栈
  15. void STpush(STNode** pphead,STDataType x);
  16. //出栈
  17. void STpop(STNode** pphead);
  18. //栈顶数据
  19. STDataType STtop(STNode* phead);
  20. //判空
  21. bool STEmpty(STNode* phead);
  22. //栈数据个数
  23. int STsize(STNode* phead);

stack.c文件

  1. #include"stack.h"
  2. STNode* STBuyNode(STDataType x)
  3. {
  4. STNode* node = (STNode*)malloc(sizeof(STNode));
  5. if (node == NULL)
  6. {
  7. perror("malloc failed!");
  8. return NULL;
  9. }
  10. node->data = x;
  11. node->next = NULL;
  12. return node;
  13. }
  14. //入栈
  15. void STpush(STNode** pphead, STDataType x)
  16. {
  17. STNode* node = STBuyNode(x);
  18. node->next = *pphead;
  19. *pphead = node;
  20. }
  21. //出栈
  22. void STpop(STNode** pphead)
  23. {
  24. assert(!STEmpty(*pphead));
  25. STNode* cur = *pphead;
  26. *pphead = (*pphead)->next;
  27. free(cur);
  28. }
  29. //栈顶数据
  30. STDataType STtop(STNode* phead)
  31. {
  32. assert(!STEmpty(phead));
  33. return phead->data;
  34. }
  35. //判空
  36. bool STEmpty(STNode* phead)
  37. {
  38. return phead == NULL;
  39. }
  40. //栈数据个数
  41. int STsize(STNode* phead)
  42. {
  43. STNode* cur = phead;
  44. int count = 0;
  45. while (cur)
  46. {
  47. count++;
  48. cur = cur->next;
  49. }
  50. return count;
  51. }
  52. //栈的销毁
  53. void STDestroy(STNode* phead)
  54. {
  55. STNode* cur = phead;
  56. while (cur)
  57. {
  58. STNode* next = cur->next;
  59. free(cur);
  60. cur = next;
  61. }
  62. }

是不是觉得今天的比较简单?好了,我们下期见!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/569466
推荐阅读
相关标签
  

闽ICP备14008679号