主要概念:
1.将两个集合合并。
2.询问两个元素是否在一个集合当中。
复杂度近乎O(1)
基本原理:每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。
问题1:如何判断树根: if (p[x] == x)
问题2:如何求x的集合编号:while(p[x] != x) x = p[x];
问题3:如何合并两个集合:px是x的集合编号,py是y的集合编号。p[x] = y
优化:路径压缩:查到祖先之后就把px存成祖先。
1.朴素并查集:
核心代码如下:
int p[N]; //存储每个点的祖宗节点
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
拓展
2.维护size的并查集
int p[N]; //存储每个点的祖宗节点
int size[N];//size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for(int i = 1; i <= n; i++)
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
size[b] += size[a];
3.维护到祖宗节点的距离的并查集
int p[N], d[N];//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}