前言
在前两次的博客中已经介绍了Quick-Find,Quick-Union算法,这两个算法或多或少都有不足,这一次我们学习最终豪华版的Union-Find算法。
Union-Find算法
在Quick-Union中我们已经采用了“树”的概念,但Quick-Find算法的union会出现极端情况,也就是产生一课光杆司令的树,这种树会极大的增加时间复杂度,不利于大的数据处理。所以我们在原有的基础上提出优化策略:
union优化策略
就是我们在每次union之前先来判断一下两个树所在的“树”的大小,然后将小的树的根连接到大的树的根上。这样可以有效避免变态树的产生,有效的减少程序的时间复杂度。
我们定义根节点的ID值为负的size(树的大小)
find优化策略
路径压缩,find在由叶子节点向上寻找根的过程中将这一条枝上的叶子的ID改为根节点的ID,这样可以让树变得扁平,提高效率。
先上代码,依然是那熟悉的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] = -1;
}
}
//find the root of numbers
int find(int *p_ID, int num){
if(p_ID[num] >= 0){
return p_ID[num] = find(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(p_ID[root_x] > p_ID[root_y]){
p_ID[root_x] = root_y;
p_ID[root_y] = p_ID[root_y] + p_ID[root_x];
}
else{
p_ID[root_y] = root_x;
p_ID[root_x] = p_ID[root_x] + p_ID[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~logN),为什么是1~logN呢?因为假如这个数连接的很矮,就只有一个根连着所有枝,那么只需要一步就可以找到根,而假如这棵树是一颗二叉树,则若有N个节点的话,那就需要logN次才能找到自己的根,也很快,不是么?
综合一下,如果是共有N个数,要连接其中M个,则最后时间复杂度为O(M*(1~logN))。
这样的算法就可以既Find的很快,又Union的很快,所以称为Find-Union算法。它可以应用于网络通信、数学集合、媒体社交等多个领域。
最后强烈推荐Coursera上普林斯顿大学的算法课点击打开链接
以上内容纯属个人学习总结,不代表任何团体或单位。若有理解不到之处请见谅!