【模板】并查集

算法指南

1.初始化

2.find()函数的实现(查找)

3.join()函数的实现(合并)

初始化

1.将所有人的boss(祖先节点)设定为自己

code↓

	for(int i=1;i<=n;i++) pre[i]=i;

find()函数的实现

1.不断的向上查找自己的boss(员工->经理->CEO->董事长[boss])

在这里面:

find(员工)=董事长,find(经理)=董事长
find(CEO)=董事长,find(董事长)=董事长

但是我们在存储时,是这样的:

pre[员工]=经理,pre[经理]=CEO
pre[CEO]=董事长,pre[董事长]=董事长

如果我们直接去用while查找是这样的

code↓

	while(pre[x]!=x)//如果x不是自己的boss就运行,如果是那就结束。	
		x=pre[x];//继续进行查找,不断进行复制
	return x;//返回最后的结果
/*
a[1] a[2] a[3] a[4] a[5]
2    3    4    5    5

这个循环的解释是怎样的↓

假设我们的x=2;
x=2,a[x]不等于自己,于是x=a[2],x=3;
x=3,a[x]不等于自己,于是x=a[3],x=3;
x=4,a[x]不等于自己,于是x=a[4],x=3;
x=5,a[x]等于自己,于是终止循环,return x;

这种做法有优点也有缺点:

1.优点是可以进行回溯

2.缺点是需要的时间复杂度太高,会TLE

优化

如果我们要进行优化,就需要使用路径压缩递归

路径压缩的思路

1.并查集的路径压缩,就是直接将每个人都指向它的boss

pre[员工]=董事长,pre[经理]=董事长
pre[CEO]=董事长,pre[董事长]=董事长

code↓

	return pre[x]=find(pre[x]);

2.而我们使用递归就需要一个退出语句,也就是当自己是自己的boss时退出。

code↓

	if(pre[x]==x) return x;

总结起来find()函数就是这样的

code↓

int find(int x){
	if(pre[x]==x) return x;
	else return pre[x]=find(pre[x]);
}

如果需要将代码压缩就是这样的

code↓

int find(int x){
	return x == pre[x] ? x : (pre[x] = find(pre[x]));
}//如果(x==fa[x])=ture return x,否则 return (pre[x] = find(pre[x]))
优缺点

1.优点是时间复杂度

2.缺点是不能回溯,第一次进行查找没有压缩效果

join()函数的定义

1.将两个员工(节点)的boss(祖先)合并在一起:

设:有2个boss,分别为a,b
合并操作就是将a的boss改为b,或将b的boss改为a

【模板】并查集_第1张图片

join()函数的实现

1.给定的是两个员工(节点),要先求出他们的boss(祖先)

code↓

	int a=find(x);//a是x的boss
	int b=find(y);//b是y的boss

2.接下来,我们需要分成两种情况进行讨论

当a=b时,说明了他们的boss是同一个人,不需要进行合并
只有当a!=b时,他们的boss不同,需要进行合并

code↓

	if(a!=b){//如果x的boss不是y的boss,进行合并
		pre[find(b)]=find(a);//多进行一次查找,确定是哪一个boss
	}

3.函数的末尾return 0;

code↓

int join(int x,int y){
	int a=find(x);
	int b=find(y);
	if(a!=b){
		pre[find(b)]=find(a);
	}
	return 0;
}

并查集的实现

code↓

int find(int x){
	if(pre[x]==x) return x;
	else return pre[x]=find(pre[x]);
}
int join(int x,int y){
	int a=find(x);
	int b=find(y);
	if(a!=b){
		pre[find(b)]=find(a);
	}
	return 0;
}

例题:洛谷p3367【模板】并查集

code↓

#include 
using namespace std;
int pre[200050]={};
int z,x,y;
int find(int x){
	if(pre[x]==x) return x;
	else return pre[x]=find(pre[x]);
}
int join(int x,int y){
	int a=find(x);
	int b=find(y);
	if(a!=b){
		pre[find(b)]=find(a);
	}
	return 0;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) pre[i]=i;
	for(int i=1;i<=m;i++){
		cin>>z>>x>>y;
		if(z==2)
			if(find(x)==find(y)) cout<<"Y"<<endl;
			else cout<<"N"<<endl;
		if(z==1) join(x,y);
	}
	return 0;
}

你可能感兴趣的:(算法,算法,前端,c++)