20220724与佬谈并查集
作者:互联网
听佬一席话,胜刷一周题。要抓住一切机会和佬尬聊啊hhh
起源
和佬的尬聊中,偶然提及了并查集。学了一周算法的\(shanzr\)自信地打出他最喜欢的板子:
int fa[10010];
int myFind(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];}
void myUnion(int a,int b){fa[myFind(a)]= myFind(b);}
很快啊,十来秒完事。
正当\(shanzr\)看着极致的压行洋洋自得时,大佬发问:
问题1 路径真的压缩了吗
\(shanzr\)根据看到的题解,不加思索地回答:这个就是优化掉了递归方法压缩路径,避免爆栈。
然后佬贴出了代码,大概是:
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
int fa[10010];
int myFind(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];}
int main(){
for(int i=2;i<=100;i++)fa[i]=i-1;//构造一个链,这是并查集搜索时最糟糕的情况
fa[1]=1;
myFind(100);//从100开始寻父
for(int i=2;i<=100;i++)cout<<fa[i]<<" \n"[i%10];//打印父亲。如果真的路径压缩,那么fa应该压缩为1
}
打印结果为:
1 2 2 4 4 6 6 8 8 10
10 12 12 14 14 16 16 18 18 20
20 22 22 24 24 26 26 28 28 30
30 32 32 34 34 36 36 38 38 40
40 42 42 44 44 46 46 48 48 50
50 52 52 54 54 56 56 58 58 60
60 62 62 64 64 66 66 68 68 70
70 72 72 74 74 76 76 78 78 80
80 82 82 84 84 86 86 88 88 90
90 92 92 94 94 96 96 98 98
啊这,不太对劲。
再看看传统递归:
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
int fa[10010];
int myFind(int x){
if(x!=fa[x])
return fa[x]= myFind(fa[x]);
}
int main(){
for(int i=2;i<=100;i++)fa[i]=i-1;
fa[1]=1;
myFind(100);
for(int i=2;i<=100;i++)cout<<fa[i]<<" \n"[i%10];
}
打印结果为:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
这下知道谁厉害了QAQ
\(\rm orz\)实践出真知
另外还学了一手,注意到代码中的
for(int i=2;i<=100;i++)cout<<fa[i]<<" \n"[i%10];
" \n"[i%10]
的结构就是string[index]
,其中string
=" \n"
,index
=i%10
。
合理使用这个语句,可以轻松处理换行。
问题2 有人秩都写错了,我不说是谁
根据一周所学,\(shanzr\)又想到了秩。或许有了秩,while
循环的表现就会好很多?他信心满满,三两下捣鼓出来了一串\(\rm bug\)。
错误太蠢了就不放这了。贴一下正确的测试代码:
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
int fa[10010];
int rk[10010];
int myFind(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];}
void myUnion(int x,int y){
x=myFind(x),y=myFind(y);
if(x==y) return ;
if(rk[x]<=rk[y]) fa[x]=y;
else fa[y] = x;
if(rk[x]==rk[y]) rk[x]++;
}
int main(){
fa[1]=1;
for(int i=1;i<=100;i++)rk[i]=1;
for(int i=2;i<=100;i++){
myUnion(i-1,i);
}
myFind(100);//从100开始寻父
for(int i=2;i<=100;i++)cout<<fa[i]<<" \n"[i%10];//打印父亲。如果真的路径压缩,那么fa应该压缩为1
}
打印结果:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
好!
问题3 返回值?
还有一个问题,为啥myFind()
不需要显式地return
呢?
笨但勤快的\(shanzr\)开始测试,代码如下:
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
int test1(){
int a=100;
}
int test2(){
int a=0;
a+=100;
}
int test3(){
int a,b;
a=b=100;
}
int main(){
cout<<"test1:"<<test1()<<endl;
cout<<"test2:"<<test2()<<endl;
cout<<"test3:"<<test3()<<endl;
}
打印结果:
test1:376741376
test2:376741376
test3:100
可见,只有a=b
赋值后才能正确传回我们需要的数。再次\(\rm orz\)
[完结撒花]
标签:shanzr,查集,int,myFind,10010,20220724,fa,include 来源: https://www.cnblogs.com/shanzr/p/16513754.html