小 F 面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符 +、- 及括号 ()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的 eval 函数。
解题思路和思维
栈的介绍
栈(Stack)是一种数据结构,它遵循后进先出(Last In First Out,LIFO)的原则。这意味着最后进入栈的数据元素会最先被取出。栈主要有两个基本操作:
-
压入(Push) :将元素添加到栈的顶部。
-
弹出(Pop) :从栈的顶部移除元素。
-
总体思路
- 使用两个栈:一个用于存储操作数(
values),另一个用于存储运算符(operators)。 - 按照运算符的优先级和括号的规则,逐步处理表达式中的每个字符,将操作数和运算符分别压入相应的栈中,并在合适的时候进行计算。
- 使用两个栈:一个用于存储操作数(
-
函数
apply_operator- 这个函数用于执行栈顶的运算符操作。
- 它从
values栈中弹出两个操作数(right和left),从operators栈中弹出一个运算符(op)。 - 根据运算符的类型(
+、-、*、/),对两个操作数进行相应的运算,并将结果压回values栈中。
-
主函数
solution-
初始化
- 定义了一个字典
precedence,用于存储运算符的优先级,其中乘除的优先级高于加减。 - 初始化两个空栈
operators和values,以及一个索引变量i为 0,用于遍历表达式字符串。
- 定义了一个字典
-
遍历表达式
-
处理数字:
- 如果当前字符是数字,通过循环读取连续的数字字符,将其转换为整数并压入
values栈中。 - 使用
continue语句跳过i的自增操作,因为在读取数字字符的循环中已经对i进行了自增。
- 如果当前字符是数字,通过循环读取连续的数字字符,将其转换为整数并压入
-
处理运算符:
- 如果当前字符是运算符(
+、-、*、/),则需要检查operators栈顶的运算符优先级。 - 只要栈顶运算符的优先级大于或等于当前运算符的优先级,就调用
apply_operator函数进行计算,直到栈顶运算符的优先级小于当前运算符的优先级。 - 然后将当前运算符压入
operators栈中。
- 如果当前字符是运算符(
-
处理括号:
- 如果当前字符是左括号
(,直接将其压入operators栈中。 - 如果当前字符是右括号
),则不断调用apply_operator函数进行计算,直到遇到左括号(,然后将左括号从operators栈中弹出。
- 如果当前字符是左括号
-
每次循环结束时,将
i自增 1,继续处理下一个字符。
-
-
处理剩余的运算符
- 当表达式遍历结束后,如果
operators栈中还有运算符,继续调用apply_operator函数进行计算,直到operators栈为空。
- 当表达式遍历结束后,如果
-
-
返回结果
-
最后,
values栈中只剩下一个元素,即表达式的计算结果,将其返回。 -
- 运算符栈(
operators)
- 运算符栈(
-
作用:用于存储表达式中的运算符,并根据运算符的优先级和括号规则来决定何时进行运算。
-
好的,让我们来检查一下你编辑区的代码,并确保乘法和除法的优先级正确处理。
-
代码分析
主要问题
从错误信息来看,问题出在calculate_operation函数中,具体是在num_stack.pop()这一行。这个错误通常意味着在尝试从空栈中弹出元素。
可能的原因
- 栈为空:在调用
calculate_operation时,num_stack可能为空。 - 括号处理不当:在处理括号时,可能没有正确地处理栈中的元素。
代码检查
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_stack和num_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辅助提问中,确认了两个基本想法的重要性和需要改进性
-
检查栈是否为空:在
calculate_operation函数中,添加了一个检查,确保num_stack中至少有两个元素。 -
改进括号处理:确保在处理括号时,栈中有足够的元素。
-
操作过程
-
当遍历表达式字符串遇到运算符时,会根据运算符的优先级规则来处理。如果当前运算符的优先级小于等于栈顶运算符的优先级,那么先对栈顶运算符进行运算。例如,对于表达式
"3+4*5",当遇到*时,因为*的优先级高于+,所以*会被压入运算符栈;而当遇到+时,由于+的优先级低于*,此时会先从操作数栈和运算符栈中取出相应元素进行*运算,然后再将+压入运算符栈。 -
当遇到左括号
(时,左括号会被压入运算符栈,表示一个新的运算优先级开始。当遇到右括号)时,会从运算符栈中不断弹出运算符进行运算,直到遇到左括号(,然后将左括号弹出,这确保了括号内的运算先执行。
-
-