返回

初识广度优先搜索(BFS):从 LeetCode 三道题领会精髓

前端

我们已经领略了深度优先搜索(DFS)的奥妙,今天,让我们将目光投向另一个强大的搜索算法——广度优先搜索(BFS)。同样通过三道 LeetCode 习题,我们将逐层揭开 BFS 的神秘面纱。

广度优先搜索简介

广度优先搜索是一种以队列数据结构为基础的遍历算法,它的核心思想是先访问当前节点的相邻节点,然后再访问相邻节点的相邻节点,以此类推,层层推进。与 DFS 不同,BFS 始终沿着同一层进行探索,直到该层的所有节点都被访问完毕,然后再继续下一层的探索。

LeetCode 习题 1:判断岛屿数量

我们从 LeetCode 中最经典的岛屿问题入手,理解 BFS 的基本原理。

给定一个由 '1'(陆地)和 '0'(水域)组成的二维网格,计算岛屿的数量。一个岛屿被定义为一块被水域包围的相邻陆地区域。
def num_islands(grid):
  if not grid:
    return 0
  rows, cols = len(grid), len(grid[0])
  num_islands = 0

  for i in range(rows):
    for j in range(cols):
      if grid[i][j] == '1':
        num_islands += 1
        bfs(grid, i, j)
  return num_islands

def bfs(grid, i, j):
  if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == '0':
    return

  grid[i][j] = '0'  # 标记已访问

  # 访问相邻节点
  bfs(grid, i+1, j)  # 向下
  bfs(grid, i-1, j)  # 向上
  bfs(grid, i, j+1)  # 向右
  bfs(grid, i, j-1)  # 向左

BFS 算法首先将网格中标记为 '1' 的陆地区域作为起始节点,然后将其入队。接下来,BFS 从队列中取出当前节点,并访问其所有相邻的 '1' 陆地区域,并将其入队。此过程一直持续到队列为空,表明当前层的所有节点均已访问完毕。

LeetCode 习题 2:计算矩阵中的最长路径

下一个习题让我们深入了解 BFS 在图论中的应用。

给定一个由 '0'(空地)和 '1'(障碍)组成的矩阵,找出机器人从左上角移动到右下角的最长路径长度。机器人每次只能向右或向下移动一步。
def longest_path(matrix):
  if not matrix:
    return 0
  rows, cols = len(matrix), len(matrix[0])
  dp = [[0] * cols for _ in range(rows)]

  # 初始化第一行和第一列
  for i in range(rows):
    dp[i][0] = matrix[i][0]
  for j in range(cols):
    dp[0][j] = matrix[0][j]

  # 动态规划计算最长路径
  for i in range(1, rows):
    for j in range(1, cols):
      if matrix[i][j] == '0':
        dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + 1

  return dp[rows-1][cols-1]

在此问题中,我们将矩阵视作一个图,其中 '0' 陆地区域表示可以通行的路径,而 '1' 障碍表示不可通行的障碍物。BFS 算法通过层层推进的方式,从左上角出发,逐渐探索矩阵中可行的路径,并记录路径长度。

LeetCode 习题 3:查找单词

最后一道习题将展示 BFS 在单词查找中的妙用。

给定一个二维字符网格和一个单词,找出该单词是否出现在网格中。单词可以由网格中的字母组成,每次只能移动相邻的单元格一次。
def word_search(board, word):
  if not board or not word:
    return False

  rows, cols = len(board), len(board[0])

  for i in range(rows):
    for j in range(cols):
      if board[i][j] == word[0]:
        if dfs(board, word, i, j, 0):
          return True
  return False

def dfs(board, word, i, j, index):
  if index == len(word):
    return True

  if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] != word[index]:
    return False

  temp, board[i][j] = board[i][j], '/'  # 标记已访问

  # 访问相邻节点
  found = (dfs(board, word, i+1, j, index+1) or
           dfs(board, word, i-1, j, index+1) or
           dfs(board, word, i, j+1, index+1) or
           dfs(board, word, i, j-1, index+1))

  board[i][j] = temp  # 恢复原值

  return found

对于单词查找问题,BFS 通过从网格中每个可能的位置出发,逐层探索单词的字母是否存在相邻单元格中。一旦找到一个匹配的字母,BFS 就继续搜索下一个字母,直到单词的所有字母都找到或搜索失败为止。

结语

通过这三道 LeetCode 习题,我们深入理解了广度优先搜索(BFS)算法的基本原理和图论中的应用场景。从队列数据结构的巧妙运用到层层推进的搜索方式,BFS 为解决图论问题提供了高效且直观的解决方案。希望这篇文章能为各位探索图论算法的世界打开一扇大门。