问题:表达式求值
请写一个整数计算器,支持加减乘三种运算和括号。
暂时不考虑字符串存在负数。除法和乘法处理相似,排除小数,不考虑除法。
输入:"(2*(3-4))*5"
输出:-10
思路:
使用逆波兰式,后缀表达式。
使用递归,把表达式分成所有项的+、-运算,把项分成因子的*、/运算,因子可以是个数、是个(表达式)加括号的表达式。这样就把定义递归回去了。比较复杂。
使用双栈。下面是双栈思路。
思考方向:
- 当前运算符是否计算,要看后一个操作符是什么。
- 当前是加法计算,后一个运算符是乘法,这个加法就不能计算。
- 当前是乘法,后一个是(,也不能计算。
- 所以,使用栈的思路就是思考当前和后一个操作符分别是什么,什么情况下可以计算当前的运算。
实现方法:
- 两个栈,符号栈和数字栈。符号栈的栈顶运算符优先级较高。
- 遍历字符串,遇到数字就进栈。
- 遇到走"(" 进栈。
- 遇到运算符(+ - *),查看符号栈顶元素(上一个运算符),当前运算符的优先级是否比栈顶优先级低,如果低,就取出上一个高优先级的运算符,和数字栈的两个数,进行运算,循环判断到当前优先级较大为止。最后进栈。
- 遇到")",直接出栈到"(",并计算。因为栈顶运算符优先级高,可以直接计算。
- 遍历完成,在出栈计算栈内剩余的表达式。
go语言代码实现
package main
import "strconv"
func solve(s string) int {
// write code here
shu := make([]int, 0)
fu := make([]byte, 0)
for i := 0; i < len(s); i++ {
switch {
case s[i] >= '0' && s[i] <= '9':
j := toInt(s, i)
a, _ := strconv.Atoi(s[i:j])
i = j - 1
shu = append(shu, a)
case s[i] == '(':
fu = append(fu, s[i])
case s[i] == ')':
for len(fu) > 0 && fu[len(fu)-1] != '(' {
pro := fu[len(fu)-1]
fu = fu[ :len(fu)-1]
cal(&shu, pro)
}
fu = fu[ :len(fu)-1]
case s[i] == '+' || s[i] == '-': //前一个不是(,就计算。
for len(fu) > 0 && fu[len(fu)-1] != '('{
pro := fu[len(fu)-1]
fu = fu[ :len(fu)-1]
cal(&shu, pro)
}
fu = append(fu, s[i])
case s[i] == '*': //前一个是*,就计算
for len(fu) > 0 && fu[len(fu)-1] == '*'{
pro := fu[len(fu)-1]
fu = fu[ :len(fu)-1]
cal(&shu, pro)
}
fu = append(fu, s[i])
}
}
for len(fu) > 0 { //最后出栈计算
pro := fu[len(fu)-1]
fu = fu[ :len(fu)-1]
cal(&shu, pro)
}
return shu[0]
}
//计算运算符p,shu栈,
func cal(shu *[]int, p byte) ( r int) {
a := (*shu)[len(*shu)-2]
b := (*shu)[len(*shu)-1]
st := (*shu)[ :len(*shu)-2]
*shu = st
switch p {
case '*':
r = a * b
case '+':
r = a + b
case '-':
r = a - b
}
*shu = append(*shu, r)
return
}
//字符串转化成数字
func toInt(s string, i int) int {
for i < len(s) && s[i] >= '0' && s[i] <= '9' {
i++
}
return i
}