🌊 LeetCode 200:岛屿数量 —— 一次吃透 BFS 的洪水填充思想

49 阅读3分钟

二维网格 + 上下左右 + 连通区域

一眼 BFS / DFS 题,但真正吃透后,能解决一整类问题。


一、题目简介

给你一个由 '1'(陆地)和 '0'(水)组成的二维网格 grid,请你计算网格中 岛屿的数量

岛屿定义

  • '1' 表示陆地
  • '0' 表示水
  • 上、下、左、右相邻的陆地视为同一座岛屿
  • 对角线不算相邻

二、问题本质:不是“数 1”,而是“数连通块”

很多人第一反应是:

“遍历数组,遇到 1 就加一?”

这是错误的直觉

正确的理解应该是:

每一座岛屿 = 一整块相互连通的 1

所以问题本质是:

👉 二维网格中的连通分量数量


三、核心思想:BFS + 洪水填充(Flood Fill)

一句话总结思路

每当遇到一个 1,就用 BFS 把它所在的整座岛屿“淹掉”。

流程拆解如下:

  1. 遍历整个网格

  2. 遇到 '1'

    • 说明发现了一座新岛屿
    • 岛屿数量 +1
  3. 以该点为起点,使用 BFS 向四周扩散

  4. 将所有相连的 '1' 全部标记为 '0'

  5. 继续扫描剩余网格


四、为什么选择 BFS?

  • BFS 非常适合:

    • 从一个点向四周扩散
    • 层层推进
  • 相比 DFS 递归:

    • BFS 不存在 Java 栈溢出风险
    • 更贴近“洪水漫延”的直觉

👉 工程实践中,BFS 更安全、更稳。


五、代码实现(BFS 标准写法)

下面是完整实现,逻辑非常清晰 👇

class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int res = 0;
        int row = grid.length;
        int col = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();

        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == '1') {
                    res++;
                    queue.add(new int[]{i, j});
                    grid[i][j] = '0';

                    while (!queue.isEmpty()) {
                        int[] cur = queue.poll();
                        int x = cur[0];
                        int y = cur[1];

                        if (x - 1 >= 0 && grid[x - 1][y] == '1') {
                            queue.add(new int[]{x - 1, y});
                            grid[x - 1][y] = '0';
                        }
                        if (y - 1 >= 0 && grid[x][y - 1] == '1') {
                            queue.add(new int[]{x, y - 1});
                            grid[x][y - 1] = '0';
                        }
                        if (x + 1 < row && grid[x + 1][y] == '1') {
                            queue.add(new int[]{x + 1, y});
                            grid[x + 1][y] = '0';
                        }
                        if (y + 1 < col && grid[x][y + 1] == '1') {
                            queue.add(new int[]{x, y + 1});
                            grid[x][y + 1] = '0';
                        }
                    }
                }
            }
        }
        return res;
    }
}

六、几个非常关键的细节(面试常问)

1️⃣ 为什么一入队就标记为 '0'

queue.add(new int[]{i, j});
grid[i][j] = '0';

这是 BFS 的标准写法

  • 防止同一个节点被多次入队
  • 保证每个格子只被访问一次

👉 “入队即标记” 是 BFS 的核心习惯


2️⃣ 为什么用 Queue 而不是 Stack

  • Queue → BFS(广度优先)
  • Stack / 递归 → DFS(深度优先)

这道题中:

  • BFS / DFS 都能做
  • BFS 在 Java 中 更安全

3️⃣ 为什么用 !queue.isEmpty() 而不是 queue.size() > 0

while (!queue.isEmpty()) { ... }

原因:

  • 语义更清晰(判断是否为空)
  • 更符合 Java 集合接口的设计初衷
  • 也是业界规范写法

七、时间 & 空间复杂度分析

⏱️ 时间复杂度

  • 每个格子最多访问一次
    👉 O(row × col)

🧠 空间复杂度

  • 队列最坏情况下存整座岛屿
    👉 O(row × col)

八、如何一眼识别这是 BFS / DFS 题?

看到这些关键词,直接条件反射:

  • 二维矩阵 / 网格
  • 上下左右
  • 连通 / 区域 / 块
  • 访问过就不能重复访问

👉 90% 都是 Flood Fill 模型


九、一句话总结

岛屿数量问题,本质不是“数 1”,
而是“发现一个 1,就用 BFS 消灭它所在的整块区域”。