力扣实训 _ [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)。
  • 最好情况: 网格非常平坦,递归深度较小。
相关推荐
JustNow_Man17 分钟前
【Harness】本地文本向量化搜索解决方案--qmd
git·python·算法
敲代码的苦1333 分钟前
头歌 数据结构—消防资源调度
数据结构·算法
布莱克60539 分钟前
C++ 中 set 与 map 的区别详解
开发语言·c++·算法·哈希
lisw051 小时前
算法与软件的本质区别
驱动开发·算法·数学建模
我不会插花弄玉1 小时前
5.Hash 【由浅入深-redis】
redis·算法·哈希算法
Nuanyt1 小时前
LeetCode Hot 100 · 双指针 3 题 Java
java·笔记·算法·leetcode·双指针
志起计算机编程2 小时前
Clickhouse的几个关键优势
算法
重生之小比特2 小时前
【算法拾光 7】双指针|两数之和 II - 输入有序数组 + 三数之和
算法
文丑颜不良啊2 小时前
LeetCode851~875题目解析
java·leetcode
Navigator_Z2 小时前
LeetCode //C - 1304. Find N Unique Integers Sum up to Zero
c语言·算法·leetcode