• 数据结构与算法5-栈


    目录

    栈的特点

    栈的面试题

    如何理解栈结构

    为什么要使用栈

    栈的分类

    如何实现栈

    算法题:括号匹配

    栈的应用


    栈的特点

    栈有一个特点,先进后出,或者说,后进先出

    栈的面试题

    有一个很经典的面试题,用栈可以比较容易解决,那就是判断括号是不是一对,比如

    {[()]}这样就是一对,{{[)}}不是一对

    这个题,可以用正则啊,可以拆分匹配等等,其实都是可以的,但是不如栈方便

    再比如,做一个历史记录功能,最贴切的,就是浏览器的后退功能,也完全可以用栈实现,点击一步,那就入栈,后退,那就出栈,当然,后退这个地方也需要用一个栈记录,毕竟浏览器有前进有后退,可以来回切换

    如何理解栈结构

    理解栈的结构,其实也很简单,我们进行一个类比,你现在有很多书,摞起来,你放的时候肯定不是放最下面,是放最上面,你拿的时候,也是从最上面开始拿,不是最下面,这也就是上面说的站点特征,后进先出,last in first out,简称LIFO

    栈,其实就是仅仅在表尾进行插入和删除操作的线性表,这一端被称为栈顶,相对的,另一边就是栈底

    往一个栈里插入元素,称为进栈、压栈、入栈,它会把新元素放在栈顶元素的上面,成为新的栈顶元素

    从一个栈里删除元素,称为出栈、退栈,它会把栈顶元素删除,相邻的元素成为新的栈顶元素

    栈,其实就是一个特殊的数组或者链表

    为什么要使用栈

    既然栈是一个特殊的数组或者链表,那为什么还要用栈,栈还有这么些个限制

    仔细想想,其实栈的限制也是一种保护,限制了只能操作这个范围,而不能操作其他的

    栈的分类

    栈分为两种,基于数组的栈和基于链表的栈

    基于数组的栈,通常以数组头为栈底,数组头到数组尾的方向为栈顶的延展方向

    基于单链表的栈,通常以链表头为栈顶,这样方便节点的插入和删除,压栈产生的节点一直出现在链表头

    这两种实现方式最大的区别,就是扩容,基于链表,那就有链表的特性,意味着天然可以扩容,而数组不行,并且因为是从头访问,所以数组和链表的访问复杂度没区别

    但是实际在使用中,我们用更多的还是数组实现的,链表的实现存在栈溢出的风险

    如何实现栈

    因为栈有多种实现,我们先写一个接口

    1. /**
    2. * @author create by YanXi on 2022/9/3 11:00
    3. * @email best.you@icloud.com
    4. * @Description: 数组实现的栈
    5. */
    6. public class AyyayStack implements MyStack{
    7. // 要用数组实现,一定有一个数组
    8. private I[] stack = (I[]) new Object[1];;
    9. // 元素个数
    10. private int itemSize = 0;
    11. public AyyayStack(int initSize) {
    12. if (0 > initSize) {
    13. this.stack = (I[]) new Object[initSize];
    14. }
    15. }
    16. /**
    17. * @Description: 入栈
    18. * @Author: create by YanXi on 2022/9/4 14:36
    19. * @Email: best.you@icloud.com
    20. *
    21. * @param item 插入的参数
    22. * @exception
    23. * @Return: void
    24. */
    25. @Override
    26. public void push(I item) {
    27. // 判断是否需要扩容
    28. if (stack.length <= itemSize) {
    29. resize(itemSize * 2);
    30. }
    31. stack[itemSize++] = item;
    32. }
    33. /**
    34. * @Description: 扩容
    35. * @Author: create by YanXi on 2022/9/4 14:36
    36. * @Email: best.you@icloud.com
    37. *
    38. * @param size
    39. * @exception
    40. * @Return: void
    41. */
    42. public void resize(int size) {
    43. I[] tmp = (I[]) new Object[size];
    44. for (int i = 0; i < itemSize; i++) {
    45. tmp[i] = stack[i];
    46. }
    47. stack = tmp;
    48. }
    49. /**
    50. * @Description: 出栈
    51. * @Author: create by YanXi on 2022/9/4 14:36
    52. * @Email: best.you@icloud.com
    53. *
    54. * @param
    55. * @exception
    56. * @Return: I
    57. */
    58. @Override
    59. public I pop() {
    60. if (isEmpty()) {
    61. return null;
    62. }
    63. // 注意,是 --size,不是size--,一定要先减去在用,因为入栈的时候加了
    64. I item = stack[--itemSize];
    65. stack[itemSize] = null;
    66. // 判断是否需要缩减容量
    67. if (itemSize > 0 && itemSize <= stack.length >> 1){
    68. resize(stack.length >> 1);
    69. }
    70. return item;
    71. }
    72. /**
    73. * @Description: 获取栈大小
    74. * @Author: create by YanXi on 2022/9/4 14:37
    75. * @Email: best.you@icloud.com
    76. *
    77. * @param
    78. * @exception
    79. * @Return: int
    80. */
    81. @Override
    82. public int size() {
    83. return itemSize;
    84. }
    85. /**
    86. * @Description: 栈是否为空
    87. * @Author: create by YanXi on 2022/9/4 14:37
    88. * @Email: best.you@icloud.com
    89. *
    90. * @param
    91. * @exception
    92. * @Return: boolean
    93. */
    94. @Override
    95. public boolean isEmpty() {
    96. return itemSize == 0;
    97. }
    98. }

    算法题:括号匹配

    看一个算法题,也是我们提到的括号匹配问题,是力扣的原题哦~

    给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

    有效字符串需满足:

        左括号必须用相同类型的右括号闭合。
        左括号必须以正确的顺序闭合。
        每个右括号都有一个对应的相同类型的左括号。

    我们用栈来实现一下,就用我们自己实现的栈来实现

    1. /**
    2. * @author create by YanXi on 2022/9/6 6:18
    3. * @email best.you@icloud.com
    4. * @Description: 括号匹配测试
    5. */
    6. public class StackTest {
    7. public static void main(String[] args) {
    8. String need = "(){}[]";
    9. System.out.println(String.format("isMatch(\"%s\") = %s" , need, isMatch(need)));
    10. String need1 = "[(){}[]";
    11. System.out.println(String.format("isMatch(\"%s\") = %s" , need1, isMatch(need1)));
    12. }
    13. public static boolean isMatch(String reg) {
    14. AyyayStack stack = new AyyayStack<>(15);
    15. char[] chars = reg.toCharArray();
    16. Character pop;
    17. for (char aChar : chars) {
    18. switch (aChar){
    19. case '(':
    20. case '[':
    21. case '{':
    22. stack.push(aChar);
    23. break;
    24. case ')':
    25. if (stack.isEmpty()) {
    26. return false;
    27. }
    28. pop = stack.pop();
    29. if (null == pop) {
    30. return false;
    31. } else if ('(' == (pop.charValue())) {
    32. break;
    33. } else {
    34. return false;
    35. }
    36. case ']':
    37. if (stack.isEmpty()) {
    38. return false;
    39. }
    40. pop = stack.pop();
    41. if (null == pop) {
    42. return false;
    43. } else if ('[' == (pop.charValue())) {
    44. break;
    45. } else {
    46. return false;
    47. }
    48. case '}':
    49. if (stack.isEmpty()) {
    50. return false;
    51. }
    52. pop = stack.pop();
    53. if (null == pop) {
    54. return false;
    55. } else if ('{' == (pop.charValue())) {
    56. break;
    57. } else {
    58. return false;
    59. }
    60. default:
    61. return false;
    62. }
    63. }
    64. return stack.isEmpty();
    65. }
    66. }

     时间复杂度为O(n)

    栈的应用

    想一想,哪些地方和栈类似

    最常用的,是不是方法调用,比如A方法调用了B,然后再调用了C,是不是A方法是最后结束的?

    在数学上,计算是不是也是这样,比如 1 + 2*2 - 4/2

    所以说,也可以用栈实现一个简易的计算器

    创建两个栈,一个存数字,一个存运算符

    只要是数字,就入栈,如果是运算符,分为两种情况

            1、当前运算符和运算符栈顶运算符比较,如果比栈顶运算符的优先级高,那就直接入栈

            2、当前运算符和运算符栈顶运算符比较,如果比栈顶运算符的优先级低或者相同,不入栈,并且数字栈出栈两个,这两个数字按照当前运算符运算,运算完成,再将数字结果和后续符号入栈

    仔细想想,是这么个道理不?代入上面写的例子

    1,入数字栈

    +,入符号栈

    2,入数字栈

    *,优先级比+高,入符号栈

    2,入数字栈

    -,比*优先级低,不入栈,取出数字栈两个,也就是2-2,执行,2*2=4,4入数字栈,-入符号栈

    往后的类似

    是不是?

    我们上面还说过,用栈也可以实现浏览器的前进和后退功能,如何实现呢?

    假设浏览网页A,点击B,再点击C,点击D

    可以每次点击都入栈,后退的时候,入另一个栈

    比如我现在在C,栈1应该是ABC,我现在点击后退,C从栈1挪到栈2,页面就到了B

    当我在B,点击前进,栈2出栈,C

    当然,还有一个现象,那就是在B页面,直接点击D,D返回不是C,而是B,所以,当点击了新的地方, 就应该清空栈2

    说白了, 就是用两个栈,来严格维护网页的前面和后面

    这就是栈大概的总结,栈是一个很神奇的结构,也是一个很好用的结构,用的好的话,能给我们一些场景简化很多,带来很多便利

  • 相关阅读:
    为什么商铺门店一定要做预约软件?
    SpringBoot 条件注解之:自定义条件注解
    共建共享数字世界的根:阿里云打造全面的云原生开源生态
    Linux - 基本开发工具
    移动端开发
    Codeforces Round 515
    链路聚合-静态和动态区别
    byName自动装配和byType自动装配
    2022最新版-李宏毅机器学习深度学习课程-P49 GPT的野望
    JMeter 常见函数讲解
  • 原文地址:https://blog.csdn.net/weixin_46097842/article/details/126571383