Find-Union算法

前言:

在前两次的博客中已经介绍了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上普林斯顿大学的算法课点击打开链接


以上内容纯属个人学习总结,不代表任何团体或单位。若有理解不到之处请见谅!




猜你喜欢

转载自blog.csdn.net/qq_39747794/article/details/80255941