1、定义
并查集:“并”Union, “查”Find 集合。并查集是一种维护集合数据结构。支持一下两个操作。
(1)合并:合并两个集合
(2)查找:查找两个元素是否在同一个集合
2、数据结构
实际上就只用一个数组就行了。farther[MaxSize]。
p[x] = y; 代表的是x的父亲是y。
3、函数实现
void Init(int n);
int FindFarther(int x);
void Union(int a, int b);
3.1 初始化
void Init(int n){
for(int i = 1; i <= n; i++){
farther[i] = i;
IsRoot[i] = false;
}
}
3.2 合并
void Union(int a, int b){
int Fa = FindFarther(a);
int Fb = FindFarther(b);
if(Fa != Fb){
farther[Fb] = Fa;
}
}
3.3 查找父节点
int FindFarther(int x){
int a = x;
while(x != farther[x]){
x = farther[x];
}
//压缩路径
while(a != farther[a]){
int z = a;
farther[a] = x;
a = farther[z];
}
}
输入:
7 5
1 2
2 3
3 1
1 4
5 6
输出:
cal: 4
cal: 2
cal: 1
3
4、完整代码
#include<iostream>
#define MaxSize 110
using namespace std;
int farther[MaxSize];
bool IsRoot[MaxSize] ={
false};
//计算每个集合中有多少个节点
int cal[MaxSize] = {
0};
void Init(int n);
int FindFarther(int x);
void Union(int a, int b);
int main(){
int n,m;
cin >> n >> m;
Init(n);
while(m--){
int a , b;
cin >> a >> b;
Union(a,b);
}
for(int i = 1; i <= n; i++){
IsRoot[FindFarther(i)] = true;
cal[FindFarther(i)]++;
}
for(int i = 1; i<=n; i++){
if(cal[i] !=0){
cout << "cal: " << cal[i] << endl;
}
}
int count = 0;
for(int i = 1;i <= n; i++){
count += IsRoot[i];
}
cout << count << endl;
return 0;
}
void Init(int n){
for(int i = 1; i <= n; i++){
farther[i] = i;
IsRoot[i] = false;
}
}
void Union(int a, int b){
int Fa = FindFarther(a);
int Fb = FindFarther(b);
if(Fa != Fb){
farther[Fb] = Fa;
}
}
int FindFarther(int x){
int a = x;
while(x != farther[x]){
x = farther[x];
}
//压缩路径
while(a != farther[a]){
int z = a;
farther[a] = x;
a = farther[z];
}
}
5、小结
并查集的实现和静态链表的实现类似,数组的下标代表着当前的节点。初始化为了让每个节点的父节点为本身。一整套Union完成以后,所有的数据都被分成几个类别了(因为每个节点的父节点都已经确定了)。在Findfarther函数中,“压缩路径”的目的是让每个节点的上一个节点直接是父节点。这种存储方式是的每次查找节点父节点的时候的时间复杂度为O(1)。如果不进行这一步的优化,每次查找父节点的时间复杂度为O(n)。