「这是我参与11月更文挑战的第 16 天,活动详情查看:2021最后一次更文挑战」
刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能
表达式求值
题目来源:牛客网-表达式求值
题目描述
请写一个整数计算器,支持加减乘三种运算和括号
数据范围:0 ≤ |s| ≤ 100,保证计算结果始终在整型范围内
要求:空间复杂度: O(n),时间复杂度 O(n)
示例
示例 1
输入:"1+2"
返回值:3
示例 2
输入:"(2*(3-4))*5"
返回值:-10
示例 3
输入:"3+2*3*4-1"
返回值:26
解题
思路
常规的表达式求值
一个表达式包含两个部分,数字和运算符。可以用两个栈来实现表达式求值,一个栈用来存储数字,一个栈用来存储运算符
从左向右遍历表达式,当遇到数字时,将数字放入到存储数字的栈;如果遇到运算符,将存储运算符栈的栈顶元素取出,进行优先级比较
如果比运算符栈顶元素优先级高,则将当前运算符压入到存储运算符的栈中;如果比运算符栈顶元素低或优先级一样,则从存储数字的栈中取出两个元素,然后进行计算,将计算的结果放入到存储数字的栈中。重复上边的操作
存在小括号的表达式求职
- 如果遇到(,直接将其压入到存储运算符的栈中
- 如果遇到),则取出符号栈中的运算符和数字栈中的数字进行计算,将计算结果放入数字栈,直到遇到(
- 如果遇到运算符,发现栈顶元素是(,则运算符入符号栈
以 ( 5 * ( 8 - 3 * 2 ) + 5 ) * 2 表达式为例 例,按照上边的思路,图解它的计算过程
代码实现起来还是有些困难的,建议自己敲一遍,需要考虑的细节挺多的。代码敲了俩小时才实现(做算法题还是不能求快,搞懂最重要,不要感觉自己脑子里边有思路就感觉自己会了,共勉! )
代码
//表达式求职
func SolveExpression( s string ) int {
// write code here
//我这里定义一个map,用来存储运算符的优先级
mapOperator := map[byte]int{
'+': 1,
'-': 1,
'*': 2,
'/': 2,
}
n := len(s)
opsStack := []byte{} // 符号栈
numStack := []int{} // 数字栈
for i := 0; i < n; i++ {
ch := s[i]
if ch == '(' {
//遇到 ( 直接入符号栈
opsStack = append(opsStack, ch)
} else if ch == ')' { // 遇到 )则取出符合和数字进行计算,直到遇到)
for len(opsStack) > 0 {
//取出符号栈的栈顶元素
operator := opsStack[len(opsStack)-1]
if operator != '(' {
//计算
if len(numStack) < 2 {
continue
}
if len(opsStack) < 1 {
continue
}
firstVal := numStack[len(numStack)-1]
secondVal := numStack[len(numStack)-2]
operator := opsStack[len(opsStack)-1]
res := calculate(firstVal, secondVal, operator)
opsStack = opsStack[:len(opsStack)-1]
numStack = numStack[:len(numStack)-2]
numStack = append(numStack, res)
} else {
opsStack = opsStack[:len(opsStack)-1]
break
}
}
} else {
if isNumber(ch) {
u := 0
j := i
//因为可能是多位数(例如:12*(3+(12/4)+5))
for j < n && isNumber(s[j]) {
u = u * 10 + int((s[j] - '0'))
j++
}
numStack = append(numStack, u)
} else {
// 只要符号栈不是空的,并且栈顶运算符不是(,就可以进行不断的运算
for len(opsStack) > 0 && popOperator(opsStack, false) != '(' {
operator := popOperator(opsStack, false)
if mapOperator[operator] >= mapOperator[ch] { //站顶运算符优先级 > 当前运算符
firstVal := numStack[len(numStack)-1]
secondVal := numStack[len(numStack)-2]
operator := opsStack[len(opsStack)-1]
res := calculate(firstVal, secondVal, operator)
opsStack = opsStack[:len(opsStack)-1]
numStack = numStack[:len(numStack)-2]
numStack = append(numStack, res)
} else {
break
}
}
opsStack = append(opsStack, ch)
}
}
}
//把剩余的计算完
if len(opsStack) > 0 && popOperator(opsStack, false) != '(' {
firstVal := numStack[len(numStack)-1]
secondVal := numStack[len(numStack)-2]
operator := opsStack[len(opsStack)-1]
res := calculate(firstVal, secondVal, operator)
opsStack = opsStack[:len(opsStack)-1]
numStack = numStack[:len(numStack)-2]
numStack = append(numStack, res)
}
return numStack[len(numStack)-1]
}
func calculate(firstVal, secondVal int, operator byte) int {
res := 0
switch operator {
case '+':
res = secondVal + firstVal //计算的时候注意顺序,栈中靠后那个数,在表达式中是在前边的
case '-':
res = secondVal - firstVal
case '*':
res = secondVal * firstVal
case '/':
res = secondVal / firstVal
}
return res
}
func isNumber(ch byte) bool {
if ch > 47 && ch < 58 {
return true
}
return false
}
func popOperator(stack []byte, flag bool) byte {
if len(stack) == 0 {
return 0
}
operator := stack[len(stack)-1]
if flag {
stack = stack[:len(stack)-1]
}
return operator
}