问题描述
小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+5*2)/3+(6+8*3)"
输出:40
解题思路
计算器的实现也是一个很经典的问题了。相比于实现一个计算器,本题的要求显得比较简单,因为没有负数,没有空格,没有错误处理什么的,所以实现起来也比较简单,核心思路就是递归。理解起来的话就是一颗语法树,每一个叶子节点存放一个数,而父节点则存放运算符。而核心难点就是对运算符的优先级的处理,只要把这个解决了,那么这个问题剩下的就是按部就班的模拟了。 那么代码实现的话也是如此。
- 首先,先定义运算符优先级,分别是:(,),*,/,+,-,如若优先级相同,则从左到右,但是实现的时候从右到左遍历的话会更简单。
- 我的解决方式是,先处理 + 号,再处理 - 号,最后处理 * 和 /,在每一次处理中都对括号进行检查和删除(当为两端括号时),对括号的检查直接用栈的思想就行。
- 注意题目中要求除法为整数除法,所以不留小数,这个容易被忽视。
- 为什么从右到左进行遍历而不是从左到右呢?因为从左到右的话容易出现一种情况,那就是当一个子表达式前面有 - 号的时候,会产生意料之外的结果,如: -(1 + 2 + 3),按理说应该是-6,但在我的实现中不是(。而从右到左遍历的话子表达式会先计算出整体值后再与 - 号做计算,这样能减少代码量。 下面是golang的实现代码,时间复杂度的话取决于运算符的数量。
package main
import (
"fmt"
"strconv"
)
func eval(expression string) int {
// 核心:以操作符优先级来划分
bracketCount := 0
length := len(expression)
for i := length - 1; i >= 0; i-- {
char := expression[i]
switch char {
case '(':
bracketCount--
case ')':
bracketCount++
default:
}
if bracketCount > 0 {
continue
}
switch char {
case '+':
left := eval(expression[:i])
right := eval(expression[i+1:])
fmt.Println(left, "+", right, "=", left+right)
return left + right
default:
continue
}
}
for i := length - 1; i >= 0; i-- {
char := expression[i]
switch char {
case '(':
bracketCount--
case ')':
bracketCount++
default:
}
if bracketCount > 0 {
continue
}
switch char {
case '-':
left := eval(expression[:i])
right := eval(expression[i+1:])
fmt.Println(left, "-", right, "=", left-right)
return left - right
default:
continue
}
}
for i := length - 1; i >= 0; i-- {
char := expression[i]
switch char {
case '(':
bracketCount--
case ')':
bracketCount++
default:
}
if bracketCount > 0 {
continue
}
switch char {
case '*':
left := eval(expression[:i])
right := eval(expression[i+1:])
fmt.Println(left, "*", right, "=", left*right)
return left * right
case '/':
left := eval(expression[:i])
right := eval(expression[i+1:])
fmt.Println(left, "/", right, "=", left/right)
return left / right
default:
continue
}
}
if expression[0] == '(' && expression[len(expression)-1] == ')' {
return eval(expression[1 : len(expression)-1])
}
result, _ := strconv.ParseInt(expression, 10, 0)
return int(result)
}
func solution(expression string) int {
result := eval(expression)
fmt.Println("res:", result)
return result
}
func main() {
// You can add more test cases here
fmt.Println(solution("1+1") == 2)
fmt.Println(solution("3+4*5/(3+2)") == 7)
fmt.Println(solution("4+2*5-2/1") == 12)
fmt.Println(solution("(1+(4+5+2)-3)+(6+8)") == 23)
fmt.Println(solution("8-3+(6*4)/3") == 13)
fmt.Println(solution("(6-2)+(5*3/5)") == 7)
fmt.Println(solution("9/(3+2)-4") == -3)
}
下面是python的代码,由marscode转化而来,能通过提交。
def eval(expression):
# 核心:以操作符优先级来划分
bracket_count = 0
length = len(expression)
# 处理加法
for i in range(length - 1, -1, -1):
char = expression[i]
if char == '(':
bracket_count -= 1
elif char == ')':
bracket_count += 1
if bracket_count > 0:
continue
if char == '+':
left = eval(expression[:i])
right = eval(expression[i+1:])
print(f"{left} + {right} = {left + right}")
return left + right
# 处理减法
for i in range(length - 1, -1, -1):
char = expression[i]
if char == '(':
bracket_count -= 1
elif char == ')':
bracket_count += 1
if bracket_count > 0:
continue
if char == '-':
left = eval(expression[:i])
right = eval(expression[i+1:])
print(f"{left} - {right} = {left - right}")
return left - right
# 处理乘法和除法
for i in range(length - 1, -1, -1):
char = expression[i]
if char == '(':
bracket_count -= 1
elif char == ')':
bracket_count += 1
if bracket_count > 0:
continue
if char == '*':
left = eval(expression[:i])
right = eval(expression[i+1:])
print(f"{left} * {right} = {left * right}")
return left * right
elif char == '/':
left = eval(expression[:i])
right = eval(expression[i+1:])
print(f"{left} / {right} = {left // right}")
return left // right
# 处理括号
if expression[0] == '(' and expression[-1] == ')':
return eval(expression[1:-1])
# 处理数字
return int(expression)
def solution(expression):
result = eval(expression)
print(f"res: {result}")
return result
def 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("8-3+(6*4)/3") == 13)
print(solution("(6-2)+(5*3/5)") == 7)
print(solution("9/(3+2)-4") == -3)
if __name__ == "__main__":
main()
总结
这是一道很经典的题目,做起来的不难,只要点通运算符的顺序以及从右到左遍历之后,那就不成问题了。