力扣实训 _ [200].岛屿数量

1.题目回顾

  • 输入: 一个由字符 '1'(陆地)和 '0'(水域)组成的 m x n 二维网格 grid

  • 输出: 计算网格中岛屿的数量。

  • 岛屿定义: 岛屿总是被水包围,并且只能由水平或垂直方向相邻的陆地连接形成。

  • 示例:
    输入:文本

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

    输出:3(左上角1个,中间1个,右下角1个)


2.核心思路:洪水填充(Flood Fill)

这道题的本质是求连通分量的数量 。最直观的解法就是使用深度优先搜索(DFS)广度优先搜索(BFS)

我们可以把这道题想象成"陆地沉没"的过程:

  1. 遍历网格: 我们挨个扫描网格中的每一个格子。
  2. 发现陆地: 当我们遇到一个 '1' 时,说明我们发现了一座新岛屿,此时岛屿计数器加 1。
  3. 洪水填充(沉没岛屿): 为了不重复计算这座岛屿的其他部分,我们需要立刻以当前格子为起点,通过 DFS 或 BFS 把它上下左右 所有相连的陆地 '1' 全部"淹没"(标记为 '0')。
  4. 重复: 继续扫描,直到整个网格都被遍历完。

3.算法详细步骤(以 DFS 为例)

初始化计数器

创建一个变量 count = 0,用来记录岛屿的数量。

双重循环遍历

使用两层 for 循环遍历 grid 中的每一个坐标 (i, j)

触发 DFS

如果当前坐标 grid[i][j] == '1'

  1. count 加 1。
  2. 调用 DFS 函数,传入当前坐标 (i, j)

DFS 递归逻辑

在 DFS 函数中:

  1. 边界检查: 如果坐标越界,或者当前格子已经是水域 '0',直接返回。
  2. 沉没当前陆地: 将当前格子 grid[i][j] 设为 '0'(标记为已访问)。
  3. 向四个方向扩散: 递归调用 DFS 处理上、下、左、右四个相邻的格子。

4. 代码实现 (Java & Kotlin)

Java 版本

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

        int count = 0;
        int rows = grid.length;
        int cols = grid[0].length;

        // 1. 双重循环遍历整个网格
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // 2. 发现陆地,岛屿数量+1,并触发DFS将其淹没
                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); // 右
    }
}

Kotlin版本

复制代码
class Solution {
    fun numIslands(grid: Array<CharArray>): Int {
        if (grid.isEmpty()) return 0

        var count = 0
        val rows = grid.size
        val cols = grid[0].size

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

    private fun dfs(grid: Array<CharArray>, i: Int, j: Int) {
        val rows = grid.size
        val cols = grid[0].size

        // 边界检查
        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)
    }
}

复杂度分析

假设网格的行数为 M ,列数为 N。

时间复杂度: O(M×N)

在最坏的情况下(比如整个网格全是陆地),我们需要访问每一个格子一次。虽然 DFS 会递归,但每个格子最多只会被访问和修改一次(从 '1' 变成 '0'),因此总的时间复杂度是线性的。

空间复杂度: O(M×N)

空间复杂度主要取决于递归调用栈的深度。

  • 最坏情况: 网格全是陆地,DFS 会递归 M×N层,此时空间复杂度为 O(M×N)。
  • 最好情况: 网格非常平坦,递归深度较小。
相关推荐
Boom_Shu1 小时前
长方形的关系
数据结构·c++·算法
ZhengEnCi1 小时前
O07-银行家算法
算法
装不满的克莱因瓶1 小时前
图像尺寸调整:缩放矩阵如何改变像素坐标?
人工智能·线性代数·数学·算法·机器学习·矩阵
Lumbrologist2 小时前
【C++】零基础入门 · 第 13 节:类与对象基础
java·c++·算法
LONGZETECH2 小时前
软硬协同+故障注入:无人机仿真维修与操控仿真底层算法逻辑拆解
大数据·c语言·算法·3d·unity·无人机
Lsk_Smion2 小时前
力扣实训 _ [543].二叉树的直径 _ [23].合并K个升序列表
数据结构·算法·leetcode
凯瑟琳.奥古斯特3 小时前
力扣1235:加权区间调度最优解
java·python·算法·leetcode·职场和发展
耶叶3 小时前
餐厅出入最少人数问题:贪心算法
算法·贪心算法