leetcode
leetcode 151 ~ 200
岛屿数量

岛屿数量

难度:

标签:

题目描述

给你一个由 '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'

代码结果

运行时间: 200 ms, 内存: 0.0 MB


/*
 * 题目思路:
 * 1. 使用Java Stream的特性处理二维网格中的每个元素。
 * 2. 找到'1'(陆地)时,使用递归消除岛屿的所有'1'。
 * 3. 计算总的岛屿数量。
 */
import java.util.stream.IntStream;
 
public class SolutionStream {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        int[] count = {0};
        IntStream.range(0, grid.length).forEach(i ->
            IntStream.range(0, grid[0].length).forEach(j -> {
                if (grid[i][j] == '1') {
                    count[0]++;
                    dfs(grid, i, j);
                }
            })
        );
        return count[0];
    }
 
    private void dfs(char[][] grid, int i, int j) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || 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);
    }
}
 

解释

方法:

这个题解使用深度优先搜索(DFS)的方法来解决岛屿数量问题。首先遍历整个网格,当遇到一个值为 '1' 的网格时,就将岛屿数量加一,然后以该网格为起点进行 DFS 搜索。在 DFS 搜索中,首先将当前网格标记为 '#' 表示已访问过,然后递归地对上下左右四个方向进行搜索。通过 DFS 搜索,可以将一个岛屿的所有陆地网格都标记为已访问,这样就不会重复计数。最终岛屿的数量即为进行 DFS 搜索的次数。

时间复杂度:

O(m*n)

空间复杂度:

O(m*n)

代码细节讲解

🦆
在 DFS 搜索中,为什么选择将遇到的 '1' 修改为 '#' 而不是其他标记,这会对算法的正确性或效率有何影响?
在 DFS 搜索中,将 '1' 修改为 '#' 是为了标记已访问的网格,避免重复访问和计数,从而减少不必要的递归调用。选择 '#' 或其他非 '1' 的字符都可以达到相同的效果,主要是为了区分未访问的 '1' 和已访问的状态。这种标记方法直接修改了原数组,节省了额外的空间开销,提升了空间效率,但要注意这种修改是破坏性的,原始数据将不被保留。
🦆
递归深度可能达到 m*n 的最坏情况下,是否有可能造成栈溢出?如果有,如何优化以避免此问题?
是的,在最坏情况下(例如一个大型网格完全由 '1' 组成),递归深度可以达到 m*n,这可能导致栈溢出。为避免此问题,可以使用非递归的方式实现 DFS,即利用一个显式的栈来模拟递归过程。另外,也可以采用广度优先搜索(BFS)使用队列来处理,这样每次扩展都是一层一层的,不会有深度递归所带来的栈溢出风险。
🦆
如何处理网格四周边界的情况?题解中是否包含了对此的特别处理?
题解中已经包含了对边界情况的处理。在 `dfs` 函数的开始,有一个判断条件检查当前坐标是否越界(即索引是否小于0或大于等于网格的长度或宽度),以及当前位置是否为 '1'。如果不满足这些条件,函数将直接返回,不会继续执行。这样的处理确保了算法只在网格的有效区域内操作,防止了数组越界的错误。
🦆
如果网格中间有大片的 '0' 区域,DFS 方法是否还是高效的?有没有更优的搜索策略?
如果网格中有大片的 '0' 区域,DFS 方法在遍历到 '0' 的格子时不会进行递归调用,因此这部分区域的处理是快速的。然而,DFS 仍然需要遍历每个格子至少一次以确认其状态。对于大型稀疏网格,一个更优的策略可能是使用并查集(Union-Find)数据结构。并查集可以更有效地处理大量分散的 '1' 网格,尤其是在处理多次查询和联合操作时,可以更快地进行群组的合并和查询操作。

相关问题

被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 'X''O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

 

示例 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

示例 2:

输入:board = [["X"]]
输出:[["X"]]

 

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j]'X''O'

墙与门

墙与门

岛屿数量 II

岛屿数量 II

无向图中连通分量的数目

无向图中连通分量的数目

不同岛屿的数量

不同岛屿的数量

岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0

 

示例 1:

输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1

示例 2:

输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • grid[i][j]01