image.png

一、算法框架

image.pngimage.png

  1. queue = [起始点] # 队列实现BFS
  2. visited = set() # 记录访问过的元素点,避免“回头”访问,陷入循环
  3. visited.add(起始点)
  4. step = 0
  5. while queue:
  6. # 将所有节点同时向前扩散一步
  7. for _ in range(len(queue)):
  8. cur = queue.pop(0)
  9. if 找到目标:
  10. return 结果
  11. # 将cur的【所有相邻且没被访问过的节点】加入队列
  12. for near in cur的邻近节点:
  13. if near not in visited:
  14. queue.append(near)
  15. visited.add(near)
  16. step += 1 # 记录路径长度
  1. def BFS(start, target):
  2. queue = collections.deque() # 核心数据结构-队列
  3. visited = set() # 记录走过的路径,避免走回头路--使用集合,检索速度更快
  4. queue.append(start) # 将起点加入队列
  5. visited.add(start)
  6. step = 0 # 记录扩散的步数
  7. while queue:
  8. size = len(queue)
  9. for i in range(size): # 将当前队列中的所有节点向四周扩散
  10. cur = queue.popleft()
  11. if cur == target: # 判断是否到终点
  12. return step
  13. for node in cur: # ???这里感觉代码不对,将cur的相邻节点加入队列
  14. if node not in visited:
  15. queue.append(node)
  16. visited.add(node)
  17. step += 1 # 更新步数
  18. return step

image.png

⼆、⼆叉树的最⼩⾼度

https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/image.pngimage.png

  1. if not node.left and not node.right:

(下面的代码缺了扩散那一步,但是二叉树只有两个结点,同时按顺序出栈的,所以在循环过程中就已经扩散了)

  1. class Solution:
  2. def minDepth(self, root: TreeNode) -> int:
  3. if not root:
  4. return 0
  5. from collections import deque
  6. q = deque([(root, 1)]) # root本身是1层,初始化为1
  7. while q:
  8. node, depth = q.popleft()
  9. if not node.left and not node.right:
  10. return depth
  11. if node.left:
  12. q.append((node.left, depth + 1))
  13. if node.right:
  14. q.append((node.right, depth + 1))
  15. return 0

image.png
image.png

三、解开密码锁的最少次数

image.pngimage.png

  1. # 将 s[j] 向上拨动一次
  2. def up(self, s:str, i:int) -> str:
  3. s_list = list(s)
  4. if s_list[i] == '9':
  5. s_list[i] = '0'
  6. else:
  7. s_list[i] = str(int(s_list[i]) + 1)
  8. return "".join(s_list)
  9. # 将 s[i] 向下拨动一次
  10. def down(self, s:str, i:int) -> str:
  11. s_list = list(s)
  12. if s_list[i] == '0':
  13. s_list[i] = '9'
  14. else:
  15. s_list[i] = str(int(s_list[i]) - 1)
  16. return "".join(s_list)
  17. def openLock(self, deadends: List[str], target: str) -> int:
  18. # BFS
  19. queue = ['0000']
  20. visited = set()
  21. visited.add('0000')
  22. step = 0
  23. while queue:
  24. for _ in range(len(queue)): # 向周围扩散
  25. cur = queue.pop(0)
  26. # 判断是否到达终点
  27. for i in range(4): # 将当前cur元素的所有邻近元素加入队列和visited
  28. up = self.up(cur, i)
  29. queue.append(up)
  30. visited.add(up)
  31. down = self.down(cur, i)
  32. queue.append(down)
  33. visited.add(down)
  34. step += 1
  35. return -1

image.png

  1. def openLock(self, deadends: List[str], target: str) -> int:
  2. # BFS
  3. queue = ['0000']
  4. visited = set()
  5. visited.add('0000')
  6. deadset = set(deadends)
  7. step = 0
  8. while queue:
  9. for _ in range(len(queue)):
  10. cur = queue.pop(0)
  11. if cur in deadset:
  12. continue # 如果cur是死亡数字,则不选这条路径
  13. if cur == target:
  14. return step # 如果cur = target,则找到目标,返回step
  15. # 将当前cur元素的所有邻近元素加入队列和visited
  16. for i in range(4):
  17. up = self.up(cur, i)
  18. if up not in visited:
  19. queue.append(up)
  20. visited.add(up)
  21. down = self.down(cur, i)
  22. if down not in visited:
  23. queue.append(down)
  24. visited.add(down)
  25. step += 1
  26. return -1

image.png

四、双向 BFS 优化

image.pngimage.png

  1. def openLock(self, deadends: List[str], target: str) -> int:
  2. # 双向BFS,用集合记录两边的扩散,set效率比list高,便于判断是否发生交集
  3. q1 = set()
  4. q1.add('0000')
  5. q2 = set()
  6. q2.add(target)
  7. visited = set()
  8. deadset = set(deadends)
  9. step = 0
  10. while q1 and q2:
  11. if len(q1) > len(q2): # 选择⼀个较⼩的集合进⾏扩散,效率就会⾼⼀ 些
  12. q1, q2 = q2, q1
  13. temp = set() # 记录扩散的结点
  14. for cur in q1:
  15. if cur in deadset:
  16. continue
  17. if cur in q2: # 如果两边扩散的元素出现交集,说明找到了最短路径
  18. return step
  19. visited.add(cur)
  20. for i in range(4):
  21. up = self.up(cur, i)
  22. if up not in visited:
  23. temp.add(up)
  24. down = self.down(cur, i)
  25. if down not in visited:
  26. temp.add(down)
  27. step += 1
  28. # 交换q1和q2,q2存储着上一次q1扩散的结果temp
  29. q1 = q2
  30. q2 = temp # q2始终记录上一次扩散时得到的元素
  31. return -1

image.pngimage.pngimage.png