一、题目描述
给你一个由 '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)或广度优先搜索(BFS)来解决。以下是使用DFS的解题思路:
- 遍历整个网格。
- 每当遇到一个’1’(陆地),则从这个位置开始进行深度优先搜索,将所有与之相连的’1’都标记为已经访问过(可以将它们标记为’0’,这样就不会再次访问)。
- 每进行一次深度优先搜索,就意味着找到了一个岛屿,因此岛屿的数量加一。
- 继续遍历网格,直到所有单元格都被访问过。
三、具体代码
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int islandCount = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
// 发现一个岛屿,进行深度优先搜索
dfs(grid, r, c);
// 岛屿数量加一
islandCount++;
}
}
}
return islandCount;
}
// 深度优先搜索函数
private void dfs(char[][] grid, int r, int c) {
int nr = grid.length;
int nc = grid[0].length;
// 检查当前位置是否超出边界或者是否为水
if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
return;
}
// 标记当前位置为已访问(即把陆地变成水)
grid[r][c] = '0';
// 继续搜索上下左右四个方向
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
-
遍历整个网格:代码中有两个嵌套的for循环,分别遍历网格的行和列。如果网格有
m
行和n
列,那么这两个循环总共执行m * n
次。 -
深度优先搜索(DFS):在每次遇到一个’1’时,会从这个位置开始执行DFS。在最坏的情况下,DFS可能需要遍历整个网格,即每个单元格最多被访问一次。
综合以上两点,整个算法的时间复杂度为O(m * n),因为每个单元格最多只会被访问一次(无论是通过遍历网格还是通过DFS)。
2. 空间复杂度
-
堆栈空间:由于DFS是递归实现的,所以在递归过程中会消耗堆栈空间。在最坏的情况下,DFS可能需要遍历整个网格,因此堆栈的深度最大可能是
m * n
。 -
辅助空间:除了递归堆栈外,算法没有使用额外的存储空间。虽然在DFS过程中,我们会修改网格中的值,但这并不算作额外的空间消耗,因为这是在原地进行的。
因此,空间复杂度为O(m * n),这是在最坏情况下递归堆栈的最大深度。
这里m
是网格的行数,n
是网格的列数。在最坏的情况下,每个单元格都会被访问一次,无论是通过网格遍历还是通过DFS递归。因此,时间复杂度和空间复杂度都是与网格的大小成线性关系的。
五、总结知识点
-
类定义:
class Solution
:定义了一个名为Solution
的类。 -
方法定义:
public int numIslands(char[][] grid)
:定义了一个公共方法numIslands
,它接受一个二维字符数组grid
作为参数,并返回一个整数。 -
数组:
char[][] grid
:使用二维字符数组来表示网格。 -
条件语句:
if (grid == null || grid.length == 0)
:使用if
语句来检查输入数组是否为空或长度为零。 -
循环结构:
for
循环:使用了两个嵌套的for
循环来遍历二维网格的每个元素。 -
递归:
dfs
方法:定义了一个递归方法dfs
,用于实现深度优先搜索。 -
基本数据类型:
int
:用于表示整数类型的变量,例如nr
、nc
和islandCount
。 -
字符比较:
grid[r][c] == '1'
:比较数组中的字符是否等于’1’。 -
修改数组元素:
grid[r][c] = '0'
:在深度优先搜索中,将已访问的陆地标记为’0’。 -
方法调用:
dfs(grid, r, c)
:在numIslands
方法中调用dfs
方法。 -
边界检查:
if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0')
:在dfs
方法中检查当前坐标是否超出网格边界或是否为水。 -
递归终止条件:在
dfs
方法中,当坐标超出边界或当前单元格为’0’时,递归终止。 -
递归调用:
dfs(grid, r - 1, c); dfs(grid, r + 1, c); dfs(grid, r, c - 1); dfs(grid, r, c + 1);
:递归地调用dfs
方法来探索四个方向。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。