• leetCode-栈类型详解


    在刷题的时候遇到一个后缀表达式的题目,然后感觉该题是比较经典的栈应用类型的题目,正好借着该题,给大家聊一聊栈的用法,顺便解决了这道题。

    众所周知,栈是是一个先进后出的数据结构,利用该特性,在程序的设计中十分常见,如:括号匹配、表达式求值、在递归中的应用等都离不开栈这一数据结构。

    我下面一一给大家简单说一下。(PS:栈的数据结构我就不再此处赘述,大家可以自行了解)

    1.栈在括号匹配中的应用

    假设表达式中允许包含两种括号 :|直|括号和方括号 ,其嵌套 的顺序任意 即([]())或[([][ ])]等均为正确的格式,[(])或([())或(()]均为不正确的格式 。

    我们考虑下列括号序列:
    在这里插入图片描述

    我们简单分析一下:

    • 计算机接收第1个括号“ [ ” 后 ,期待与之匹配的第 8 个括号“ ]” 出现 。
    • 获得了第 2 个括号 “(”,此时第1 个括号“[” 暂时放在一边,而急迫期待与之匹配的第7 个括号“)”出现 。
    • 获得了第 3 个括号“ [ ”,此时第 2 个括 号“(” 暂时放在一边,而急迫期待与之匹配的第4 个括号“]”出现 。第 3 个括号 的期待得到满足,消解之后 ,第 2 个括号 的期待匹配又成为 当前最急迫的任务。
    • 以此类推,可见该处理过程与栈的思想、吻合。

    我们就按照上面的过程,可以给出以下设计思想:

    • 初始化一个堆栈stack,开始依次扫描括号字符串string,当遇到[ ’ 、 ( ’ 左括号时将其压入栈中。
    • 当遇到右括号时将检查栈顶元素是否为与此右括号对应的左括号,若对应,则将其对应左括号出栈,若不对应,则表达式有误,返回false。
    • 在过程中只有左括号才会压入栈内,当表达式正确时,最后栈将一次次左括号出栈而变为空栈。
    • 扫描完所有字符后,检查堆栈是否为空栈,若是,返回true,否则返回false。

    题目来源:20. 有效的括号
    答案:(PS:Java版)

        public boolean isValid(String s) {
            Stack<Character> stack = new Stack<Character>();
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (c == '(' || c == '[' || c == '{') {
                    stack.push(c);
                } else {
                    if (stack.isEmpty()) return false;
                    char topChar = stack.pop();
                    if (c == ')' && topChar != '(') return false;
                    if (c == ']' && topChar != '[') return false;
                    if (c == '}' && topChar != '{') return false;
                }
            }
            //判断是否栈空
            if (stack.isEmpty()) return true;
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.栈在表达式求值中的应用

    表达式求值是程序设计语言编译 中 一个最基本 的问题,它的实现是找应用的一个典型范例 。

    中缀表达式不仅依赖运算符的优先级,而且还要处理括号 。后缀表达式的运算符在操作数后面,在后缀表达式中己考虑了运算符的优先级,没有括号,只有操作数和运算符。

    中缀表达式A+B*(C-D)-E/F所对应的后缀表达式为ABCD-*EF/-。

    里面就涉及到一个知识点,中缀即我们正常的运算顺序如何变为后缀表达式呢?如果你数据结构学的还不错可以跳过该节。

    2.1 中缀表达式转化为后缀表达式

    我直接举个例子吧。
    中缀表达式 a+b - a* ( (c+d) /e- f) + g
    这个式子应该相对比较有代表性了,我们就看看怎么把它变成后缀的。

    我们先确定好运算符的优先级,即 ‘*,/’ > ‘±’>‘()’
    对于(),左括号只有在遇到右括号则表示括号匹配成功,直接退栈,左括号遇到其他字符,直接进栈。
    右括号,在遇到其他字符,会让其他字符一直退栈,一直退到左括号为止,和左括号一同出栈,不进行输出。

    下面我们的操作步骤就是:遇到优先级大的,直接进栈,遇到优先级小的或者相等的就退栈(PS:如果符号一样,无需操作,直接进栈),同时让优先级进栈。

    同时,如果遇到数值则直接输出
    下面我们看看:a+b - a* ( (c+d) /e- f) + g这个式子的后缀是怎么一步步实现的

    步骤扫描项项类型动作栈内容输出
    1a数值直接输出a
    2+操作符栈空,无须比较优先级,进栈+无输出
    3b数值直接输出+b
    4-操作符+和-优先级相同,先执行退栈,即输出+,在让-进栈-+
    5a操作数直接输出-a
    6*操作符*比-优先级高,直接进栈-*无输出
    7操作符直接进栈-*(无输出
    8(操作符直接进栈-*((无输出
    9c操作数直接输出-*((c
    10+操作符+优先级>(,直接入栈-*((+无输出
    11d操作数直接输出-*((+d
    12)操作符让栈顶元素退栈,直至遇到(为止-*(+
    13/操作符优先级比(高,进栈-*(/无输出
    14e操作数直接输出-*(/e
    15-操作符-比/优先级低,/退栈,-进栈-*(-/
    16f操作数直接输出-*(-f
    17)操作符让栈顶元素一直输出,同时和(一起退栈-*-
    18+操作符优先级比*低,*退栈并输出,和-优先级相同,-退栈并输出,+进栈+* -
    19g操作数直接输出+g
    20扫描字符串结束,让栈内元素依次退栈-直接退栈输出空栈+

    最后将输出元素依次连接起来:

    ab+acd+e/f-*-g+

    这个式子就是我们的后缀表达式

    2.2 后缀表达式的计算

    最后开始进行计算,而我们最经常使用的便是通过后缀表达式计算最终结果,计算过程如下:

    步骤扫描项项类型动作栈内容
    1--置空栈
    2a操作数进栈a
    2b操作数进栈a b
    3+操作符b,a依次退栈,计算结果b+a,结果r1进栈r1
    4a操作数进栈r1 a
    5c操作数进栈r1 a c
    6d操作数进栈r1 a c d
    7+操作符d,c依次退栈,计算结果d+c,结果r2进栈r1 a r2
    8e操作数进栈r1 a r2 e
    9/操作符e,r2依次退栈,计算e/r2,结果r3进栈r1 a r3
    10f操作数直接进栈r1 a r3 f
    11-操作符f,r3依次退栈,计算f-r3,结果r4进栈r1 a r4
    12*操作符r4 a依次退栈,计算a*r4,结果r5进栈r1 r5
    13-操作符r5,r1依次退栈,计算r5-r1,结果r6进栈r6
    14g操作数进栈r6 g
    15+操作符g r6依次退栈,计算g+r6,结果r7进栈r7

    最终的结果即为栈中的最后一个元素。

        public int evalRPN(String[] tokens) {
            Stack<Integer> stack = new Stack<Integer>();
            int n = tokens.length;
            for (int i = 0; i < n; i++) {
                String token = tokens[i];
                if (isNumber(token)) {
                    stack.push(Integer.parseInt(token));
                } else {
                    int num2 = stack.pop();
                    int num1 = stack.pop();
                    switch (token) {
                        case "+":
                            stack.push(num1 + num2);
                            break;
                        case "-":
                            stack.push(num1 - num2);
                            break;
                        case "*":
                            stack.push(num1 * num2);
                            break;
                        case "/":
                            stack.push(num1 / num2);
                            break;
                        default:
                    }
                }
            }
            return stack.pop();
        }
    
        public boolean isNumber(String token) {
            return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    参考题目:剑指 Offer II 036. 后缀表达式

  • 相关阅读:
    SSM+Vue+Element-UI实现教资考前指导系统
    名词解析与经验分享(前端)
    Transformers的RoBERTa model怎么使用word level的tokenizer
    套接字选项
    android上架备案公钥和md5获取工具
    路由器漏洞的分类
    【力扣10天SQL入门】Day5+6 合并表
    Sklearn基础教程
    设计模式(十):抽象工厂模式(创建型模式)
    MinDoc文档管理系统在宝塔环境安装教程
  • 原文地址:https://blog.csdn.net/zhiyikeji/article/details/125508011