并查集总结

1.作用

并查集,顾名思义“合并”和“查找”,是一种树形的数据结构,合并一些不相交的集合以及查找根节点等问题,可以用来解决连通性问题。
下面用一个例子“诠释”这个合并和查找的问题:
假设某市里有10个小镇,两个小镇之间的连线表示连接该两个小镇的路,比如小镇1和小镇2直接相连,小镇1和小镇3间接相连。
”:给你两个小镇,让你判断两个小镇是否相连(直接相连或者间接相连),则查找每个小镇的“根节点”是否相同,如果“根节点”为同一个镇的话则说明这两个镇是相连的,否则不相连。
”:问这些小镇是否已经全连通,如果已经全连通了则不需要再修路了,如果没有全连通则需要修一条路将两个不相连的小镇集合连起来,也就是合并
请添加图片描述
设小镇集合为x = 1,2,3,4,5,6,7,8,9,10,他们对应的前驱用数组表示pre = [1,1,1,2,3,6,6,6,6,9]:
请添加图片描述

2.查

由以上给出的信息找“根节点”

private int find(int x){
	while(pre[x] != x){
		x = pre[x];
	}
	return x
}

由于“并”和“查”的结果与元素间的连通路线无关,所以把所有子节点直接连接到对应的根节点,以减少计算量
请添加图片描述

private int find(int x){
        if(pre[x] != x) 
        	pre[x] = find(pre[x]);  
        return pre[x];//如果前驱等于它自己说明自己就是根节点
    }
3.并

查出“根节点”之后通过判断有几个根节点,就这点要修多少条路连通,接下来就是合并:
矮的树并到高的树下面去,若两棵树树高相等则选择其中一个作为最后的根节点,为了判断树高,运用加权标记法,sz[i]表示每个节点的树高,合并之后的树如下;
请添加图片描述

public void union(int p, int q){
        int pRoot = find(p);//查找p的根节点
        int qRoot = find(q);//查找q的根节点
        if(pRoot == qRoot)//如果相等则说明p和q同根节点
            return;
        if(sz[pRoot] > sz[qRoot]){
            pre[qRoot] = pRoot;//如果p的高度大于q的,则令q的上级为p
        }
        else{
            if(sz[qRoot] == sz[pRoot])//如果高度相同,则令q的高度加1
                sz[qRoot]++;
            pre[pRoot] = qRoot;//则令p的上级为q
        }
    }

创建并查集

//加权并查集
class WeightedUnionFind{
    private int[] pre;//每个节点的前驱
    private int[] sz;//每个节点的权值
    private int count;//一共有多少个元素
    public WeightedUnionFind(int count){//对录入的n个节点进行初始化
        this.count = count;//并查集所包含的元素
        pre = new int[count];
        sz = new int[count];
        for(int i=0; i<count; i++){
            pre[i] = i;//初始化每个节点的上级都是他自己
            sz[i] = 1;//每个节点构成的树高度是1
        }
    }
    //两节点是否相连
    public boolean isConnetion(int p, int q){
        return find(p) == find(q);
    }
    //合并两个节点所在的树
    public void union(int p, int q){
        int pRoot = find(p);//查找p的根节点
        int qRoot = find(q);//查找q的根节点
        if(pRoot == qRoot)//如果相等则说明p和q同根节点
            return;
        if(sz[pRoot] > sz[qRoot]){
            pre[qRoot] = pRoot;//如果p的高度大于q的,则令q的上级为p
        }
        else{
            if(sz[qRoot] == sz[pRoot])//如果高度相同,则令q的高度加1
                sz[qRoot]++;
            pre[pRoot] = qRoot;//则令p的上级为q
        }
    }
    //查找根节点
    private int find(int p){
        while(p != pre[p]){
            pre[p] = pre[pre[p]];//压缩查找路线
            p = pre[p];
        }
        return p;
    }

例题1(LeetCode 684)

请添加图片描述
请添加图片描述

class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        int len = edges.length;
        int[] parent = new int[len+1];
        for(int i=1; i<len+1; i++){
            parent[i] = i;
        }
        for(int i=0; i<len; i++){
            int[] edge = edges[i];
            int node1 = edge[0], node2 = edge[1];//node1和node2之间是有线相连的
            if(find(parent, node1) != find(parent, node2)){//如果两个数的前驱不相等的话,就合并
                union(parent, node1, node2);
            }
            else{
                return edge;//如果两个点的前驱相等,则他们之间就不应该再相连,不然会形成闭环,所以要返回这对数
            }               
        }
        return new int[0];
    }
    
    public void union(int[] parent, int node1, int node2){
        parent[find(parent, node1)] = find(parent, node2);
    }
    public int find(int[] parent, int node){
        if(parent[node] != node)
            parent[node] = find(parent, parent[node]);
        return parent[node];
    }
}

例题2(LeetCode 547)

请添加图片描述
请添加图片描述

class Solution {
    public int findCircleNum(int[][] isConnected) {
        WeightedUnionFind wuf = new WeightedUnionFind(isConnected.length);
        for(int i=0; i < isConnected.length; i++){
            for(int j=0; j<isConnected.length; j++){
                if(isConnected[i][j] == 1){
                    wuf.union(i,j);
                }
            }
        }
        return wuf.getCount();
    }
}
class WeightedUnionFind{
    private int[] pre;//每个节点的前驱
    private int[] sz;//每个节点的权值
    private int count;//一共有多少个元素
    public WeightedUnionFind(int count){//对录入的n个节点进行初始化
        this.count = count;//并查集所包含的元素
        pre = new int[count];
        sz = new int[count];
        for(int i=0; i<count; i++){
            pre[i] = i;//初始化每个节点的上级都是他自己
            sz[i] = 1;//每个节点构成的树高度是1
        }
    }
    //两节点是否相连
    public boolean isConnetion(int p, int q){
        return find(p) == find(q);
    }
    //合并两个节点所在的树
    public void union(int p, int q){
        int pRoot = find(p);//查找p的根节点
        int qRoot = find(q);//查找q的根节点
        if(pRoot == qRoot)//如果相等则说明p和q同根节点
            return;
        if(sz[pRoot] > sz[qRoot]){
            pre[qRoot] = pRoot;//如果p的高度大于q的,则令q的上级为p
        }
        else{
            if(sz[qRoot] == sz[pRoot])//如果高度相同,则令q的高度加1
                sz[qRoot]++;
            pre[pRoot] = qRoot;//则令p的上级为q
        }
        count--;
    }
    //查找根节点
    private int find(int p){
        while(p != pre[p]){
            pre[p] = pre[pre[p]];//压缩查找路线
            p = pre[p];
        }
        return p;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值