题目描述
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解答
1、DFS
分析
- 修改原数组,将每次dfs到的1修改为0,然后更新结果res
- 迭代判断是1就进行dfs,然后res++
- dfs的时候,遇到当前为0的直接return,遇见当前为1的修改为0,接着像周围4个方向dfs
class Solution {
public int numIslands(char[][] grid) {
int row = grid.length;
int col = grid[0].length;
int res = 0;
for(int i = 0;i<row;i++){
for(int j = 0;j<col;j++){
if(grid[i][j] == '1'){
dfs(grid,i,j,row,col);
++res;
}
}
}
return res;
}
public void dfs(char[][] grid,int r1,int c1,int row,int col){
if(r1 >= row || c1 >= col || r1 < 0 || c1 < 0){
return;
}
if(grid[r1][c1] == '0'){
return;
}
grid[r1][c1] = '0';
dfs(grid,r1 + 1,c1,row,col);
dfs(grid,r1,c1 + 1,row,col);
dfs(grid,r1 - 1,c1,row,col);
dfs(grid,r1,c1 - 1,row,col);
}
}
2、BFS
分析
- 外层仍是迭代
- 维护一个队列代替递归栈,同样也是将入队的位置对应的1改为0
- 这个队列记录当前位置、四周邻居位置的方式可以:1、直接队列存储数组表示当前位置的
int[]{i,j}
2、采用一种一个值可以隐式计算出行、列位置的算式,队列内存储这种位置id
写法1
class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[0].length; j++) {
if(grid[i][j] == '1'){
bfs(grid, i, j);
count++;
}
}
}
return count;
}
private void bfs(char[][] grid, int i, int j){
// 相邻位置入队
Queue<int[]> list = new LinkedList<>();
list.add(new int[] { i, j });
while(!list.isEmpty()){
int[] cur = list.remove();
i = cur[0]; j = cur[1];
if(0 <= i && i < grid.length && 0 <= j && j < grid[0].length && grid[i][j] == '1') {
grid[i][j] = '0';
list.add(new int[] { i + 1, j });
list.add(new int[] { i - 1, j });
list.add(new int[] { i, j + 1 });
list.add(new int[] { i, j - 1 });
}
}
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/number-of-islands/solution/number-of-islands-shen-du-you-xian-bian-li-dfs-or-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
写法2
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
++num_islands;
grid[r][c] = '0';
// 当前位置唯一标识入队,也就是使用一个值可以隐式计算出位置
Queue<Integer> neighbors = new LinkedList<>();
neighbors.add(r * nc + c);
while (!neighbors.isEmpty()) {
int id = neighbors.remove();
int row = id / nc;
int col = id % nc;
// 四周相邻的位置1修改0,加入队
if (row - 1 >= 0 && grid[row-1][col] == '1') {
neighbors.add((row-1) * nc + col);
grid[row-1][col] = '0';
}
if (row + 1 < nr && grid[row+1][col] == '1') {
neighbors.add((row+1) * nc + col);
grid[row+1][col] = '0';
}
if (col - 1 >= 0 && grid[row][col-1] == '1') {
neighbors.add(row * nc + col-1);
grid[row][col-1] = '0';
}
if (col + 1 < nc && grid[row][col+1] == '1') {
neighbors.add(row * nc + col+1);
grid[row][col+1] = '0';
}
}
}
}
}
return num_islands;
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3、并查集
构建并查集的三步
- 初始化
- 1、初始化
parent[]
存储当前块号的祖先块号。(块号就是行、列运算出来的位置id,可以i * 列数 + j
得出), - 2、初始化
rank[]
存储当前元素的祖先集合元素的数量,开始全部初始化为0,便于后续的union操作
- 递归寻找祖先(路径压缩:类链表转为类多叉树)
- 合并:将含有子代少的祖先指向含有子代多的祖先
然后
- 迭代矩阵,将1改为0,然后完成并查集的合并即可
class Solution {
class UnionFind {
int count;
// 保存当前块 所在集合的 祖先块
int[] parent;
// 保存当前块的子元素数量
int[] rank;
// 初始化
public UnionFind(char[][] grid) {
count = 0;
int m = grid.length;
int n = grid[0].length;
parent = new int[m * n];
rank = new int[m * n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
parent[i * n + j] = i * n + j;
// 初始化岛屿最多的数量就是1的个数,后面合并会不断的count--
++count;
}
// 默认子元素数量为0
rank[i * n + j] = 0;
}
}
}
// 递归的寻找祖先
public int find(int i) {
if (parent[i] != i) {
// 这里是路径压缩,链表变为类多叉树
parent[i] = find(parent[i]);
}
return parent[i];
}
// 连接祖先
public void union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
// 还不是一个祖先,就要合并,两个祖先集合的合并
if (rootx != rooty) {
if (rank[rootx] > rank[rooty]) {
parent[rooty] = rootx;
} else if (rank[rootx] < rank[rooty]) {
parent[rootx] = rooty;
} else {
// 数量相当
/*
parent[rootx] = rooty;
rank[rooty] += 1;
*/
parent[rooty] = rootx;
rank[rootx] += 1;
}
--count;
}
}
public int getCount() {
return count;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
UnionFind uf = new UnionFind(grid);
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
// 修改
grid[r][c] = '0';
// 并查集合并
if (r - 1 >= 0 && grid[r-1][c] == '1') {
uf.union(r * nc + c, (r-1) * nc + c);
}
if (r + 1 < nr && grid[r+1][c] == '1') {
uf.union(r * nc + c, (r+1) * nc + c);
}
if (c - 1 >= 0 && grid[r][c-1] == '1') {
uf.union(r * nc + c, r * nc + c - 1);
}
if (c + 1 < nc && grid[r][c+1] == '1') {
uf.union(r * nc + c, r * nc + c + 1);
}
}
}
}
return uf.getCount();
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。