【算法入门-栈】逆波兰表达式求值


今天又刷了一道题,奥利给
刷题地址: 点击跳转

✅描述

给定一个逆波兰表达式,求表达式的值。

数据范围:表达式长度满足 1≤𝑛≤104 1≤n≤104 ,表达式中仅包含数字和 + ,- , * , / ,其中数字的大小满足 ∣𝑣𝑎𝑙∣≤200 ∣val∣≤200 。

示例1

输入:

["2","1","+","4","*"]

返回值:

12

示例2

输入:

["2","0","+"]

返回值:

2

✅扩展:什么是逆波兰表达式

表达式一般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间,这称为中缀表达式(Infix Expression),如A+B。

波兰数学家Jan Lukasiewicz提出了另一种数学表示法,它有两种表示形式:

把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB;把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+;前后缀表达式的出现是为了方便计算机处理,它的运算符是按照一定的顺序出现,所以求值过程中并不需要使用括号来指定运算顺序,也不需要考虑运算符号(比如加减乘除)的优先级。

先介绍中简单的人工转化方法:

假设有一个中缀表达式a+b*c-(d+e):

  1. 首先将这个中缀表达式的所有运算加括号((a+(b*c))-(d+e))
  2. 然后将所有运算符放到括号后面,这样就变成了((a(bc)* )+ (de)+ )-
  3. 把所有括号去掉abc*+de±,最后得出的结果就是后缀表达式。

✅题解方法一:栈

具体做法:

逆波兰表达式可以看成一种后序表达式,只需要在遇到符号的时候计算它前面两个数字即可,因此可以使用栈的先进后出原理。

遍历整个字符串数组,遇到数字就将其从字符串转变成int数字,然后加入栈中等待计算。遇到符号先取出栈中最后一位,然后与取出后的最后一位计算,结果存入最后一位,如下图所示:

alt

public int evalRPN (String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if (tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")) {
                if (tokens[i].equals("+")) {
                    stack.push(stack.pop() + stack.pop());
                }else if (tokens[i].equals("-")) {
                    stack.push(-stack.pop() + stack.pop());
                }else if (tokens[i].equals("*")) {
                    stack.push(stack.pop() * stack.pop());
                }else if (tokens[i].equals("/")) {
                    int a = stack.pop();
                    int b = stack.pop();
                    stack.push(b / a);
                }
            }else {
                stack.push(Integer.parseInt(tokens[i]));
            }
        }
        return stack.pop();
    }

复杂度分析:

  • 时间复杂度:𝑂(𝑛)O(n),遍历整个字符串数组
  • 空间复杂度:𝑂(𝑛)O(n),栈空间最大为数组长度,即全是数字

✅题解方法二(数组模拟栈)

与方法一的思路差不多,不过可以考虑使用数组来模拟栈。

假设逆波兰表达式中总共有n个元素,则n必定是奇数,并且数字的个数恰好比运算符个数多1。因为起初只有1个数字时,每加一个运算符,必定会加1个数字,依次类推,数字恰好比运算符多1。所以数字个数有(𝑛+1)/2(n+1)/2个,运算符个数有(𝑛−1)/2(n−1)/2个。用栈模拟的过程中,每次遇到数字,直接压入栈中,栈的容量会加1,遇到运算符时,先弹出两个元素,做运算后再压入栈中,栈的容量会减1。最坏情况下,数字全在前面,运算符全在后面,栈的容量最多达到(𝑛+1)/2(n+1)/2。

我们初始化arr数组的容量为(𝑛+1)/2(n+1)/2。用一个变量index指向栈顶的位置,index为-1表示栈容量为0。当压栈时,index加1,再将元素赋给当前位置,弹栈时,index减1即可。

public int evalRPN (String[] tokens) {
       int n = tokens.length;
       int[] arr = new int[(n+1)/2];
       int index = -1;
        for (String token : tokens) {
            if (!(token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/"))){
                arr[++index] = Integer.parseInt(token);
            }else {
                if (token.equals("+")){
                    index--;
                    arr[index] += arr[index+1];
                }else if (token.equals("-")){
                    index--;
                    arr[index] -= arr[index+1];
                }else if (token.equals("*")){
                    index--;
                    arr[index] *= arr[index+1];
                }else if (token.equals("/")){
                    index--;
                    arr[index] /= arr[index+1];
                }
            }
        }
        return arr[0];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小满@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值