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]);
}