广度优先搜索

240 阅读13分钟
  • 齐头并进的广度优先遍历

    • Screenshot 2022-08-31 at 02.07.53.png
    • 另外在带权图中,有一些专门的算法使用场景:
      • 带权有向图、且所有权重都非负的单源最短路径问题:使用 Dijkstra 算法
      • 带权有向图的单源最短路径问题:Bellman-Ford 算法
      • 一个图的所有结点对的最短路径问题:Floy-Warshall 算法
    • 102. 二叉树的层序遍历

      # 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
              ans = []
              def bfs(node: Optional[TreeNode]):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      current_level = []
                      for _ in range(level_size):
                          node = q.popleft()
                          current_level.append(node.val)
                          if node.left:
                              q.append(node.left)
                          if node.right:
                              q.append(node.right)
                      ans.append(current_level)
      
              if not root:
                  return []
              bfs(root)
              return ans
      
    • 323. 无向图中连通分量的数目

      class Solution:
          def countComponents(self, n: int, edges: List[List[int]]) -> int:
              graph = defaultdict(list)
              for u, v in edges:
                  graph[u].append(v)
                  graph[v].append(u)
      
              inQ = set()
              def bfs(node: int):
                  q = deque([i])
                  inQ.add(i)
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          node = q.popleft()
                          for nei in graph[node]:
                              if nei not in inQ:
                                  q.append(nei)
                                  inQ.add(nei)
      
              ans = 0
              for i in range(n):
                  if i not in inQ:
                      bfs(i)
                      ans += 1
              return ans
      
    • 107. 二叉树的层序遍历 II

      # 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 levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
              ans = []
              def bfs(node: Optional[TreeNode]):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      current_level = []
                      for _ in range(level_size):
                          node = q.popleft()
                          current_level.append(node.val)
                          if node.left:
                              q.append(node.left)
                          if node.right:
                              q.append(node.right)
                      ans.append(current_level)
      
              if not root:
                  return []
              bfs(root)
              return ans[::-1]
      
    • 剑指 Offer 32 - I. 从上到下打印二叉树

      # Definition for a binary tree node.
      # class TreeNode:
      #     def __init__(self, x):
      #         self.val = x
      #         self.left = None
      #         self.right = None
      class Solution:
          def levelOrder(self, root: TreeNode) -> List[int]:
              if not root:
                  return []
                  
              ans = []
              def bfs(node: TreeNode):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          node = q.popleft()
                          ans.append(node.val)
                          if node.left:
                              q.append(node.left)
                          if node.right:
                              q.append(node.right)
      
              bfs(root)
              return ans
      
    • 剑指 Offer 32 - III. 从上到下打印二叉树 III && 103. 二叉树的锯齿形层序遍历

      # Definition for a binary tree node.
      # class TreeNode:
      #     def __init__(self, x):
      #         self.val = x
      #         self.left = None
      #         self.right = None
      class Solution:
          def levelOrder(self, root: TreeNode) -> List[List[int]]:
              if not root:
                  return []
                  
              ans = []
              def bfs(node: TreeNode):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      current_level = []
                      for _ in range(level_size):
                          node = q.popleft()
                          current_level.append(node.val)
                          if node.left:
                              q.append(node.left)
                          if node.right:
                              q.append(node.right)
                      ans.append(current_level)
      
              bfs(root)
              for i in range(len(ans)):
                  if i % 2:
                      ans[i].reverse()
              return ans
      
    • 429. N 叉树的层序遍历

      """
      # Definition for a Node.
      class Node:
          def __init__(self, val=None, children=None):
              self.val = val
              self.children = children
      """
      class Solution:
          def levelOrder(self, root: 'Node') -> List[List[int]]:
              if not root:
                  return []
                  
              ans = []
              def bfs(node: 'Node'):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      current_level = []
                      for _ in range(level_size):
                          node = q.popleft()
                          current_level.append(node.val)
                          for child in node.children:
                              q.append(child)
                      ans.append(current_level)
      
              bfs(root)
              return ans
      
    • 993. 二叉树的堂兄弟节点

      # 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 isCousins(self, root: Optional[TreeNode], x: int, y: int) -> bool:
              parent, depth = {}, {}
              parent[root.val] = None
              depth[root.val] = 0
              def bfs(node: Optional[TreeNode]):
                  q = deque([node])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          node = q.popleft()
                          if node.left:
                              q.append(node.left)
                              parent[node.left.val] = node.val
                              depth[node.left.val] = depth[node.val] + 1
                          if node.right:
                              q.append(node.right)
                              parent[node.right.val] = node.val
                              depth[node.right.val] = depth[node.val] + 1
      
              bfs(root)
              return depth[x] == depth[y] and parent[x] != parent[y]
      
  • 二维平面上的搜索问题

    • Screenshot 2022-08-31 at 02.20.24.png
    • 695. 岛屿的最大面积

      class Solution:
          def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
              m, n = len(grid), len(grid[0])
              inQ = set()
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int) -> int:
                  q = deque([(r, c)])
                  inQ.add((r, c))
                  area = 1
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc]==1 and (nr,nc) not in inQ:
                                  q.append((nr, nc))
                                  inQ.add((nr, nc))
                                  area += 1
                  return area
      
              ans = 0
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == 1 and (i,j) not in inQ:
                          area = bfs(i, j)
                          ans = max(ans, area)
              return ans
      
    • 200. 岛屿数量

      class Solution:
          def numIslands(self, grid: List[List[str]]) -> int:
              m, n = len(grid), len(grid[0])
              inQ = set()
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int):
                  q = deque([(r, c)])
                  inQ.add((r, c))
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc]=="1" and (nr,nc) not in inQ:
                                  q.append((nr, nc))
                                  inQ.add((nr, nc))
      
              ans = 0
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == "1" and (i, j) not in inQ:
                          bfs(i, j)
                          ans += 1
              return ans
      
    • 417. 太平洋大西洋水流问题

      class Solution:
          def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
              m, n = len(heights), len(heights[0])
              dr = [0,0,-1,1]
              dc = [-1,1,0,0]
              def bfs(r: int, c: int, arr: List[List[int]]):
                  q = deque([(r, c)])
                  arr[r][c] = 1
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and arr[nr][nc] == 0 and heights[nr][nc] >= heights[r][c]:
                                  q.append((nr, nc))
                                  arr[nr][nc] = 1
      
              pacific = [[0] * n for _ in range(m)]
              atlantic = [[0] * n for _ in range(m)]
              for j in range(n):
                  bfs(0, j, pacific)
                  bfs(m-1, j, atlantic)
              for i in range(m):
                  bfs(i, 0, pacific)
                  bfs(i, n-1, atlantic)
              return [[i,j] for i in range(m) for j in range(n) if pacific[i][j] and atlantic[i][j]]
      
    • 130. 被围绕的区域

      class Solution:
          def solve(self, board: List[List[str]]) -> None:
              m, n = len(board), len(board[0])
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int):
                  if board[r][c] != "O":
                      return
                  q = deque([(r, c)])
                  board[r][c] = "A"
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and board[nr][nc] == "O":
                                  q.append((nr, nc))
                                  board[nr][nc] = "A"
      
              for i in range(m):
                  bfs(i, 0)
                  bfs(i, n-1)
      
              for j in range(1, n-1):
                  bfs(0, j)
                  bfs(m-1, j)
      
              for i in range(m):
                  for j in range(n):
                      if board[i][j] == "A":
                          board[i][j] = "O"
                      elif board[i][j] == "O":
                          board[i][j] = "X"
      
    • 934. 最短的桥

      class Solution:
          def shortestBridge(self, grid: List[List[int]]) -> int:
              # 因为grid中总共只有两座岛,所以把每座岛看成一个set整体即可
              # 通过dfs将一座岛的合并到一个set
              m, n = len(grid), len(grid[0])
              set1, set2 = set(), set()
              dr = [0,0,-1,1]
              dc = [-1,1,0,0]
              def dfs(r: int, c: int, s: Set[int]):
                  if not (0<=r<m and 0<=c<n) or (r,c) in s or grid[r][c]==0:
                      return
                  s.add((r, c))
                  for i in range(4):
                      nr, nc = r + dr[i], c + dc[i]
                      dfs(nr, nc, s)
      
              flag = 0
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == 1 and (i,j) not in set1:
                          dfs(i, j, set1)
                          flag = 1
                          break
                  if flag == 1:
                      break
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == 1 and (i,j) not in set1:
                          dfs(i, j, set2)
      
              # 然后选出岛屿面积比较小的那个做bfs
              small, big = (set1, set2) if len(set1) < len(set2) else (set2, set1)
              def bfs() -> int:
                  q = deque(list(small))
                  inQ = set(list(small))
                  step = -1
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          if (r, c) in big:
                              return step
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and (nr,nc) not in inQ:
                                  q.append((nr,nc))
                                  inQ.add((nr,nc))
                      step += 1
                  return step
      
              return bfs()
      
    • 529. 扫雷游戏

      class Solution:
          def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]:
              i, j = click[0], click[1]
              if board[i][j] == 'M':
                  board[i][j] = 'X'
                  return board
      
              m, n = len(board), len(board[0])
              dr = [-1,-1,-1,0,0,1,1,1]
              dc = [-1,0,1,-1,1,-1,0,1]
              def bfs(r: int, c: int):
                  q = deque([(r, c)])
                  board[r][c] = 'B'
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          mineCnt = 0
                          for i in range(8):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and board[nr][nc] == 'M':
                                  mineCnt += 1
      
                          if mineCnt > 0:
                              board[r][c] = str(mineCnt)
                              continue
      
                          for i in range(8):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and board[nr][nc] == 'E':
                                  q.append((nr, nc))
                                  board[nr][nc] = 'B'
      
              bfs(i, j)
              return board
      
    • 1020. 飞地的数量

      class Solution:
          def numEnclaves(self, grid: List[List[int]]) -> int:
              m, n = len(grid), len(grid[0])
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int):
                  if grid[r][c] == 0:
                      return
                  q = deque([(r, c)])
                  grid[r][c] = 0
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 1:
                                  q.append((nr, nc))
                                  grid[nr][nc] = 0
      
              for i in range(m):
                  bfs(i, 0)
                  bfs(i, n-1)
              for j in range(1, n-1):
                  bfs(0, j)
                  bfs(m-1, j)
      
              ans = 0
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == 1:
                          ans += 1
              return ans
      
    • 1254. 统计封闭岛屿的数目

      class Solution:
          def closedIsland(self, grid: List[List[int]]) -> int:
              m, n = len(grid), len(grid[0])
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int):
                  if grid[r][c] == 1:
                      return
                  q = deque([(r, c)])
                  grid[r][c] = 1
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 0:
                                  q.append((nr, nc))
                                  grid[nr][nc] = 1
      
              for i in range(m):
                  bfs(i, 0)
                  bfs(i, n-1)
              for j in range(1, n-1):
                  bfs(0, j)
                  bfs(m-1, j)
      
              ans = 0
              for i in range(m):
                  for j in range(n):
                      if grid[i][j] == 0:
                          bfs(i, j)
                          ans += 1
              return ans
      
    • 1034. 边界着色

      class Solution:
          def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]:
              m, n = len(grid), len(grid[0])
              inQ = set()
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def isBorder(r: int, c: int, originalColor: int) -> bool:
                  if r in [0, m-1] or c in [0, n-1]: return True
                  for i in range(4):
                      nr, nc = r + dr[i], c + dc[i]
                      if (nr, nc) not in inQ and grid[nr][nc] != originalColor:
                          return True
                  return False
      
              def bfs(r: int, c: int, originalColor: int):
                  q = deque([(r, c)])
                  inQ.add((r, c))
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          if isBorder(r, c, originalColor):
                              grid[r][c] = color
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == originalColor and (nr, nc) not in inQ:
                                  q.append((nr, nc))
                                  inQ.add((nr, nc))
      
              bfs(row, col, grid[row][col])
              return grid
      
    • 733. 图像渲染

      class Solution:
          def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
              originalColor = image[sr][sc]
              if originalColor == color:
                  return image
      
              m, n = len(image), len(image[0])
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int):
                  q = deque([(r, c)])
                  image[r][c] = color
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and image[nr][nc] == originalColor:
                                  q.append((nr, nc))
                                  image[nr][nc] = color
      
              bfs(sr, sc)
              return image
      
    • 面试题13. 机器人的运动范围

      class Solution:
          def movingCount(self, m: int, n: int, k: int) -> int:
              board = [[0] * n for _ in range(m)]
              for i in range(m):
                  for j in range(n):
                      total = 0
                      for ch in str(i):
                          total += int(ch)
                      for ch in str(j):
                          total += int(ch)
                      if total <= k:
                          board[i][j] = 1
      
              inQ = set()
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs(r: int, c: int) -> int:
                  if board[r][c] == 0:
                      return 0
                  q = deque([(r, c)])
                  inQ.add((r, c))
                  count = 1
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and board[nr][nc] == 1 and (nr, nc) not in inQ:
                                  q.append((nr, nc))
                                  inQ.add((nr, nc))
                                  count += 1
                  return count
      
              return bfs(0, 0)
      
    • 909. 蛇梯棋

      class Solution:
          def snakesAndLadders(self, board: List[List[int]]) -> int:
              n = len(board)
              # 得到下一步的行列
              def idx2rc(idx: int) -> (int, int):
                  r, c = (idx - 1) // n, (idx - 1) % n
                  if r % 2 == 1:
                      c = n - 1 - c
                  return n - 1 - r, c
      
              def bfs() -> int:
                  q = deque([(1, 0)])
                  inQ = set([1])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          idx, step = q.popleft()
                          if idx == n * n:
                              return step
                          for idx_nxt in range(idx+1, min(idx+6, n*n)+1):
                              r, c = idx2rc(idx_nxt)
                              if board[r][c] > 0:
                                  idx_nxt = board[r][c]
                              if idx_nxt not in inQ:
                                  q.append((idx_nxt, step + 1))
                                  inQ.add(idx_nxt)
                  return -1
      
              return bfs()
      
  • 抽象成图论问题

    • 279. 完全平方数

      class Solution:
          def numSquares(self, n: int) -> int:
              def bfs() -> int:
                  q = deque([(n, 0)])
                  inQ = set([n])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          num, step = q.popleft()
                          if num == 0:
                              return step
                          targets = [num - i*i for i in range(1, int(num**0.5)+1)]
                          for target in targets:
                              if target not in inQ:
                                  q.append((target, step + 1))
                                  inQ.add(target)
                  return -1
      
              return bfs()
      
    • 322. 零钱兑换

      class Solution:
          def coinChange(self, coins: List[int], amount: int) -> int:
              def bfs() -> int:
                  q = deque([(amount, 0)])
                  inQ = set([amount])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          num, step = q.popleft()
                          if num == 0:
                              return step
                          targets = [num - coin for coin in coins if num >= coin]
                          for target in targets:
                              if target not in inQ:
                                  q.append((target, step + 1))
                                  inQ.add(target)
                  return -1
      
              return bfs()
      
    • 22. 括号生成

      class Solution:
          def generateParenthesis(self, n: int) -> List[str]:
              ans = []
              def bfs():
                  q = deque([('', n, n)])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          # left, right代表剩余的还未使用的左/右括号
                          s, left, right = q.popleft()
                          if left == right == 0:
                              ans.append(s)
                          if left > 0:
                              q.append((s + '(', left - 1, right))
                          if right > left:
                              q.append((s + ')', left, right - 1))
      
              bfs()
              return ans
      
    • 690. 员工的重要性

      """
      # Definition for Employee.
      class Employee:
          def __init__(self, id: int, importance: int, subordinates: List[int]):
              self.id = id
              self.importance = importance
              self.subordinates = subordinates
      """
      class Solution:
          def getImportance(self, employees: List['Employee'], id: int) -> int:
              emap = {e.id: e for e in employees}
              def bfs() -> int:
                  ans = 0
                  q = deque([id])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          idx = q.popleft()
                          e = emap[idx]
                          ans += e.importance
                          for sub in e.subordinates:
                              q.append(sub)
                  return ans
      
              return bfs()
      
    • 365. 水壶问题

      class Solution:
          def canMeasureWater(self, jug1Capacity: int, jug2Capacity: int, targetCapacity: int) -> bool:
              if jug1Capacity + jug2Capacity < targetCapacity:
                  return False
      
              def bfs() -> bool:
                  q = deque([(0, 0)])
                  inQ = set([(0, 0)])
                  while q:
                      a, b = q.popleft()
                      if a + b == targetCapacity:
                          return True
      
                      states = set()
                      # 装满水壶一
                      states.add((jug1Capacity, b))
                      # 装满水壶二
                      states.add((a, jug2Capacity))
                      # 清空水壶一
                      states.add((0, b))
                      # 清空水壶二
                      states.add((a, 0))
                      # 从水壶一向另外水壶二倒水,直到装满或者倒空
                      states.add((min(jug1Capacity, b + a), 0 if b <= jug1Capacity - a else b - (jug1Capacity - a)))
                      # 从水壶二向另外水壶一倒水,直到装满或者倒空
                      states.add((0 if a + b <= jug2Capacity else a - (jug2Capacity - b), min(b + a, jug2Capacity)))
      
                      for state in states:
                          if state not in inQ:
                              q.append(state)
                              inQ.add(state)
                  return False
      
              return bfs()
      
    • 1306. 跳跃游戏 III

      class Solution:
          def canReach(self, arr: List[int], start: int) -> bool:
              if arr[start] == 0:
                  return True
                  
              def bfs() -> bool:
                  q = deque([start])
                  inQ = set([start])
                  while q:
                      i = q.popleft()
                      for idx in [i + arr[i], i - arr[i]]:
                          if 0 <= idx < len(arr) and idx not in inQ:
                              if arr[idx] == 0:
                                  return True
                              q.append(idx)
                              inQ.add(idx)
                  return False
      
              return bfs()
      
  • 拓扑排序

    • Screenshot 2022-09-05 at 22.33.50.png
    • 1136. 并行课程

      class Solution:
          def minimumSemesters(self, n: int, relations: List[List[int]]) -> int:
              inDeg = [0] * (n+1)
              graph = defaultdict(list)
              for u, v in relations:
                  inDeg[v] += 1
                  graph[u].append(v)
      
              def bfs() -> int:
                  q = deque([i for i in range(1, n+1) if inDeg[i] == 0])
                  term = 0
                  while q:
                      level_size = len(q)
                      term += 1
                      for _ in range(level_size):
                          node = q.popleft()
                          for nei in graph[node]:
                              inDeg[nei] -= 1
                              if inDeg[nei] == 0:
                                  q.append(nei)
                  return term
      
              ans = bfs()
              if sum(inDeg) > 0:
                  return -1
              return ans
      
    • 269. 火星词典

      class Solution:
          def alienOrder(self, words: List[str]) -> str:
              graph = defaultdict(list)
              inDeg = {c: 0 for c in words[0]}
              for i in range(len(words) - 1):
                  s, t = words[i], words[i+1]
                  for c in t:
                      inDeg.setdefault(c, 0)
                  for u, v in zip(s, t):
                      if u != v:
                          graph[u].append(v)
                          inDeg[v] += 1
                          break
                  else:
                      if len(s) > len(t):
                          return ""
      
              def bfs() -> List[int]:
                  ans = []
                  q = deque([u for u, d in inDeg.items() if d == 0])
                  while q:
                      ans.extend(q)
                      level_size = len(q)
                      for _ in range(level_size):
                          u = q.popleft()
                          for v in graph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 0:
                                  q.append(v)
                  return ans
      
              ans = bfs()
              return "".join(ans) if len(ans) == len(inDeg) else ""
      
    • 207. 课程表

      class Solution:
          def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
              graph = defaultdict(list)
              inDeg = [0] * numCourses
              for v, u in prerequisites:
                  graph[u].append(v)
                  inDeg[v] += 1
      
              def bfs() -> int:
                  q = deque([i for i in range(numCourses) if inDeg[i] == 0])
                  cnt = 0
                  while q:
                      level_size = len(q)
                      cnt += level_size
                      for _ in range(level_size):
                          u = q.popleft()
                          for v in graph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 0:
                                  q.append(v)
                  return cnt
      
              return bfs() == numCourses
      
    • 210. 课程表 II

      class Solution:
          def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
              graph = defaultdict(list)
              inDeg = [0] * numCourses
              for v, u in prerequisites:
                  graph[u].append(v)
                  inDeg[v] += 1
      
              ans = []
              def bfs():
                  q = deque([i for i in range(numCourses) if inDeg[i] == 0])
                  while q:
                      level_size = len(q)
                      ans.extend(q)
                      for _ in range(level_size):
                          u = q.popleft()
                          for v in graph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 0:
                                  q.append(v)
      
              bfs()
              return ans if len(ans) == numCourses else []
      
    • 310. 最小高度树

      class Solution:
          def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:
              if n == 1:
                  return [0]
      
              graph = defaultdict(list)
              inDeg = [0] * n
              for u, v in edges:
                  graph[u].append(v)
                  graph[v].append(u)
                  inDeg[u] += 1
                  inDeg[v] += 1
      
              # 已知最小树的根节点一定为该路径中的中间节点,且和总个数奇偶性有关
              def bfs() -> List[int]:
                  q = deque([i for i, d in enumerate(inDeg) if d == 1])
                  remainNodes = n
                  while remainNodes > 2:
                      level_szie = len(q)
                      remainNodes -= level_szie
                      for _ in range(level_szie):
                          u = q.popleft()
                          for v in graph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 1:
                                  q.append(v)
                  return list(q)
      
              return bfs()
      
    • 1245. 树的直径

      class Solution:
          def treeDiameter(self, edges: List[List[int]]) -> int:
              graph = defaultdict(list)
              for u, v in edges:
                  graph[u].append(v)
                  graph[v].append(u)
      
              n = len(edges) + 1
              parents = [0] * n
              def bfs(start: int) -> int:
                  q = deque([start])
                  inQ = set([start])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          u = q.popleft()
                          for v in graph[u]:
                              if v not in inQ:
                                  parents[v] = u
                                  q.append(v)
                                  inQ.add(v)
                  return u
      
              u = bfs(0)  # 找到与节点 0 最远的节点 u
              v = bfs(u)  # 找到与节点 u 最远的节点 v
              path = []
              parents[u] = -1
              while v != -1:
                  path.append(v)
                  v = parents[v]
              return len(path) - 1
      
    • 802. 找到最终的安全状态

      class Solution:
          def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
              reversedGraph = defaultdict(list)
              for u, neighbours in enumerate(graph):
                  for v in neighbours:
                      reversedGraph[v].append(u)
              inDeg = [len(neighbours) for neighbours in graph]
      
              def bfs():
                  q = deque([i for i, d in enumerate(inDeg) if d == 0])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          u = q.popleft()
                          for v in reversedGraph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 0:
                                  q.append(v)
      
              bfs()
              return [i for i, d in enumerate(inDeg) if d == 0]
      
    • 1203. 项目管理

      class Solution:
          def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
              # 为每一个没有所属组的item创建一个新的group
              for u in range(n):
                  if group[u] == -1:
                      group[u] = m
                      m += 1
      
              # 建立item之间和group之间的先后关系
              graph4Items = [[] for _ in range(n)]
              inDeg4Items = [0] * n
              graph4Groups = [[] for _ in range(m)]
              inDeg4Groups = [0] * m        
              for u in range(n):
                  for v in beforeItems[u]:                
                      graph4Items[v].append(u)
                      inDeg4Items[u] += 1
                      if group[u] != group[v]:
                          graph4Groups[group[v]].append(group[u])
                          inDeg4Groups[group[u]] += 1
      
              # 返回拓扑排序
              def bfs(graph: List[int], inDeg: List[int]) -> List[int]:
                  topOrder = []
                  q = deque([i for i in range(len(graph)) if inDeg[i] == 0])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          u = q.popleft()
                          topOrder.append(u)
                          for v in graph[u]:
                              inDeg[v] -= 1
                              if inDeg[v] == 0:
                                  q.append(v)
                  return topOrder if len(topOrder) == len(graph) else []
      
              # 得到item和group各自的拓扑排序
              itemOrder = bfs(graph4Items, inDeg4Items)
              groupOrder = bfs(graph4Groups, inDeg4Groups)
              if not itemOrder or not groupOrder: return []
      
              # 得到组内的item的拓扑排序
              orderWithinGroup = defaultdict(list)
              for u in itemOrder:
                  orderWithinGroup[group[u]].append(u)
      
              # 得到最后组间的拓扑排序
              ans = []
              for group in groupOrder:
                  ans += orderWithinGroup[group]
              return ans
      
  • 双向 BFS 与多源 BFS

    • 127. 单词接龙

      class Solution:
          def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
              wordId = dict()
              nodeNum = 0
              def addWord(word: str):
                  if word not in wordId:
                      nonlocal nodeNum
                      wordId[word] = nodeNum
                      nodeNum += 1
      
              edge = defaultdict(list)
              def addEdge(word: str):
                  addWord(word)
                  id1 = wordId[word]
                  chars = list(word)
                  for i in range(len(chars)):
                      tmp = chars[i]
                      chars[i] = "*"
                      newWord = "".join(chars)
                      addWord(newWord)
                      id2 = wordId[newWord]
                      edge[id1].append(id2)
                      edge[id2].append(id1)
                      chars[i] = tmp
      
              for word in wordList:
                  addEdge(word)
      
              addEdge(beginWord)
              if endWord not in wordId:
                  return 0
      
              # 使用两个同时进行的广搜,每次从两边各扩展一层节点
              def bfs() -> int:
                  disBegin = [inf] * nodeNum
                  beginId = wordId[beginWord]
                  disBegin[beginId] = 0
                  qBegin = deque([beginId])
      
                  disEnd = [inf] * nodeNum
                  endId = wordId[endWord]
                  disEnd[endId] = 0
                  qEnd = deque([endId])
                  while qBegin or qEnd:
                      qBeginSize = len(qBegin)
                      for _ in range(qBeginSize):
                          u = qBegin.popleft()
                          if disEnd[u] != inf:
                              return (disBegin[u] + disEnd[u])//2 + 1
                          for v in edge[u]:
                              if disBegin[v] == inf:
                                  disBegin[v] = disBegin[u] + 1
                                  qBegin.append(v)
      
                      qEndSize = len(qEnd)
                      for _ in range(qEndSize):
                          u = qEnd.popleft()
                          if disBegin[u] != inf:
                              return (disBegin[u] + disEnd[u])//2 + 1
                          for v in edge[u]:
                              if disEnd[v] == inf:
                                  disEnd[v] = disEnd[u] + 1
                                  qEnd.append(v)
                  return 0
      
              return bfs()
      
    • 994. 腐烂的橘子

      class Solution:
          def orangesRotting(self, grid: List[List[int]]) -> int:
              m, n = len(grid), len(grid[0])
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              def bfs() -> int:
                  ans = 0
                  q = deque([(r, c, 0) for r, row in enumerate(grid) for c, val in enumerate(row) if val == 2])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c, ans = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 1:
                                  grid[nr][nc] = 2
                                  q.append((nr, nc, ans+1))
                  return ans
      
              ret = bfs()
              if any(1 in row for row in grid):
                  return -1
              return ret
      
    • 542. 01 矩阵

      class Solution:
          def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]:
              m, n = len(mat), len(mat[0])
              zeroes_pos = [(i, j) for i in range(m) for j in range(n) if mat[i][j] == 0]
      
              dr = [-1,1,0,0]
              dc = [0,0,-1,1]
              dist = [[0] * n for _ in range(m)]
              def bfs() -> int:
                  # 将所有的 0 添加进初始队列中
                  q = deque(zeroes_pos)
                  inQ = set(zeroes_pos)
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and (nr, nc) not in inQ:
                                  dist[nr][nc] = dist[r][c] + 1
                                  q.append((nr, nc))
                                  inQ.add((nr, nc))
                  return dist
      
              return bfs()
      
    • 1162. 地图分析

      class Solution:
          def maxDistance(self, grid: List[List[int]]) -> int:
              m, n = len(grid), len(grid[0])
              # 将所有陆地网格存入起始数组
              landCells = [(i, j, 0) for i in range(m) for j in range(n) if grid[i][j] == 1]
              if len(landCells) in [0, m*n]:
                  return -1
      
              dr = [1,0,-1,0]
              dc = [0,-1,0,1]
              def bfs() -> int:
                  q = deque(landCells)
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          r, c, dis = q.popleft()
                          for i in range(4):
                              nr, nc = r + dr[i], c + dc[i]
                              if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 0:
                                  # 访问过的位置标记为 1
                                  q.append((nr, nc, dis+1))
                                  grid[nr][nc] = 1
                  return dis
      
              return bfs()
      
    • 433. 最小基因变化

      class Solution:
          def minMutation(self, start: str, end: str, bank: List[str]) -> int:
              bankSet = set(bank)
              def bfs() -> int:
                  count = 0
                  q = deque([start])
                  inQ = set(start)
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          s = q.popleft()
                          if s == end:
                              return count
                          candidates = []
                          for i, ch in enumerate(s):
                              for c in "ACGT":
                                  if c != ch:
                                      candidates.append(s[:i] + c + s[i+1:])
                          for can in candidates:
                              if can in bankSet and can not in inQ:
                                  q.append(can)
                                  inQ.add(can)
                      count += 1
                  return -1
      
              return bfs()
      
    • 752. 打开转盘锁

      class Solution:
          def openLock(self, deadends: List[str], target: str) -> int:
              dead = set(deadends)
              def bfs() -> int:
                  count = 0
                  q = deque(["0000"])
                  inQ = set(["0000"])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          s = q.popleft()
                          if s == target: return count
                          if s in dead: continue
                          for i in range(4):
                              digit = int(s[i])
                              for d in (-1, 1):
                                  ns = s[:i] + str((digit + d)%10) + s[i+1:]
                                  if ns not in inQ:
                                      q.append(ns)
                                      inQ.add(ns)
                      count += 1
                  return -1
      
              return bfs()
      
    • 773. 滑动谜题

      class Solution:
          # neighbours是相邻位置的编号,按照行优先顺序
          NEIGHBORS = [[1,3], [0,2,4], [1,5], [0,4], [1,3,5], [2,4]]
          def slidingPuzzle(self, board: List[List[int]]) -> int:
              # 枚举 status 通过一次交换操作得到的状态
              def get(status: str) -> Generator[str, None, None]:
                  s = list(status)
                  x = s.index("0")
                  for y in Solution.NEIGHBORS[x]:
                      s[x], s[y] = s[y], s[x]
                      yield "".join(s)
                      s[x], s[y] = s[y], s[x]
      
              initial = "".join(str(num) for num in sum(board, []))
              if initial == "123450":
                  return 0
      
              def bfs() -> int:
                  q = deque([(initial, 0)])
                  inQ = set([initial])
                  while q:
                      level_size = len(q)
                      for _ in range(level_size):
                          status, step = q.popleft()
                          if status == "123450":
                              return step
                          for next_status in get(status):
                              if next_status not in inQ:
                                  q.append((next_status, step + 1))
                                  inQ.add(next_status)
                  return -1
      
              return bfs()