栈题目:逆波兰表达式求值

题目

标题和出处

标题:逆波兰表达式求值

出处:150. 逆波兰表达式求值

难度

4 级

题目描述

要求

给定一个字符串数组 tokens \texttt{tokens} tokens,表示一个逆波兰表达式。

计算表达式的值。返回表示表达式结果的一个整数。

逆波兰表达式的规则如下:

  • 有效的运算符包括 ‘+’ \texttt{`+'} ‘+’ ‘-’ \texttt{`-'} ‘-’ ‘*’ \texttt{`*'} ‘*’ ‘/’ \texttt{`/'} ‘/’
  • 每个运算对象可以是整数,也可以是另一个逆波兰表达式。
  • 两个整数之间的除法总是向零取整
  • 不会出现除数为零的情况。
  • 输入表示有效的逆波兰表达式。
  • 答案和所有中间结果在 32 \texttt{32} 32 位有符号整数范围内。

示例

示例 1:

输入: tokens   =   ["2","1","+","3","*"] \texttt{tokens = ["2","1","+","3","*"]} tokens = ["2","1","+","3","*"]
输出: 9 \texttt{9} 9
解释: ((2   +   1)   *   3)   =   9 \texttt{((2 + 1) * 3) = 9} ((2 + 1) * 3) = 9

示例 2:

输入: tokens   =   ["4","13","5","/","+"] \texttt{tokens = ["4","13","5","/","+"]} tokens = ["4","13","5","/","+"]
输出: 6 \texttt{6} 6
解释: (4   +   (13   /   5))   =   6 \texttt{(4 + (13 / 5)) = 6} (4 + (13 / 5)) = 6

示例 3:

输入: tokens   =   ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] \texttt{tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]} tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出: 22 \texttt{22} 22
解释:
((10   *   (6   /   ((9   +   3)   *   -11)))   +   17)   +   5 =   ((10   *   (6   /   (12   *   -11)))   +   17)   +   5 =   ((10   *   (6   /   -132))   +   17)   +   5 =   ((10   *   0)   +   17)   +   5 =   (0   +   17)   +   5 =   17   +   5 =   22 \begin{aligned} & \quad \texttt{((10 * (6 / ((9 + 3) * -11))) + 17) + 5} \\ & \texttt{= ((10 * (6 / (12 * -11))) + 17) + 5} \\ & \texttt{= ((10 * (6 / -132)) + 17) + 5} \\ & \texttt{= ((10 * 0) + 17) + 5} \\ & \texttt{= (0 + 17) + 5} \\ & \texttt{= 17 + 5} \\ & \texttt{= 22} \end{aligned} ((10 * (6 / ((9 + 3) * -11))) + 17) + 5= ((10 * (6 / (12 * -11))) + 17) + 5= ((10 * (6 / -132)) + 17) + 5= ((10 * 0) + 17) + 5= (0 + 17) + 5= 17 + 5= 22

数据范围

  • 1 ≤ tokens.length ≤ 10 4 \texttt{1} \le \texttt{tokens.length} \le \texttt{10}^\texttt{4} 1tokens.length104
  • tokens[i] \texttt{tokens[i]} tokens[i] 或者是一个运算符( "+" \texttt{"+"} "+" "-" \texttt{"-"} "-" "*" \texttt{"*"} "*" "/" \texttt{"/"} "/"),或者是一个在范围 [-200,   200] \texttt{[-200, 200]} [-200, 200] 内的整数

解法一

思路和算法

逆波兰表达式又称后缀表达式,由波兰的逻辑学家 J・卢卡西维兹于 1929 年提出。逆波兰表达式的特点是:没有括号,运算符总是放在和它相关的操作数之后,严格遵循从左到右的运算。

计算逆波兰表达式的值时需要使用栈存储操作数,从左到右遍历并计算。具体操作如下:

  • 如果遇到操作数,则将操作数入栈;

  • 如果遇到运算符,则将两个操作数出栈,其中先出栈的是第二个操作数,后出栈的是第一个操作数,使用运算符对两个操作数运算,得到新操作数,将新操作数入栈。

遍历结束之后,栈内只有一个元素,该元素即为逆波兰表达式的值。

下图为示例 1 的计算逆波兰表达式的过程。

在这里插入图片描述

代码

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int length = tokens.length;
        for (int i = 0; i < length; 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 Character.isDigit(token.charAt(token.length() - 1));
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。需要遍历数组 tokens \textit{tokens} tokens 一次,计算逆波兰表达式的值。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。空间复杂度主要取决于栈空间,栈内元素个数不会超过逆波兰表达式的长度。

解法二

思路和算法

解法一使用栈存储操作数。也可以使用数组模拟栈操作。

对于长度为 n n n 的逆波兰表达式,其中有 n + 1 2 \dfrac{n + 1}{2} 2n+1 个操作数和 n − 1 2 \dfrac{n - 1}{2} 2n1 个运算符。当遇到操作数时,将操作数入栈,栈内元素个数加 1 1 1;当遇到运算符时,将 2 2 2 个操作数出栈,使用运算符运算后得到 1 1 1 个新的操作数并入栈,因此栈内元素个数减 1 1 1。根据上述分析可知,栈内元素个数不会超过原始逆波兰表达式中的操作数个数,即任何时候栈内元素个数不会超过 n + 1 2 \dfrac{n + 1}{2} 2n+1。因此,使用数组模拟栈操作时,将数组的长度定义为 n + 1 2 \dfrac{n + 1}{2} 2n+1 即可。

数组的左端即下标 0 0 0 的位置为栈底。使用 top \textit{top} top 表示栈顶元素所在下标,初始时 top = − 1 \textit{top} = -1 top=1,表示栈为空。

当元素入栈时,首先将 top \textit{top} top 的值加 1 1 1,然后将入栈元素赋值到下标 top \textit{top} top 处。

当元素出栈时,首先获得下标 top \textit{top} top 处的元素,然后将 top \textit{top} top 的值减 1 1 1

遍历结束之后,栈内只有一个元素,此时 top = 0 \textit{top} = 0 top=0,位于下标 0 0 0 处的元素即为逆波兰表达式的值。

代码

class Solution {
    public int evalRPN(String[] tokens) {
        int length = tokens.length;
        int[] stack = new int[(length + 1) / 2];
        int top = -1;
        for (int i = 0; i < length; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack[++top] = Integer.parseInt(token);
            } else {
                int num2 = stack[top--];
                int num1 = stack[top--];
                switch (token) {
                case "+":
                    stack[++top] = num1 + num2;
                    break;
                case "-":
                    stack[++top] = num1 - num2;
                    break;
                case "*":
                    stack[++top] = num1 * num2;
                    break;
                case "/":
                    stack[++top] = num1 / num2;
                    break;
                default:
                }
            }
        }
        return stack[top];
    }

    public boolean isNumber(String token) {
        return Character.isDigit(token.charAt(token.length() - 1));
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。需要遍历数组 tokens \textit{tokens} tokens 一次,计算逆波兰表达式的值。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。空间复杂度主要取决于模拟栈操作的数组,其长度为 n + 1 2 \dfrac{n + 1}{2} 2n+1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伟大的车尔尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值