岛屿问题的各种变形
岛屿问题框架:
// 二维矩阵遍历框架
void dfs(int[][] grid, int i, int j, boolean[] visited) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (visited[i][j]) {
// 已遍历过 (i, j)
return;
}
// 进入节点 (i, j)
visited[i][j] = true;
dfs(grid, i - 1, j, visited); // 上
dfs(grid, i + 1, j, visited); // 下
dfs(grid, i, j - 1, visited); // 左
dfs(grid, i, j + 1, visited); // 右
}
用方向数组来处理:
// 方向数组,分别代表上、下、左、右
int[][] dirs = new int[][]{{-1,0}, {1,0}, {0,-1}, {0,1}};
void dfs(int[][] grid, int i, int j, boolean[] visited) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (visited[i][j]) {
// 已遍历过 (i, j)
return;
}
// 进入节点 (i, j)
visited[i][j] = true;
// 递归遍历上下左右的节点
for (int[] d : dirs) {
int next_i = i + d[0];
int next_j = j + d[1];
dfs(grid, next_i, next_j, visited);
}
// 离开节点 (i, j)
}
1、岛屿数量
200. 岛屿数量 - 力扣(LeetCode) (leetcode-cn.com)
// 主函数,计算岛屿数量
int numIslands(char[][] grid) {
int res = 0;
int m = grid.length, n = grid[0].length;
// 遍历 grid
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
// 每发现一个岛屿,岛屿数量加一
res++;
// 然后使用 DFS 将岛屿淹了
dfs(grid, i, j);
}
}
}
return res;
}
// 从 (i, j) 开始,将与之相邻的陆地都变成海水
void dfs(char[][] grid, int i, int j) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (grid[i][j] == '0') {
// 已经是海水了
return;
}
// 将 (i, j) 变成海水
grid[i][j] = '0';
// 淹没上下左右的陆地
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
}
2、封闭岛屿的数量
1254. 统计封闭岛屿的数目 - 力扣(LeetCode) (leetcode-cn.com)
把上一题中那些靠边的岛屿排除掉,剩下的就是「封闭岛屿」
// 主函数:计算封闭岛屿的数量
int closedIsland(int[][] grid) {
int m = grid.length, n = grid[0].length;
for (int j = 0; j < n; j++) {
// 把靠上边的岛屿淹掉
dfs(grid, 0, j);
// 把靠下边的岛屿淹掉
dfs(grid, m - 1, j);
}
for (int i = 0; i < m; i++) {
// 把靠左边的岛屿淹掉
dfs(grid, i, 0);
// 把靠右边的岛屿淹掉
dfs(grid, i, n - 1);
}
// 遍历 grid,剩下的岛屿都是封闭岛屿
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
// 从 (i, j) 开始,将与之相邻的陆地都变成海水
void dfs(int[][] grid, int i, int j) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == 1) {
// 已经是海水了
return;
}
// 将 (i, j) 变成海水
grid[i][j] = 1;
// 淹没上下左右的陆地
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
}
1020. 飞地的数量 - 力扣(LeetCode) (leetcode-cn.com)
public int numEnclaves(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
dfs(grid, i, 0);
dfs(grid, i, grid[0].length - 1);
}
for (int i = 0; i < grid[0].length; i++) {
dfs(grid, 0, i);
dfs(grid, grid.length - 1, i);
}
int res = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1){
res++;
}
}
}
return res;
}
void dfs(int[][] grid, int i, int j){
if ( i < 0 || i >= grid.length || j < 0 || j >= grid[0].length)
return;
if (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);
}
695. 岛屿的最大面积 - 力扣(LeetCode) (leetcode-cn.com)
void dfs(int[][] grid, int i, int j){
if ( i < 0 || i >= grid.length || j < 0 || j >= grid[0].length)
return;
if (grid[i][j] == 0)
return;
grid[i][j] = 0;
area++;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i ,j + 1);
dfs(grid, i, j - 1);
}
int area = 0;
int max = 0;
public int maxAreaOfIsland(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
area = 0;
dfs(grid, i, j);
max = max > area ? max : area;
}
}
}
return max;
}
3、子岛屿的数量
1905. 统计子岛屿 - 力扣(LeetCode) (leetcode-cn.com)
当岛屿 B
中所有陆地在岛屿 A
中也是陆地的时候,岛屿 B
是岛屿 A
的子岛。
反过来说,如果岛屿 B
中存在一片陆地,在岛屿 A
的对应位置是海水,那么岛屿 B
就不是岛屿 A
的子岛。
那么,我们只要遍历 grid2
中的所有岛屿,把那些不可能是子岛的岛屿排除掉,剩下的就是子岛。
int countSubIslands(int[][] grid1, int[][] grid2) {
int m = grid1.length, n = grid1[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid1[i][j] == 0 && grid2[i][j] == 1) {
// 这个岛屿肯定不是子岛,淹掉
dfs(grid2, i, j);
}
}
}
// 现在 grid2 中剩下的岛屿都是子岛,计算岛屿数量
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid2[i][j] == 1) {
res++;
dfs(grid2, i, j);
}
}
}
return res;
}
// 从 (i, j) 开始,将与之相邻的陆地都变成海水
void dfs(int[][] grid, int i, int j) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == 0) {
return;
}
grid[i][j] = 0;
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
}
4、不同岛屿的数量
694. 不同岛屿的数量 - 力扣(LeetCode) (leetcode-cn.com)
序列化:将二维矩阵中的「岛屿」进行转化,变成比如字符串这样的类型,然后利用 HashSet
这样的数据结构去重,最终得到不同的岛屿的个数
void dfs(int[][] grid, int i, int j, StringBuilder sb, int dir) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n
|| grid[i][j] == 0) {
return;
}
// 前序遍历位置:进入 (i, j)
grid[i][j] = 0;
sb.append(dir).append(',');
dfs(grid, i - 1, j, sb, 1); // 上
dfs(grid, i + 1, j, sb, 2); // 下
dfs(grid, i, j - 1, sb, 3); // 左
dfs(grid, i, j + 1, sb, 4); // 右
// 后序遍历位置:离开 (i, j)
sb.append(-dir).append(',');
}
int numDistinctIslands(int[][] grid) {
int m = grid.length, n = grid[0].length;
// 记录所有岛屿的序列化结果
HashSet<String> islands = new HashSet<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
// 淹掉这个岛屿,同时存储岛屿的序列化结果
StringBuilder sb = new StringBuilder();
// 初始的方向可以随便写,不影响正确性
dfs(grid, i, j, sb, 666);
islands.add(sb.toString());
}
}
}
// 不相同的岛屿数量
return islands.size();
}