并查集用来管理元素分组情况的数据结构。
1.初始化集合。数组fa记录每个节点的父节点编号,初始时各元素单独形成一个集合,fa[x]=x,x是所在树的根。另设rank数组记录根节点的秩,可以是树高度,也可以是集合内元素个数。
void init(int n)
{
for(int i=0;i<n;i++)
{
fa[i]=i;
rank[i]=1;
}
}
2.查找元素所属集合。沿着树向上走,找到该树的根元素。为了避免出现退化,这里采用路径压缩的优化方法,将查询过程中向上经过的所有节点都改为直接连到根节点上,这样在之后查询这些节点时可以很快找到根节点。
int find(int x)
{
if(fa[x]!=x)
fa[x]=find(fa[x]);
return fa[x];
}
//非递归方法
int find(int x)
{
int r = x;
while (fa[r] != r) //查找根节点r
r = fa[r];
int y;
while (x != r) //路径压缩算法
{
y = fa[x]; //记录x当前父节点
fa[x] = r; //将根节点设为x父节点
x = y;
}
return r;
}
3.合并集合。先找出x,y各自所属集合的根节点,若不属于一个集合则进行合并,将一个组的根连向另一个组的根。可采用启发式合并的优化:根据根节点rank[]记录的秩,将rank小的根向rank大的根连边。
void unite(int x,int y)
{
x=find(x),y=find(y);//各自根节点
if(x==y) return;
if(rank[x]>rank[y])
fa[y]=x;
else
{
fa[x]=y;
if(rank[x]==rank[y]) rank[y]++;//高度+1
}
}
判断是否属于同一集合:
bool same(int x,int y)
{
return find(x)==find(y);
}
并查集平均复杂度为O(α(n))。