题目
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’
463. 岛屿的周长
给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
示例 1:
输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边
示例 2:
输入:grid = [[1]]
输出:4
示例 3:
输入:grid = [[1,0]]
输出:4
提示:
row == grid.length
col == grid[i].length
1 <= row, col <= 100
grid[i][j] 为 0 或 1
695. 岛屿的最大面积
给你一个大小为 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] 为 0 或 1
827. 最大人工岛
思路
常规的深度优先算法如下
void dfs(TreeNode root) {
// 判断 base case
if (root == null) {
return;
}
dfs(root.left);
dfs(root.right);
}
网格DFS,改动点如下
- 移动:变为上下左右四个节点移动
- 避免重复遍历:遍历后的值要重新赋值,说明已被遍历
void dfs(int[][] grid, int r, int c) {
// 判断 base case,如果坐标 (r, c) 超出了网格范围,直接返回
if (!inArea(grid, r, c)) {
return;
}
// 如果这个格子不是岛屿,直接返回
if (grid[r][c] != 1) {
return;
}
grid[r][c] = 2; // 将格子标记为「已遍历过」
// 访问上、下、左、右四个相邻结点
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length
&& 0 <= c && c < grid[0].length;
}
岛屿问题(网格DFS),常见的有下面几种类型的题目
-
L200. 岛屿数量 :
当遇到 grid[i][j] == '1' 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿(说明此岛已被遍历了)
如果两个点是相邻的岛屿,那么这个点便会一直遍历,直到整个岛都遍历完
-
L463. 岛屿的周长 : 可以拆分为两种类型的边
- 网格边界:dfs 函数因为「坐标 (r, c) 超出网格范围」返回的时候,实际上就经过了一条黄色的边;
- 海洋边界:而当函数因为「当前格子是海洋格子」返回的时候,实际上就经过了一条蓝色的
- 值判断:
- grid[i][j]=0:说明是海洋,返回1
- grid[i][j]=2:说明是已经遍历的陆地,返回0
- 当前节点超出边界,返回1
- 返回:上下左右计算到的所有长度
-
L695. 岛屿的最大面积 :
每遍历到一个格子(如果是陆地),就把面积加一
-
L827. 最大人工岛(填海造陆问题):
两遍 DFS,第一遍 DFS 遍历陆地格子,计算每个岛屿的面积并标记岛屿;第二遍 DFS 遍历海洋格子,观察每个海洋格子相邻的陆地格子
第一次DFS:标记岛屿并计算面积
,使用 DFS 遍历网格,给每个独立岛屿分配唯一编号(从 2 开始,避免与 0、1 冲突),并记录每个岛屿的面积。获取到最大的岛屿第二次DFS,评估水域变陆地后的最大面积
:遍历所有水域(0),检查其上下左右四个方向的相邻岛屿,通过集合去重后计算连接后的总面积(相邻岛屿面积之和 + 1),更新最大面积
。Set的add方法
- 若集合中不存在当前添加的元素e,则将元素e加入集合,并返回true;
- 若集合中已存在元素e(基于equals()和hashCode()判断),则不添加元素,并返回false。
只有下面这种情况生效,所以才需要去判断set中是否有重复元素
下面的这种情况是不生效的
算法
200. 岛屿数量
class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == '1') {
dfs(grid, r, c);
count++;
}
}
}
return count;
}
private void dfs(char[][] grid, int r, int c) {
if (!inArea(grid.length, grid[0].length, r, c)) {
return;
}
if(grid[r][c] != '1'){
return;
}
grid[r][c] = '2';
dfs(grid, r + 1, c);
dfs(grid, r, c + 1);
dfs(grid, r - 1, c);
dfs(grid, r, c - 1);
}
private boolean inArea(int maxR, int maxC, int r, int c) {
return r >= 0 && maxR > r && c >= 0 && maxC > c;
}
}
463. 岛屿的周长
class Solution {
public int islandPerimeter(int[][] grid) {
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 1) {
// 其中恰好有一个岛屿
return dfs(grid, r, c);
}
}
}
return 0;
}
private int dfs(int[][] grid, int r, int c) {
if (!inArea(grid.length, grid[0].length, r, c)) {
return 1;
}
if (grid[r][c] == 0) {
return 1;
}
if (grid[r][c] == 2) {
return 0;
}
grid[r][c] = 2;
return dfs(grid, r - 1, c) + dfs(grid, r, c + 1)
+ dfs(grid, r + 1, c) + dfs(grid, r, c - 1);
}
private boolean inArea(int maxR, int maxC, int r, int c) {
return r >= 0 && maxR > r && c >= 0 && maxC > c;
}
}
695. 岛屿的最大面积
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int max = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
int area = dfs(grid, r, c);
max = Math.max(max, area);
}
}
return max;
}
private int dfs(int[][] grid, int r, int c) {
if (!inArea(grid.length, grid[0].length, r, c)) {
return 0;
}
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return 1 + dfs(grid, r, c - 1) + dfs(grid, r, c + 1)
+ dfs(grid, r - 1, c) + dfs(grid, r + 1, c);
}
private boolean inArea(int maxR, int maxC, int r, int c) {
return r >= 0 && maxR > r
&& c >= 0 && maxC > c;
}
}
827. 最大人工岛
class Solution {
public int largestIsland(int[][] grid) {
List<Integer> area = new ArrayList<>();
int n = grid.length;
int ans = 0;
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
int a = dfs(grid, r, c, area.size() + 2);
area.add(a);
ans = Math.max(a, ans);
}
}
if (area.isEmpty()) {
return 1;
}
// 上 下 左 右
int[][] dirs = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
Set<Integer> set = new HashSet<>();
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] != 0) {
continue;
}
// 每次遍历水域的上下左右节点都清空一遍
set.clear();
int newArea = 1;
for (int[] dir : dirs) {
int x = c + dir[1];
int y = r + dir[0];
if (inArea(n, y, x)
&& grid[y][x] != 0 && set.add(grid[y][x])) {
newArea += area.get(grid[y][x] - 2);
}
}
ans = Math.max(ans, newArea);
}
}
return ans;
}
private int dfs(int[][] grid, int r, int c, int id) {
if (!inArea(grid.length, r, c)) {
return 0;
}
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = id;
return 1 + dfs(grid, r, c - 1, id) + dfs(grid, r, c + 1, id)
+ dfs(grid, r - 1, c, id) + dfs(grid, r + 1, c, id);
}
private boolean inArea(int n, int r, int c) {
return r >= 0 && n > r && c >= 0 && n > c;
}
}