• 并查集


    并查集

             并查集是一种简单且强大的工具。本文我们就是学习并介绍一下并查集。并查集的资料网上有很多,也有很多代码实现。博主在学习的过程中也查阅了很多资料,有兴趣的话可以Google之。

             并查集有三个数据结构:

             集合:实际元素组成的集合

             类信息:记录元素的类信息

             秩信息:记录元素的高度

            

             并查集包含三种基本操作:

             Init:初始化,将集合每个元素看做一个类别,元素的类信息即为自身,秩信息都为0

             Find:查找,根据给定的元素,返回其类别信息。在执行查找的过程中进行路径压缩优化,以提高后续Find的效率。

             Union:合并,将两个元素所在的集合进行合并(集合用其中的元素来表示),在合并的过程中同样进行优化,这里的优化方法为按秩合并。秩低的类别归到秩高类别,这样合并后的树的最大高度做多增加1(只有两个集合的秩相等时,才增加1;否则都不增加)。

             下面,我们给出并查集的一种实现。相关说明会在程序注释中解释。

    // 并查集
    #include <iostream>
    #include <vector>
    using namespace std;
    
    const int NUM = 101;
    
    vector<int> data(101);   // 元素集合
    vector<int> father(101); // 类别信息
    vector<int> mRank(101);   // 秩信息
    
    // 初始化并查集
    // 初始化n个元素的集合
    void Init(int n)
    {
        n = n <= NUM ? n : NUM;
        for (auto i = 0; i != n; ++i)
        {
            data[i] = i + 1;
            father[i] = i;
            mRank[i] = 0;   // 叶子节点高度为0
        }
    }
    
    // 查找
    // 查找x元素
    // 注意,这里的x不是元素的实际值,而是元素的在data中的索引
    int Find(int x)
    {
        if (x == father[x]) // x为根节点
        {
            return father[x];
        }
        else // x不为根节点
        {
            // return Find(father[x]);          // 直接返回,并没有进行路径压缩优化
            return father[x] = Find(father[x]); // 进行路径压缩优化
        }
    }
    
    // 合并
    // 合并的是两个集合(类别)
    // 两个元素x、y,注意这里x、y不是元素的值,而是其在data中的索引
    // 用x、y来表示其所在的集合
    // 集合的表示都是用data的索引来表示
    // 元素的表示,也都是用data的索引来表示
    void Union(int x, int y)
    {
        x = Find(x);
        y = Find(y);
        if (x == y) // 属于同一个类别,不合并
        {
            return;
        }
        // 不是同一个类别
        if (mRank[x] > mRank[y]) // 这里的x表示类别,也是节点,rand[x]既是类别的秩也是节点的秩,同理y
        {
            father[y] = x; // 将y类别归到x类别
            // 不对x的秩进行修改
        }
        else
        {
            if (mRank[x] == mRank[y])
            {
                // 由于是将x类别归到y类别,所以对y的秩加1
                ++mRank[y];
            }
            father[x] = y; // 将x类别归到y类别
            // 不对y的秩进行修改,在y的秩大于x的秩的情况下
        }
    }
    
    void Display(int n)
    {
        for (auto i = 0; i != n; ++i)
        {
            cout << data[i] << '	';
        }
        cout << endl;
    
        for (auto i = 0; i != n; ++i)
        {
            cout << father[i] << '	';
        }
        cout << endl;
    
        for (auto i = 0; i != n; ++i)
        {
            cout << mRank[i] << '	';
        }
        cout << endl;
    }
    
    int main()
    {
        int n = 9;
        Init(n);
        Display(n);
    
        cout << Find(5) << endl;
        cout << Find(8) << endl;
    
        Union(5, 8);
    
        cout << Find(5) << endl;
        cout << Find(8) << endl;
    
        Union(1, 2);
        Union(1, 3);
    
        Union(8, 1);
    
        cout << Find(1) << endl;
        cout << Find(2) << endl;
        cout << Find(3) << endl;
        cout << Find(5) << endl;
        cout << Find(8) << endl;
    
        Display(n);
    
        return 0;
    }

             

             关于程序的说明可以参考注释。

             这里我们对集合(类别)和元素的表示进行一下说明。Find函数的作用是查找元素x的所属类别,这里的x不是元素的值,而是元素在data中的索引,也就是说元素是用data的索引来表示的。这里我们无法针对元素的秩,进行Find其类别。

             Union函数是合并x和y两个类别,注意,这里的x和y实质上还是data的索引,是用x和y两个元素在data中的索引来表示其各自所属的类别。在Union函数中,x和y有的时候表示元素的索引,有的时候表示类别信息。

             个人认为,理解并查集的关键最在于理解元素的表示、类别的表示、元素的索引,以及三者之间的关系。

             元素的索引用来表示元素。

             元素用来表示其所在的类别,实质上是用元素的索引来表示元素所在的类别。

             后续我们将学习并实现一些经典的数据结构算法,对数据结构和算法能够一个更深的理解和实现能力。

  • 相关阅读:
    properties to json (通过前缀手动创建json, bean) propsutils
    Drill 常用时间函数 drill
    ubuntu20.04 修改 DNS  ip
    javascript 获取图片的尺寸 how to get image size using javascript
    javascript小数点后保留N位并可以四舍五入
    C# 递归算法求 1,1,2,3,5,8,13···
    自加入屠龙后的成长记
    Session丢值的问题
    第二个网站成长经历,http://www.chaomagou.com/ 潮妈购
    回想自己2012年1月1日到2012年6月19日的所作所为
  • 原文地址:https://www.cnblogs.com/unixfy/p/3363763.html
Copyright © 2020-2023  润新知