[随笔] 并查集
老师の问题
1.并查集适用场景,用于解决什么样的问题
并查集可以高效地进行集合的合并和查询操作;广泛应用于解决连通性问题;
2.并查集的原理是什么
用一个数组表示了整片森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素;
- 以下内容来自OI WIKI -
并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。(暂时不用理解)
并查集支持两种操作:
- 查询(Find):查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合
- 合并(Union):合并两个元素所属集合(合并对应的树)
并查集在经过修改后可以支持单个元素的删除、移动;使用动态开点线段树还可以实现可持久化并查集。
并查集的模板基本拥有以下三项内容:初始化、查询(Find)、合并(Union)
1.初始化
初始化father(或parent)数组。为了后续的操作,一般初始化并查集中一个节点的父亲节点为这个节点本身
1 void init(){ 2 for(int i=0;i<n;i++){ 3 fa[i]=i; 4 } 5 }
2.查询(Find)
查询当前节点的祖先,这里是基础代码
1 int find(int x){ 2 if(fa[x]==x){ 3 return x; 4 } 5 else{ 6 return find(fa[x]); 7 } 8 }
if语句判断当前节点所记录的父亲节点是否为本身,否则则一直向上寻找该节点的祖先
这里会有一个优化算法——路径压缩,我们递归寻找祖先的途中扫描过的节点肯定属于一个祖先(集合),那我们便可以将他们的祖先都设为目标查询的那一个祖先(连到根节点),这样就 可以加快后续的操作
1 int find(int x){ 2 if(fa[x]==x){ 3 return x; 4 } 5 else{ 6 fa[x]=find(fa[x]); 7 return fa[x]; 8 } 9 }
3.合并(Union)
合并两棵树(x和y),即合并两棵树的根节点,所以我们只需要find该节点(x)的祖先(根节点),然后将该节点(x)的祖先设为y的祖先(y设为x的祖先也可以)
1 void unionn(int x,int y){ 2 fa[find(x)]=find(y); 3 return ; 4 }
由并查集衍申的还有最小生成树Kruscal和Tarjan算法(还没搞懂。。。)
模板不难,看几遍就背会了
既然算法已经学完了,直接两件套——模板和例题
P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
很简单的一道例题,我直接放代码了
1 #include <bits/stdc++.h> 2 using namespace std; 3 int n,m; 4 int fa[100000]; 5 6 void init(){ 7 for(int i=0;i<n;i++){ 8 fa[i]=i; 9 } 10 } 11 12 int find(int x){ 13 if(fa[x]==x){ 14 return x; 15 } 16 else{ 17 fa[x]=find(fa[x]); 18 return fa[x]; 19 } 20 } 21 22 void unionn(int x,int y){ 23 fa[find(x)]=find(y); 24 return ; 25 } 26 27 int main(){ 28 cin>>n>>m; 29 init(); 30 for(int i=0;i<m;i++){ 31 int op,x,y; 32 cin>>op>>x>>y; 33 if(op==1){ 34 unionn(x,y); 35 } 36 if(op==2){ 37 if(find(x)==find(y)){ 38 cout<<"Y"<<endl; 39 } 40 else{ 41 cout<<"N"<<endl; 42 } 43 } 44 } 45 //system("pause"); 46 return 0; 47 }
P1551 亲戚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
同样一道学会基本并查集后秒杀的题,即先合并后查询,也是直接放代码了
#include <bits/stdc++.h> using namespace std; int fa[6000]; int n,m,p,m1,m2,p1,p2;
void init(){ for(int i=0;i<n;i++){ fa[i]=i; } }
int find(int x){ if(fa[x]==x){ return x; } else{ fa[x]=find(fa[x]); return fa[x]; } }
void unionn(int a,int b){ fa[find(a)]=find(b); return ; }
int main(){ cin>>n>>m>>p; init(); for(int i=0;i<m;i++){ cin>>m1>>m2; unionn(m1,m2); } for(int i=0;i<p;i++){ cin>>p1>>p2; if(find(p1)==find(p2)){ cout<<"Yes"<<endl; } else{ cout<<"No"<<endl; } } //system("pause"); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!