0代表陆地,1代表水,上下左右都被水包围的陆地才叫一个岛。
数组边界外面的不算。
问有多少个岛。
思路:
这种求连通区域的一般用DFS。
和leetcode.200题很像,区别是200题默认数组边界外面都是水,所以只要有陆地,至少就会有一个岛出现,只需要找出所有连通的陆地。
而本题不算边界外面,如果是陆地连着数组边界,那就不算岛。
所以如果行或列 < 3,肯定形不成岛。
既然边界部分的一圈不能被水包围,也就不能形成岛,所以边界的一圈不考虑,
DFS只对1 ~ row-2, 1 ~ col-2部分操作。
DFS终止条件:如果到了边界的一圈,肯定不能形成岛,需要返回false.
如果四周都是水, 或者四周都已经访问过,返回true.
当上下左右都是true时,才能形成封闭的岛。
注意:
最后可以写成left && right && up && down
, 但不能把4个dfs写在一起,即dfs && dfs && dfs && dfs
的形式。
dfs函数要一个一个计算,不能拼在一个逻辑式里面计算,否则有的case会得到错误的结果。
分析原因,几个dfs &&
运算内部不得而知,估计是有并行计算的,而visited数组的修改并不是线程安全的,
多个dfs一起计算时可能有的visited在不恰当的时机被修改了,从而得到错误的true和false.
class Solution {
int rows = 0;
int cols = 0;
//boolean[][] visited;
public int closedIsland(int[][] grid) {
rows = grid.length;
cols = grid[0].length;
//visited = new boolean[rows][cols];
int res = 0;
if(rows < 3 || cols < 3) return 0;
for(int r = 1; r < rows-1; r++) {
for(int c = 1; c < cols-1; c++) {
//if(grid[r][c]==1 || visited[r][c]) continue;
if(grid[r][c] == 0)
if(dfs(grid, r, c)) res ++;
}
}
return res;
}
boolean dfs(int[][] grid, int r, int c) {
//if(visited[r][c] || grid[r][c] == 1) return true;
if(grid[r][c] == -1 || grid[r][c] == 1) return true;
if(r <= 0 || c <= 0 || r >= rows-1 || c >= cols-1) return false;
grid[r][c] = -1;
//visited[r][c] = true;
boolean left = dfs(grid, r,c-1);
boolean right = dfs(grid, r, c+1);
boolean up = dfs(grid, r-1, c);
boolean down = dfs(grid, r+1, c);
//注意:不要写成dfs(grid, r,c-1) && dfs(grid, r, c+1) && dfs(grid, r-1, c) && dfs(grid,r+1,c)!
return left && right && up && down;
}
}