目录
1、find_root寻根函数,这个函数是寻找当前节点所在集合的代表元素。
一、并查集的介绍:
1、为什么要使用并查集
并查集可以简化算法设计、提高运行效率,并且适用于一些需要处理集合连通性问题的场景,是一种非常有用的数据结构。
不使用并查集时合并或查询集合的普通暴力解法时间复杂度普遍在o(n),而使用之后并查集之后,通过使用路径压缩和按秩合并等技术(在之后的find_root函数中有体现),可以实现近似常数时间的平均复杂度。
2、并查集的数据结构
在并查集中,我们有若干个集合,每个集合都有一个代表数字,我们通过递归的方式可以找到这个集合中每个集合的root节点,每个集合的root节点都是一样的
类似这样,集合中每个节点都指向1,1指向他自己,因此这个集合中的代表数字就是1。
或是这样
通过递归一层指向一层,最终节点就是1
二、并查集的代码介绍
1、find_root寻根函数,这个函数是寻找当前节点所在集合的代表元素。
int find_root(int i){
if(i==a[i]){
return i;
}
return a[i]=find_root(a[i]);
}
return a[i]=find_root(a[i]); 的原因是在每次寻根时可以把这个节点直接指向代表节点,这样在后续的查询是可以节省时间
P1:
P2:
以这两个图为例同样是5要寻找他的代表节点P1只需要寻找一次,而P2要一层一层向上找寻找3次,因此这个return a[i]=find_root(a[i]); 是路径压缩 的一种方式,在查询量大或集合中元素多时可以大大提高查询效率。
2、集合的合并
若现在输入u,v两个数,假设这两个数属于同一个集合,但是find_root(u)!=find(v),因此我们可以知道,u所在的集合,和v所在的集合是一个集合,因此我们要合并他们,即a[find(u)]=find(v)让u所在集合的的代表元素指向v所在集合代表元素,即完成集合的合并
if(find_root(u) != find_root(v)){
a[find_root(u)] = find_root(v);
}
三、例题推荐
1、 链接
牛客【模版】https://ac.nowcoder.com/acm/problem/226370
2、题目
3、完整代码
#include<bits/stdc++.h>
const int N=1e5+1;
using namespace std;
int a[N], cnt[N];
int find_root(int i){
if(i==a[i]){
return i;
}
return a[i]=find_root(a[i]);
}
int main(){
int n, m;
cin>>n>>m;
for(int i=1; i<=n; i++){
a[i] = i;
cnt[i] = 1;
}
int connected_components = n, max_num = 1;
while(m--){
int u, v;
cin>>u>>v;
if(find_root(u) != find_root(v)){
cnt[find_root(v)] += cnt[find_root(u)];
max_num = max(max_num, cnt[find_root(v)]);
connected_components--;
a[find_root(u)] = a[find_root(v)];
}
}
cout<<connected_components<<" "<<max_num;
return 0;
}