并查集分为合并查找两个部分。它是一种树形的结构,通常用于解决如下的问题:
问题1:在一个区域内有很多个城镇,它们之间的道路错综复杂,但是也会有某两个城镇之间不通路的情况,问:需要修建多少条路可以保证所有的城镇都连通。
问题2:武林江湖有各式各样的门派,各个门派间有很多的门人,但他们可能互不相识,却有着相同的掌门,江湖规矩门派之间不得内斗,但是门派之间往往可以大打出手,问:如何判断两个人之间能不能动手。
以上的问题都可以抽象为一个图的连通性问题,但是使用图的数据结构往往会导致时间空间复杂度很高,这个问题还可以看作一个集合之间是否有共同成员的问题。
以门派为例,将明教教主作为根节点,坐下四大法王各成一系,每一系再统领五行尊者,九大护法等等,这样会形成一个庞大的树状结构,但是问题来了,一些低等级的小兵并不认识教主是谁,他只知道他的上司是谁,因此他遇到另一个小兵时由于二人的上司不同而大打出手,但他们又都是明教中人。
因此这样规定,每次遇到人时依次向上级打听,直到教主为止,如果是同一个教主则不打,否则开打。
这样的一个过程我们称之为查找:
int Find(int x)//小兵x
{
int i=x;
while(i!=pre[i])//pre[i]称为i的上级
i=pre[i];//一直找上级,直到其上级是自己
return i;//掌门人
}
好了,目前两个同门是不会打架了,又来了一个这样的问题,有两个人是发小,不过一个是武当的,一个是少林的,江湖规矩不同门派要厮杀,不过这两个人是好朋友不想打架,因此我同意他们结成友好,但是这样会引起很大的问题,这两个门派间不能有任何的两个人打架。我作为一个武林至尊,少林武当掌门给我几分面子决定和平共处。如何和平共处呢?前面说到只有一派才能和平,因此我决定把两派合成一派,可以做一步最简单的操作,把少林掌门的上级修改为武当掌门(咳咳,别问我为什么不反过来,说实话我对秃驴应该不会太满意),好了皆大欢喜,这个操作我们称之为合并:
void join(int x,int y)
{
fx=Find(x);
fy=Find(y);
if(fx!=fy)
pre[fx]=fy;
}
至此武当派一统少林。
等等,感觉还有一个地方有些不舒适,就是现在这个大武当派啊人实在是太多了,武当三营六连七排八班的小兵王二小和武当七营九连三排二班的小兵王小二打一架还要问到张真人的头上,显得有点呆,如果这些小兵都可以直接归张真人管就好了(什么?管不过来,我管他呢。。。),这个过程我们称之为路径压缩。
int Find(int x)
{
int i=x;
while(i!=pre[i])
i=pre[i];
int j;
while(i!=x)//如果小兵x的上级不是掌门
{
j=pre[x];//先把小兵的上级记录下来,等会再找你算账
pre[x]=i;//把小兵的上级修改为掌门
x=j;//再把刚才的上级抓来继续找他的上级
}
return i;
}
至此武当派,千秋万代,掌门人弟子桃李三千。。。。累死你。。。
别急还有这个。。。。。。
问题来了,我们要修多少条路啊。。。。。。
草,我都当掌门了还要修路,容我来修。。。。
我们运用了查找合并的方法显然可以知道哪些路是连通的,它们之间的连通上下级的关系,那么我们可以把一条通路上的所有点看成一个根节点,于是我们修的路的数目即是根节点的数目减去1,使用标记的方式:
t[Find(x)]=1;这一步即可把x的所有上级都包含起来了为他的根节点,因此t数组的其他位置都不会为1.除非不是这条通路上面的,那就说明要修路啦!!!!!!
OK,讲到这里我掌门也当了,路也修好了,武林也太平了,所以。。。。。
并查集大家都明白了吧1!!!!