并查集
并查集
介绍:
并查集是一种高效处理集合的数据结构,可以执行元素所在集合的查找,集合的合并等等,是一种非常高效的算法,常常与其他算法结合来高效的解决问题。
基本操作:
- 合并(Union):合并两个集合。
- 查询(find):查询元素所在集合。
具体实现:
并查集是一种树形结构,在实际操作中我们将每一个集合用一颗树来代表,而树的标志就是根节点。用一组数据举例:
1,2,3,4,5,6,7,8;最开始他们自己各自为一个集合,对于两个基本操作,先说查找,我们将树根作为标记节点,所以对于每个数据我们都去查找他的根节点即可,所以他的find操作为:
int find(int x){
int now=x;
while(now!=father[now]){
now=father[now];
}
return now;
}
对于合并操作,因为一个集合的代表为树的根节点,所以当合并两个集合时,我们先进行判断两个元素所在集合是否为一个,不为一个集合的话,我们只需要将其中一个集合的根接到另一个集合的根上即可将这两个集合合并:
void uniongroup(int x,int y){
int boot1=find(x);
int boot2=find(y);
if(boot1!=boot2){
father[boot1]=boot2;
}
return;
}
对于以上两部分代码,存在一个严重的效率问题,观察之后我们会发现,如果经过一些操作后。一棵树变成了一条长长的单链,那么查询操作将会变得十分的缓慢,同时由于合并操作也会用到查询才做,所以算法效率是十分不够的,所以引入路径压缩的做法。
路径压缩:
对于一颗树,在并查集里我们并不关心路径如何,我们只关心这颗树有哪些节点,以及这颗树的根节点是谁,所以我们可以将非根节点间的路径全部取消,将每一个非根节点都连接到根节点上,这样我们每次的查询几乎都是O(1),这回大大提高效率,具体做法就是,我们每查询一个节点的所在树根节点,我们都在查询过程中不断更改节点的位置,为了便于修改,我们采用递归的方式。
int Find(int x){
if(x==father[x])
return x;
return father[x]=Find(father[x]);
}
这样Find每次返回的都是根节点,所以我们同时更新了x点的父节点为根节点,这就相当于把节点直接连到了根节点上,这样下次的查询就变得相当快捷了。同理只需要将uniongroup中的find函数替换为Find操作即可。
void uniongroup(int x,int y){
int boot1=Find(x);
int boot2=Find(y);
if(boot1!=boot2){
father[boot1]=boot2;
}
return;
}
例题:
题目描述
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
输入格式
第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式
P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
样例
输入
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出
Yes
Yes
No
/******************************************
/@Author: LeafBelief
/@Date: 2020-03-07
/@Remark:
/@FileName: bingchaji
******************************************/
#include <bits/stdc++.h>
#define CSE(x, y) memset(x, y, sizeof(x))
#define lowbit(x) (x & (-x))
#define INF 0x3f3f3f3f
#define FAST \
ios::sync_with_stdio(false); \
cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int maxn = 11111;
int father[maxn];
int Find(int x)
{
if (x == father[x])
return x;
return father[x] = Find(father[x]);
}
void uniongroup(int x, int y)
{
int boot1 = Find(x);
int boot2 = Find(y);
if (boot1 != boot2)
{
father[boot1] = boot2;
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
FAST;
int n, m, p;
cin >> n >> m >> p;
for (int i = 1; i <= n; i++)
father[i] = i;
for (int i = 0; i < m; i++)
{
int x, y;
cin >> x >> y;
uniongroup(x, y);
}
for (int i = 0; i < p; i++)
{
int x, y;
cin >> x >> y;
if (Find(x) == Find(y))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}