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) 理论优雅;代码复杂;适合更大规模场景
相关推荐
不穿鞋的懒羊羊17 分钟前
dfs深度优先搜索
算法
OYYHXPJR38 分钟前
【算法】区间重叠模板和矩形重叠模板
算法
shehuiyuelaiyuehao40 分钟前
算法46,分治快排,排序数组
java·算法·排序算法·排序
圣保罗的大教堂1 小时前
leetcode 3483. 不同三位偶数的数目 简单
leetcode
远航计算机1 小时前
GEO 选题从哪来?用一份 Query 词库把一年内容排出来
大数据·人工智能·算法·aigc
宣宣猪的小花园.1 小时前
【机器学习】神经网络与表征:为什么深度学习能自动提取特征
人工智能·嵌入式硬件·算法·机器学习
mmmmath_31 小时前
LeetCode.018.四数之和
数据结构·算法·leetcode
AIGCmagic社区1 小时前
腾讯混元 AuK 技术报告拆读:1.5B 统一语音生成与编辑,蒸馏后 4 步无 CFG 提速 4.5 倍
人工智能·算法·aigc·ai多模态
大熊背1 小时前
基于照明色度表征的颜色恒常性的白平衡算法实现
算法·白平衡·色温·色度坐标·普朗克曲线
高亦真1 小时前
今天是学习嵌入式的第38天
linux·学习·算法