并查集解决朋友圈问题

来源:互联网 发布:李天生sqlserver 编辑:程序博客网 时间:2024/05/22 15:22

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

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

假设每个元素在集合中的关系如下:
这里写图片描述
看上图中的第一个树,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,如图:
这里写图片描述
介绍完了之后,我们来看一下代码,解决刚开始的朋友圈问题:

#include <iostream>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;}
原创粉丝点击