并查集

大家好,我们又回来了。

这次,我们带来的是并查集。

现在有n个人,每次会告诉我们哪两个人有关系,最后求输入的两个人有没有关系。

输入 a b 1 表示 a b 有关系

输入 a b 2 表示查询 a b 有没有关系

有的话输出YES,没有输出NO

  • 样例1:

INPUT:

xiebohong liliuyi 1

panyan lingyutong 1

chengchangcai huangtianyu 1

liliuyi lingyutong 1

huangtianyu panyan 1

chengchangcai panyan 2

panyan xiebohong 2


OUTPUT:

YES

YES

  • 样例2:

INPUT:

yangyibo hushihao 1

yangyibo tangkaixu 1

xiebohong caishiming 1

xiebohong liliuyi 1

yangyibo caishiming 2

xiebohong tangkaixu 2

caishiming liliuyi 2


OUTPUT:

NO

NO

YES


根据暴力出奇迹的原则,我们可以建一个图,当有两个人有关系时,我们就对他们连一条线,查询时就dfs一下看是否联通

不过这样复杂度无论时间还是空间都太大了。


然后,那个“好吃”的冰茶鸡并查集就出场了。。。


冰茶鸡并查集由两部分组成:每个元素以及它的父亲,也可以说是和它有关系的人中的“领袖”

假设:每个元素存放数组a中,每个元素的父亲存放在数组fa中,它是每个元素的父亲在a数组中的下标

它初始时就是这个样子的,每个元素的父亲都是自己

a 1 2 3 4
fa 1 2 3 4


然后,每当有两个元素有关系时,就把其中一个元素的父亲指向另一个。

这样每次我们要找到他的最终父亲是谁时,就可以找到他的父亲,再找到他父亲的父亲。最后当找到一个父亲是自己的元素时,我们就知道它就是我们要寻找的最终父亲了。为了下次寻找方便,我们把一路经过的所有点的父亲都修改成最终的父亲(这就是路压,路径压缩,后面会有,不是必要的,但可以加快速度,建议使用)。

看看动画

假设我们知道2 4有关系

然后我们的数组就变成了这样:

a 1 2 3 4
fa 1 2 3 2

把2的fa变成4还是把4的变成2是随机的,而且对结果不影响。


可能会有一点挤,链接:我们将会多次引用的Visualgo

最后,上代码

unionn函数:把a,y并在一起

find函数:找到x的父亲,有带路压,不带路压两种(路径压缩)

主函数。。。被吃了。。。


 1 int find(int x){//带路压
 2      if(fa[x]!=x)//不是最终父亲,即父亲是自己的元素
 3          fa[x]=find(fa[x])//递归更新,路径压缩。。。这里特别复杂,建议没有学过的人好好理解
 4      return fa[x];
 5 }
 6 
 7 int find(int x){//不带路压
 8 
 9     while(fa[x]!=x)
10 
11         x=fa[x];
12 
13     return x;
14 
15 }
16 
17 void unionn(int x,int y){//合并函数,带路压
18      int r1=find(x),r2=find(y);//找到xy的父亲,把其中一个父亲的父亲设置为另一个的父亲。
19      fa[r2]=r1;
20 
21     find(y);//路径压缩,把y的每个父亲及其父亲的父亲设置为改变后的父亲。
22 }
23 
24 void unionn(int x,int y){//合并函数,不带路压
25      int r1=find(x),r2=find(y);//找到xy的父亲,把其中一个父亲的父亲设置为另一个的父亲。
26      fa[r2]=r1;//不带路压
27 
28 }
posted @ 2018-08-10 13:51  OI-er  阅读(140)  评论(0)    收藏  举报