栈和队列是操作受限的线性结构
栈:先进后出,压栈和出栈的时间复杂度为O(1)
队列:
队尾进入,队首出去。先进先出,出队和入队的时间复杂度为O(1)
collections.deque() deque是指双端队列,队首和队尾均可入队和出队
20.有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
思路:
为了实现匹配的操作,当检测到输入的符号为 (,[,{时,向栈中压入),],}。
当输入一个字符不为上述符号时,如果栈是空的,或者栈顶元素和它不相等,返回false。
这里注意的是,运用了逻辑短路,如果stack已经是空的了,就不会执行i!=stack.pop(),防止空数组无法执行pop而报错。
此外,stack.pop()放入语句中执行时,都会弹出最外面的元素。
class Solution:
def isValid(self, s: str) -> bool:
if not s:return True
stack = []
for i in s:
if i == '(':
stack.append(')')
elif i == '[':
stack.append(']')
elif i == '{':
stack.append('}')
elif not stack or i!=stack.pop():
return False
if not stack:
return True
else:
return False
71.简化路径
思路:
(1)首先,将输入的字符串看作一个数组,把每个符号拆分开来,就能看出问题的本质是解决当遇到’.’、’…’、以及’ '时应当如何处理这个问题。
(2)遇到这种问题时,首先应该将path按照’/'分割开,得到每个单独的字符。代码为:
path = path.split('/')
(3)其次,遍历分割好的字符串,如果该字符是空,或者’.‘时,不要管他(continue)
如果是’…’,但queue是空的时候,不管他(continue)
如果queue不空,则弹出栈顶的字符,因为要返回上一级。
以上情况均不符合时,入栈。
(4)到了输出的环节,注意输出的语句:
res = res+'/'
res = res + queue.popleft()
其中,为什么使用deque,是因为如果使用栈的话,弹出的不是最根的路径。应该弹出栈中先进入的元素,只有deque才能做得到。
class Solution:
def simplifyPath(self, path: str) -> str:
queue = collections.deque()
path = path.split('/')
for i in path:
if not i or i == '.':
continue
elif i == '..' and not queue:
continue
elif i == '..' and queue:
queue.pop()
else:
queue.append(i)
res = ''
if not queue:
return '/'
while(queue):
res = res+'/'
res = res + queue.popleft()
return res
394.字符串解码
该题主要难度在于处理像 3[a2[c]]这样的循环结构。
numstack用于存储循环多少次的栈
strstack用于存储循环部分的栈
(1)
num = num*10+int©是将多个单独字符串数字转换成一整个数字的常用方法。
(2)当c==’[‘时,将数字num压入栈,字符res压入栈(字符res代表一整个字符,从后面的res += c可以看出)。清空res和num
(3)当c==’]'时,将res转化为num个res,具体做法见程序,其中,因为item已经包括了一个res,所以循环结构是从1开始的。
class Solution:
def decodeString(self, s: str) -> str:
num = 0
res = ''
numstack = []
strstack = []
for c in s:
if c >= '0' and c <= '9':
num = num*10+int(c)
elif c == '[':
numstack.append(num)
strstack.append(res)
res = ''
num = 0
elif c == ']':
item = res
for i in range(1,numstack.pop()):
res += item
res = strstack.pop() + res
else:
res += c
return res
224.基本计算器
遇到这种情况,都需要把字符串看作中的每个字符单独地一个一个去处理。
class Solution:
def calculate(self, s: str) -> int:
presign = 1
num = 0
res = 0
resstack = []
signstack = []
for c in s:
if c >= '0' and c <='9':
num = num*10+int(c)
elif c == '+':
res += presign*num
presign = 1
num = 0
elif c == '-':
res += presign*num
presign = -1
num = 0
elif c == '(':
resstack.append(res)
signstack.append(presign)
presign = 1
res = 0
elif c == ')':
res += presign*num
res *= signstack.pop()
res += resstack.pop()
num = 0
return res + presign*num
class Solution:
def calculate(self, s: str) -> int:
presign = 1
num = 0
res = 0
resstack = []
signstack = []
for c in s:
if c >= '0' and c <='9':
num = num*10+int(c)
elif c == '+':
res += presign*num
presign = 1
num = 0
elif c == '-':
res += presign*num
presign = -1
num = 0
elif c == '(':
resstack.append(res)
signstack.append(presign)
presign = 1
res = 0
elif c == ')':
res += presign*num
res *= signstack.pop()
res += resstack.pop()
num = 0
return res + presign*num
227.基本计算器二
class Solution:
def calculate(self, s: str) -> int:
n = len(s)
stack = []
preSign = '+'
num = 0
for i in range(n):
if s[i] != ' ' and s[i].isdigit():
num = num * 10 + ord(s[i]) - ord('0')
if i == n - 1 or s[i] in '+-*/':
if preSign == '+':
stack.append(num)
elif preSign == '-':
stack.append(-num)
elif preSign == '*':
stack.append(stack.pop() * num)
else:
stack.append(int(stack.pop() / num))
preSign = s[i]
num = 0
return sum(stack)
946.验证栈序列
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
pushstack = []
for i in range(len(pushed)):
if pushed[i] != popped[0]:
pushstack.append(pushed[i])
elif pushed[i] == popped[0]:
popped.pop(0)
while pushstack and pushstack[-1] == popped[0]:
pushstack.pop()
popped.pop(0)
if not popped:
return True
else:
return False
单调栈:
要求栈中元素必须为升序元素排列或降序排列的。
739.每日温度
启发:
除了学习到一个解决单调递减栈的方法外,还要注意到在循环结构中,一定要有可以跳出循环的变量,这里的
prev = stack.pop()
我本来写成了prev= stack[-1],就跳不出来了。
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
if len(temperatures) == 1 :return 0
stack = []
res = [0]*len(temperatures)
for i in range(len(temperatures)):
x = temperatures[i]
#单调递减栈
while stack and x > temperatures[stack[-1]]:
prev = stack.pop()
res[prev] = i - prev
stack.append(i)
return res
739.每日温度
暴力求解
class Solution:
def trap(self, height: List[int]) -> int:
if len(height)<=2:
return 0
n = len(height)
leftMax = [0]*n
leftMax[0] = height[0]
for i in range(1,n):
leftMax[i] = max(leftMax[i-1],height[i])
rightMax = [0]*n
rightMax[n-1] = height[n-1]
for i in range(n-2,-1,-1):
rightMax[i] = max(rightMax[i+1],height[i])
ans = 0
for i in range(1,len(height)-1):
maxHeight = min(rightMax[i],leftMax[i])
if maxHeight>height[i]:
ans += maxHeight - height[i]
return ans
由于上述两个数组leftMax和rightMax只用了一次,所以完全可以用两个数字来代替。所以我们采用双指针算法(对撞指针)。
class Solution:
def trap(self, height: List[int]) -> int:
if len(height)<=2:
return 0
n = len(height)
leftMax = 0
rightMax = 0
left = 0
right = n-1
ans = 0
while left<right:
leftMax = max(leftMax,height[left])
rightMax = max(rightMax,height[right])
if height[left] < height[right]:
ans += leftMax - height[left]
left += 1
else:
ans += rightMax - height[right]
right = right - 1
return ans
找到右边第一个,大于heigh[i]的才可以计算,否则就把heigh[i]压入栈中。
单调栈做法,看不太懂。
class Solution:
def trap(self, height: List[int]) -> int:
if len(height)<=2:
return 0
n = len(height)
ans = 0
stack = []
for i in range(n):
while stack and height[i]>height[stack[-1]]:
top = stack.pop()
if not stack:
break
leftIndex = stack[-1]
currWidth = i -leftIndex -1
currHeight = min(height[leftIndex],height[i])-height[top]
ans += currWidth*currHeight
stack.append(i)
return ans
84.柱状图中的最大矩形
1)枚举宽
遍历每个矩阵,求出它们的面积
首先是得到矩阵的方法:
设立两个指针left和right
在left固定的情况下挑出比heights[left]小的heights[right],得到最小的高度(也是矩阵的高度),再乘宽度,和之前的ans做比较。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
ans = 0
for left in range(0,len(heights)):
minHeight = heights[left]
for right in range(left,len(heights)):
minHeight = min(minHeight,heights[right])
currWidth = right-left+1
ans = max(ans,minHeight*currWidth)
return ans
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
ans = 0
for mid in range(0,len(heights)):
height = heights[mid]
left = mid
right = mid
while left>=0 and heights[left]>=height:left = left-1
while right<=len(heights)-1 and heights[right]>=height:right = right +1
ans = max(ans,height * (right-left-1))
return ans
单调栈(一)
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
ans = 0
left = [-1]*len(heights)
right = [len(heights)]*len(heights)
stack = []
for i in range(len(heights)-1,-1,-1):
while stack and heights[i]<heights[stack[-1]]:
left[stack[-1]] = i
stack.pop()
stack.append(i)
stack = []
for j in range(0,len(heights)):
while stack and heights[j]<heights[stack[-1]]:
right[stack[-1]] = j
stack.pop()
stack.append(j)
for mid in range(0,len(heights)):
height = heights[mid]
ans = max(ans,height * (right[mid]-left[mid]-1))
return ans
单调栈(二)
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
ans = 0
left = [-1]*len(heights)
right = [len(heights)]*len(heights)
stack = []
for i in range(0,len(heights)):
while stack and heights[i]<=heights[stack[-1]]:
stack.pop()
if not stack: left[i]=-1
else:left[i] = stack[-1]
stack.append(i)
stack = []
for j in range(len(heights)-1,-1,-1):
while stack and heights[j]<=heights[stack[-1]]:
stack.pop()
if not stack: right[j]=len(heights)
else:right[j] = stack[-1]
stack.append(j)
for mid in range(0,len(heights)):
height = heights[mid]
ans = max(ans,height * (right[mid]-left[mid]-1))
return ans
单调栈(三)
一次遍历
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
ans = 0
left = [-1]*len(heights)
right = [len(heights)]*len(heights)
stack = []
for i in range(0,len(heights)):
while stack and heights[i]<=heights[stack[-1]]:
right[stack[-1]]= i
stack.pop()
if not stack: left[i]=-1
else:left[i] = stack[-1]
stack.append(i)
for mid in range(0,len(heights)):
height = heights[mid]
ans = max(ans,height * (right[mid]-left[mid]-1))
return ans