zoukankan      html  css  js  c++  java
  • 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的值
            }
        }
    }
    

      

  • 相关阅读:
    Convert CString to std::string
    VC 使用预编译头
    [转]Windows下使用doxygen阅读和分析C/C++代码
    [SCOI2016]背单词
    Linux配置日志服务器
    网络学习day02_OSI七层模型及数据的传输过程
    网络学习day04_VLSM、子网划分
    XSS闯关游戏准备阶段及XSS构造方法
    网络学习day03_IP地址概述与应用
    网络学习day01_计算机网络与分层思想
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/10746822.html
Copyright © 2011-2022 走看看