逆波兰表达式求值

标签: 数组 数学

难度: Medium

根据 逆波兰表示法,求该后缀表达式的计算结果。

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

说明:

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

示例 1:

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

示例 2:

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

示例 3:

输入:tokens = ["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 <= tokens.length <= 104
  • tokens[i] 要么是一个算符("+""-""*""/"),要么是一个在范围 [-200, 200] 内的整数

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

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

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

注意:本题与主站 150 题相同: https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/

Submission

运行时间: 29 ms

内存: 17.5 MB

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        calc = {'+': lambda x, y: x + y,
                '-': lambda x, y: x - y,
                '*': lambda x, y: x * y,
                '/': lambda x, y: int(x / y)}
        for i in tokens:
            if i in calc:
                num2, num1 = stack.pop(), stack.pop()
                stack.append(calc[i](num1, num2))
            else:
                stack.append(int(i))
        
        return stack[0]

Explain

此题解使用栈来处理逆波兰表达式的求值。栈是一种先进后出的数据结构,非常适合此类问题。在遍历表达式中的每个元素时,如果遇到数字,则将其转换为整数并压入栈中;如果遇到运算符,则从栈中弹出两个元素(最近压入的两个数字),使用相应的运算符进行计算,然后将结果压回栈中。这种处理方式确保了运算的顺序符合逆波兰表达式的定义。最后,栈中剩余的唯一元素即为整个表达式的计算结果。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []  # 创建一个空栈用于存储数字和中间结果
        calc = {'+': lambda x, y: x + y,  # 定义四种运算的匿名函数
                '-': lambda x, y: x - y,
                '*': lambda x, y: x * y,
                '/': lambda x, y: int(x / y)}  # 除法需要取整
        for i in tokens:
            if i in calc:
                num2, num1 = stack.pop(), stack.pop()  # 弹出栈顶的两个数字进行运算
                stack.append(calc[i](num1, num2))  # 计算结果再压回栈
            else:
                stack.append(int(i))  # 将数字转换为整数后压栈
        return stack[0]  # 栈顶元素即为最终计算结果

Explore

逆波兰表达式是一种后缀表达式,其中运算符位于其对应的操作数之后。栈的特性是后进先出(LIFO),这使得最后进入栈的元素可以最先被处理。在逆波兰表达式中,运算符后的两个数字是最近入栈的,应该首先使用这两个数字进行运算。如果使用最底部的元素,将无法保证运算的顺序和正确性,因为这将忽略逆波兰表达式的结构规则。

Python本身对整数的处理具有一定的优势,因为它可以处理比标准整数类型更大的数(长整型),并且会根据需要自动扩展数的大小。因此,在Python中处理大数通常不会导致整数溢出。然而,在其他编程语言中,如C++或Java,可能需要特别注意数据类型和溢出的问题。在算法设计时,应该考虑使用能够处理大数的数据类型或库,以防溢出。

为了确保逆波兰表达式的正确求值,必须保证每次计算时栈中至少有两个数字。这通常通过检查输入的合法性和适当的错误处理来实现。算法实现应在遇到运算符之前检查栈的大小。如果在尝试执行任何运算之前栈中的元素少于两个,则应抛出错误或返回一个指示问题的值。这种严格的检查帮助避免运行时错误,并确保表达式的结构正确无误。