Java 并查集Union Find

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

union

isConnected

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

  

public class UnionFind1 implements UF{
	private int[] id;
	public UnionFind1(int size){
		id=new int[size];
		for (int i = 0; i < id.length; i++) {
		     id[i]=i;
		}
	}
	@Override
	public int getSize(){
		return id.length;
	}
	//查找元素p所对应的集合编号
	private 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);
	}
	//合并元素p和元素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;
		}
	}

}

  由子数指向父的并差集

public class UnionFind2 implements UF {

	private int[] parent;
	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所对应的集合编号
	private int find(int p){
		if(p<0&&p>=parent.length)
			throw new IllegalArgumentException("p is out of bound.");
		while (p!=parent[p])
			p=parent[p];
		return p;	
	}
	//查找元素p和元素q是否所属一个集合
	@Override
	public boolean isConnected(int p,int q){
		return find(p)==find(q);
	}
	//合并元素p和元素q所属的集合
	@Override
	public void unionElements(int p,int q){
		int pRoot =find(p);
		int qRoot=find(q);
		
		if(pRoot==qRoot)
			return;
		parent[pRoot]=qRoot;
	}
}

  测试:

import java.util.Random;

public class Main {

	 private static double testUF(UF uf,int m ){
		 int size=uf.getSize();
		 Random random =new Random();
		 long startTime=System.nanoTime();
		 for(int i=0;i<m;i++){
			 int a=random.nextInt(size);
			 int b=random.nextInt(size);
			 uf.unionElements(a, b);
		 }
		 
		 for (int i = 0; i < m; i++) {
			 int a=random.nextInt(size);
			 int b=random.nextInt(size);
			 uf.isConnected(a, b);
		}
		 long endTime=System.nanoTime();
		 return (endTime-startTime)/1000000000.0;
	 }
	  
	 public static void main(String[] args){
		 int size=10000;
		 int m=10000;
		 
		 UnionFind1 uf1=new UnionFind1(size);
		 System.out.println("UnionFind1:"+testUF(uf1, m)+"s");
		 
	    UnionFind2 uf2=new UnionFind2(size);
	    System.out.println("UnionFind1:"+testUF(uf2, m)+"s");
	 }
} 

第三种:(size)

public class UnionFind3 implements UF{

    private int[] parent; // parent[i]表示第一个元素所指向的父节点
    private int[] sz;     // sz[i]表示以i为根的集合中元素个数

    // 构造函数
    public UnionFind3(int size){

        parent = new int[size];
        sz = new int[size];

        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for(int i = 0 ; i < size ; i ++){
            parent[i] = i;
            sz[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // 不断去查询自己的父亲节点, 直到到达根节点
        // 根节点的特点: parent[p] == p
        while( p != parent[p] )
            p = parent[p];
        return p;
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合并元素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;
            sz[qRoot] += sz[pRoot];
        }
        else{ // sz[qRoot] <= sz[pRoot]
            parent[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];
        }
    }

第四种:(rank)

public class UnionFind4 implements UF {

    private int[] rank;   // rank[i]表示以i为根的集合所表示的树的层数
    private int[] parent; // parent[i]表示第i个元素所指向的父节点

    // 构造函数
    public UnionFind4(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // 不断去查询自己的父亲节点, 直到到达根节点
        // 根节点的特点: parent[p] == p
        while(p != parent[p])
            p = parent[p];
        return p;
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合并元素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;

        // 根据两个元素所在树的rank不同判断合并方向
        // 将rank低的集合合并到rank高的集合上
        if(rank[pRoot] < rank[qRoot])
            parent[pRoot] = qRoot;
        else if(rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此时, 我维护rank的值
        }
    }
}

  第五种:(路径压缩)

public class UnionFind5 implements UF {

    // rank[i]表示以i为根的集合所表示的树的层数
    // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
    // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
    private int[] rank;
    private int[] parent; // parent[i]表示第i个元素所指向的父节点

    // 构造函数
    public UnionFind5(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        while( p != parent[p] ){
            parent[p] = parent[parent[p]];
            p = parent[p];
        }
        return p;
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合并元素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;

        // 根据两个元素所在树的rank不同判断合并方向
        // 将rank低的集合合并到rank高的集合上
        if( rank[pRoot] < rank[qRoot] )
            parent[pRoot] = qRoot;
        else if( rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此时, 我维护rank的值
        }
    }
}

  第六种:(递归)

public class UnionFind6 implements UF {

    // rank[i]表示以i为根的集合所表示的树的层数
    // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
    // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
    private int[] rank;
    private int[] parent; // parent[i]表示第i个元素所指向的父节点

    // 构造函数
    public UnionFind6(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // path compression 2, 递归算法
        if(p != parent[p])
            parent[p] = find(parent[p]);
        return parent[p];
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合并元素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;

        // 根据两个元素所在树的rank不同判断合并方向
        // 将rank低的集合合并到rank高的集合上
        if( rank[pRoot] < rank[qRoot] )
            parent[pRoot] = qRoot;
        else if( rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此时, 我维护rank的值
        }
    }
}

  

原文地址:https://www.cnblogs.com/sunliyuan/p/10746822.html