实现一个基本的计算器来计算一个简单的字符串表达式 s 的值。
示例 1:
输入:s = "1 + 1"
输出:2
示例 2:
输入:s = " 2-1 + 2 "
输出:3
示例 3:
输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23
提示:
- s 由数字、’+’、’-‘、’(‘、’)’、和 ‘ ‘ 组成
- s 表示一个有效的表达式
题解
感觉不难,实际上无从下笔,明后天面试咋办!!!😿
栈
参考
一个表达式分为三部分:
左边表达式①,运算符③, 右边表达式②
本题中,左边和右边的表达式可以是一个数字,也可以是一个括号包起来的表达式;运算符可以是加减。
一个只包含加减和括号的表达式,可以从左到右计算,遇到括号就先算括号里面的。具体来说就是先计算左边的表达式,再计算右边表达式,最后根据运算符,计算 ①和②的运算 。
用题目示例 “(1+(4+5+2)-3)+(6+8)
“ 来说明运算符计算的顺序:
要计算表达式的时候,需要先计算👈左边表达式①,然后把①的结果和运算符③保存起来,再计算👉右边表达式②,最后计算①和②,这就是一个递归的过程。
递归的过程可以用栈来模拟:栈保存左边表达式①的计算结果和运算符③,在计算右边表达式②的结果之后,从栈中取出运算符③和①的结果,再进行计算整个表达式的结果。
例如:(1 + (2 + (3 + 4)))
。栈顶保留的是最里层嵌套的运算,弹出栈的时候,正好先算的是最里面括号的,再算外边括号的。这种情况时,栈里面保存的是 [“1”, “+”, “2”, “+”, “3”, “+”],然后遇到 4,此时计算的是 3 + 4,然后算 7 + 2,再算 9 + 1。可以通过递归来帮助理解。
代码里面:
- res 表示左边表达式除去栈内保存元素的计算结果;
- sign 表示运算符;
- num 表示当前遇到的数字,会更新到 res 中;
- 用栈保存遇到左括号时前面计算好了的结果和运算符。
操作的步骤是:
- 如果当前是数字,那么更新计算当前数字;
- 如果当前是操作符+或者-,那么需要更新计算当前计算的结果 res,并把当前数字 num 设为 0,sign 设为正负,重新开始;
- 如果当前是 ( ,那么说明遇到了右边的表达式,而后面的小括号里的内容需要优先计算,所以要把 res,sign 进栈,更新 res 和 sign 为新的开始;
- 如果当前是 ) ,那么说明右边的表达式结束,即当前括号里的内容已经计算完毕,所以要把之前的结果出栈,然后计算整个式子的结果;
- 最后,当所有数字结束的时候,需要把最后的一个 num 也更新到 res 中。
Python
def calculate(s):
res,num,sign = 0,0,1
stack=[]
for c in s:
if c.isdigit():
num = 10*num+int(c) # 这里10*num是为了避免出现连续两个数的情况,比如12,num就应该是1*10+2
elif c == '+' or c == '-':
res += sign*num
num=0
sign=1 if c == '+' else -1
elif c == '(':
stack.append(res)
stack.append(sign)
res=0
sign=1
elif c==')':
res+=sign*num
num=0
res*=stack.pop()
res+=stack.pop()
res+=sign*num
return res
JavaScript
/**
* @param {string} s
* @return {number}
*/
var calculate = function(s) {
let res = 0
let num = 0
let sign = 1
let stack = []
for(const c of s){
if (c === ' '){
continue
}
const n = Number(c)
if (!isNaN(n)){
num = 10* num + parseInt(n)
} else if (c === '+' || c === '-') {
res += sign*num
num = 0
if (c === '+') {
sign = 1
} else {
sign = -1
}
} else if (c === '(') {
stack.push(res)
stack.push(sign)
res = 0
sign = 1
} else if (c === ')') {
res += sign*num
num = 0
res *= stack.pop()
res += stack.pop()
}
}
res += sign*num
return res
};
复杂度分析
- 时间复杂度:O(n),其中 n 为字符串 s 的长度。需要遍历字符串 s 一次,计算表达式的值。
- 空间复杂度:O(n),其中 n 为字符串 s 的长度。空间复杂度主要取决于栈的空间,栈中的元素数量不超过 n。