Find-Union算法 并查集

本文介绍了Find-Union算法,作为Quick-Find和Quick-Union的优化版本,旨在解决极端情况下的性能问题。通过union优化策略,即连接大小树的根节点,以及find操作的路径压缩,使得算法的时间复杂度降低,适用于大规模数据处理。C++实现的代码展示了具体操作,最终得出Find和Union的时间复杂度均为O(1~logN)。Find-Union算法广泛应用于网络通信、数学集合等领域。
摘要由CSDN通过智能技术生成

前言

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

 

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

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值