并查集(Union-Find)
概述
并查集是一种高效的数据结构,以多棵树的形式存储,每棵树有一个被打上标记的根节点
主要用途:
- 合并两个集合
- 查询元素所属的集合
常见题型:
- 朴素并查集
- 带权并查集
- 维护size并查集
结构
并查集通常使用三个数组实现:
p[]
(parent)
数组:记录每个元素的父节点。d[]
(distance)
数组:记录每个元素到父节点距离size[]
数组:记录每个并查集的大小
ps:虽然并查集在逻辑结构上是一棵棵在同一集合的树,但是存储结构仍可以用普通的数组实现
操作
1.建立
初始化并查集
并查集根节点root
:的标记方式p[root] = root
表示根节点的父节点就是自己
void init()
{
// 一开始每个节点各自为营
for (int i = 1; i <= n; i ++ )
p[i] = i;
}
2.查找
查找元素 x
所在集合的根节点:
优化:利用递归在查找的时候就直接把每个节点的父节点压缩向了祖宗节点,此时距离d也更新为了到父节点(祖宗节点)的距离
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
// p[x]效果为找x的祖宗节点
// 这里调用p[x] = find(p[x])直接让x的父节点指向了p[x]的祖宗节点
// 经过层层递归会最终指向整棵树的根节点
int root = find(p[x]);
// 先要存root是因为只有先跑find()才能让d[p[x]]变为p[x]到祖宗节点的距离
d[x] += d[p[x]];
return root;
}
3.合并
合并两棵树:
void merge(int x, int y)
{
// 找出两棵树的根节点
int px = find(x), py = find(y);
// 根节点不同才需要合并
if (px != py)
{
p[px] = py;
size[py] += size[px];
// 可能还需要对d[px]进行操作,具体情形依题目而定
}
}
至此,并查集的基本操作均已介绍完毕,操作简洁作用大,用处这么大的数据结构上哪里找呀~
来几道例题尝试尝试~
例题
朴素并查集
维护size的并查集
维护到祖宗节点距离的并查集(带权并查集)
洛谷P1196 银河英雄传说