路径压缩
寻找祖先时采用递归,但是一旦元素一多起来,或退化成一条链,每次GetFather都将会使用O(n)的复杂度,这显然不是我们想要的。对此,我们必须要进行路径压缩,即我们找到最久远的祖先时“顺便”把它的子孙直接连接到它上面。这就是路径压缩了。使用路径压缩的代码如下,时间复杂度基
本可以认为是常数的。
路径压缩可以采用迭代和递归方式递归方式实现简单但是有些题目会爆栈的。
//递归形式的路径压缩
int getf(int v)
{
if(v==f[v]) return v;
return f[v]=getf(f[v]);
}
//迭代形式的路径压缩
int getf(int v) {
int p = v, t;
while (f[p] != p) p = f[p];//找到祖先p
while (v != p) { t = f[x]; f[x] = p; x = t; } //路径压缩
return v;
}
按秩合并
这里也可以应用一个简单的启发式策略——按秩合并。该方法使用秩来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将比较矮的树作为子树,添加到较高的树中。为了保存秩,需要额外使用一个与 uset 同长度的数组,并将所有元素都初始化为 0。这样找祖先会减少递归迭代的次数,最坏只有logN次。
void Merge(int x,int y)
{
int t1=getf(x),t2=getf(y);
if(t1==t2) return ;//已合并返回
if(rnk[t1]>rnk[t2]) f[t2]=t1; //把y的祖先t2和并到x的祖先t1上。因以t1为根的树更高
else {
f[t1]=t2;
if(rnk[t1]==rnk[t2]) rnk[t2]++; //若两树一样高,那么合并后,高度加一。
}
}