题目描述

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

CodeTop6_dfs、bfs、并查集_岛屿数量_leetcode

解答

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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。