并查集解决朋友圈问题

首先我们来看一道题笔试题,关于朋友圈的问题:
并查集解决朋友圈问题_第1张图片
解决这个问题,我们可以用并查集来解决。
并查集是一种数据结构,用于处理不相交集合中的合并以及查询问题,将N个元素分成一组不相交的集合,开始时我们把每一个元素当成一个集合,然后按规律将集合合并。

举例说明,首先定义一个只有10个元素的数组,并将每个元素的值设置为-1这里写图片描述

假设每个元素在集合中的关系如下:
并查集解决朋友圈问题_第2张图片
看上图中的第一个树,0,6,7,8在用一个集合中,这样我们可以以0为根节点,分别把以6,7,8为下标的数组中的元素值累加到以0为下标的元素上,并且将以6,7,8为下标的数组中的值改为0,第二棵第三棵树分别以1,2位根节点,这是数组中的每个数据的值变成如下结果:
这里写图片描述
从图中我们可以看出,0,1,2分别为我们假定的根节点,其中所存储数值的绝对值就是集合中元素的个数,以3,4,5,6,7,8,9为下标的数组元素中所存储的值是其所属于的集合。
接下来再继续合并,将1为根节点的集合合并到以0为根节点的集合中,这时候又继续改变数组中元素的值,将下标为1的数组元素的值加到下标为0的数组元素的值上,并将下标为1的数组元素值改为0,如图:
并查集解决朋友圈问题_第3张图片
介绍完了之后,我们来看一下代码,解决刚开始的朋友圈问题:

#include 
using namespace std;
class UnionFindSet
{
public:
    UnionFindSet(int n)
        :_set(new int[n])
        , _n(n)
    {
        for (size_t i = 0; i < n; i++)
        {
            _set[i] = -1;
        }
    }
    int FindRoot(int x)
    {
        while (_set[x] >= 0)
        {
            x = _set[x];
        }
        return x;
    }
    //合并集合
    void Union(int x1, int x2)
    {
        int root1 = FindRoot(x1);
        int root2 = FindRoot(x2);
        if (root1 != root2)
        {
            _set[root1] += _set[root2];
            _set[root2] = root1;
        }
    }
    //判断2个元素是否在一个集合
    bool IsIn(int x1,int x2)
    {
        return FindRoot(x1) == FindRoot(x2);
    }
    //求集合的个数
    int Count()
    {
        int count = 0;
        for (size_t i = 0; i < _n; i++)
        {
            if (_set[i] < 0)
                count++;
        }
        return count;
    }
protected: 
    int* _set;
    size_t _n;
};
int Friends(int n, int m, int r[][2])
{
    UnionFindSet ufs(n+1);
    for (size_t i = 0; i < m; ++i)
    {
        ufs.Union(r[i][0], r[i][1]);
    }

    return ufs.Count()-1;
}

void TestFriends()
{
    const int n = 5;
    const int m = 4;
    int r[m][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 }, { 1, 3 } };

    cout << "朋友圈的个数?" << Friends(n, m, r) << endl;
}

你可能感兴趣的:(数据结构,集合合并)