树-并查集-实现和应用
并查集
定义
并查集是一种维护集合的数据结构,并:Union
,查:Find
,集:Set
。
并查集支持下面两种操作:
- 合并:合并两个集合
- 查找:判断两个元素是否在一个集合
并查集使用一个数组实现
int father[N];
其中 father[i] 代表元素 i 的父亲节点,而父亲节点本身也是这个集合内的元素。
如果 father[i] == i
说明元素 i 是该集合的根结点,但是对于同一个结合来说只存在一个根结点,且将其作为所属集合的标识
并查集的基本操作
一个实现
初始化father
数组,但我们通常还会初始化另外一个辅助的数据结构 height[N]
, 来记录每个结点的深度,以使得我们的并查集的效率不会太差,不会生成一个非常长高的单链。
初始化
#define N 1001
int father[N]; //记录父节点
int height[N]; //记录每个节点的高度
void Initial(int n)
{
for (int i = 0; i <= n; i++)
{
father[i] = i; // 初始化,父节点是自己
height[i] = 0; // 高度为0
}
}
查找
由于同一个集合只存在一个根结点,所以查找操作就是对给定的结点寻找其根结点的过程。
// 递归
int Find(int x)
{
if (x != father[x])
{
x = Find(father[x]);
}
return x;
}
// 非递归
int Find(int x)
{
while (x != father[x])
{
x = father[x];
}
return x;
}
合并
把两个集合合并成一个,思路:
- 首先判断两个元素是否是同一个集合,就是看二者是否有相同的根结点
- 如果不是同一个集合,那么把一个集合的根结点的父亲指向另一个集合的根结点
- 我们一般会将比较矮的集合(树)指向比较高的树
void Union(int x, int y)
{
x = Find(x);
y = Find(y);
if (x != y)
{
if (height[x] < height[y])
{ // 矮树添加到高树上
father[x] = y;
}
else if (height[x] > height[y])
{
father[y] = x;
}
else
{
father[y] = x;
height[x]++;
}
}
}