整理并查集

以一道力扣题来引出这个问题,此题就是查并集的原型问题,如果当问题关注连通团体的数量时,可以使用该算法来解决问题

算法包含两个关键的函数和一个关键的parent数组:

上级:每个团队里的代表任务,每个团队里有且只有一个,用它来标识整个团队,每个团队的其他元素通过find算法最终都能找到唯一的上级

parent数组:对于parent[i]=j,它表示的是对于顶点i,它的上级是j,一般情况下会将它初始化为parent[i]=i,因为每个顶点都至少是自己的上级

for (int i = 1; i <= n; i++) {
            parent[i]=i;
        }

find函数:如果当前的顶点上级不是自己的时候(已经和别的点进行连通了),去寻找到最顶层的上级

private int find(int point) {
        return parent[point]==point?point:find(parent[point]);
    }

uinon函数:如果当前两个点还没有联系,但是函数判断两点应该有联系,使用此函数将一个点的上级添加为另一个点的上级

 public void union(int index1, int index2) {
        parent[find(index1)] = find(index2);
    }

整体的解法如下:

int[] parent;
    public int findCircleNum(int[][] isConnected) {
        int cities = isConnected.length;
        parent = new int[cities];
        for (int i = 0; i < cities; i++) {
            parent[i] = i;
        }
        //对于如题的数组可以只看数组的上三角
        for (int i = 0; i < cities; i++) {
            for (int j = i + 1; j < cities; j++) {
                if (isConnected[i][j] == 1) {
                    union(i, j);
                }
            }
        }
        //parent[i]==i相当于是每个团体的标志性人物,也就是团体的总个数
        int provinces = 0;
        for (int i = 0; i < cities; i++) {
            if (parent[i] == i) {
                provinces++;
            }
        }
        return provinces;
    }

    //随意选择一个人当另一个人的上级
    public void union(int index1, int index2) {
        parent[find(index1)] = find(index2);
    }

    public int find(int index) {
       return parent[index]==index?index:find(parent[index]);
    }

为什么可以使用parent[i]==i来判断团体的数量?

对于如下的算例:

 算法一开始初始化为三个小团体,parent数组最后打印出来是[1,1,2],表示0->1,1->1,2->2,由于初始化时我们认定parent[i]=i,即i的上级是自己,即一个团体中最上级的上级肯定是自己,因此可以通过parent[i]==i来判断团体的数量

相似的字符串

 此问题对比原型问题其实只改变了一个条件,如果两个顶点要被分为一组,必须是异位词

    int[] parent;
    public int numSimilarGroups(String[] strs) {
        int m=strs.length;
        parent=new int[m];
        int n=strs[0].length();
        for (int i = 0; i < m; i++) {
            parent[i]=i;
        }
        for (int i = 0; i < m; i++) {
            for (int j = i+1; j < m; j++) {
                int fi=find(i);
                int fj=find(j);
                if(fi!=fj&&isValid(strs[i],strs[j],n)){
                    //如果两个字符串满足异位词就设置为一组
                    union(i,j);
                }
            }
        }
        int res=0;
        for (int i = 0; i < m; i++) {
            if(parent[i]==i)res++;
        }
        return res;
    }

    private boolean isValid(String str, String str1, int n) {
        int num=0;
        for (int i = 0; i < n; i++) {
            if(str.charAt(i)!=str1.charAt(i))num++;
            if(num>2)return false;
        }
        return true;
    }
    //随意选择一个人当另一个人的上级
    public void union(int index1, int index2) {
        parent[find(index1)] = find(index2);
    }

    public int find(int index) {
       return parent[index]==index?index:find(parent[index]);
    }

多余的边

该题理解起来比较晦涩,其大致意思是所给的三条边中,有一条是可以去掉的,且去掉之后依旧可以让剩余部分形成一个整体,并且如果可以删除多条边,那么选择最后一条边进行删除

  • 如果两个顶点属于不同的连通分量,说明在遍历到当前的边之前,两个顶点并不连通,因此当前边不会导致环的出现,于是我们合并这两个顶点的连通分量
  • 如果两个顶点属于同一个连通分量了,说明在遍历到当前边之前,两个顶点已经连通了,因此当前的边导致了环的出现,直接返回当前边就行了
 int[] parent;
    public int[] findRedundantConnection(int[][] edges) {
        int n = edges.length;
        parent = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            parent[i] = i;
        }
        for (int i = 0; i < n; i++) {
            int edge1 = edges[i][0], edge2 = edges[i][1];
            int fi = find(edge1), fo = find(edge2);
            if (fi != fo) {
                union(i, j);
            } else {
                return edges[i];
            }
        }
        return new int[]{};
    }

    public void union(int index1, int index2) {
        parent[find(index1)] = find(index2);
    }

    public int find(int index) {
        return parent[index] == index ? index : find(parent[index]);
    }

从此题我们可以总结出并查集算法的另一个作用:找到导致成环的最后一条边

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值