1.并查集是什么?
用一个例子来说明。。。
江湖不是打打杀杀,是人情世故。在一个江湖中,有许许多多的人物张三,李四,王二,麻子。为了方便起见用编号 1 2 3 4 5 6 7 8 9…
初始时,他们互不相识
某一天,2号和7号一见如故,又某一天 4和5号一见钟情,7号和8号义结金兰等等等…
随着时间发展,因为某种原因江湖乱了起来,腥风血雨。这天4号和7号相遇了,4号和7号也不知道对方是不是自己朋友的朋友,万一是自己人动手可就不太好了
于是武林关系谱诞生了,当2和7为朋友时,在谱上划一条由2到7的箭头(7到2也可以,没有影响),这样将关系表示出来,我们就可以知道是不是有关系了。
可江湖人太多了,如果按这样的方式表示的话,查一个人的关系可能要一直查到其路径尽头,显然太复杂了,如果能优化就好了。
于是,我们可以在一群人中选出带头大哥来代表,若有x和y是朋友,只要让x的带头大哥和y的带头大哥有建立关系就可以了。
具体步骤如下:
初始时,互不相识,每个人都是自己的带头大哥(初始化操作)
2和7是朋友 4和5是朋友
现在有这种关系并入 (加入):7和8是朋友,先找7的带头大哥2,8的带头大哥8,2和8建立联系。(合并操作)
合并4 8,同样的操作
合并5 3,这一步最为不同,也最为关键。找5的带头大哥4,4找到2。返回时,可以利用递归将得到的带头大哥号返回给所有路径上的结点,这一步叫做路径压缩。结合代码来理解:
int find(int x){
if(x!=fa[x])
fa[x]=find(fa[x]);
return fa[x];
}
//上述操作如下
find(5)-->5不是带头大哥,find(4)-->4不是带头大哥,find(2)-->2是带头大哥,2返回上一层
-->设fa[4]=2,2返回上一层-->设fa[5]=2--返回2结束
2和3建立联系
补充一点:是先路径压缩了再合并,而不是合并了再路径压缩。所以合并4和8后没有立刻路径压缩,而是合并5 3 时再压缩
2.算法步骤代码
2.1 初始化
void init(){
for(int i=1;i<=n;i++)
fa[i]=i;
}
2.2 查找祖宗并统一祖宗
//找祖宗并同统一祖宗
int find(int x){
if(x!=fa[x])
fa[x]=find(fa[x]);
return fa[x];
}
2.3合并操作
void Union(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
b=fa[a];
}
}