并查集
大家好,我们又回来了。
这次,我们带来的是并查集。
现在有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 }


浙公网安备 33010602011771号