leetCode-栈类型详解

43 篇文章 1 订阅
30 篇文章 2 订阅

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

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

我下面一一给大家简单说一下。(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;
    }

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));
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZNineSun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值