并查集

并查集算法中,为了避免发生树的退化,利用了两个重要的优化思想:

优化一:

对于每棵树,记录这棵树的高度(rank)。

合并时如果两棵树的rank不同,那么从rank小的向rank大的连边。

优化二:

路径压缩——对于每个节点,一旦向上走了一次节点,就把经过的这个点携带着一起指向最后的根节点。(路径压缩时,为了简单起见,即使树的高度发生了变化,我们也不修改rank的值)。

/*
 * 在这里,“树”与“集合”同义,“根节点”与“祖先”同义
 */
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	static int n,m;//节点数,边数
	static int[] f;//根
	static int[] rank;//树的高度
    
    static void init(){//初始化
    	n=sc.nextInt();
    	m=sc.nextInt();
    	f=new int[n];
    	rank=new int[n];
    	for(int i=0;i<n;i++){
    		f[i]=i;
    		rank[i]=1;
    	}
    }
    
    static int find(int x){//递归得到x的根节点
    	if(f[x]==x)
    		return x;
    	else
    		return f[x]=find(f[x]);//路径压缩:函数返回时,顺带把路上遇到的节点的根节点改为最后找到的根节点
    }
    
    static void merge(int x,int y){//合并x和y所属的集合
    	x=find(x);
    	y=find(y);
    	if(x!=y){//不属于同一集合,合并
    		if(rank[x]<rank[y])
    			f[x]=y;//矮树向高树合并,(高树的)高度不变
    		else{
    			f[y]=x;//矮树向高树合并,(高树的)高度不变
    			if(rank[x]==rank[y])//合并前两棵树高度相等,合并后高度一定加1
    				rank[x]++;
    		}
    	}
    }
    
    static boolean same(int x,int y){//判断x和y是否属于同一个集合
    	return find(x)==find(y);
    }
    
    public static void main(String[] args) {
    	init();
    	for(int i=0;i<m;i++)
    		merge(sc.nextInt(),sc.nextInt());
    }
}

时间复杂度:

加入了这两个优化后的并查集效率非常高。对n个元素的并查集进行一次操作的复杂度是O(α(n))。在这里,α(n)是阿克曼函数的反函数,比O(log(n))还要快

不过,这是“均摊复杂度”。也就是说,并不是每一次操作都满足这个复杂度,而是多次操作后平均每一次操作的复杂度是O(α(n))的意思。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值