1 问题描述
中等
给你一个由 '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
相关的题型还有:
- 463. 岛屿的周长 (Easy)
- 695. 岛屿的最大面积 (Medium)
- 827. 最大人工岛 (Hard)
2 主要思路
主要思路为DFS(广度优先遍历),类似于二叉树中,广度优先讲究每一个节点都会先遍历完下一步的所有节点。那在本问题中的广度优先递归应该是:
- 每一步之后遍历上下左右四个节点;
- 递归的结束条件应该是超出网格的范围(类似于二叉树中为null);
- 要注意不重复递归的问题,就是判断过的点,不会再进行判断。
3 技法实现
3.1 遍历上下左右
对于遍历上下左右的问题,学到了一种值得收藏的方法:
private static final int[][] DIRS = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
for (int[] dir : DIRS) {
int x = r + dir[0];
int y = c + dir[1];
dfs(grid, x, y);
}
3.2 不重复递归
对于不重复递归的问题,解法是改变岛屿上陆地网格的值。这样网格上的值就分成了三种:
- 值为0,表示海洋;
- 值为1,表示还未递归到的陆地;
- 值为x,x为我们自定义改变的数,表示已经递归后的陆地
3.3 周长的巧妙实现
463. 岛屿的周长本题中的周长的有效边为陆地相邻边界或者海洋的边,所以计算时有效的边长就为:从陆地上跨到边界或海洋的边:
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if(grid[i][j] == 1) {
//陆地网格进入DFS
return DFS(grid, i, j);
}
}
int DFS(int[][] grid, int r, int c) {
//已经从陆地网格跨过来
if(!(0<= r && r < grid.length && 0 <= c && c < grid[0].length)) return 1;
if(grid[r][c] == 0) return 1;
if(grid[r][c] != 1) return 0;
3.4 DFS一次遍历完整个岛屿
以下代码是遍历岛屿的通用代码,可以好好体会
public int islandPerimeter(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if(grid[i][j] == 1) {
//这是关键
return DFS(grid, i, j);
}
}
}
return 0;
}