目录
❤️什么是并查集?
实现方法1
实现方法2
题目1
并查集是一种数据结构,用于处理一些不交集(Disjoint sets,一系列没有重复元素的集合)的合并及查询问题。
常见的两种操作:
-合并两个集合; //并
-查找某元素属于哪个集合; //查
-用编号最小的元素标记所在的集合;
-定义一个数组 Set[1..n] ,其中 Set[i] 表示元素 i 所在的集合;
不相交集合:{1 , 3 , 7} , {4} , {2, 5, 9, 10} , {6, 8}
每个元素 i 用数组的下标表示,集合中保存的元素是集合中元素的标记。
例如:第一个不相交集合{1 , 3 , 7},这个集合的标记为最小的元素 1,则图中Set[1]、Set[3]、Set[7]都保存这个集合的标记 1。(就相当于1是集合{1 , 3 , 7}的队长,2是集合{2, 5, 9, 10}的队长)
-查找元素:时间复杂度O(1)
find_1(x){
return Set[x];
}
例如:查找元素9是哪个集合则返回Set[9],也就是标记为2的集合。
-合并操作:时间复杂度O(N)
merge_1(a, b){
i = min(a, b);
j = max(a, b);
for(int k=1; k<=N; k++){
if(Set[k] == j)
Set[k] = i;
}
}
例如:合并两个集合{1 , 3 , 7} , {4},执行merge_1(1, 4),用两个集合的标记的较小值重新标记每个元素。
-每个集合用树来表示;
对于数组Set[1..n],Set[i] = i,则 i 就是队长,也就是树根;Set[i] = j,且 j != i,则 j 就是 i 的上级,也就是父节点。
-查找元素:最坏情况的时间复杂度O(N)
find_2(x){
r = x;
while(Set[r]!=r)
r = Set[r];
return r;
}
例如:查找元素10的大队长,10的上级是4,4的上级是,2的上级是他自己,那么2就是大队长。
-合并操作:时间复杂度O(1)
void merge_2(a, b){
Set[a] = b;
}
-避免最坏情况:上面说到,查找的最坏情况时间复杂度是O(N),如何避免最坏情况?
方法:降低查找深度,将深度低的树合并到深度高的树。如下图所示:第一种合并结果高度为4,第二种合并结果高度为3。
实现:时间复杂度O(1)
merge_3(a, b){
if(height(a)==height(b)){
height(a) = height(a)+1;
Set[b] = a;
}
else if(height(a)
效果:最坏情况的时间复杂度变为O(logN)
Problem - 1232https://acm.hdu.edu.cn/showproblem.php?pid=1232AC代码:
#include
using namespace std;
int city[1001];
int findBoss(int a){
int boss = a;
while(city[boss]!=boss){
boss = city[boss];
}
return boss;
}
void changeBoss(int a, int b){
int aBoss = findBoss(a);
int bBoss = findBoss(b);
if(aBoss != bBoss){
city[aBoss] = bBoss;
}
}
int main (){
int N, M, x, y, number;
while(cin >> N, N){
number = -1;
for(int i=1; i<=N; i++)
city[i] = i;
cin >> M;
for(int i=0; i> x >> y;
changeBoss(x, y);
}
for(int i=1; i<=N; i++)
if(city[i]==i) number++;
cout << number << endl;
}
return 0;
}