文章摘要:
- 题目要求在给定的二维矩阵中,统计由相邻陆地('1')组成的岛屿数量,其中相邻指水平或垂直方向相连。通过深度优先搜索(DFS)解决:遍历矩阵,每发现未被访问的陆地时标记为已访问并计数,然后递归搜索其四周相邻陆地。全局变量记录矩阵信息及访问状态,方向数组简化四向搜索。无需回溯或剪枝,当所有陆地访问完毕即返回岛屿总数。代码实现中,主函数初始化并遍历矩阵,DFS函数处理相邻陆地标记,确保每个岛屿只统计一次。
文章目录
题目链接:200. 岛屿数量
一、题目解析
题目给我们一个由 1(陆地)和 0(水) 组成的的二维网格 grid,我们需要计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向 和/或竖直方向 上相邻的陆地连接形成。
示例1,题目给出的二维网格 grid 是:
| 1 | 1 | 1 | 1 | 0 |
|---|---|---|---|---|
| 1 | 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 |
那么岛屿只有一块:
1 |
1 |
1 |
1 |
0 |
|---|---|---|---|---|
1 |
1 |
0 | 1 |
0 |
1 |
1 |
0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 |
示例2,题目给出的二维网格 grid:
| 1 | 1 | 0 | 0 | 0 |
|---|---|---|---|---|
| 1 | 1 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 |
| 0 | 0 | 0 | 1 | 1 |
则有三块岛屿:
1 |
1 |
0 | 0 | 0 |
|---|---|---|---|---|
1 |
1 |
0 | 0 | 0 |
| 0 | 0 | 1 |
0 | 0 |
| 0 | 0 | 0 | 1 |
1 |
二、算法原理 + 代码实现
题目的本质是在二维矩阵中搜索,因此我们可以:
- 遍历矩阵,当找到一个未被标记过的岛屿时就更新岛屿的数量并标记,
- 然后从这个位置开始向四周深度优先搜索相邻的未被标记过的陆地,直到搜索不到陆地为止。
- 当矩阵遍历完毕,返回所记录的岛屿的数量即可
全局变量
为了 dfs 函数递归方便,将矩阵 grid 改成全局变量;m 和 n 记录矩阵的大小;ret 用于记录岛屿的数量。
布尔类型的数组 visit 则用于标记已经发现的岛屿和陆地,防止重复计入;dx 和 dy 方向数组,用于访问指定位置的上下左右四个方向。
java
char[][] grid;
int m, n, ret;
boolean[][] visit;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
dfs 函数
函数头
dfs 函数的任务是从指定位置出发,访问它的四个方向,因此函数的参数是 row 和 col,表示某位置的坐标。
java
dfs(int row, int col);
函数体
我们在 dfs 函数体中要做的事情就是:从指定位置(坐标 row, col )出发,逐一访问该位置的上、下、左、右四个方向的位置,看看是否是未记录过的陆地,如果是,就继续递归深搜,否则不进行深搜。
具体就是循环四次然后计算出下一个位置的坐标,判断坐标是否合法,若合法,就进一步判断:
- 该坐标在 grid 矩阵中的值是否是 '1' ------ 该位置是陆地
- 该坐标的在 visit 中的值是否不等于 "true" ------ 该位置未被标记过
若以上两个条件都满足,就继续递归深搜。
细节问题
回溯
本题不需要回溯操作,我们标记一个岛屿之后不需要将其状态修改回去。
剪枝
由于解法是基于暴搜的,每一个节点都会被遍历到,因此不涉及到剪枝操作。
递归出口
当所有的岛屿都被遍历到并记录之后,就直接退出递归,无需手动设置递归出口。
代码实现
java
class Solution {
char[][] grid;
int m, n, ret;
boolean[][] visit;
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};
public int numIslands(char[][] givenGrid) {
grid = givenGrid; m = grid.length; n = grid[0].length;
visit = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1' && !visit[i][j]) {
// 找到未被标记过的岛屿,标记该岛屿并更新岛屿数量
visit[i][j] = true; ret++;
dfs(i, j); // 从该位置开始向四周寻找相邻的陆地
}
}
}
return ret;
}
private void dfs(int row, int col) {
for (int k = 0; k < 4; k++) {
int x = row + dx[k], y = col + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n) {
if (grid[x][y] == '1' && !visit[x][y]) {
// 找到未被标记过的相邻陆地
visit[x][y] = true;
dfs(x, y);
}
}
}
}
}
文章到这里就告一段落了,若有错误请尽管指出~
完