目录
1.并查集概念与定义
并查集就是将一些不相交的集合,不断的归并、查询的一种数据结构。
举个例子:在一个群聊中,每个人不仅都是独立的个体,有的还会因为血缘,亲缘,地缘等关系而联系到一起形成一个个小群。而并查集就是根据一些条件“建立”小群并查询的过程。
在这个例子中,比如说小明是这个群里其中一个,那么根据血缘关系,他的父亲可以看作是他的“前辈”,而小明的学弟可以把小明当作“前辈”,同时小明的学弟的“前辈”也可以是小明的父亲,所以总的来说他们两个“小辈”的“前辈”都是小明的父亲。所以说经过不同的关系可一最终决定所有人的“前辈”是谁,然后可以进行查询或者计算“后辈”的数量等操作。
2.并查集常见操作
2.1 find操作的实现
find(查询“前辈”操作)是并查集中最重要的操作。下来给一段伪代码:
#include <bits/stdc++.h>
using namespace std;
const int M=1e7+9;
int f[M];//定义一个数组来表示当前人的“前辈”是谁
int find(int k){
if(f[k]==k)return k;//如果他的“前辈”是他自己的话直接返回他本身
return f[k]=find(f[k]);//否则返回最更高一级的“前辈”
}
int main() {
return 0;
}
注释中已经给好了解释也不必再多说,就是一个不断递归的过程。这个find是经过优化过的一般来说是不优化的话每一个人的“前辈”就只是上一辈,优化过后就将所有此人的“后辈”的“前辈”都化为他。
2.2 归并操作的实现
一般我自己是将归并操作直接写到main函数中,但大多数较为简洁的做法就是将其放到函数里进行实现。下面放一下函数的模板:
void join(int x,int y) //让y的“前辈”成为x的“前辈”
{
int a=find(x), b=find(y); //分别找到他们两个人的“前辈”
if(a != b) //如果他们的“前辈”不为同一人
f[a]=b; //就将y的“前辈”化为x的“前辈”
}
伪代码中注释已经很清楚了所以不过多解释。
2.3 初始化数据的实现
首先,在一切操作进行之前,要先对f[i]数组进行初始化使得每个人的前辈都是他自己,伪代码如下:
void init(int n) //初始化函数,对录入的 n个结点进行初始化
{
for(int i = 1 ;i < =n; i++){
f[i] = i; //每个结点的上辈都是自己
}
}
每道题要求不同因此初始化的代码也会有所区别,此模板只是一个示例,具体题目具体编译。
3.并查集例题
3.1 P3367 【模板】并查集
题目来源:P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,MN,M ,表示共有 NN 个元素和 MM 个操作。
接下来 MM 行,每行包含三个整数 Zi,Xi,YiZi,Xi,Yi 。
当 Zi=1Zi=1 时,将 XiXi 与 YiYi 所在的集合合并。
当 Zi=2Zi=2 时,输出 XiXi 与 YiYi 是否在同一集合内,是的输出 Y
;否则输出 N
。
输出格式
对于每一个 Zi=2Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
输入输出样例
输入:
4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4输出:
N Y N Y
说明/提示
对于 30%30% 的数据,N≤10N≤10,M≤20M≤20。
对于 70%70% 的数据,N≤100N≤100,M≤103M≤103。
对于 100%100% 的数据,1≤N≤1041≤N≤104,1≤M≤2×1051≤M≤2×105,1≤Xi,Yi≤N1≤Xi,Yi≤N,Zi∈{1,2}Zi∈{1,2}。
本题就是一道非常基础的并查集模板题具体做法就不必多说了看完代码就会了。
#include<bits/stdc++.h>
using namespace std;
const int M=10009;
int n,m,x,y,z,f[M],ans;
int find(int k){
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
cin>>z>>x>>y;
int a=find(x),b=find(y);
if(z==1){
f[a]=b;
}else{
if(a==b) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
return 0;
}
3.2 P2256 一中校运会之百米跑
题目来源:P2256 一中校运会之百米跑 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目背景
在一大堆秀恩爱的 ** 之中,来不及秀恩爱的苏大学神踏着坚定(?)的步伐走向了 100100 米跑的起点。这时苏大学神发现,百米赛跑的参赛同学实在是太多了,连体育老师也忙不过来。这时体育老师发现了身为体育委员的苏大学神,便来找他帮忙。
可是苏大学神需要热身,不然跑到一半就会抽(筋)、于是他就找到了你。。。如果你帮助体育老师解决了问题,老师就会给你 55 个积分。
题目描述
假设一共有 NN(2≤N≤2×1042≤N≤2×104)个参赛选手。(尼玛全校学生都没这么多吧)
老师会告诉你这 NN 个选手的名字。
接着会告诉你 MM(1≤M≤1061≤M≤106)句话,即告诉你学生 A 与学生 B 在同一个组里。
如果学生 A 与学生 B 在同一组里,学生 B 与学生 C 也在同一组里,就说明学生 A 与学生 C 在同一组。
然后老师会问你 KK(1≤K≤1061≤K≤106)句话,即学生 X 和学生 Y 是否在同一组里。
若是则输出 Yes.
,否则输出 No.
。
输入格式
第一行输入 NN 和 MM。
接下来 NN 行输入每一个同学的名字。
再往下 MM 行每行输入两个名字,且保证这两个名字都在上面的 NN 行中出现过,表示这两个参赛选手在同一个组里。
再来输入 KK。
接下来输入 KK 个体育老师的询问。
输出格式
对于每一个体育老师的询问,输出 Yes.
或 No.
。
输入输出样例
输入:
10 6 Jack Mike ASDA Michel brabrabra HeHe HeHE papapa HeY Obama Jack Obama HeHe HeHE brabrabra HeHe Obama ASDA papapa Obama Obama HeHE 3 Mike Obama HeHE Jack papapa brabrabra输出:
No. Yes. Yes.
本题与上一题不同的是他需要将对应字符串(姓名)的下标用函数算出来,然后再进行合并和查询操作,代码如下:
#include<bits/stdc++.h>
using namespace std;
const int M=100009;
int n,m,k;
string a[M],f[M];
int turn(string name){
for(int i=1;i<=n;i++){
if(name==a[i]) return i;
}
}
string find(string name){
if (f[turn(name)]==name) return name;
return f[turn(name)]=find(f[turn(name)]);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
f[i]=a[i];
}
for(int i=1;i<=m;i++){
string o,b;
cin>>o>>b;
f[turn(find(o))]=find(b);
}
cin>>k;
for(int i=1;i<=k;i++){
string c,d;
cin>>c>>d;
if(find(c)==find(d)) cout<<"Yes." <<endl;
else cout<<"No."<<endl;
}
return 0;
}
4. 总结
总的来说,并查集的用法,针对性较为强,主要是用于理清各个事物的等级关系。
这就是我对并查集的理解。
感谢鉴赏,留个赞再走吧( ̄▽ ̄)~■□~( ̄▽ ̄)。