并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
并查集是由一个数组pre[]
,和两个函数构成的,一个函数为find()
函数,用于寻找前导点的,第二个函数是join()
用于合并路线的
find:
//路径压缩
int find(int k)
{
if(pre[k]==k)return k;
return pre[k]=find(pre[k]);
//return pre[k]==k?k:pre[k]=find(pre[k]);
}
路径压缩为了加快查找的速度,将x点与其根节点直接相连,构造成类似于只有叶子结点而没有分支结点的树。但是在树形结构中,一旦发生退化的现象,复杂度就会变得很高,为了避免这种情况,我们用一个rank来记录树的高度,rank小的向rank大的边连
union:
void join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)//如果x,y不是相同的根节点,则说明xy不是连通的
{
if(rank[x]<rank[y]) pre[x]=y;//我们将xy相连 将x的前导结点设置为y
else
{
pre[y]=x;
if(rank[x]==rank[y]) rank[x]++;
}
}
}
初始化:
for(int i=0;i<n;i++)
{
pre[i]=i;
}
我们将每一个结点的前导结点设置为自己,如果在join函数时未能形成连通,将独立成点