BFS(广度优先遍历)
B队 使用队列实现
由于图会存在回路和重复值。因此需要有一个判断重复的列表visited。对于树,visited列表可以省略。
如果不需要确定当前遍历到了哪一层,BFS代码如下
def BFS(graph,start,end):
queue = []
queue.append([start])
visited.append(start)
while queue:
# 队列第一个元素出去
node = queue.pop(0)
# 访问列表添加元素
visited.append(node)
# 处理这个节点
process(node)
# 产生后续节点,并且判断后继节点是否被访问
nodes = generate_related_nodes(node)
# 后续节点进队列
queue.append(nodes)
如果需要知道遍历到那一层了,需要增加level表示当前遍历到二叉树中的哪一层了,也可以理解为在一个图中,现在已经走了多少步了。size表示在当前遍历层有多少个元素,也就是队列中的元素数,我们把这些元素一次性遍历完,即把当前层的所有元素都向外走了一步。
代码如下:
level = 0
result = []
while queue 不空:
size = len(queue)
tmp =[]
for i in range(size):
node = queue.pop(0)
tmp.append(node.val)
for 节点 in cur的所有相邻节点:
if 该节点有效且未被访问过:
queue.append(该节点)
result.append(ls)
level += 1
BFS和DFS最主要的区别是:BFS找到的路径一定是最短的,但代价就是空间复杂度比DFS高很大。
BFS出现的场景:问题的本质就是在图中找到从起点start到终点end的最近距离。
应用题1——leetcode111最小深度
分析:起点就是root根节点,终点就是靠近根节点的那个叶子节点。当当前节点的left和right都是None就是,那么这个节点就是叶子节点。
def minDepth(self, root: TreeNode) -> int:
if root is None:
return 0
level = 1
queue = [root]
while queue:
n = len(queue)
for i in range(n):
cur = queue.pop(0)
if cur.left == cur.right == None:
return level
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
level += 1
return level
应用题2——752. 打开转盘锁
1.不考虑限制条件,穷尽所有密码组合,整个转动过程就是一个图。每个节点的有八个相邻节点(‘0000’->‘0100’,‘0001’,‘0010’,‘1000’,‘0900’,‘9000’,‘0009’,‘0090’。求图中的最短距离。所以可以用BFS求解。
def openLock(self, deadends: List[str], target: str) -> int:
def plusOne(s,j):
s = list(s)
if s[j] == '9':
s[j] = '0'
else:
s[j] = str(int(s[j])+ 1)
return ''.join(s)
# 向下波动一次
def minusOne(s,j):
s = list(s)
if s[j] == '0':
s[j] = '9'
else:
s[j] = str(int(s[j])- 1)
return ''.join(s)
# python在set中查找的时间复杂度为o(1)
deadends = set(deadends)
visited = set()
q = ['0000']
# 从起点开始BFS
step = 0
visited.add('0000')
while q:
n = len(q)
for i in range(n):
cur = q.pop(0)
# 判断是否到达终点
if cur == target:
return step
# 跳过死亡密码
if cur in deadends:
continue
# 将一个节点的未遍历相邻节点加入队列
for j in range(4):
up = plusOne(cur,j)
down = minusOne(cur,j)
if up not in visited:
q.append(up)
visited.add(up)
if down not in visited:
q.append(down)
visited.add(down)
step += 1
return -1
应用3—— leetcode102——二叉树的层次遍历
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if root is None:
return []
result= []
queue = [root]
while queue:
size = len(queue)
tmp = []
for i in range(size):
node = queue.pop(0)
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(tmp)
return result
时间复杂度O(n)
2. DFS( 深度优先遍历)
递归:
visited = set()
def dfs(node,visited):
visited.append(node)
# 处理访问到的节点
for next_node in node.children():
if not next_node in visited:
dfs(next_node,visited)
非递归:
用stack实现
def dfs(tree):
if tree.root is None:
return []
visited,stack = [],[tree.root]
while stack:
node = stack.pop()
visited.append(node)
process(node)
nodes = generate_related_nodes(node)
stack.insert(0,nodes)
应用题—— 二叉树的最大深度和最小深度
(1) leetcode55最大深度
递归
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
return 1 + max(self.maxDepth(root.left),self.maxDepth(root.right))
非递归,使用BFS
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
level = 0
queue = [root]
while queue:
size = len(queue)
for i in range(size):
node = queue.pop(0)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
level += 1
return level
(2) leetcode111最小深度
def minDepth(self, root: TreeNode) -> int:
if root is None:
return 0
# 没有左子树,直接return 1 + 右子树
if root.left is None:
return 1 + self.minDepth(root.right)
# 没有右子树,直接return 1 + 左子树
if root.right is None:
return 1 + self.minDepth(root.left)
leftM = self.minDepth(root.left)
rightM = self.minDepth(root.right)
result = 1+ min(leftM,rightM)
return result