首先我们来看一道题笔试题,关于朋友圈的问题:
解决这个问题,我们可以用并查集来解决。
并查集是一种数据结构,用于处理不相交集合中的合并以及查询问题,将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;
}