队列和栈-18/18
队列-1
设计循环队列,中等
class MyCircularQueue:
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the queue to be k.
"""
self.queue = [0]*k
self.headIndex = 0
self.count = 0 #这个属性至关重要
self.capacity = k
def enQueue(self, value: int) -> bool:
"""
Insert an element into the circular queue. Return true if the operation is successful.
"""
if self.count == self.capacity:
return False
self.queue[(self.headIndex + self.count) % self.capacity] = value
self.count +=1
return True
def deQueue(self) -> bool:
"""
Delete an element from the circular queue. Return true if the operation is successful.
"""
if self.count == 0:
return False
self.headIndex = (self.headIndex + 1) % self.capacity
self.count -= 1
return True
def Front(self) -> int:
"""
Get the front item from the queue.
"""
if self.count == 0:
return -1
return self.queue[self.headIndex]
def Rear(self) -> int:
"""
Get the last item from the queue.
"""
if self.count == 0:
return -1
return self.queue[(self.headIndex + self.count -1) % self.capacity]
def isEmpty(self) -> bool:
"""
Checks whether the circular queue is empty or not.
"""
if self.count == 0:
return True
return False
#直接 return self.count == 0 即可
def isFull(self) -> bool:
"""
Checks whether the circular queue is full or not.
"""
return self.count == self.capacity
队列和BFS-3
岛屿数量,中等
用BFS做,代码如下:
- 队列可以直接用列表,入队用append,出队用 pop(0)。
- 遍历四周位置可以用
for x,y in [(h-1,l),(h,l-1),(h+1,l),(h,l+1)]:
- 可以使用双边判断来写区间判定:
if 0 <= x < m and 0 <= y < n and grid[x][y] == "1" :
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
m = len(grid)
if m == 0: return 0
n = len(grid[0])
queue = []
count = 0
for i in range(m):
for j in range(n):
if grid[i][j]== "1":
count += 1
grid[i][j] = "0"
queue.append((i,j))
while queue:
temp = queue.pop(0)
h,l = temp[0],temp[1]
for x,y in [(h-1,l),(h,l-1),(h+1,l),(h,l+1)]:
if 0 <= x < m and 0 <= y < n and grid[x][y] == "1" :
grid[x][y] = '0'
queue.append((x,y))
return count
打开转盘锁,中等
用BFS,数据结构需要两个 set 和一个 queue
一个 set 用来存deadends, 一个用来存 已经遍历过的 code ,
queue 存 ( code , step )的元组,分别记录遍历到的密码和走到这个密码的最小步数。
代码如下:
class Solution:
def openLock(self, deadends: List[str], target: str) -> int:
dead = set(deadends)
visited = {'0000'}
queue = [('0000',0)]
while queue:
code , step = queue.pop(0)
if code == target:
return step
if code in dead:
continue
for i in range(4):
for j in (-1,1):
s = (int(code[i])+j) % 10
nextcode = code[:i] + str(s) + code[i+1:]
if nextcode not in visited:
queue.append((nextcode,step+1))
visited.add(nextcode)
return -1
完全平方数,中等
可以用动态规划做
这里主要训练BFS
以 n 作为根,其子节点为 n 减去平方数序列的余数,如果余数为0,则层数就为最少平方数
代码如下:
class Solution:
def numSquares(self, n: int) -> int:
m = int(n**0.5)
numlst = [i*i for i in range(1,m+1)]
queue = [(n,0)]
while queue:
num,step = queue.pop(0)
if num == 0:
return step
for item in numlst:
if num - item < 0:
break
else:
queue.append((num-item,step+1))
用队列存储,同一层会产生大量的重复元素,比如先减1再减4,和先减4再减1,得到的结果相同,层数深了以后会产生很大的时间消耗。
对此进行优化,每一层的情况用集合来存储,下一层的元素添加到新一层的集合中
代码如下:
class Solution:
def numSquares(self, n: int) -> int:
m = int(n**0.5)
numlst = [i*i for i in range(1,m+1)]
queue = {n}
step = 0
while queue:
step += 1
nextqueue = set()
for reminder in queue:
for item in numlst:
if reminder - item == 0:
return step
elif reminder < item:
break
else:
nextqueue.add(reminder - item)
queue = nextqueue
栈-4
最小栈,简单
#无穷大常数:math.inf
维护两个数组,其一为普通栈,其二为最小值栈,存的是当前栈中元素的最小值。
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min_stack = [math.inf]
def push(self, x: int) -> None:
self.stack.append(x)
self.min_stack.append(min(x,self.min_stack[-1]))
def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
有效的括号,简单
简单的配对问题,左括号入栈,右括号匹配,将字符串转化为数字便于处理,代码如下:
class Solution:
def trans(self,a: str) -> int:
if a == '(':
return 1
if a == '[':
return 2
if a == '{':
return 3
if a == ')':
return -1
if a == ']':
return -2
if a == '}':
return -3
def isValid(self, s: str) -> bool:
lst = list(s)
stack = []
for item in lst:
temp = self.trans(item)
if temp > 0:
stack.append(temp)
else:
if not stack:return False #这一句别漏了,否则如果右括号多了会对空栈进行弹出
left = stack.pop()
if left + temp != 0:
return False
return not stack
官解使用字典,将左右括号分别作为值和键,缩短了代码:
class Solution:
def isValid(self, s: str) -> bool:
if len(s) % 2 == 1:
return False
pairs = {
")": "(",
"]": "[",
"}": "{",
}
stack = list()
for ch in s:
if ch in pairs:
if not stack or stack[-1] != pairs[ch]:
return False
stack.pop()
else:
stack.append(ch)
return not stack
每日温度,中等
单调栈
维护一个单调不增的栈,如果当前温度小于等于栈顶温度,则入栈。
如果新温度大于栈顶温度,则用新日期减栈顶日期,就是栈顶日期的ans值
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
stack = []
n = len(T)
ans = [0]*n
for i in range(n):
while stack and stack[-1][0] < T[i]:
ans[stack[-1][1]] = i - stack[-1][1]
stack.pop()
stack.append((T[i],i))
return ans
逆波兰表达式求值,中等
简单
注意:整除运算应该写return int(a/b)
栈和DFS-4
岛屿数量,中等
DFS,用递归做,注意边界
class Solution:
def dfs(self,grid,x,y,m,n):
grid[x][y] = '0'
for a,b in [(x-1,y),(x,y-1),(x+1,y),(x,y+1)]:
if 0<=a<m and 0<=b<n and grid[a][b] == '1':
self.dfs(grid,a,b,m,n)
def numIslands(self, grid: List[List[str]]) -> int:
m = len(grid)
if not m:
return 0
n = len(grid[0])
count = 0
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
count += 1
self.dfs(grid,i,j,m,n)
return count
克隆图,中等
无向连通图,输入为第一个节点,用DFS进行克隆,递归调用克隆方法本身
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
"""
class Solution:
def __init__(self):
self.visited = {}
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return node
#递归出口1,节点已经被克隆,直接返回
if node in self.visited:
return self.visited[node]
#递归出口2,节点未被克隆,创建新节点,并创建/查找其邻接节点
cloned_node = Node(node.val,[])
self.visited[node] = cloned_node
for item in node.neighbors:
cloned_node.neighbors.append(self.cloneGraph(item))
return cloned_node
目标和,中等
DFS复杂度 O(2^N),超时
不知道为什么放在这一节
class Solution:
def __init__(self):
self.count = 0
def DFS(self,nums,x,Sum,S):
n = len(nums)
if x == n:
if Sum == S:
self.count += 1
return
self.DFS(nums,x+1,Sum+nums[x],S)
self.DFS(nums,x+1,Sum-nums[x],S)
def findTargetSumWays(self, nums: List[int], S: int) -> int:
if not nums and S != 0:
return 0
self.DFS(nums,0,0,S)
return self.count
得用DP:
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
if not nums:
if S == 0:return 1
else: return 0
#S的范围需要在-1000到1000
if S < -1000 or S > 1000:
return 0
n = len(nums)
dp = [[0 for _ in range(2001)] for _ in range(n)]
dp[0][1000+nums[0]] = 1
dp[0][1000-nums[0]] += 1
for i in range(1,n):
for j in range(2001):
if dp[i-1][j] > 0:
dp[i][j+nums[i]] += dp[i-1][j]
dp[i][j-nums[i]] += dp[i-1][j]
return dp[n-1][S+1000] #这里记得是S+1000
二叉树的中序遍历,中等
简单
递归:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def __init__(self):
self.ans = []
def DFS(self,node):
if not node:
return
self.DFS(node.left)
self.ans.append(node.val)
self.DFS(node.right)
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root: return root
self.DFS(root)
return self.ans
非递归:**
小结-6
用栈实现队列,简单
用两个栈实现:
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.stack1 = []
self.stack2 = []
self.size = 0
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
while self.stack2:
self.stack1.append(self.stack2.pop())
self.stack2.append(x)
while self.stack1:
self.stack2.append(self.stack1.pop())
self.size += 1
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
self.size -= 1
return self.stack2.pop()
def peek(self) -> int:
"""
Get the front element.
"""
if self.stack2:
return self.stack2[-1]
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return self.size == 0
用队列实现栈,简单
用一个队列即可实现,入队列时,先将当前元素压如队尾,再将其他元素依次从队头弹出压入队尾
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue = []
self.size = 0
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue.append(x)
for _ in range(self.size):
self.queue.append(self.queue.pop(0))
self.size += 1
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
self.size -= 1
return self.queue.pop(0)
def top(self) -> int:
"""
Get the top element.
"""
return self.queue[0]
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return self.size == 0
用栈来对括号进行配对,注意各种边界条件
字符串列表合成一个串:不能用str()强制转化,要用join()
class Solution:
def decodeString(self, s: str) -> str:
zhongkuohao = 0
lst = list(s)
stack = []
num = []
ans = []
while lst:
temp = lst.pop()
if not zhongkuohao:
if temp != ']':
ans.append(temp)
else:
zhongkuohao += 1
stack.append(temp)
else:
if temp != '[':
stack.append(temp)
if temp == ']':
zhongkuohao += 1
else:
zhongkuohao -= 1
lap = ''
st = ''
num.clear()
tempnum = lst.pop()
tempalph = stack.pop()
while tempalph != ']':
lap += tempalph
tempalph = stack.pop()#bug2:这句漏了导致有[]的循环一直不结束
while '0' <= tempnum <= '9':
num.append(tempnum)
if lst:
tempnum = lst.pop()
#bug3:数字开头的输入会死循环,
#进行如下修改
#if not ('0' <= tempnum <= '9'):
#lst.append(tempnum)
else:
tempnum = ''
if tempnum:
lst.append(tempnum)
n = int(''.join(reversed(num)))
for _ in range(n):
st += lap
lst.append(st)
ans.reverse()
#return str(ans)
#bug1:用str强制转换列表会变成这样"['a', 'b', 'c']"
return "".join(ans)
图像渲染,简单
简单的DFS,有一点需要注意:
不引入visited 直接修改原矩阵的情况下,
如果 oldColor == newColor,递归会进入死循环爆栈
class Solution:
def DFS(self,image,x,y,oldColor,newColor):
m = len(image)
n = len(image[0])
if x < 0 or x >= m or y < 0 or y >= n:
return
else:
if image[x][y] == oldColor:
image[x][y] = newColor
self.DFS(image,x-1,y,oldColor,newColor)
self.DFS(image,x+1,y,oldColor,newColor)
self.DFS(image,x,y-1,oldColor,newColor)
self.DFS(image,x,y+1,oldColor,newColor)
return
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
oldColor = image[sr][sc]
#下面的判断很有必要
if oldColor == newColor:
return image
self.DFS(image,sr,sc,oldColor,newColor)
return image
01矩阵,中等
用BFS做
思路1:从 1 开始BFS,则每个 1 都需重新BFS一次,复杂度高
思路2:从 0 开始BFS,将所有 0 都设置为原点,从原点向外泛洪,每次泛洪都将答案矩阵标记+1
将答案矩阵的初始值设为-1,则判断是否未负可以保证不会重复添加节点
以下代码用 set 来存每次泛洪的点集,由于不会重复添加节点,也可以使用 list
但是语句queue = nextqueue
应该改为:
queue.clear()
queue.extend(nextqueue)
class Solution:
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
if not matrix:return matrix
if not matrix[0]:return matrix
queue = set()
d = [[-1,0],[0,-1],[1,0],[0,1]]
count = 0
m,n = len(matrix),len(matrix[0])
ans = [[-1 for _ in range(n)]for _ in range(m)]
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
ans[i][j] = 0
queue.add((i,j))
while queue:
count += 1
nextqueue = set()
for item in queue:
for i in range(4):
x,y = item[0]+d[i][0],item[1]+d[i][1]
if 0 <= x < m and 0 <= y < n and ans[x][y] < 0:
nextqueue.add((x,y))
ans[x][y] = count
queue = nextqueue
return ans
钥匙和房间,中等
简单的BFS
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
n = len(rooms)
roomset = set()
for i in range(1,n):
roomset.add(i)
queue = [0]
while queue:
temp = queue.pop(0)
keynum = len(rooms[temp])
for i in range(keynum):
key = rooms[temp][i]
if key in roomset:
queue.append(key)
roomset.remove(key)
return len(roomset) == 0