四则运算解析器
问题描述
小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。这个挑战不仅仅是技术上的考验,更是一次思维的磨砺。在这个过程中,我深刻体会到了编程的乐趣和挑战。字符串表达式可能包含数字(0-9)、运算符+、-及括号(),需要注意的是,字符串中不包含空格。此外,除法运算应只保留整数结果。我们的目标是实现一个解析器计算这些表达式的值,且不使用任何内置的 eval 函数。
测试样例
为了确保我们的解决方案能够应对各种情况,我们准备了几个测试样例:
样例1: 输入:expression = "1+1" 输出:2 样例2: 输入:expression = "3+4*5/(3+2)"
输出:7
样例3:
输入:expression = "4+2*5-2/1"
输出:12
样例4:
输入:expression = "(1+(4+5+2)-3)+(6+8)"
输出:23
样例5:
输入:expression = "2*(5+52)/3+(6+83)"
输出:40
算法设计
在解决这个问题的过程中,我首先思考了如何有效地解析和计算表达式。这是一个经典的栈问题,通过使用栈可以很好地处理括号和运算符的优先级。具体来说,我们需要处理以下几个方面:
数字处理:正确读取多位数字。例如,当我们遇到数字字符时,需要将其转换为整数并继续读取完整的数字。
运算符优先级:确保先进行乘除运算,再进行加减运算。这可以通过比较运算符的优先级来实现。
括号处理:正确处理嵌套括号内的子表达式。当遇到右括号时,需要处理栈顶的运算符,直到遇到左括号。
整数除法:确保除法运算的结果是整数。Python 中的除法运算默认是浮点数除法,我们需要使用 int() 函数将其转换为整数。
实现步骤
接下来,我详细介绍了实现这个计算器的具体步骤:
初始化栈:我们使用两个栈,一个用于存储数值,另一个用于存储运算符。
遍历表达式:逐字符遍历输入的表达式字符串,根据不同类型的字符进行相应的处理。
数字:如果遇到数字字符,将其转换为整数并继续读取完整的数字,然后将结果压入数值栈。
运算符:如果遇到运算符,需要处理栈顶的运算符,直到栈顶的运算符优先级低于当前运算符,然后将当前运算符压入运算符栈。
左括号:如果遇到左括号,直接将其压入运算符栈。
右括号:如果遇到右括号,处理栈顶的运算符,直到遇到左括号,然后弹出左括号。
处理栈中的剩余运算符:遍历结束后,栈中可能还剩下一些运算符,需要继续处理这些运算符,直到栈为空。
返回结果:栈中最后剩下的数字即为表达式的值。
代码实现
下面是具体的实现代码:
python
深色版本
def solution(expression):
def apply_operator(operators, values):
operator = operators.pop()
right = values.pop()
left = values.pop()
if operator == '+':
values.append(left + right)
elif operator == '-':
values.append(left - right)
elif operator == '*':
values.append(left * right)
elif operator == '/':
values.append(int(left / right)) # 整数除法
def greater_precedence(op1, op2):
precedence = {'+': 1, '-': 1, '*': 2, '/': 2}
return precedence[op1] >= precedence[op2]
def evaluate(expression):
values = []
operators = []
i = 0
while i < len(expression):
if expression[i].isdigit():
val = 0
while i < len(expression) and expression[i].isdigit():
val = val * 10 + int(expression[i])
i += 1
values.append(val)
i -= 1
elif expression[i] == '(':
operators.append(expression[i])
elif expression[i] == ')':
while operators[-1] != '(':
apply_operator(operators, values)
operators.pop() # 弹出左括号
else:
while operators and operators[-1] != '(' and greater_precedence(operators[-1], expression[i]):
apply_operator(operators, values)
operators.append(expression[i])
i += 1
while operators:
apply_operator(operators, values)
return values[0]
return evaluate(expression)
if name == "main":
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+52)/3+(6+83)") == 40)
print(solution("2+3*(4-1)+5/(2+3)") == 14)
print(solution("100-(2+3)*4+5/2") == 85)
print(solution("(2+3)*(4+5)") == 45)
print(solution("100/(2+3)*4+5/2") == 82)
print(solution("100-2*3+4/2") == 96)
print(solution("2*(3+4)/(1+1)") == 7)
print(solution("2*(3+4)/(1+1)+5") == 12) # 新增测试用例
print(solution("2*(3+4)/(1+1)+5-1") == 11) # 新增测试用例
解释
apply_operator:执行栈顶的运算符,并将结果压入数值栈。这个函数负责实际的运算操作,根据不同的运算符进行相应的计算。
greater_precedence:比较运算符的优先级,确保优先级相等时也进行处理。这个函数用于决定是否需要将当前运算符压入栈中。
evaluate:主函数,解析表达式并计算结果。这个函数负责整体的解析和计算逻辑。
遍历表达式:处理数字、运算符和括号,确保运算符的优先级正确处理。这是整个算法的核心部分,通过遍历表达式字符串,逐步构建数值栈和运算符栈,并进行相应的运算。
心路历程
在这个过程中,我遇到了不少挑战。一开始,我对如何处理括号和运算符的优先级感到困惑。经过一番思考和查阅资料,我逐渐理解了栈在处理这类问题中的重要性。通过使用栈,我可以轻松地处理嵌套括号和运算符的优先级问题。
此外,我还遇到了一些边界条件的处理问题,例如如何处理多位数字和连续的运算符。通过不断调试和优化代码,我最终解决了这些问题。在这个过程中,我深刻体会到了编程的乐趣和挑战。每一次成功解决问题后的成就感,让我更加热爱编程。
时间复杂度分析
初始化:初始化 values 和 operators 栈的时间复杂度为 O(1)。
数据处理:遍历输入的表达式,时间复杂度为 O(n),其中 n 是表达式的长度。
查找和更新:在遍历过程中,处理数字和运算符的时间复杂度为 O(n)。
排序:对 operators 栈进行操作的时间复杂度为 O(n)。
结果提取:提取最终结果的时间复杂度为 O(1)。
总体时间复杂度为 O(n)。
空间复杂度分析
栈:存储数值和运算符的空间复杂度为 O(n)。
结果列表:存储最终结果的空间复杂度为 O(1)。
总体空间复杂度为 O(n)。
总结
通过这次挑战,我不仅提升了自己的编程技能,还学会了如何更好地处理复杂的算法问题。这个计算器的实现过程让我深刻体会到了编程的魅力,也让我更加自信地面对未来的挑战。