实现基本计算器解析字符串表达式值|豆包MarsCode AI刷题

65 阅读4分钟

解题思路

小F面临的问题是实现一个基本的计算器,用于计算包含数字(0-9)、运算符(+、-、*、/)及括号(())的字符串表达式的值。字符串表达式有效且不包含空格,除法运算应只保留整数结果。为了解决这个问题,可以采用以下思路:

  1. 逆波兰表达式(后缀表达式) :将中缀表达式(如 3 + 4)转换为后缀表达式(如 3 4 +),再进行计算。这种方法可以避免处理括号和运算符优先级的复杂性。
  2. 栈的应用:使用两个栈,一个用于存储操作数(数字),另一个用于存储操作符(运算符)。通过遍历字符串表达式,逐步将数字和运算符压入相应的栈中,并根据运算符的优先级进行计算。

算法设计

  1. 初始化:创建两个栈,一个用于操作数(operands),一个用于操作符(operators)。

  2. 遍历表达式

    • 当遇到数字时,解析多位数字并压入操作数栈。
    • 当遇到运算符时,根据优先级决定是否计算栈中的操作数。如果当前运算符的优先级小于或等于栈顶运算符的优先级,则先计算栈顶的操作符,再将当前运算符压入栈。
    • 当遇到左括号时,直接压入操作符栈。
    • 当遇到右括号时,弹出并计算操作符栈中的运算符,直到遇到左括号。
  3. 最终计算:遍历结束后,处理操作符栈中剩余的运算符,直到栈为空。

代码分析

  1. 辅助函数 apply_operator
    这个函数负责从操作符栈和操作数栈中取出操作符和操作数,进行相应的运算,并将结果压回操作数栈。

    def apply_operator(operators, operands):
        if not operators or len(operands) < 2:
            return
        right = operands.pop()
        left = operands.pop()
        op = operators.pop()
        if op == '+':
            operands.append(left + right)
        elif op == '-':
            operands.append(left - right)
        elif op == '*':
            operands.append(left * right)
        elif op == '/':
            operands.append(int(left / right))  # 只保留整数部分
    
  2. 优先级函数 precedence
    这个函数定义了运算符的优先级。

    def precedence(op):
        if op in ('+', '-'):
            return 1
        if op in ('*', '/'):
            return 2
        return 0
    
  3. 主解析函数 parse
    这个函数通过遍历字符串表达式,逐步解析数字和运算符,并根据运算符的优先级进行计算。

    def parse(expression):
        operators = []  # 操作符栈
        operands = []   # 操作数栈
        i = 0
        n = len(expression)
    
        while i < n:
            if expression[i].isdigit():
                num = 0
                while i < n and expression[i].isdigit():
                    num = num * 10 + int(expression[i])
                    i += 1
                operands.append(num)
                continue
            elif expression[i] in "+-*/":
                while (operators and operators[-1] != '(' and
                       precedence(operators[-1]) >= precedence(expression[i])):
                    apply_operator(operators, operands)
                operators.append(expression[i])
            elif expression[i] == '(':
                operators.append(expression[i])
            elif expression[i] == ')':
                while operators and operators[-1] != '(':
                    apply_operator(operators, operands)
                operators.pop()
    
            i += 1
    
        while operators:
            apply_operator(operators, operands)
    
        return operands[-1] if operands else 0
    

有可能出现的问题

  1. 边界条件处理

    • 空字符串的处理:虽然题目假设表达式有效,但实际应用中应考虑空字符串的情况。
    • 单个数字的表达式:例如 "1",需要确保能够正确返回结果。
  2. 负数处理

    • 当表达式中存在负数时,如何正确解析负号?例如 "-1+2"
  3. 除法处理

    • 除法运算中,当被除数为0时,需要处理除零错误。
    • 例如:1/0 会引发除零错误。
  4. 非法字符处理

    • 如果表达式中包含非法字符,例如 "1+1a",需要进行异常处理。
  5. 括号不匹配

    • 如果表达式中的括号不匹配,例如 "(1+1))" 或 "(1+1",需要处理这种情况。

个人的思考与改进

  1. 负数处理

    • 在解析数字时,增加对负号的处理。当遇到负号时,解析其后的数字并将其作为负数压入操作数栈。
    • 例如,解析 "-1+2" 时,-1 应该被视为一个负数。
  2. 代码简化

    • 通过使用 Python 的 collections.deque 来替代普通的列表作为栈,可以更高效地进行弹出和压入操作。
    • 例如,将 operators 和 operands 替换为 deque
  3. 增加异常处理

    • 在解析过程中增加异常处理,处理非法字符、括号不匹配等问题。
    • 例如,处理非法字符时可以抛出 ValueError
  4. 测试用例

    • 增加更多的测试用例,特别是边界条件和特殊情况,以确保代码的正确性和鲁棒性。
    • 例如,测试空字符串、单个数字、负数、除零错误等。
  5. 性能优化

    • 通过减少不必要的栈操作和循环,可以提高代码的性能。
    • 例如,可以将数字解析的内部循环提取到一个单独的函数中,减少代码重复。