题解:实现一个基本的计算器
问题描述
小F需要实现一个基本的计算器,能够计算包含数字、运算符+、-、*、/和括号()的字符串表达式的值。具体要求如下:
-
输入:
- 一个有效的字符串表达式。
- 可能包含正整数(0-9)、运算符
+、-、*、/及括号()。 - 字符串中没有空格。
-
输出:
- 表达式的计算结果。
- 除法结果保留整数部分(类似于地板除
//的效果)。
-
限制:
- 不能使用 Python 的内置函数
eval。
- 不能使用 Python 的内置函数
解题思路
本题要求实现一个解析器,处理表达式中的数字和运算符,同时考虑运算优先级和括号的影响。以下是完整的解决方案步骤:
算法设计
-
核心概念:
-
运算符优先级:
+和-的优先级最低。*和/的优先级较高。- 括号
()可以改变默认的运算顺序。
-
栈的应用:
- 使用 数字栈 存储操作数。
- 使用 运算符栈 存储运算符,帮助处理优先级。
-
计算规则:
- 碰到低优先级的运算符时,先处理栈顶的高优先级运算符。
- 碰到右括号
)时,处理括号内的所有运算。 - 表达式解析完毕后,清空运算符栈。
-
-
处理流程:
-
遍历表达式的每个字符:
- 如果是数字,解析完整的数字并压入 数字栈。
- 如果是运算符,按优先级与 运算符栈 中的运算符进行比较,处理优先级较高的运算符。
- 如果是左括号
(,直接压入 运算符栈。 - 如果是右括号
),处理栈中的运算直到遇到左括号。
-
遍历完成后,处理 运算符栈 中剩余的运算。
-
-
辅助函数:
apply_operation(op, b, a):执行单次运算,返回结果。precedence(op):返回运算符的优先级。
代码实现
def calculate(expression: str) -> int:
# 运算符优先级
precedence = {'+': 1, '-': 1, '*': 2, '/': 2}
# 应用运算符的操作
def apply_operation(op, b, a):
if op == '+':
return a + b
elif op == '-':
return a - b
elif op == '*':
return a * b
elif op == '/':
return a // b # 整数除法
raise ValueError("Invalid operator")
# 栈定义
numbers = [] # 数字栈
ops = [] # 运算符栈
i = 0
while i < len(expression):
char = expression[i]
if char.isdigit():
# 解析完整的数字(支持多位数)
num = 0
while i < len(expression) and expression[i].isdigit():
num = num * 10 + int(expression[i])
i += 1
numbers.append(num)
continue # 已经移动了指针,跳过 i 的递增
elif char in precedence:
# 处理当前运算符与栈中运算符的优先级
while ops and ops[-1] != '(' and precedence[char] <= precedence[ops[-1]]:
numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))
ops.append(char)
elif char == '(':
# 左括号直接入栈
ops.append(char)
elif char == ')':
# 右括号:处理括号内的运算
while ops and ops[-1] != '(':
numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))
ops.pop() # 弹出左括号
i += 1
# 处理剩余运算符
while ops:
numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))
# 最终结果
return numbers[0]
# 测试样例
if __name__ == '__main__':
assert calculate("1+2*3") == 7
assert calculate("(1+(4+5+2)-3)+(6+8)") == 23
assert calculate("3+2*2") == 7
assert calculate("3/2") == 1
assert calculate("3+5/2") == 5
测试样例
-
样例 1:
calculate("1+2*3")-
运算步骤:
- 解析:数字
1,压入数字栈。 - 解析:
+,压入运算符栈。 - 解析:数字
2,压入数字栈。 - 解析:
*,压入运算符栈。 - 解析:数字
3,压入数字栈。 - 处理高优先级的
*运算:结果6压入数字栈。 - 处理低优先级的
+运算:结果7。
- 解析:数字
-
输出:
7
-
-
样例 2:
calculate("(1+(4+5+2)-3)+(6+8)")-
运算步骤:
- 处理括号中的表达式。
- 按运算优先级计算子表达式。
-
输出:
23
-
总结
-
数据结构:
- 使用栈来处理括号和运算符优先级。
- 数字栈存储操作数,运算符栈存储操作符。
-
时间复杂度:
- 遍历表达式:O(n)。
- 处理运算符栈:每个运算符最多入栈出栈一次,复杂度 O(n)。
- 总复杂度:O(n)。
-
优点:
- 支持多位数字和嵌套括号。
- 遵循运算优先级规则。
- 不依赖
eval,逻辑清晰、易扩展。