并查集要用到的概念和一些人性化的例子很容易查找到。
其thought core 是,要集合里的元素代表集合,可以用各成一派,各自为战来人性的表达。
所以第一步很水到渠成,设定成员函数使传入的元素自己等于自己(根节点等于自己,自己就是父节点。
同时我引入一个第一原则,就是两个树合并,树高度高的或者结点多的更适合做根节点,如果你不遵守这个原则,你的代码时间复杂度就会增加,虽然不一定change数量级,但仍不推荐这样做,所以我们先引入一个rank数组,你可以叫他秩或者等级,随便什么都可以,他是为了帮我们的编译器在合并树的时候找到性价比最高的路线,
int father[maxn],
int rank[maxn]
inline void init(int n)
{
for(int i=1;i<=n ++i)
{
father[i]=i;
rank[i]//
}
如上是这个程序的初始化操作
接下来则要编写有关查找集合内某个元素的根节点的成员函数。
int findRootElement(int X)
{
return father[i]==x?x:findRootElement(father[x]);
}
这里是路径压缩,你可以把它近似理解成,为了下次查找方便,让查找过的元素指向根节点,这样他就从原本下面的层数一跃而到此树的第二层了,
inline void compress(int x)
if(x==father[x])
return x;
else
father[x]=compress(father[x])
return father[x]
}
然后就是合并,这也是我前文提到的,因为考虑到合并的倾向性,这与我前文提到的rank密切相关,你可以理解他为倾向等级更specialized的说法叫秩,我们暂时把他理解成合并时寻找最优线路的一个对照物。有意思的是,我们可以选择使用其他的方法替代我刚刚所说的,比如使用结点数量来作为合并时寻找最优线路的对照物,这里我们只说前者。
想象一个集合,一开始他们各自是各自的根节点(各自为战),后来经历一番厮杀过后,有两个最大的门派诞生了,他们的小弟为了跟老大近一些,与是纷纷吧各自的信物交给老大,这也就是都指向了根节点的人性化说法更specialized的说法叫路径压缩。
那么如何
inline void merger(int i,int j)
{
int x=findRootElement[i],y=findRootElement[j];
if(rank[x]<=rank[y])
father[x]=y;
else
father[y]=x;
if(rank[x]=rank[y]&&x!=y)
rank[y]++/
到这里也就结束了,值得注意的是,合并后原来的老大也成了小弟,所以要rank的值应该加1,因为深度增加了,她不再是第一层,而是第二层,她原本的小弟依旧指向它,只不过不再是1的深度,而是2,因为曾经的老大已经是别人小弟,深度加1变为2
在启动函数里面,要善用路径压缩和树的合并,这点尤为重要。