LeetCode Hot100(35/100)——200. 岛屿数量

文章目录

一、题目简介

题目链接:
LeetCode - Number of Islands

题目描述:

给你一个 m × n 的二维网格 grid,由字符 '1'(陆地)和 '0'(水域)组成。请你计算网格中 岛屿 的数量。岛屿被水包围,可以由水平或垂直方向相邻的陆地连接形成。

示例输入:

复制代码
grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]

示例输出:

复制代码
3

解释:上图中存在三个岛屿。


二、问题本质

岛屿数量问题 的核心是:

将矩阵看作一个图,"1" 表示陆地,"0" 表示水域。岛屿是一个连通分量(Connected Component)。

我们需要计算:

  • '1' 为节点;
  • 上下左右方向为边;
  • 统计这个"图"中连通分量的数量。

三、解法总体思维导图

岛屿数量
DFS
标记访问过的节点
递归遍历相邻陆地
时间复杂度 O(m*n)
空间复杂度 O(m*n)
BFS
使用队列保存待访问节点
层层扩展陆地
时间复杂度 O(m*n)
空间复杂度 O(min(m,n))
Union-Find
每个格子设为一个节点
通过合并操作连接陆地
最终统计父节点数量
时间复杂度 O(mnα(m*n))
空间复杂度 O(m*n)


四、方法一:深度优先搜索(DFS)

解题思路

  1. 遍历整个网格;
  2. 每当遇到陆地 '1',计数器 +1;
  3. 调用 DFS,将该陆地及所有相连的陆地标记为 '0'(即访问过);
  4. 遍历结束后,计数值即为岛屿数量。

DFS 过程流程图



开始遍历grid
当前格子是1吗?
岛屿数量+1
调用DFS递归标记相邻陆地
继续遍历下一个格子
遍历结束返回计数

Java代码实现

java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        int rows = grid.length;
        if (rows == 0) return 0;
        int cols = grid[0].length;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1') {
                    count++;
                    dfs(grid, i, j);
                }
            }
        }
        return count;
    }

    private void dfs(char[][] grid, int i, int j) {
        int rows = grid.length;
        int cols = grid[0].length;
        if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == '0') {
            return;
        }
        grid[i][j] = '0'; // 标记访问过
        dfs(grid, i + 1, j);
        dfs(grid, i - 1, j);
        dfs(grid, i, j + 1);
        dfs(grid, i, j - 1);
    }
}

复杂度分析

类别 复杂度
时间复杂度 O(m × n) --- 每个格子只会被访问一次
空间复杂度 O(m × n) --- 递归栈深度最大为网格大小

五、方法二:广度优先搜索(BFS)

解题思路

  • 与 DFS 类似,只是使用队列代替栈;
  • 每当发现新的陆地,将其入队;
  • 不断扩展相邻节点,完成一整个岛屿的遍历。

BFS 时序图

Algorithm Queue Grid Algorithm Queue Grid loop [每个队列元素] 遍历发现'1' 加入队列 取出当前节点 标记为'0' 加入相邻陆地 完成一个岛屿扩展

Java代码实现

java 复制代码
import java.util.*;

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        int rows = grid.length;
        if (rows == 0) return 0;
        int cols = grid[0].length;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1') {
                    count++;
                    bfs(grid, i, j);
                }
            }
        }
        return count;
    }

    private void bfs(char[][] grid, int x, int y) {
        int rows = grid.length;
        int cols = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{x, y});
        grid[x][y] = '0';

        int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
        while (!queue.isEmpty()) {
            int[] cur = queue.poll();
            for (int[] d : dirs) {
                int nx = cur[0] + d[0];
                int ny = cur[1] + d[1];
                if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == '1') {
                    grid[nx][ny] = '0';
                    queue.offer(new int[]{nx, ny});
                }
            }
        }
    }
}

复杂度分析

类别 复杂度
时间复杂度 O(m × n)
空间复杂度 O(min(m, n)) --- 队列最大长度

六、方法三:并查集(Union-Find)

解题原理

  • 将每个陆地格子看作一个节点;
  • 若相邻两格都是 '1',则进行 "合并" 操作;
  • 最后统计有多少个不同的集合(根节点数量),即岛屿数量。

并查集流程图



初始化并查集结构
遍历grid
当前格子为1?
跳过
检查上下左右相邻节点
合并相邻陆地节点
更新集合数量
返回岛屿数量

Java代码实现

java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        UnionFind uf = new UnionFind(grid);
        int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1') {
                    for (int[] d : dirs) {
                        int nx = i + d[0];
                        int ny = j + d[1];
                        if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == '1') {
                            uf.union(i * cols + j, nx * cols + ny);
                        }
                    }
                }
            }
        }
        return uf.getCount();
    }
}

class UnionFind {
    int[] parent;
    int count;
    int rows, cols;

    public UnionFind(char[][] grid) {
        rows = grid.length;
        cols = grid[0].length;
        parent = new int[rows * cols];
        count = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == '1') {
                    int id = i * cols + j;
                    parent[id] = id;
                    count++;
                }
            }
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            parent[rootX] = rootY;
            count--;
        }
    }

    public int getCount() {
        return count;
    }
}

复杂度分析

类别 复杂度
时间复杂度 O(m × n × α(m × n)) (α为Ackermann函数,极小趋近常数)
空间复杂度 O(m × n)

七、总结对比

解法 思路类型 时间复杂度 空间复杂度 优缺点
DFS 递归搜索 O(m×n) O(m×n) 实现简单,逻辑直观;递归栈较深时占内存较大
BFS 队列搜索 O(m×n) O(min(m,n)) 可避免递归栈溢出;代码稍繁琐
Union-Find 数据结构合并 O(m×n×α) O(m×n) 理论优雅;代码复杂;适合更大规模场景
相关推荐
寻寻觅觅☆5 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
偷吃的耗子5 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
化学在逃硬闯CS6 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar1236 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS7 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
夏鹏今天学习了吗7 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
忙什么果8 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发
董董灿是个攻城狮8 小时前
AI 视觉连载4:YUV 的图像表示
算法
ArturiaZ9 小时前
【day24】
c++·算法·图论
大江东去浪淘尽千古风流人物9 小时前
【SLAM】Hydra-Foundations 层次化空间感知:机器人如何像人类一样理解3D环境
深度学习·算法·3d·机器人·概率论·slam