leetcode200题岛屿的数目-染色法和并查集实现java代码

题目:

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
11110
11010
11000
00000

输出: 1

示例 2:

输入:
11000
11000
00100
00011

输出: 3

 

直接上代码:

染色法实现:

 

public class 岛屿的数目 {

    public static int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        int count = 0;
        //遍历所有的节点
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                //对于陆地计数+1,然后通过深度优先算法把相连的所有的陆地变成水
                if (grid[i][j] != '0') {
                    count++;
                    dfs(grid, i, j, m, n);
                }
            }
        }
        return count;
    }

    /**
     * 深度优先算法
     *
     * @param grid
     * @param i
     * @param j
     * @param m
     * @param n
     */
    public static void dfs(char[][] grid, int i, int j, int m, int n) {
        if (i < 0 || j < 0 || j >= n || i >= m) {
            return;
        }
        if (grid[i][j] == '1') {
            grid[i][j] = '0';
        } else {
            return;
        }
        //上下左右继续递归把关联的陆地变成水。这里也可以使用二维方向数组遍历前后左右
        //dir[-1,1,0,0
        //     0,0,1,-1] ;
        
        dfs(grid, i + 1, j, m, n);
        dfs(grid, i - 1, j, m, n);
        dfs(grid, i, j - 1, m, n);
        dfs(grid, i, j + 1, m, n);
    }

    public static void main(String[] args) {
        char[][] grid = {{'1', '1', '0', '0', '0'}, {'1', '1', '0', '0', '0'}, {'0', '0', '1', '0', '0'}, {'0', '0', '0', '1', '1'}};
        int result = numIslands(grid);
        System.out.println(result);
    }
}

并查集实现:

 

public class 岛屿数目并查集 {

    public static int count = 0;

    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        //行
        int nr = grid.length;
        //列
        int nc = grid[0].length;
        //节点的最大的父亲
        int[] parent = new int[nr * nc];
        //为1的节点所在的团队的深度
        int[] deep = new int[nr * nc];
        //初始化各项数据
        for (int i = 0; i < nr; ++i) {
            for (int j = 0; j < nc; ++j) {
                //首先查找所有为1的节点把他作为自己的最大父亲
                if (grid[i][j] == '1') {
                    parent[i * nr + j] = i * nr + j;
                    //每个为1的节点都计数一次,后面多个节点合并的时候每合并一个节点就是-1.这样最后的count就是所有的领头人的数量
                    ++count;
                }
                //记录节点所在团队的深度
                deep[i * nr + j] = 0;
            }
        }

        //遍历所有节点
        for (int i = 0; i < nr; i++) {
            for (int j = 0; j < nc; j++) {
                //对于每个不为0的节点  依次找他的上下左右节点上级节点合并
                if (grid[i][j] == '1') {
                    //将当前1置为0防止重复计算,也可以使用visit数组标记当前是否已读
                    grid[i][j] = '0';
                    //左
                    if (i - 1 > 0 && grid[i - 1][j] == '1') {
                        union(nr * i + j, nr * (i - 1) + j, parent, deep);
                    }
                    //右
                    if (i + 1 < nr && grid[i + 1][j] == '1') {
                        union(nr * i + j, nr * (i + 1) + j, parent, deep);
                    }
                    //上
                    if (j + 1 < nc && grid[i][j + 1] == '1') {
                        union(nr * i + j, nr * (i) + j + 1, parent, deep);
                    }
                    //下
                    if (j - 1 > 0 && grid[i][j - 1] == '1') {
                        union(nr * i + j, nr * i + j - 1, parent, deep);
                    }

                }
            }
        }
        return count;
    }

    /**
     * 合并节点,为了后面的计算效率,不合并也可以,但是合并更快
     * x是节点grid[i][j]在parent数组中的位置,y同理。
     */
    public void union(int x, int y, int[] parent, int deep[]) {
        //x找到的根节点,这个节点就是grid[i][j]所在的团队的最大的领头人
        int xRoot = findParent(x, parent);
        //y找到的根节点
        int yRoot = findParent(y, parent);
        //当x,y的最大领头人不一样的时候才表明他们不在一个团队,这时候才寻找
        //为什么相等就不用找了呢?因为我们初始化的时候给每个节点作为他自己的最大领头人,如果两个节点的最大领头人一样了那肯定已经合并过了
        if (xRoot != yRoot) {
            //x的最大的领头人深度比y大,所以把y的最大的领头人节点指向x的最大的领头人
            if (deep[xRoot] > deep[yRoot]) {
                parent[yRoot] = xRoot;
            } else if (deep[xRoot] < deep[yRoot]) {
                parent[xRoot] = yRoot;
            } else {
                //深度相同随便怎么合并
                parent[xRoot] = yRoot;
                deep[yRoot] += 1;
            }
            //因为合并了此时需要减1,表示传进来的节点的上下左右节点合并了一个
            --count;
        }
    }

    /**
     * 递归寻找x的最大领头人(指向自己的节点)
     *
     * @param x
     * @param parent
     * @return
     */
    public int findParent(int x, int[] parent) {
        if (x != parent[x]) {
            parent[x] = findParent(parent[x], parent);
        }
        return parent[x];
    }


    public static void main(String[] args) {
        岛屿数目并查集 c = new 岛屿数目并查集();
        char[][] grid = {{'1', '1', '0', '0', '0'}, {'1', '1', '0', '0', '0'}, {'0', '0', '1', '0', '0'}, {'0', '0', '0', '1', '1'}};
        int result = c.numIslands(grid);
        System.out.println(result);
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值