堆栈
首先想到的思路是,遍历字符串
- 空格:什么都不操作
- 加号或者减号:入栈
- 数字:入栈
- 乘号或者除号:弹出栈顶元素,和符号后的数字做运算,将运算结果入栈
遍历后的堆栈只有数字、加号或者减号,再遍历一遍堆栈,运算结果就可以了
然而,事实不是那么简单的,有这么几个坑坑
- 数字和数字之间有空格,数字可能超过1位,那么需要记录一个数字变量num,更新num的值(*)
- 栈内的数字是int类型,字符串的数字是字符类型,字符类型需要转换成int类型
个人推测,(*)部分增加了一些常数项的时间,拉低了计算效率
class Solution:
def calculate(self, s: str) -> int:
'''
单个栈,遍历两次
'''
stack = []
# s = ''.join(s.split()) # Note(*)去除空格
i = 0
while i < len(s):
if s[i] == '+':
stack.append(s[i])
i += 1
elif s[i] == '-':
stack.append(s[i])
i += 1
elif s[i] == '*':
num1 = stack.pop()
# 找到后面的数字
num2 = 0
i += 1
while i < len(s) and s[i] not in ['+', '-', '*', '/']: # Note(2)
if s[i] == ' ': # Note(1)
i += 1 # Note(1)
else: # Note(1)
num2 = num2*10 + int(s[i])
i += 1
stack.append(num1 * num2)
elif s[i] == '/':
num1 = stack.pop()
# 找到后面的数字
num2 = 0
i +=1
while i < len(s) and s[i] not in ['+', '-', '*', '/']:
if s[i] == ' ': # Note(1)
i += 1 # Note(1)
else: # Note(1)
num2 = num2*10 + int(s[i])
i += 1
stack.append(int(num1 / num2))
elif s[i] == ' ':
i += 1
else:
# s[i]是数字, '0' <= s[i] <= '9'
num = 0
while i < len(s) and s[i] not in ['+', '-', '*', '/']:
if s[i] == ' ': # Note(1)
i += 1 # Note(1)
else: # Note(1)
num = num*10 + int(s[i])
i += 1
stack.append(num)
res = stack[0]
j = 1
while j < len(stack):
if stack[j] == '+':
j += 1
num = stack[j]
res = res + num
j += 1
elif stack[j] == '-':
j += 1
num = stack[j]
res = res - num
j += 1
return res
- Note(1)和Note(*)单独处理空格
- 时间复杂度:O(N),然而Note(2)部分的循环增加了多次的判断,导致O(N)的N前面有个常数,算法整体上的运算时间增加了,stack堆栈里边判断加号还是减号也要花一点点时间,那么看看下面的大佬们的解法吧~
- 空间复杂度:O(N),堆栈存储是O(N)
单次遍历,时间复杂度和空间复杂度O(N)
参考题解:https://leetcode-cn.com/problems/basic-calculator-ii/solution/ji-ben-ji-suan-qi-ii-by-leetcode-solutio-cm28/
class Solution:
def calculate(self, s: str) -> int:
'''
Solution 2: 另一种思路,不得不说不要吝惜常数变量
'''
stack = []
sign = '+' # 记录num前的符号,初始值是正
num = 0 # 当前的数字
n = len(s)
for i in range(0, n):
# Note(1)
if '0' <= s[i] <= '9':
num = num * 10 + int(s[i])
# Note(2)
if i == n-1 or s[i] in ['+', '-', '*', '/']:
# 触发入栈条件1: 遍历到符号的时候,触发上一次计算或者入栈的操作
# 触发入栈条件2: 遍历到了字符串的最后一个位置
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign == '*':
stack.append(stack.pop() * num)
else:
# sign == '/':
stack.append(int(stack.pop() / num))
sign = s[i] # 更新运算符号
num = 0 # num重置为0
# 累加堆栈的元素,是返回结果
res = 0
for item in stack:
res += item
return res
不得不说,增加一些辅助变量,很妙啊~
维护两个全局变量
- num: 数字,将会是入栈的数字
- sign: num的运算符号,数字入栈的符号
- 入栈条件1: 遍历到运算符号
- 入栈条件2: 遍历到字符串的最后(⭐️这一条件很容易被遗漏)
- 一直在更新数字,数字更新结束一个,判断这个数字num以什么样的方式入栈
-
- sign = ‘+’, num入栈
-
- sign = ‘-’ , num相反数入栈
-
- sign = ‘*’ , pop出来栈顶元素,和num相乘后,运算结果入栈
-
- sign = ‘/’, pop出来栈顶元素,除以num取整后,运算结果入栈
- Note(1)和Note(2)两个部分之间没有else的关系,这也很重要啊,遍历到每个元素的时候都去判断下有没有遇到新的运算符号,如果遇到了新的运算符号,那么执行入栈的操作
遍历结束后的堆栈中只有数字,数字和数字之间的关系只有加法的关系,节省了更多的时间
哈哈哈,小伙伴们,千万不要吝惜指针和辅助变量哇~
今天的分享就到这里了,我们下次再见啦~
喜欢的话请收藏、点赞、转发啊~
复习重写代码
class Solution:
def calculate(self, s: str) -> int:
stack = [] # 堆栈
num = 0 # 数字的值
res = 0 # 存放结果
sign = '+' # 数字前的符号
i = 0
while i <= len(s):
# 把符号更新到sign
if i==len(s) or s[i] in ['+', '-', '*', '/']:
# 入栈
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign == '*':
stack.append(stack.pop() * num)
elif sign == '/':
stack.append(int(stack.pop() / num))
# 更新sign,num清零
if i<len(s):
sign = s[i]
i += 1
num = 0
# 空格,过滤
elif s[i] == ' ':
i += 1
# 数字,找到最后一个不是数字的,假设数字和数字之间没有空格
elif s[i] >= '0' and s[i] <= '9':
while i < len(s) and s[i] >= '0' and s[i] <= '9':
num = num*10 + int(s[i]) # 注意这里要把s[i]从字符变成int类型
i += 1
# 栈里元素累加
while stack != []:
res = res + stack.pop()
return res