并查集:
在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
并查集hdu所有题目(点击)
思路:
原始
int find(int a)
{
int r = a;
while(s[r] != r) //寻找根结点
r = s[r];
return r;
}
void memge(int x,int y)
{
x = find(x);
y = find(y);
if(x!=y) //为了不杂乱,此处可以设置大的赋给小的,判断条件改一下
{
s[x] = y;
}
}
优化1
寻找祖先时采用递归,但是一旦元素一多起来,或退化成一条链,每次找先祖都将会使用O(n)的复杂度,这显然不是我们想要的。对此,我们必须要进行路径压缩,即我们找到最久远的祖先时“顺便”把它的子孙直接连接到它上面。这就是路径压缩了。使用路径压缩的代码如下,时间复杂度基
本可以认为是常数的。
路径压缩可以采用迭代和递归方式递归方式实现简单但是有些题目会爆栈的。
//递归形式的路径压缩
int find(int a)
{
if(a==f[a]) return a;
return f[a]=find(f[a]); //当查询时,若这个时子节点,那就一直会递归到祖先,然后回溯时给每一个递归过的结点的父节点都变成祖先
}
//迭代形式的路径压缩
int find(int a)
{
int p = a, t;
while (f[p] != p) p = f[p];//跟递归不同的是,通过循环先找到祖先p,然后再接着改结点
while (a!= p)//赋值
{
t = f[a];
f[a] = p;
a = t;
} //路径压缩
return a;
}
这样的优化会随着查找变得更优化。
优化2
按高度合并
就是将比较矮的树作为子树,添加到较高的树中。为了保存高度,需要额外使用一个与 f 同长度的数组,并将所有元素都初始化为 0。这样找祖先会减少递归迭代的次数,最坏只有logN次。
void Merge(int x,int y)
{
int t1=find(x),t2=find(y);
if(t1==t2) return ;//已合并返回
if(height[t1]>height[t2])
{
f[t2]=t1; //把y的祖先t2和并到x的祖先t1上,因以t1为根的树更高
}
else
{
f[t1]=t2;
if(height[t1]==height[t2])
height[t2]++; //若两树一样高,那么合并后,高度加一
}
}