150 逆波兰表达式求值

2 分钟阅读

标签:

题目

题目链接:150 逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。  

示例 1:

输入: ["2", "1", "+", "3", "*"]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9 示例 2:

输入: ["4", "13", "5", "/", "+"]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6 示例 3:

输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
输出: 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  

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。

逆波兰表达式主要有以下两个优点:

去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。

思路

  1. 由题意可以得到的思路:

    1. 当遇到运算符时,需要将运算符前 2 个数字与当前运算符进行计算,并生成一个中间结果

    2. 这个中间结果也是可参与计算的数字,因此,需将其保存下来

  2. 用栈的实现方式就是:
    1. 遇到运算符,弹出栈顶的两个元素,与当前运算符做运算。并将计算结果压入栈
    2. 遇到括号,由题意可知,有无括号不影响计算结果,可以直接 contiue
    3. 遇到数字,压入栈
  3. 🌰:["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]

    stack t computing
    []    
    ['10'] 10 是数字,直接入栈
    ['10', '6'] 6 同上
    ['10', '6', '9'] 9 同上
    ['10', '6', '9', '3'] 3 同上
    ['10', '6', '12'] + 是运算符,计算 9 + 3 = 12,将 12 压入栈
    ['10', '6', '12', '-11'] -11 是数字,直接入栈
    ['10', '6', '-132'] * 是运算符,计算 12 * (-11) = -132,将 -132 压入栈
    ['10', '0'] / 是运算符,计算 6 / (-132) = 0,将 0 压入栈
    ['0'] * 是运算符,计算 10 * 0 = 0,将 0 压入栈
    ['0', '17'] 17 是数字,直接入栈
    ['17'] + 是运算符,计算 0 + 17 = 17,将 17 压入栈
    ['17', '5'] 5 是数字,直接入栈
    ['22'] + 是运算符,计算 17 + 5 = 22,将 22 压入栈

代码

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        op = {'+', '-', '*', '/'}
        op2 = {'(', ')'}
        res = 0
        for t in tokens:
            # 1. 遇到运算符
            if t in op:
                n1 = stack.pop()
                n2 = stack.pop()

                # 除法需要单独处理
                if t == '/':
                    tmp = abs(int(n2) ) // abs(int(n1))
                    # 求出 n1 n2 的正负号,还要排除 0 对该步骤的影响
                    if int(n2) != 0 and int(n2) * -1 == abs(int(n2)):
                        tmp *= -1
                    if int(n1) != 0 and int(n1) * -1 == abs(int(n1)):
                        tmp *= -1
                    stack.append(str(tmp))
                
                # 其他运算符可直接运算
                else:
                    stack.append(str(eval(n2 + t + n1)))
            
            # 2. 遇到括号,可以直接跳过左右括号
            elif t in op2:
                continue
            
            # 3. 遇到数字,直接将数字字符压入栈
            else:
                stack.append(t)

        # 防止最后的栈顶元素是个浮点数
        return int(eval(stack.pop() +  "// 1"))

分析

  1. 时间复杂度需要 O(n),需要一次完整的线性遍历。
  2. 空间复杂度需要 O(n),需要保存未参与计算的数字。

留下评论