引言
并查集(Union-Find Set),顾名思义,是实现快速合并集合与查询元素所在集合的数据结构。
它在包括但不限于最小生成树Kruskal算法的众多算法中有很好的使用效果。
结构
形象地看,并查集是一个类似于森林的结构,同一个集合的元素都连在同一棵树上,查询操作即寻找树的根节点(唯一编码),合并即将一个集合的根连在另一棵树上。
抽象地看,这是一个记录父节点的数组(优化算法还包括这个集合的元素数量)。
性质
- 该数组记录父节点编号。
- 每个集合的根节点编号是其本身。
操作
- 查询元素所在集合
根据性质,只需要依照沿着这个集合的树向上爬即可。 - 合并两个元素所在的集合
根据操作1找到两个根节点后连接即可。
代码实现
基础版
//////////////////////////////////////////////////////////////////////
//Target: To implement the Union-Find Set
//@Author: Pisceskkk
//Date: 2019-2-24
//////////////////////////////////////////////////////////////////////
#define Max_N (int)1e6
int n,f[Max_N];
void init(int n){
for(int i=1;i<=n;i++){
f[i] = i;
}
}
int find(int x){
return (f[x) == x)?x:find(f[x]);
}
bool mege(int x,int y){
x = find(x);
y = find(y);
if(x == y) return 0;
f[x] = y;
return 1;
}
问题来了:在特殊情况下,这棵树可能是一条长长的链。设链的最后一个结点为x,则
每次执行find(x)都会遍历整条链,效率十分低下。看上去是个很棘手的问题,其实改进方法
很简单。既然每棵树表示的只是一个集合,因此树的形态是无关紧要的,并不需要在“查
找”操作之后保持树的形态不变,只要顺便把遍历过的结点都改成树根的子结点,下次查找
就会快很多了。
——《算法竞赛入门经典》刘汝佳