前言:
在前两次的博客中已经介绍了Quick-Find,Quick-Union算法,这两个算法或多或少都有不足,这一次我们学习最终豪华版的Union-Find算法。
Union-Find算法
在Quick-Union中我们已经采用了“树”的概念,但Quick-Find算法的union会出现极端情况,也就是产生一课光杆司令的树,这种树会极大的增加时间复杂度,不利于大的数据处理。所以我们在原有的基础上提出优化策略:就是我们在每次union之前先来判断一下两个树所在的“树”的大小,然后将小的树的根连接到大的树的根上。这样可以有效避免变态树的产生,有效的减少程序的时间复杂度。
我们定义一个数组size[]来记录每个节点所在数的大小。
先上代码,依然是那熟悉的C++:
class quick_union
{
private:
//sz_array is used to record the size of connected components
int sz[10];
//the count of connected components
int cnt = 0;
public:
//initialize the ID_array and size array
void init(int *p_ID, int n){
cnt = n;
int i;
for(i = 0; i < n; i++){
p_ID[i] = i;
sz[i] = 1;
}
}
//find the root of numbers
int find(int *p_ID, int num){
while(p_ID[num] != num){
num = p_ID[num];
}
return num;
}
//connect two numbers' root
void union_num(int *p_ID, int x, int y){
int root_x;
int root_y;
root_x = find(p_ID, x);
root_y = find(p_ID, y);
if(connected(p_ID, x, y) == false){
if(sz[root_x] < sz[root_y]){
p_ID[root_x] = root_y;
sz[root_y] = sz[root_y] + sz[root_x];
}
else{
p_ID[root_y] = root_x;
sz[root_x] = sz[root_x] + sz[root_y];
}
cnt = cnt - 1;
}
}
//judge two numbers
bool connected(int *p_ID, int x, int y){
int root_x;
int root_y;
root_x = find(p_ID, x);
root_y = find(p_ID, y);
if(root_x == root_y){
return true;
}
else{
return false;
}
}
//cout the count of connected components
void display(){
cout << cnt << endl;
}
};
运行一下:
#include <iostream>
using namespace std;
int main()
{
int n, i;
int *p_ID;
quick_union f;
cout << "Please enter the count of numbers: ";
cin >> n;
p_ID = new int [n];
f.init(p_ID, n);
for(i = 0; i < n; i++){
cout << p_ID[i] << " ";
}
cout << endl;
f.union_num(p_ID, 2, 3);
f.union_num(p_ID, 1, 0);
f.union_num(p_ID, 0, 4);
f.union_num(p_ID, 5 ,7);
f.union_num(p_ID, 6, 2);
for(i =0; i < n; i++){
cout << p_ID[i] << " ";
}
cout << endl;
f.display();
return 0;
}
结果:
Please enter the count of numbers: 8
0 1 2 3 4 5 6 7
1 1 2 2 1 5 2 5
3
这里find的时间复杂度为O(1~logN),union的时间复杂度为O(1),为什么是1~logN呢?因为假如这个数连接的很矮,就只有一个根连着所有枝,那么只需要一步就可以找到根,而假如这棵树是一颗二叉树,则若有N个节点的话,那就需要logN次才能找到自己的根,也很快,不是么?
综合一下,如果是共有N个数,要连接其中M个,则最后时间复杂度为O(M*(1~logN))。
总结:
这样的算法就可以既Find的很快,又Union的很快,所以称为Find-Union算法。它可以应用于网络通信、数学集合、媒体社交等多个领域。
最后强烈推荐Coursera上普林斯顿大学的算法课点击打开链接
以上内容纯属个人学习总结,不代表任何团体或单位。若有理解不到之处请见谅!