周知,并查集学习过程中有朴素做法和路径压缩做法,之前一直不明白即使是朴素做法为什么会有形成一条很长的链的情况,而且讲解者一直不讲实例,只是画出一颗很长的链。
先把两种不同的模板放上:
朴素
int find(int x)
{
if(x==fa[x]) return x;
return find(fa[x]);
}
... ...
main:
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
int u=find(x),v=find(y);
if(u!=v)//合并
fa[u]=v;
}
路径压缩:
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
... ...
main:
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
int u=find(x),v=find(y);
if(u!=v)//合并
fa[u]=v;
}
递归解释路径压缩:
下面代码样例:
5 4
4 5
3 4
2 3
1 2
在求find(x),x的父节点时,顺带将该路径上的点的父节点更新了,从而达到路径压缩。比如,下面代码中find(5)这一步,就可以使每个节点的father更新成1,
下次再find(4)时,if(x!=father[x])father[x]=find(father[x]);
那么4的父节点是1,下次递归就出来了。
而不用4->3->2->1这样寻找了。
#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 20001
int father[maxn];
int m,n,i,x,y,q;
/*
int find(int x) //用非递归的实现
{
while (father[x] != x) x= father[x];
return x;
}
*/
int find(int x) //用递归的实现
{
if (father[x] != x) father[x] = find(father[x]); //路径压缩
return father[x];
}
void unionn(int r1,int r2)
{
father[r2] = r1;
}
int main()
{
// freopen("relation.in","r",stdin);
// freopen("relation.out","w",stdout);
cin >> n >> m;
for (i = 1; i <= n; i++)
father[i] = i; //建立新的集合,其仅有的成员是i
for (i = 1; i <= m; i++)
{
cin >> x >> y;
int r1 = find(x);
int r2 = find(y);
if (r1 != r2) unionn(r1,r2);
}
// cin >> q;
// for (i = 1; i <= q; i++)
// {
// cin >> x >> y;
// if (find(x) == find(y)) cout << "Yes" << endl;
// else cout << "No" << endl;
// }
***find(5);***
for(int i=1;i<=n;i++)
cout<<i<<","<<father[i]<<endl;
return 0;
}