简单四则运算解析器 | 豆包MarsCode AI刷题

111 阅读4分钟

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+、-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。

测试样例

样例1:

输入:expression = "1+1"
输出:2

样例2:

输入:expression = "3+4*5/(3+2)"
输出:7

样例3:

输入:expression = "4+2*5-2/1"
输出:12

样例4:

输入:expression = "(1+(4+5+2)-3)+(6+8)"
输出:23

样例5:

输入:expression = "2*(5+5*2)/3+(6+8*3)"
输出:40

要实现一个基本的计算器解析器,我们需要处理运算符的优先级和括号。以下是一个可能的实现方法,使用两个栈(stack):一个用于存储数字,另一个用于存储运算符。这个算法被称为 Shunting Yard 算法,它将中缀表达式转换为后缀表达式,然后进行计算。

注:Shunting Yard算法的名字来源于其操作过程与铁路调度场的操作类似。这个算法是由Edsger Dijkstra发明的,用于将中缀表达式转换为后缀表达式(逆波兰表示法)。在铁路调度场中,火车车厢被推到不同的轨道上,以便它们可以按照正确的顺序被重新组合成列车。类似地,Shunting Yard算法使用栈来暂存操作符,并根据操作符的优先级和结合性将它们重新排序,最终生成一个后缀表达式,这个表达式可以按照从左到右的顺序直接计算,而不需要括号来指示操作顺序

算法的核心在于用栈暂存操作符以备“调度”,比较新符号和栈顶原有符号的优先级,选择其中更容易结合的(根据优先级、结合方向)出栈——更容易结合就意味着更早地参与计算 。这个过程类似于铁路调度场中调度火车车厢的操作,因此得名“Shunting Yard算法” 。

  1. 定义运算符的优先级
  • 加(+)和减(-)的优先级为 1。
  • 乘(*)和除(/)的优先级为 2。
  • 括号(())用于改变运算顺序。
  1. 使用两个栈
  • 一个栈用于存储数字(数字栈)。
  • 另一个栈用于存储运算符(运算符栈)。
  1. 遍历表达式
  • 从左到右遍历表达式的每个字符。
  • 如果是数字,将其推入数字栈。
  • 如果是运算符,比较其优先级,并在需要时将其推入运算符栈,或者从运算符栈中弹出运算符进行计算。
  • 如果是左括号,直接推入运算符栈。
  • 如果是右括号,弹出运算符栈中的运算符并计算,直到遇到左括号。
  1. 计算后缀表达式
  • 遍历完成后,运算符栈中可能仍有运算符,将它们依次弹出并计算。

以下是这个问题的 Python 代码实现:

def solution(expression):
    def precedence(op):
        if op in ('+', '-'):
            return 1
        if op in ('*', '/'):
            return 2
        return 0

    def apply_op(a, b, op):
        if op == '+': return a + b
        if op == '-': return a - b
        if op == '*': return a * b
        if op == '/': return int(a / b)  # 保留整数结果

    def parse_expression(expr):
        values = []
        ops = []
        i = 0
        while i < len(expr):
            if expr[i].isdigit():
                j = i
                while i + 1 < len(expr) and expr[i + 1].isdigit():
                    i += 1
                values.append(int(expr[j:i+1]))
            elif expr[i] == '(':
                ops.append(expr[i])
            elif expr[i] == ')':
                while ops[-1] != '(':
                    val2 = values.pop()
                    val1 = values.pop()
                    op = ops.pop()
                    values.append(apply_op(val1, val2, op))
                ops.pop()  # Pop the '('
            else:
                while (len(ops) != 0 and
                       precedence(ops[-1]) >= precedence(expr[i])):
                    val2 = values.pop()
                    val1 = values.pop()
                    op = ops.pop()
                    values.append(apply_op(val1, val2, op))
                ops.append(expr[i])
            i += 1

        while len(ops) != 0:
            val2 = values.pop()
            val1 = values.pop()
            op = ops.pop()
            values.append(apply_op(val1, val2, op))

        return values[0]

    return parse_expression(expression)

# Example usage
print(solution("1+1"))  # Output: 2
print(solution("3+4*5/(3+2)"))  # Output: 7
print(solution("4+2*5-2/1"))  # Output: 12
print(solution("(1+(4+5+2)-3)+(6+8)"))  # Output: 23
print(solution("2*(5+5*2)/3+(6+8*3)"))  # Output: 40

这段代码定义了一个 calculate 函数,它接受一个字符串表达式并返回计算结果。函数内部定义了 precedence 函数来确定运算符的优先级,apply_op 函数来应用运算符,以及 parse_expression 函数来解析和计算表达式。对于给定的例子,输出分别是 2、7、12、23 和 40。