其他分享
首页 > 其他分享> > 20220724与佬谈并查集

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\)看着极致的压行洋洋自得时,大佬发问:

image

问题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

image

\(\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呢?

image

笨但勤快的\(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\)

image

image

[完结撒花]

标签:shanzr,查集,int,myFind,10010,20220724,fa,include
来源: https://www.cnblogs.com/shanzr/p/16513754.html