并查集(模版)洛谷P3367b
1、算法主要操作
包括查Find(x)和合并Union(x,y)两个操作
2、算法优化
(1)查操作的路径压缩
Find(x)中一次循环找到x所在树的根结点,第二次循环,修改结点的par[]值,使得查找路径上的所有结点的父亲结点都变为这个集合的根结点,从而实现了路径压缩。
(2)合并操作的小树合并到大树中
设置一个数组height[i]表示根结点为i的集合的所有结点个数
在合并操作时,比较两颗树的height值,将值小的树的根结点的par[]修改为值大的根结点。下面是个例子,忘记树的双亲表示法,再去看看王道书。
对应模版题目洛谷P3367题
#include<bits/stdc++.h>
using namespace std;
const int MAXcount = 100000;
int par[MAXcount],height[MAXcount];
//par[i]表示i结点的父结点 “par[i]=-1”表示为根结点
//height[i]表示以i为根结点的树的结点个数
int Find(int x)//返回x的父亲结点
{
int ini = x;
while(1){
if(par[x]==-1){
//朴素
// return x;
break;
}
x=par[x];
}
//优化--路径压缩,再重复一次查找根结点操作,
//每查找完一次父亲结点,
//就将该结点的父亲par[]设置为该树的根结点
//(也就是上一次循环查找到的x)
while(1){
if(par[ini]==-1){
break;;
}
int zj = ini;
ini=par[ini];
par[zj]=x;
}
return x;
}
void Union(int x,int y)
{
int rootx,rooty;
rootx=Find(x);rooty=Find(y);
if(rootx!=rooty){
//如果根结点不同,进行合并操作
//朴素--从x和y集合的根结点中随机选取一个作为另一个集合的根结点,完成合并操作
// par[rooty]=par[rootx];
//优化--选择树高小的合并到树高(集合的结点个数)大的根结点下
if(height[rooty]>=height[rootx]){
//如果y集合的结点数大于x集合的结点数,则将x集合合并到y结合的根结点下
par[rootx]=rooty;
height[rooty]+=height[rootx];//同时更新树高
}else{
par[rooty]=rootx;
height[rootx]+=height[rooty];
}
}
}
int main()
{
int N,M;
memset(par,-1,sizeof(par));//初始化为-1,每个结点刚开始都是一个集合,所以其父结点为-1,表示其为根结点
memset(height, 0, sizeof(height));//初始化数组为0
cin>>N>>M;
for(int i=0;i<M;i++){
int z,x,y;
cin>>z>>x>>y;
if(z==1){
Union(x,y);//合并集合x和y
}else{
int roox=Find(x);//查找x结点的父亲结点
int rooy=Find(y);//查找y结点的父亲结点
if(roox==rooy)cout<<"Y"<<endl;//比较两结点的父亲结点是否相同,可知是否在同一集合之中
else cout<<"N"<<endl;
}
}
}