并查集——实例一到四 从简单到理解(一)

下一篇文章,大概的一些优化和,统一的理解重要代码

可以在这写问题上用并查集

连接问题

 

网络中节点间的链接状态

 网络是个抽象的概念:用户之间形成的网络

 连接问题和路径问题

比路径问题要回答的问题少

和堆作比较

 

并查集的基本数据表示

编号分为0和1,只要编号相同就是,同种

 

看下代码吧,今天学到这里,

首先创建接口方法,在实现接口

 

package com.binglian.UnionFind;


public interface UF {

	int getSize();
	boolean isConnected(int p,int q);
	void unionElements(int p,int q);
}

数组来实现

初始化

 

private int[] id;
	
	/**
	 * 初始化
	 * @param size
	 */
	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元素的集合编号

    //查找元素p所对应的集合编号
	private int find(int p){
		if(p < 0 && p>=id.length)
			throw new IllegalArgumentException("边界不合法");
		return id[p];
	}

判断是否是同一种集合

用find获取编号进行判断

//查看元素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 那么id[i]=pId;
				id[i]=qId;
	}

整体的并查集实例代码一

package com.binglian.UnionFind;

/**
 * 第一版本的并查集(Union-Find)
 * @author binglian
 *
 */
public class UnionFind1 implements UF {

	private int[] id;
	
	/**
	 * 初始化
	 * @param size
	 */
	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("边界不合法");
		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 那么id[i]=pId;
				id[i]=qId;
	}
}

第二个版本

package com.binglian.UnionFind;

/**
 * 第二个版本 Quick Union
 * @author binglian
 *
 */
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所对应的集合编号
	 * O(h)复杂度,h为树的高度
	 * @param p
	 * @return
	 */
	private int find(int p){
		
		if(p < 0 && p>=parent.length)
			throw new IllegalArgumentException("下标不合法");
		
		
		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所属的集合
	 * 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;
	}
}

 

第三种版本

package com.binglian.UnionFind;

/**
 * 第三个版本 Quick Union 基于的size优化
 * @author binglian
 *
 */
public class UnionFind3 implements UF{

	private int[] parent;
	private int[] sz;	//sz[i]表示以根的集合中元素个数
	
	
	
	public UnionFind3(int size){
		
		parent=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){
		
		if(p < 0 && p>=parent.length)
			throw new IllegalArgumentException("下标不合法");
		
		
		while(p !=parent[p]){
			parent[p]=parent[parent[p]];
			p=parent[p];
		}
		return p;
	}

	
	//查找元素p和元素q是否所属一个集合
	@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]){//pRoot 小于的话 就指向pRoot中
			parent[pRoot] = qRoot;
			sz[qRoot] +=sz[pRoot];//计算元素多少
			
		}else{//反之亦然	sz[qRoot]<=sz[pRoot]
			parent[qRoot] =pRoot;
			sz[pRoot] +=sz[qRoot];//计算元素多少
		}
		
	}
}

第四版本

 

package com.binglian.UnionFind;

/**
 * 第四个版本 Quick Union 基于的size优化
 * rank[i]表示根节点为i的树的高度
 * @author binglian
 *
 */
public class UnionFind4 implements UF{

	private int[] parent;
	private int[] rank;	//rank[i]表示以i为根的集合所表示的树的层数
	
	
	
	public UnionFind4(int size){
		
		parent=new int[size];
		
		for(int i=0;i<size;i++){
			parent[i]=i;
			rank[i]=1;
		}
	}
	
	@Override
	public int getSize(){
		return parent.length;
	}

	
	
	/**
	 * 查找过程,查找元素p所对应的集合编号
	 * O(h)复杂度,h为树的高度
	 * @param p
	 * @return
	 */
	private int find(int p){
		
		if(p < 0 && p>=parent.length)
			throw new IllegalArgumentException("下标不合法");
		
		
		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所属的集合
	 * O(h)复杂度,h为树的高度
	 */
	@Override
	public void unionElements(int p,int q){
		int pRoot=find(p);
		int qRoot=find(q);
		
		if(pRoot == qRoot)
			return ;
		
		//根据两个元素所在树的元素个数不同判断合并方向
		//将元素个数少的集合合并到元素个数多的集合上
		if(rank[pRoot] < rank[qRoot])//如果pRoot元素高度小于 就指向高的元素 pRoot=qRoot
			parent[pRoot] = qRoot;//pRoot 指向qRoot
		else if(rank[qRoot] < rank[pRoot])//反之亦然 q小于p qRoot就指向pRoot parent[qRoot]=parent[pRoot]
			parent[qRoot]=pRoot;//qRoot 指向pRoot
		else{//rank[qRoot] == rank[pRoot]
			parent[qRoot]=pRoot;
			rank[pRoot]+=1;//高度加1
		}
	}
}

第五个版本

// 我们的第五版Union-Find
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的值
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值