[随笔] 并查集

老师の问题

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; }
复制代码
复制代码

参考网站:并查集 - OI Wiki (oi-wiki.org)

posted @   qrrrrr  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示