解题思路
小F面临的问题是实现一个基本的计算器,用于计算包含数字(0-9)、运算符(+、-、*、/)及括号(())的字符串表达式的值。字符串表达式有效且不包含空格,除法运算应只保留整数结果。为了解决这个问题,可以采用以下思路:
- 逆波兰表达式(后缀表达式) :将中缀表达式(如
3 + 4)转换为后缀表达式(如3 4 +),再进行计算。这种方法可以避免处理括号和运算符优先级的复杂性。 - 栈的应用:使用两个栈,一个用于存储操作数(数字),另一个用于存储操作符(运算符)。通过遍历字符串表达式,逐步将数字和运算符压入相应的栈中,并根据运算符的优先级进行计算。
算法设计
-
初始化:创建两个栈,一个用于操作数(
operands),一个用于操作符(operators)。 -
遍历表达式:
- 当遇到数字时,解析多位数字并压入操作数栈。
- 当遇到运算符时,根据优先级决定是否计算栈中的操作数。如果当前运算符的优先级小于或等于栈顶运算符的优先级,则先计算栈顶的操作符,再将当前运算符压入栈。
- 当遇到左括号时,直接压入操作符栈。
- 当遇到右括号时,弹出并计算操作符栈中的运算符,直到遇到左括号。
-
最终计算:遍历结束后,处理操作符栈中剩余的运算符,直到栈为空。
代码分析
-
辅助函数
apply_operator:
这个函数负责从操作符栈和操作数栈中取出操作符和操作数,进行相应的运算,并将结果压回操作数栈。def apply_operator(operators, operands): if not operators or len(operands) < 2: return right = operands.pop() left = operands.pop() op = operators.pop() if op == '+': operands.append(left + right) elif op == '-': operands.append(left - right) elif op == '*': operands.append(left * right) elif op == '/': operands.append(int(left / right)) # 只保留整数部分 -
优先级函数
precedence:
这个函数定义了运算符的优先级。def precedence(op): if op in ('+', '-'): return 1 if op in ('*', '/'): return 2 return 0 -
主解析函数
parse:
这个函数通过遍历字符串表达式,逐步解析数字和运算符,并根据运算符的优先级进行计算。def parse(expression): operators = [] # 操作符栈 operands = [] # 操作数栈 i = 0 n = len(expression) while i < n: if expression[i].isdigit(): num = 0 while i < n and expression[i].isdigit(): num = num * 10 + int(expression[i]) i += 1 operands.append(num) continue elif expression[i] in "+-*/": while (operators and operators[-1] != '(' and precedence(operators[-1]) >= precedence(expression[i])): apply_operator(operators, operands) operators.append(expression[i]) elif expression[i] == '(': operators.append(expression[i]) elif expression[i] == ')': while operators and operators[-1] != '(': apply_operator(operators, operands) operators.pop() i += 1 while operators: apply_operator(operators, operands) return operands[-1] if operands else 0
有可能出现的问题
-
边界条件处理:
- 空字符串的处理:虽然题目假设表达式有效,但实际应用中应考虑空字符串的情况。
- 单个数字的表达式:例如
"1",需要确保能够正确返回结果。
-
负数处理:
- 当表达式中存在负数时,如何正确解析负号?例如
"-1+2"。
- 当表达式中存在负数时,如何正确解析负号?例如
-
除法处理:
- 除法运算中,当被除数为0时,需要处理除零错误。
- 例如:
1/0会引发除零错误。
-
非法字符处理:
- 如果表达式中包含非法字符,例如
"1+1a",需要进行异常处理。
- 如果表达式中包含非法字符,例如
-
括号不匹配:
- 如果表达式中的括号不匹配,例如
"(1+1))"或"(1+1",需要处理这种情况。
- 如果表达式中的括号不匹配,例如
个人的思考与改进
-
负数处理:
- 在解析数字时,增加对负号的处理。当遇到负号时,解析其后的数字并将其作为负数压入操作数栈。
- 例如,解析
"-1+2"时,-1应该被视为一个负数。
-
代码简化:
- 通过使用 Python 的
collections.deque来替代普通的列表作为栈,可以更高效地进行弹出和压入操作。 - 例如,将
operators和operands替换为deque。
- 通过使用 Python 的
-
增加异常处理:
- 在解析过程中增加异常处理,处理非法字符、括号不匹配等问题。
- 例如,处理非法字符时可以抛出
ValueError。
-
测试用例:
- 增加更多的测试用例,特别是边界条件和特殊情况,以确保代码的正确性和鲁棒性。
- 例如,测试空字符串、单个数字、负数、除零错误等。
-
性能优化:
- 通过减少不必要的栈操作和循环,可以提高代码的性能。
- 例如,可以将数字解析的内部循环提取到一个单独的函数中,减少代码重复。