并查集的原理与应用
并查集是一种树型的数据机构,常用于处理一些不相交集合的合并及查询问题
.
并查集是一种简单的数据结构,它主要涉及两个基本操作,分别为:
1.合并两个不相交的集合.
2.判断两个元素是否属于同一个集合.
并查集的实现原理:
首先并查集会开辟出一个数组,假设数组有10个人,那么就会开辟10个大小的数组,然后每一个下标代表一个人.数组每一个节点的值都为-1.表示每一
个节点都是相对应的一个集合.大家都不相交.这个时候开始添加关系.如下图:

所以我们发现现在确定一共有几个集合,还有查找两个数据在不在一个集合当中就很容易了. 首先确定几个集合你只需要便利一遍,如果那个下标的值
小于0,那么就是一个集合.然后判断两个数据是不是在同一个集合里面也很简单. 分别找两个数据的根.如果根相同那么他两个就在一个集合里,反之
就不在.好了现在思想确定了,那么我们过来实现代码.
class UnionFind
{
public:
UnionFind(int n)
{
_UnionTable.resize(n+1); //因为一般0号下标没有用,所以多开出来一个空间.
for (int i = 0; i < n+1; ++i)
{
_UnionTable[i] = -1;
}
}
void AddUnion(int i,int j)
{
if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
{
_UnionTable[i] += _UnionTable[j];
_UnionTable[j] = i;
}
else
{
int k = FindRoot(i);
int l = FindRoot(j);
_UnionTable[k] += _UnionTable[l];
_UnionTable[l] = k;
}
}
int FindRoot(int i)
{
assert(i >= 0);
int k = i;
while (_UnionTable[k] >= 0)
{
k = _UnionTable[k];
}
return k;
}
int size()
{
int count = 0;
for (int i = 0; i < _UnionTable.size(); ++i)
{
if (_UnionTable[i] < 0)
{
++count;
}
}
return count - 1;
}
private:
vector<int> _UnionTable;
};
这个代码就是实现一个最简易的并查集,可能会有一点小BUG,不过我们可以用它解决一点实际问题,比如当年小米的一道面试题! 40分啊! 好看题:
假设一组有N个人和M对好友关系(存于数组R).如果两个人是直接或者间接好友(好友的好友就是间接好友),则认为他们属于同一个朋友圈,请写出程
序求出这个N个人里面一共有多少个朋友圈.
例如:N =5,M =3,R = {{1,2},{2,3},{4,5}} 表示有5个人,1和2是还有,2和3是好友,4和5是好友,则1,2,3属于一个朋友圈,4,5属于一个朋友
圈. 则一共拥有两个朋友圈. 函数原型 :
int friends(int m,int n,int* r[]);
首先拿到这个题我在不知道并查集这个数据结构之前是真的一头雾水,用其他数据结构真的很难解决.但是使用并查集轻而易举就解决掉了,不相信?
我下面利用并查集演示解决该题:
class UnionFind
{
public:
UnionFind(int n)
{
_UnionTable.resize(n+1); //因为一般0号下标没有用,所以多开出来一个空间.
for (int i = 0; i < n+1; ++i)
{
_UnionTable[i] = -1;
}
}
void AddUnion(int i,int j)
{
if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
{
_UnionTable[i] += _UnionTable[j];
_UnionTable[j] = i;
}
else
{
int k = FindRoot(i);
int l = FindRoot(j);
_UnionTable[k] += _UnionTable[l];
_UnionTable[l] = k;
}
}
int FindRoot(int i)
{
assert(i >= 0);
int k = i;
while (_UnionTable[k] >= 0)
{
k = _UnionTable[k];
}
return k;
}
int size()
{
int count = 0;
for (int i = 0; i < _UnionTable.size(); ++i)
{
if (_UnionTable[i] < 0)
{
++count;
}
}
return count - 1;
}
private:
vector<int> _UnionTable;
};
int Friends(int m,int n,int a[][2])
{
UnionFind T(m);
for (int i = 0; i < n; i++)
{
T.AddUnion(a[i][0],a[i][1]);
}
return T.size();
}
void Test()
{
int n = 5;
int a[][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 } };
cout << "一共有"<<Friends(5, 3, a)<<"个朋友圈"<<endl;
}
