并查集简介:
并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。
使用并查集时,首先会存在一组不相交的动态集合 S={S1,S2,⋯,Sk}S={S1,S2,⋯,Sk},一般都会使用一个整数表示集合中的一个元素。
每个集合可能包含一个或多个元素,并选出集合中的某个元素作为代表。每个集合中具体包含了哪些元素是不关心的,具体选择哪个元素作为代表一般也是不关心的。我们关心的是,对于给定的元素,可以很快的找到这个元素所在的集合(的代表),以及合并两个元素所在的集合,而且这些操作的时间复杂度都是常数级的。
并查集的实现原理也比较简单,就是使用树来表示集合,树的每个节点就表示集合中的一个元素,树根对应的元素就是该集合的代表,同一个根节点,就在一个集合内。开始时,每个元素都是一个集合,按规律进行合并。
如图:
eg:小米笔试题
代码实现:
#include<iostream>
using namespace std;
class UnionSet
{
public:
UnionSet(int n)
:_n(n)
{
_us = new int[n + 1];
for (int i = 0; i <= _n; i++)
{
_us[i] = -1;
}
}
~UnionSet()
{
delete[] _us;
_us = NULL;
}
protected:
int _Findroot(int x)
{
while (_us[x] >= 0)
x = _us[x];
return x;
}
public:
void _Union(int x, int y)
{
int root1 = _Findroot(x);
int root2 = _Findroot(y);
if (root1 != root2)
{
_us[root1] += _us[root2];
_us[root2] = root1;
}
}
int _GetUnionCount()
{
int count = 0;
for (int i = 0; i <= _n;i ++)
{
if (_us[i] < 0)
count++;
}
return --count; //减去_us[0]的-1
}
protected:
int* _us;
int _n;
};
int _Friendcircle(int n, int m, int r[][2])
{
UnionSet uf(n);
for (int i = 0; i < m; i++)
{
uf._Union(r[i][0], r[i][1]);
}
return uf._GetUnionCount();
}
int main()
{
int r[][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 }, { 0, 7 }, { 6, 8 } };
int ret = _Friendcircle(9, 5, r);//当有0的时候 _n=n+1
cout<< "朋友圈个数:" << ret << endl;
return 0;
}