注释版:
/* 普通版(无路径压缩) */
int pre[1000];
int find(int x) // 查找我(x)的掌门
{
int r=x; // 委托 r 去找掌门
while(pre[r]!=r) // 如果r的上级不是r自己(也就是说找到的大侠他不是掌门 = =)
r=pre[r]; // r 就接着找他的上级,直到找到掌门为止。
return r; // 掌门驾到~
}
/* 升级版(有路径压缩) */
int pre[1000];
int find(int x) // 查找根节点
{
int r=x;
while( pre[r]!= r ) // 返回根节点 r
r=pre[r];
int i=x, j;
while( i != r ) // 路径压缩
{
j = pre[i]; // 在改变上级之前用临时变量 j 记录下他的值
pre[i] = r ; // 把上级改为根节点
i = j;
}
return r ;
}
void join(int x,int y) // 我想让虚竹和周芷若做朋友
{
int fx=find(x),fy=find(y); // 虚竹的老大是玄慈,芷若MM的老大是灭绝
if(fx!=fy) // 玄慈和灭绝显然不是同一个人
pre[fx]=fy; // 方丈只好委委屈屈地当了师太的手下啦,注意这里谁是谁的root,以及这里是 x 的祖先成为 fy 的子孙,而不是 x 成为 fy 的子孙
}
简化版:
int pre[1000];
int find(int x) // 普通最简版
{
return pre[x]==x ? x : pre[x]=find(pre[x]);
}
int find(int x) // 优化版
{
int r=x;
while( pre[r]!= r )
r=pre[r];
int i=x, j;
while( i != r )
{
j = pre[i];
pre[i] = r ;
i = j;
}
return r ;
}
void join(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx]=fy;
}
应用:
/* 统计 root 结点个数(必要时适当修改) */
int cnt=0;
for(int i=1;i<=n;i++)
if(pre[i]==i) // 有局限性,前提确保在 n 的范围内,而且一定会出现[1,n]
cnt++;
/* 统计每个 root 的内部总数(必要时适当修改) */
int rs=0;
for(int i=1;i<=n;i++)
{
int rt=find(i);
vis[rt]++;
rs=max(rs,vis[rt]);
}
常见问题:
- 并查集中,不能参与统计 / 运算操作,因为会出现中途连接之前断开的结点,这样之前的计算就会有误差,所以要并查集完了之后,再做这类操作。