题目传送门
本题是基本的写法就是并查集的基本的应用
c++代码
#include<algorithm>
#include<cstring>
using namespace std;
const int N =100000;
int n,m;
int p[N];
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main()
{
scanf("%d%d",&n,&m);
//首先先对每一个数列集合进行初始化
for(int i=0;i<n;i++) p[i]=i;
while (m -- ){
char op;
int a,b;
scanf("%s%d%d",&op,&a,&b);
if(op=='M')
p[find(a)]=find(b);//将两个集合合并
else{
if(find(a)==find(b)) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
算法基础课复盘
并查集的具有如下的两个作用:
1.将两个集合合并
2.询问两个元素是否在一个集合中
使用并查集的原因
假设用一个元素来存储某个元素属于哪一个集合:
belong[x]=a
判断两边元素是否在同一个集合中:
通常写作
if(belong[x]==belong[y])
通常其时间复杂度为o(1)
但上述的问题是如果数据很大,则暴力的情况会出现耗时
运用并查集可以完美的解决问题,使得在近乎o(1)
来完成操作
实现的基本思想
利用树的形式来维护所有的集合
那么每个集合的编号即为根节点的编号
在每个点上存储的是该点的父节点。
当想求某一个点属于哪一个集合的时候,从底部往上遍历,直到找到树根为止(当前元素的树根编号也就是集合的编号)
几个问题:
1.如何判断是不是树根
if(p[x]=x)//也就是说,当前节点的编号x与树中存储的编号p[x]是相同的,此时就到达了树根
2.如何求x的集合编号(判断两个元素是否在同一个集合中)
while(p[x]!=x)x=p[x];/一路往上走,如果当前的编号x与集合编号p[x]不相同,那么就进行更新编号(也就是处于的新的节点)直到`p[x]=x`
3.如何合并两个集合
加边:把右集合当作左集合的儿子或者把左集合当做右集合的儿子(插在另一个集合的某一个位置)。
假设p[x]
是集合x的编号,p[y]
是集合y的编号
合并:p[x]=y;直接更新集合x的编号,也就是把x插到y集合里。
如此以上的时间复杂度为o(n)
并查集优化:
并查集具有特殊的能力进行优化
一般情况是从树底一路走上去,而并查集在第一个找到树根之后,剩下的全部的则可以直接的指向树根,如下图所示:
这样使得时间复杂度变成了o(1)
另一个优化是按秩合并,但是由于效果并不明显,因此并不使用