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

217 阅读6分钟

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

解题思路和思维

栈的介绍

栈(Stack)是一种数据结构,它遵循后进先出(Last In First Out,LIFO)的原则。这意味着最后进入栈的数据元素会最先被取出。栈主要有两个基本操作:

  1. 压入(Push) :将元素添加到栈的顶部。

  2. 弹出(Pop) :从栈的顶部移除元素。

  3. 总体思路

    • 使用两个栈:一个用于存储操作数(values),另一个用于存储运算符(operators)。
    • 按照运算符的优先级和括号的规则,逐步处理表达式中的每个字符,将操作数和运算符分别压入相应的栈中,并在合适的时候进行计算。
  4. 函数apply_operator

    • 这个函数用于执行栈顶的运算符操作。
    • 它从values栈中弹出两个操作数(rightleft),从operators栈中弹出一个运算符(op)。
    • 根据运算符的类型(+-*/),对两个操作数进行相应的运算,并将结果压回values栈中。
  5. 主函数solution

    • 初始化

      • 定义了一个字典precedence,用于存储运算符的优先级,其中乘除的优先级高于加减。
      • 初始化两个空栈operatorsvalues,以及一个索引变量i为 0,用于遍历表达式字符串。
    • 遍历表达式

      • 处理数字

        • 如果当前字符是数字,通过循环读取连续的数字字符,将其转换为整数并压入values栈中。
        • 使用continue语句跳过i的自增操作,因为在读取数字字符的循环中已经对i进行了自增。
      • 处理运算符

        • 如果当前字符是运算符(+-*/),则需要检查operators栈顶的运算符优先级。
        • 只要栈顶运算符的优先级大于或等于当前运算符的优先级,就调用apply_operator函数进行计算,直到栈顶运算符的优先级小于当前运算符的优先级。
        • 然后将当前运算符压入operators栈中。
      • 处理括号

        • 如果当前字符是左括号(,直接将其压入operators栈中。
        • 如果当前字符是右括号),则不断调用apply_operator函数进行计算,直到遇到左括号(,然后将左括号从operators栈中弹出。
      • 每次循环结束时,将i自增 1,继续处理下一个字符。

    • 处理剩余的运算符

      • 当表达式遍历结束后,如果operators栈中还有运算符,继续调用apply_operator函数进行计算,直到operators栈为空。
  6. 返回结果

    • 最后,values栈中只剩下一个元素,即表达式的计算结果,将其返回。

      1. 运算符栈(operators
    • 作用:用于存储表达式中的运算符,并根据运算符的优先级和括号规则来决定何时进行运算。

    • 好的,让我们来检查一下你编辑区的代码,并确保乘法和除法的优先级正确处理。

代码分析

主要问题

从错误信息来看,问题出在calculate_operation函数中,具体是在num_stack.pop()这一行。这个错误通常意味着在尝试从空栈中弹出元素。

可能的原因
  1. 栈为空:在调用calculate_operation时,num_stack可能为空。
  2. 括号处理不当:在处理括号时,可能没有正确地处理栈中的元素。

代码检查

1. 检查calculate_operation函数
def calculate_operation():
    if len(num_stack) < 2:
        raise ValueError("Not enough operands in stack")
    operator = operator_stack.pop()
    num2 = num_stack.pop()
    num1 = num_stack.pop()
    if operator == '+':
        num_stack.append(num1 + num2)
    elif operator == '-':
        num_stack.append(num1 - num2)
    elif operator == '*':
        num_stack.append(num1 * num2)
    elif operator == '/':
        num_stack.append(num1 // num2)

这里假设operator_stacknum_stack中都有足够的元素。如果num_stack为空,就会抛出IndexError

2. 检查括号处理
elif char == ')':
    while operator_stack and operator_stack[-1] != '(':
        calculate_operation()
    operator_stack.pop()  # 弹出 '('

在处理右括号时,我们调用calculate_operation直到遇到左括号。如果在这个过程中num_stack为空,就会导致错误。

解决方案

1. 检查栈是否为空

在调用calculate_operation之前,检查num_stack是否至少有两个元素。

2. 改进括号处理

确保在处理括号时,栈中有足够的元素。

修改后的代码

def solution(s: str) -> int:
    num_stack = []
    operator_stack = []
    i = 0
    precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '(': 0}
    n = len(s)

    def calculate_operation():
        if len(num_stack) < 2:
            raise ValueError("Not enough operands in stack")
        operator = operator_stack.pop()
        num2 = num_stack.pop()
        num1 = num_stack.pop()
        if operator == '+':
            num_stack.append(num1 + num2)
        elif operator == '-':
            num_stack.append(num1 - num2)
        elif operator == '*':
            num_stack.append(num1 * num2)
        elif operator == '/':
            num_stack.append(num1 // num2)  # 注意:这里使用整数除法

    while i < n:
        char = s[i]
        if char.isdigit():
            num = 0
            while i < n and s[i].isdigit():
                num = num * 10 + int(s[i])
                i += 1
            num_stack.append(num)
            i -= 1
        elif char in precedence:
            if char == '(':
                operator_stack.append(char)
            elif char == ')':
                while operator_stack and operator_stack[-1] != '(':
                    calculate_operation()
                operator_stack.pop()  # 弹出 '('
            else:
                while operator_stack and operator_stack[-1] != '(' and precedence[char] <= precedence.get(operator_stack[-1], 1):
                    calculate_operation()
                operator_stack.append(char)
        i += 1

    while operator_stack:
        calculate_operation()

    return num_stack.pop()

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("1+1") == 2)
    print(solution("3+4*5/(3+2)") == 7)
    print(solution("4+2*5-2/1") == 12)
    print(solution("(1+(4+5+2)-3)+(6+8)") == 23)
    print(solution("2*(5+5*2)/3+(6+8*3)") == 40)

在对豆包ai辅助提问中,确认了两个基本想法的重要性和需要改进性

  1. 检查栈是否为空:在calculate_operation函数中,添加了一个检查,确保num_stack中至少有两个元素。

  2. 改进括号处理:确保在处理括号时,栈中有足够的元素。

    • 操作过程

      • 当遍历表达式字符串遇到运算符时,会根据运算符的优先级规则来处理。如果当前运算符的优先级小于等于栈顶运算符的优先级,那么先对栈顶运算符进行运算。例如,对于表达式"3+4*5",当遇到*时,因为*的优先级高于+,所以*会被压入运算符栈;而当遇到+时,由于+的优先级低于*,此时会先从操作数栈和运算符栈中取出相应元素进行*运算,然后再将+压入运算符栈。

      • 当遇到左括号(时,左括号会被压入运算符栈,表示一个新的运算优先级开始。当遇到右括号)时,会从运算符栈中不断弹出运算符进行运算,直到遇到左括号(,然后将左括号弹出,这确保了括号内的运算先执行。