简单四则运算解析器
问题描述
小F需要编写一个程序来计算给定的字符串表达式的值。表达式有效且可能包含以下元素:
- 数字(0-9)
- 运算符:加(+)、减(-)、乘(*)、除(/)
- 括号:左括号
(和右括号)
在计算时需注意以下规则:
-
无空格处理: 输入的表达式中不包含空格。
-
整数除法: 对于除法操作,结果需要取整(向零取整)。
-
优先级处理:
- 乘法和除法优先于加法和减法。
- 括号的优先级最高,括号内的表达式需要先计算。
目标是通过实现一个自定义的解析器计算表达式的值,不得使用内置的 eval 函数。
分析问题与难点
- 操作符优先级:
乘法和除法的优先级高于加法和减法。这意味着我们需要按照运算优先级的规则正确处理不同操作符。 - 括号嵌套:
括号表示优先级最高,嵌套括号需要递归求解。每遇到一对括号时,需要单独计算括号内部的表达式。 - 字符串解析:
需要逐字符解析字符串并将数字、运算符和括号分开处理,同时管理一个栈来帮助进行运算。 - 整数运算:
除法的结果需要向零取整。例如,对于表达式7 / 3,结果应该是2而不是2.333。
算法思路
为了解决上述问题,我们需要设计一个解析器,遵循以下步骤:
-
定义支持的操作符和数据结构
- 使用一个栈(
stack)来存储临时结果和中间运算符的优先级信息。 - 将加减法的操作符视为低优先级,乘法和除法为高优先级。
- 使用一个栈(
-
逐字符扫描表达式
- 如果是数字:将连续数字解析为整数。
- 如果是加减法运算符:将当前的结果压入栈并记录运算符。
- 如果是乘法或除法:从栈中取出上一个操作数,与当前操作数进行计算,并将结果重新压入栈。
- 如果是左括号:递归调用,计算括号内的结果。
- 如果是右括号:结束当前括号的递归处理。
-
栈中求和
- 当所有字符处理完成时,栈中的所有元素代表一个加减法链表。直接求和即可得出结果。
代码实现
以下是完整的 Python 代码实现:
from collections import deque
from collections import Counter
def solution(s: str) -> int:
strings = ["+","-","*","/"]
n = len(s)
res = 0
pre = "+"
stack = []
i = 0
while i < n:
if s[i] != " " and s[i] in [str(i) for i in range(10)]:
res = res*10 + ord(s[i]) - ord('0')
if i == n-1 or s[i] in strings:
if pre == "+":
stack.append(res)
elif pre == "-":
stack.append(-res)
elif pre == "*":
stack.append(stack.pop()*res)
else:
stack.append(int(stack.pop()/res))
res = 0
pre = s[i]
if s[i] == "(":
balance = 1
j = i+1
while j < n:
if s[j] == '(':
balance+=1
elif s[j] == ')':
balance -= 1
if balance == 0:
break
j += 1
res = solution(s[i+1:j])
i = j
if i == n-1:
if pre == "+":
stack.append(res)
elif pre == "-":
stack.append(-res)
elif pre == "*":
stack.append(stack.pop()*res)
else:
stack.append(int(stack.pop()/res))
i += 1
return sum(stack)
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)
测试样例解析
样例1:
输入:"1+1"
- 无括号、无优先级,直接计算 1+1=21+1=21+1=2。
输出:2
样例2:
输入:"3+4*5/(3+2)"
-
按优先级计算:
- 4∗5=204*5=204∗5=20
- 20/(3+2)=420/(3+2)=420/(3+2)=4
- 3+4=73+4=73+4=7。
输出:7
样例3:
输入:"4+2*5-2/1"
-
按优先级计算:
- 2∗5=102*5=102∗5=10
- 2/1=22/1=22/1=2
- 4+10−2=124+10-2=124+10−2=12。
输出:12
样例4:
输入:"(1+(4+5+2)-3)+(6+8)"
-
递归计算括号内的表达式:
- (4+5+2)=11(4+5+2)=11(4+5+2)=11
- 1+11−3=91+11-3=91+11−3=9
- (6+8)=14(6+8)=14(6+8)=14
- 9+14=239+14=239+14=23。
输出:23
样例5:
输入:"2*(5+5*2)/3+(6+8*3)"
-
递归计算括号内的表达式并遵循优先级:
- (5+5∗2)=15(5+5*2)=15(5+5∗2)=15
- 2∗15=302*15=302∗15=30
- 30/3=1030/3=1030/3=10
- (6+8∗3)=30(6+8*3)=30(6+8∗3)=30
- 10+30=4010+30=4010+30=40。
输出:40
复杂度分析
-
时间复杂度:
- 表达式中的每个字符会被扫描一次,递归计算括号内部时可能重新扫描部分字符,复杂度为 O(n),其中 n 是表达式的长度。
-
空间复杂度:
- 递归栈的深度与括号的嵌套层数有关,栈的空间复杂度为 O(d),其中 d 是括号的最大嵌套深度。
总结
该算法充分利用栈和递归机制,完成了一个基本计算器的实现,能够正确解析和计算复杂的嵌套表达式。在面试场景或工程中,该算法展示了对优先级、括号处理及整数除法等关键问题的细致考虑,是一个高效且易于扩展的实现方式