并查集的几种实现方式(java版)

并查集:Union Find :一种不一样的树形结构:由孩子指向父亲

用处:网络中节点间的连接状态,路径问题,朋友问题。

对于一组数据,主要支持两个动作:

  union(p,q)

  isConnected(p,q)

上面代表的是数字,下面代表的是 id值,这样可以明显看到,0到4属于同一个集合,5到9属于第二个集合。因为各自的id值相等 

 第一个并查集的实现方法:

 定义一个接口

public interface UF {
    int getSize();
    boolean isConnected(int p,int q);
    void unionElements(int p,int q);

}
import java.util.Scanner;

public class UnionFind implements UF {

    private int [] id ;//初始定义的数组

    public UnionFind(int size){
        id = new int[size];
        for (int i=0;i<id.length;i++){
            id[i]=i;//把每个id相当于集合的索引设置初始的值
        }
    }
    @Override
    public int getSize() {
        return id.length;
    }
    //查找元素p所对应的集合编号
    public int find(int p){
        if (p <0 && p>=id.length)
            throw new IllegalArgumentException("p is out of bound");
        return id[p];
    }
    //用于查看元素p和元素q是否属于同一个集合
    @Override
    public boolean isConnected(int p, int q) {
        return find(p)==find(q);
    }
    //合并元素
    @Override
    public void unionElements(int p, int q) {
        int pid = find(p);
        int qid = find(q);
        if (pid==qid){
            return;
        }
        for (int i = 0;i < id.length;i++){
            if (id[i] == pid){
                id[i] = qid;
            }
        }
    }
}
class Main{
    public static void main(String[] args){
        Scanner sr = new Scanner(System.in);
        UnionFind u = new UnionFind(4);
            int a = sr.nextInt();
            int b = sr.nextInt();
//            int c = sr.nextInt();
            u.unionElements(a,b);
        System.out.println(u.find(a));
        System.out.println(u.find(b));
    }
        }

第二个方法用树来实现:

每一个节点都指向自己都是一棵树 :注意下面的值的变化,每次联合其实相当于去联合根节点

 

 

 

 

 查询这是就是log(h)h代表的深度,虽然牺牲了查询时间但是节省了合并时间

public interface UF {
    int getSize();
    boolean isConnected(int p, int q);
    void unionElements(int p, int q);

}
public class UnionFind2 implements UF{
    private int [] parent;//表示第i个元素指向那个节点
    public UnionFind2(int size){
        parent = new int[size];
        for (int i = 0; i <size; i++) {
            parent[i]=i;//初始化时候每一个几点都指向他自己
        }
    }
    @Override
    public int getSize() {
        return parent.length;
    }

    /**
     * 查找过程,查找元素p所对应的集合编号
     * O(h)的复杂度,h为树的深度
     * @param p
     * @return
     */
    private int find(int p){
        while(p!=parent[p]){
            p=parent[p];//不断去找根节点,知道找到对应的根节点
        }
        return p;
    }
    //查看元素p和元素q是否属于一个集合
    @Override
    public boolean isConnected(int p, int q) {
        return false;
    }
    //合并元素p和元素q所属的集合
    //O(h)的复杂度,h为树的深度
    @Override
    public void unionElements(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        if (pRoot==qRoot){
            return;
        }
        parent[pRoot]=qRoot;//指向根节点即可
    }
}

 加入了节点的判断 这样非常省时间:

public interface UF {
    int getSize();
    boolean isConnected(int p, int q);
    void unionElements(int p, int q);

}
public class Main implements UF {
    private int [] parent;//表示第i个元素指向那个节点
    private int [] sz;//表示以sz[i]以i为根的集合中元素的个数
    public Main(int size){
        parent = new int[size];
        sz = new int [size];
        for (int i = 0; i <size; i++) {
            parent[i]=i;//初始化时候每一个几点都指向他自己
            sz[i]=1;//每一个元素初始时候自己都是一个独立的集合
        }
    }
    @Override
    public int getSize() {
        return parent.length;
    }

    /**
     * 查找过程,查找元素p所对应的集合编号
     * O(h)的复杂度,h为树的深度
     * @param p
     * @return
     */
    private int find(int p){
        while(p!=parent[p]){
            p=parent[p];//不断去找根节点,知道找到对应的根节点
        }
        return p;
    }
    //查看元素p和元素q是否属于一个集合
    @Override
    public boolean isConnected(int p, int q) {
        return false;
    }
    //合并元素p和元素q所属的集合
    //O(h)的复杂度,h为树的深度
    @Override
    public void unionElements(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        if (pRoot==qRoot){
            return;
        }
        if (sz[pRoot]<sz[qRoot]){
            parent[pRoot]=qRoot;//p个几点所在的个数相对于比较少,让元素个数比较少的指向元素个数比较多的根节点
            sz[qRoot]+=sz[pRoot];
        }else{
            parent[qRoot]=pRoot;
            sz[pRoot]+=sz[qRoot];
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41479464/article/details/88312511