200. 岛屿数量

LeetCode 原题链接

题目描述

给定由字符 "1"(陆地)和 "0"(水)组成的二维网格,计算岛屿数量。陆地只按上、下、左、右四个方向相连。

输入:
[
  ["1","1","0","0"],
  ["1","0","0","1"],
  ["0","0","1","1"]
]
输出:2

题型判断

每座岛屿就是一个由陆地组成的连通分量。扫描网格时,每遇到一块尚未访问的陆地:

  • 岛屿数加一。
  • 从这里做一次 BFS,将整座岛屿标记为已访问。

之后扫描到同一岛屿的其他格子时,就不会重复计数。

核心过程

以上面示例为例:

  1. 扫描到 (0, 0),计数变为 1,BFS 淹没 (0,0)(0,1)(1,0)
  2. 继续扫描到 (1, 3),计数变为 2,BFS 淹没 (1,3)(2,3)(2,2)
  3. 没有剩余陆地,返回 2

代码实现

var numIslands = function (grid) {
  const rows = grid.length;
  const columns = grid[0].length;
  const directions = [
    [1, 0],
    [-1, 0],
    [0, 1],
    [0, -1],
  ];
  let count = 0;

  const bfs = (startRow, startColumn) => {
    const queue = [[startRow, startColumn]];
    let head = 0;

    // 入队时立即标记,避免被相邻格重复入队
    grid[startRow][startColumn] = "0";

    while (head < queue.length) {
      const [row, column] = queue[head++];

      for (const [rowOffset, columnOffset] of directions) {
        const nextRow = row + rowOffset;
        const nextColumn = column + columnOffset;
        const isInGrid =
          nextRow >= 0 &&
          nextRow < rows &&
          nextColumn >= 0 &&
          nextColumn < columns;

        if (isInGrid && grid[nextRow][nextColumn] === "1") {
          grid[nextRow][nextColumn] = "0";
          queue.push([nextRow, nextColumn]);
        }
      }
    }
  };

  for (let row = 0; row < rows; row++) {
    for (let column = 0; column < columns; column++) {
      if (grid[row][column] === "1") {
        count++;
        bfs(row, column);
      }
    }
  }

  return count;
};

复杂度分析

  • 时间复杂度:O(rows × columns),每个格子最多被访问一次。
  • 空间复杂度:最坏 O(rows × columns),整张网格都是陆地时队列可能保存大量坐标。

边界与易错点

  • 题目网格非空;通用实现可先判断 grid.length === 0
  • 网格元素是字符串 "1""0",不是数字。
  • 对角线不算连通。
  • 代码会原地修改 grid。若必须保留输入,应使用单独的 visited 集合或布尔矩阵。
  • 必须在入队时标记访问,不能等到出队后再标记。