概念
1.并查集:用于解决元素分组问题,管理一系列不相交的集合
2.两个关键函数:合并 + 查询
3.思想:
f[i]:存i的父节点,令根节点的f[i]等于其本身
合并时,将其中一个根节点的父节点设为另一个节点的根节点即可
查询时,根据f[]判断,f[a] == f[b]
朴素版
int f[n];
//初始化,所有节点都看成根节点
void init()
{
for(int i = 0;i < n;i ++) f[i] = i;
}
//查询
int find(int x)
{
if(f[x] == x) return x;
else return find(f[x]);
}
//合并
void merge(int a,int b)
{
f[find(a)] = find(b);
}
路径压缩
在查询、合并时,都只需要关心根节点。所以,在查询时,将每个节点的父节点都设为根节点。
int find(int x)
{
if(f[x] == x) return x;
else
{
//在查询时,沿途将所有节点的父节点设置为根节点
f[x] = find(f[x]);
return f[x];
}
}
按秩合并
当一个深度为7的树和一个元素合并时,显然将一个元素合并到树上更合适,反之,会增加树的高度,增加复杂度
r[i] :以 i 为根节点的树的深度
1.初始化:r[ ] = 1
2.合并:
(1)找到各自的根节点
(2)根据根节点的r[]大小进行合并
(3)如果两个根节点的秩相同,且合并的两个节点不在一个集合上时,合并时,应将合并后根节点的秩+1
void init()
{
for(int i = 0;i < n;i ++)
{
f[i] = i;
r[i] = 1;
}
}
int merge(int a,int b)
{
int fa = find(a),fb = find(b);
if(r[fa] >= r[fb]) f[fb] = fa;
else f[fa] = fb;
//在秩相等时,处理成b合并到a上
if(r[fa] == r[fb] && a != b) r[fa] ++;
}