并查集原理及C++实现

本文深入浅出地介绍了并查集的基本概念,包括自反性、对称性和传递性,展示了其在求连通分支数上的重要作用。讲解了如何通过fa[]数组和秩合并、路径压缩优化find和union操作,以及使用实例和模板题来巩固理解。
摘要由CSDN通过智能技术生成

并查集是什么

并查集就如字面上,是一个具有合并(union)与查找(find)功能的集合。它的本质是一个等价类(具有自反性、对称性、传递性的图)。

(该段是自反、对称、传递的说明,已学过的可以跳过)

通俗地解释下自反、对称以及传递,如果每个人都和自己互为亲人,即“我是我的亲人”,那么可以称这是一种自反的;假如我是你的亲人,那么也可以说你是我的亲人,这样的关系称为对称的,如果只是单方面的,比如我喜欢你,你不喜欢我,这样的就不能称之为对称的;你是我的亲人,他是你的亲人,他也是我的亲人,即"我的亲人的亲人还是我的亲人",这样的关系称为传递的


作用: 并查集的主要作用是求连通分支数(如果一个图中所有点都存在可达关系(直接或间接相连),则此图的连通分支数为1;如果此图有两大子图各自全部可达,则此图的连通分支数为2……)

并查集的实现

由于满足自反、对称zhao、传递,那么对于并查集中所有点,查找他们的“亲人”的“亲人”若干次,总会落到同一个点上(所有人都可以成为这个点),我们称这些点是同一个集合,并且将该集合结构构造成一棵树,将任意一个共同“亲人”作为根节点,查找(find)功能就是要查找根节点。

对于这样的树结构,我们是这样构造的,设置一个fa[]数组用于存储当前点的父节点,对于根节点,他的父节点就是他自己即fa[root]=root一直查找+

find函数的实现

对于当前点一直向上找下去直到满足fa[root]==root,即找到根节点。

int find(int x){
	while(fa[x] != x)			
		x = fa[x];				
	return x;			
}

union函数的实现

若为同一个根,则已在集合中,无需添加。

void union(int x,int y){
	int r1=find(x);
	int r2=find(y);bin
	if(r1!=r2)
		fa[r1]=r2;
} 

并查集的优化

按秩合并

按秩合并中的秩说白了就是树的深度,因为find函数的复杂度是取决于树的深度的,所以我们希望将秩小的合并到秩大的根上去,尽可能减少秩增大的可能,以减少树的深度。优化体现在合并(union)操作上。

void union(int x,int y){
	int r1=find(x);
	int r2=find(y);
	if(r1!=r2){
		if(rank[r1]<rank[r2]){
			fa[r2]=r1;
		}
		else{
			if(rank[r1]==rank[r2]) rank[r2]++;
			fa[r1]=r2;
		}
	}
} 

路径压缩

在并查集中实际上我们并不关心某个节点的父亲是谁,或者他的父亲的父亲是谁。我们只在乎他的根节点是谁,因此我们根据“我的爸爸的爸爸是我的爸爸”的方法,将根节点下所有的点直接与根节点连接,这样就可以极大的减少树的深度。优化体现在查找(find)功能上。

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

模板题

P3367

题解;

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define go(i,l,r) for(int i=l;i<=r;i++)
#define rgo(i,r,l) for(int i=r;r>=l;i--)
const int maxn(10000+10);
int fa[maxn],rank[maxn];
int n,m;
//求父亲 
int father(int x){
	if(x==fa[x]) return x;
	if(x!=fa[x]){
		fa[x]=father(fa[x]);
	}
	return fa[x];
}
//建立联合
void Union(int x,int y){
	int r1=father(x);
	int r2=father(y);
	if(rank[r1]<rank[r2]){
		fa[r2]=r1;
	}
	else{
		if(rank[r1]==rank[r2]) rank[r2]++;
		fa[r1]=r2;
	}
} 
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    go(i,1,n) fa[i]=i;
    go(i,1,m){
    	int z,x,y;
    	cin>>z>>x>>y;
    	if(father(x)!=father(y)){
    		if(z==1) Union(x,y);
    		else cout<<"N"<<endl;
    	}
    	else {
    		if(z==2) cout<<"Y"<<endl;
    	}
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒商

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值