用并查集的方法求解岛屿问题(java)

首先第一步

具体题目见力扣,点击这里

创建并查集,就本题而言,需要创建二维矩阵大小的子节点个数。
方法有很多,这里就只选取从零开始直到把矩阵所有点遍历完的个数。

首先声明,本题不仅仅只有一种并查集的解法,但鉴于本人最近在学习并查集,所以在看到官方给的并查集答案稍有一点长就想自己写一遍Java代码。

首先想要用并查集解决问题先要创建一个并查集的类,这样有助于是代码更加规范化,也更加便于理解。

另外提醒一下,这题给出的数组值均为字符。
并查集类:Uf如下:

class Uf{
        int[] id;
        int a;//该属性代表所要解决的矩阵列数
        public Uf(int m,int n){
            a=n;
            id=new int[m*n];
            for(int i=0;i<m*n;i++){
                id[i]=i;
            }
        }
        public int find(int m,int n){
            int b=m*a+n;
            while(b!=id[b]){
                id[b]=id[id[b]];
                b=id[b];
            }
            return b;
        }
        public void un(int i,int j,int p,int q){
            id[find(i,j)]=find(p,q);
        }
    }

其中,

  1. id是对于每个可以用并查集解决的问题都必须创建的数组,
    他的大小与图中节点的个数相同,数组中的每个数表示当前节点的父节点代号。
  2. find方法表示传入一对横纵坐标,然后找到该节点对应的祖宗节点,具体的操作为:
  • 查找当前节点的父节点,如果还是本身则表示父节点不存在,该节点作为一个独立分支出现。
  • 否则表示该节点存在父亲节点,就将父亲节点的父亲节点赋值给当前节点的父亲节点,

这样就存储了一段寻亲的路径,方便下次寻找祖宗节点。
接着将当前节点转移到父节点。

  1. un方法即为链接的意思,即:将传入的两个节点的祖宗节点合二为一。
    利用find方法找到其中一个节点的祖宗节点并修改其父节点为传入的另一个节点的祖宗节点。
以上就是并查集类的三个方法。
public int numIslands(char[][] grid) {
        int m=grid.length,n=grid[0].length;
        Uf pp=new Uf(m,n);
        int res=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]=='1'){
                    if(i>0 && grid[i-1][j]=='1')
                        pp.un(i,j,i-1,j);
                    if(j>0 && grid[i][j-1]=='1')
                        pp.un(i,j,i,j-1);
                }
            }
        }
        for(int i=0;i<m*n;i++){
            if(pp.id[i]==i && grid[i/n][i%n]=='1')
                res++;
        }
        return res;
    }

接下来就是着手修改numIslands方法传入的图的各种节点的血缘关系。

为了减少重复,可以只选择每个节点的左邻和上邻。

  1. 首先对每个节点进行边界判断,如果在边界并且邻居为’0’,则暂且作为祖宗节点,不过这并不影响整体的结果。由于并查集的存在就导致了对于刚刚那种情况还会再次修改祖宗节点。

  2. 经过整体遍历之后就得到了存放每个节点的父节点的数组pp.id。
    并且在里面寻找父节点为本身的节点这代表着当前节点为祖宗节点。
    (当然我们要过滤掉值为’0’的节点)

  3. 这样我们再对id数组进行最后一次的遍历,得到最终的祖宗节点个数即为岛屿个数。
    WNOEV6{5E46I%F0MWV7WU8L.png

代码

class Solution {
    class Uf{
        int[] id;
        int a;
        public Uf(int m,int n){
            a=n;
            id=new int[m*n];
            for(int i=0;i<m*n;i++){
                id[i]=i;
            }
        }
        public int find(int m,int n){
            int b=m*a+n;
            while(b!=id[b]){
                id[b]=id[id[b]];
                b=id[b];
            }
            return b;
        }
        public void un(int i,int j,int p,int q){
            if(find(i,j)!=find(p,q))
                id[find(i,j)]=find(p,q);
        }
    }
    public int numIslands(char[][] grid) {
        int m=grid.length,n=grid[0].length;
        Uf pp=new Uf(m,n);
        int res=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]=='1'){
                    if(i>0 && grid[i-1][j]=='1')
                        pp.un(i,j,i-1,j);
                    if(j>0 && grid[i][j-1]=='1')
                        pp.un(i,j,i,j-1);
                }
            }
        }
        for(int i=0;i<m*n;i++){
            if(pp.id[i]==i && grid[i/n][i%n]=='1')
                res++;
        }
        return res;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值