导入
老伙计,如果我问一个问题,你爷爷的女儿的姐姐的表妹的儿子和你爷爷的儿子的表弟的姐姐的女儿有没有血缘关系(其中关系都是血亲关系),你会怎么判断?
需要画一棵负责的家族图谱树来判断吗?
不需要!
可以很快判断出来,他们是有血缘关系的,因为他们都有一个共同的祖宗——你的爷爷。
这就是并查集(Union-find set)的核心思维。
理解
定义:
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。(来自百度)
我的理解:并查集解决的问题是,在一个错综复杂的可传递的关系中,通过指定同一祖宗的方式,过滤了在该情景下无用的中间关系,实现了信息的提炼。
C++实现
环境:
系统:
win10
IDE:Visual Studio 2022
//输入第一行:关系数n
//后续n行输入关系
//输出为集合数
//Ufset的中5代表并查集的大小,可以根据情景自行修改。
//关系输入要统一升序或降序,如:1 2/2 3不可1 2/3 2
#include<iostream>
#include<string>
using namespace std;
class UfSet {
private:
int size=5;
int fa[5];
public:
UfSet(int n);
void init();
void Union(int x, int y);
int Find(int z);
int Count();
};
UfSet::UfSet(int n) {
for (int i = 0; i < size; i++)
{
fa[i] = i;
}
}
void UfSet:: init() {
}
int UfSet::Find(int z) {
if (z != fa[z]) {
fa[z] = Find(fa[z]);
return fa[z];
}
else {
return fa[z];
}
}
int UfSet::Count() {
int ans=0;
for (int i = 0;i < 5;i++) {
if (i == fa[i]) {
ans++;
}
}
return ans;
}
void UfSet::Union(int x, int y) {
int _x = x;
int _y = y;
int a = Find(_x);
int b = Find(_y);
if (a != b) {
fa[b] = a;
}
}
int main()
{
//Union-Find Set 大小
UfSet ufset(5);
//输入的关系数
int n;
cin >> n;
while (n--) {
int x, y;
cin >> x;
cin>> y;
ufset.Union(x, y);
}
cout << ufset.Count() << endl;
return 0;
}
谢谢你的阅读