2021-01-21 ACM训练笔记

Day2:并查集基础

零、简介:

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
所谓并查集算法就是对不相交集合进行如下两种操作:

(1)检索某元素属于哪个集合;
(2)合并两个集合。

一、一种基础的并查集实现思路:

1、定义一个整型数组保存节点之间的父子关系,数组下标即为节点编号,所保存的数据即为该节点对应的父节点编号。
2、初始化:遍历数组,将所有节点的父节点设为它本身,此时,所有的树都仅包含一个节点。(也可将所有节点的父节点编号设为一个“不存在”的节点编号,例如-1。)
3、合并:当满足某种条件,某两个节点可以合并到一棵树中时,将其中一个节点所在的树(称为树A)的根节点的父节点设为另一节点所在的树(称为树B)的根节点。显然,在合并操作中,被合并的树A的深度增加了1。(通常为了保证算法的执行效率,应当尽量将较浅的树并入较深的树。)
4、若要在并查集的节点中存储其他数据,可以使用结构体数组来替换上述的整型数组,此时,每个节点的父节点编号存储在结构体的一个整型字段中。

二、基础实现代码(以整型数组为例):

	数组定义:int fa[N+1];
	初始化,令每个节点单独成树:
	void init() { 
    	for(int i = 1; i <= n; ++i) fa[i] = i;
	}
	递归寻找节点所在树的根节点:
	int find(int x) {
    	return fa[x] == x? fa[x]: find(fa[x]);
	}
	合并两节点所在的树(不考虑性能优化):
	void merge(int x, int y) {
    	int fx = find(x);
    	int fy = find(y);
    	if(fx != fy) fa[fx] = fy;
	}

三、优化:

按元素个数优化:为了优化合并操作,我们引入一个新的size[]整型数组,用size[i]表示以编号为i的节点为根的树的元素个数。在进行合并操作时比较两树的元素个数,将较小的树并入较大的树,并更新较大的树的元素个数。
	优化后的合并函数:
	void merge(int x, int y) {
    	int fx = find(x);
    	int fy = find(y);
    	if (fx != fy) {
        	if (size[fx] < size[fy]) {
            	fa[fx] = fy;
            	size[fy] += size[fx];
        	}else {
            	fa[fy] = fx;
            	size[fx] += size[fy];
        	}
    	}
	}
其他优化:
按秩(即树的深度)的大小进行的优化合并:其中单个元素秩定义为0;当两棵秩同为r的树联合时,他们的秩为r+1;将秩较小的树并入秩较大的树,新的树与较深的树同秩。
路径压缩:执行"查找"时扁平化树结构的方法,路径上每个节点都可以直接连接到根上。根节点查找函数find()递归地经过树,改变每一个节点的引用到根节点,得到的树更加扁平,为以后直接或者间接引用节点的操作加速。
路径压缩时的find()函数实现:
int find(int x) {
	return fa[x] == x? fa[x]: fa[x] = find(fa[x]);
}

四、时间复杂度:

并查集进行n次查找的时间复杂度是O(n )(执行n-1次合并和m≥n次查找)。其中 是一个增长极其缓慢的函数,它是阿克曼函数(Ackermann Function)的某个反函数。它可以看作是小于5的。所以可以认为并查集的时间复杂度几乎是线性的。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值