【LeetCode刷题笔记】岛屿数量

题目描述

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

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1

示例 2:
输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3

提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’

思考一:DFS

深度优先搜索(DFS)的核心思路是:遍历网格中的每个单元格,当遇到未访问的陆地(‘1’)时,启动一次深度优先搜索,递归探索所有与之相连的陆地并标记为已访问,每完成一次这样的搜索就代表发现了一个岛屿。通过这种方式,可以完整统计所有独立的岛屿数量。

算法过程

  1. 初始化

    • 计数器ans记录岛屿数量(初始为0)
    • 二维数组visited标记已访问的单元格(初始全为false
  2. 遍历网格

    • 对每个单元格(i,j),若未访问且为陆地(‘1’):
      • 启动DFS探索整个连通的岛屿
      • 岛屿计数器ans加1
  3. DFS探索

    • 递归访问当前单元格的上、下、左、右四个方向
    • 遇到边界、水域(‘0’)或已访问的单元格则停止递归
    • 访问时标记当前单元格为已访问

时空复杂度分析

  • 时间复杂度:O(m×n),其中m和n分别为网格的行数和列数,每个单元格最多被访问一次
  • 空间复杂度:O(m×n),最坏情况下(全为陆地),递归栈深度可达m×n,加上visited数组的空间

代码

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    let ans = 0;
    const [m, n] = [grid.length, grid[0].length];
    const visited = Array.from({length: m}, () => Array(n).fill(false));

    const dfs = function(i, j) {
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === '0' || visited[i][j]) return;
        visited[i][j] = true;
        dfs(i-1, j);
        dfs(i+1, j);
        dfs(i, j-1);
        dfs(i, j+1);
    };

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (visited[i][j] || grid[i][j] === '0') continue;
            dfs(i, j);
            ans++;
        }
    }

    return ans;
};

思考二:BFS

广度优先搜索(BFS)的核心思路是:当发现未访问的陆地时,使用队列存储当前岛屿的所有单元格,逐层向外扩展探索所有相连的陆地并标记为已访问。每完成一次这样的层级遍历,就统计一个岛屿。BFS通过队列实现层级扩展,避免了递归可能带来的栈溢出问题。

算法过程

  1. 初始化

    • 计数器ans记录岛屿数量(初始为0)
    • 二维数组visited标记已访问的单元格
  2. 遍历网格

    • 对每个单元格(i,j),若未访问且为陆地:
      • 初始化队列并将(i,j)入队,标记为已访问
      • 岛屿计数器ans加1
  3. BFS探索

    • 从队列中取出单元格,将其上下左右四个方向的未访问陆地单元格入队
    • 每个入队的单元格都标记为已访问
    • 直到队列为空,当前岛屿探索完毕

时空复杂度分析

  • 时间复杂度:O(m×n),每个单元格最多被访问一次
  • 空间复杂度:O(min(m,n)),最坏情况下队列存储最多单元格数量为网格中较短边的长度(如满是陆地的网格中,队列最大长度为边界单元格数)

代码

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    let ans = 0;
    const [m, n] = [grid.length, grid[0].length];
    const visited = Array.from({length: m}, () => Array(n).fill(false));
    // 定义四个方向的偏移量:上、下、左、右
    const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
    
    // BFS函数:探索从(i,j)开始的整个岛屿
    const bfs = function(i, j) {
        // 创建队列并将起始位置入队
        const queue = [[i, j]];
        // 标记为已访问
        visited[i][j] = true;
        
        // 当队列不为空时,继续探索
        while (queue.length > 0) {
            // 取出队首元素
            const [x, y] = queue.shift();
            
            // 探索四个方向
            for (const [dx, dy] of directions) {
                const nx = x + dx;
                const ny = y + dy;
                
                // 检查新位置是否有效且未访问且是陆地
                if (nx >= 0 && nx < m && ny >= 0 && ny < n 
                    && !visited[nx][ny] && grid[nx][ny] === '1') {
                    // 标记为已访问并加入队列
                    visited[nx][ny] = true;
                    queue.push([nx, ny]);
                }
            }
        }
    };
    
    // 遍历整个网格
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            // 发现未访问的陆地,启动BFS并计数
            if (!visited[i][j] && grid[i][j] === '1') {
                bfs(i, j);
                ans++;
            }
        }
    }
    
    return ans;
};

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值