题目:
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
LeetCode:https://leetcode-cn.com/problems/basic-calculator-ii/
思路
先不考虑空格,以及乘除、括号,假设现在有一个最简单的表达式,只有加减两种运算符,也没有括号,应该如何运算?
- 显而易见,从左到右,依次遍历即可,那我们先把这样的情况翻译成代码,借助队列,队列中从0位置到最后一个位置,对应表达式中从左到右每个字符的位置(假设一个字符就是一个数值,先不考虑字符拼数字),代码如下所示:
def get_num(arr):
res = 0
tmp = True
while len(arr) != 0:
cur = arr.popleft()
if cur == '+':
tmp = True
elif cur == '-':
tmp = False
else:
res += int(cur) if tmp else -int(cur)
return res
现在,再来考虑如何将一个表达式转变成队列,首先考虑到乘除的计算优先级问题,也就是说当我们从左到右将字符压入队列时,要优先保证乘除先被计算,所以当我们遇到加减运算符时,可以直接入队列,但如果遇到了乘除法,要先将乘除法计算完毕后再将结果入队列,这样就避免了后续计算顺序的问题。
新的问题又来了,如何在遇到乘除时,先计算呢?
- 解决策略是,入队列时,不管当前是什么运算符,暂且先压入队列,当遇到下一个数字要压入队列时,再判断这个数字之前的运算符是什么,如果是乘除,就弹出运算符和运算符之前的数字,先计算,将计算结果入队列。
def add_new(stack, num):
if len(stack) != 0:
if stack[-1] == '-' or stack[-1] == '+':
stack.append(num)
elif stack[-1] == '*':
stack.pop()
new = int(stack.pop()) * int(num)
stack.append(str(int(new)))
else:
stack.pop()
new = int(stack.pop()) / int(num)
stack.append(str(int(new)))
else:
stack.append(num)
return stack
其中,num是要新入队列的数。有一点要注意,当队列为空时,不需要判断计算,直接压入即可;
乘除的问题解决了,现在考虑括号的问题,因为括号里的表达式同样也是一个完整的计算过程,并且是独立于括号外面的,所以利用递归解决。
所以,总的思路为:
- 从头开始遍历字符串中每个位置的字符,如果遇到的字符是数字,收集起来;
- 如果遇到的字符是左括号,进入递归过程;
- 如果遇到了运算符,说明当前收集到的数字字符已经构成一个完整的计算数值了,将收集到的数字压入队列,再将运算符压入队列,同时之前收集的数字字符需要归零;
- 如果遇到了空格,则结束当前循环,进行下一位置判断;
- 整个过程的结束条件是,已经到达字符串末尾了或者遇到了右括号,此时需要将队列中表达式计算并返回结果;
整个代码如下:
def process(string, i):
num = '0'
stack = collections.deque()
while (i < len(string) and string[i] != ')'):
if string[i] == ' ':
i += 1
continue
if string[i] >= '0' and string[i] <= '9': # 遇到数字了,收集
num += string[i]
i += 1
elif string[i] == '(': # 遇到左括号,要进入递归
num, i = process(string, i + 1)
stack = add_new(stack, num) # 可有可无,因为左括号之前一定是某个运算符,此时num一定是0,加入不影响计算
i += 1
else: # 遇到运算符
stack = add_new(stack, num)
stack.append(string[i])
num = '0'
i += 1
add_new(stack, num) # 此时,还需要将字符串中最后一个数字或者右括号前面的数字压入队列
return calc(stack), i
def add_new(stack, num):
if len(stack) != 0:
if stack[-1] == '-' or stack[-1] == '+':
stack.append(num)
elif stack[-1] == '*':
top = stack.pop()
new = int(stack.pop()) * int(num)
stack.append(str(int(new)))
else:
top = stack.pop()
new = int(stack.pop()) / int(num)
stack.append(str(int(new)))
else:
stack.append(num)
return stack
def calc(stack):
res = 0
tmp = True
while len(stack) != 0:
cur = stack.popleft()
if cur == '-':
tmp = False
elif cur == '+':
tmp = True
else:
res += int(cur) if tmp else -int(cur)
return res
def main():
res, _ = process(string, 0)
return res