并查集
并查集在逻辑上是一片森林,在物理上是一个数组。与堆类似,堆在逻辑上是二叉树,在物理上是数组,并查集用于解决人际关系的问题。
下面通过一个实际的社会关系来认识并查集。
如上图所示,一共有三个朋友圈(一般假设朋友的朋友也是朋友,可以组成朋友圈),使用并查集来表示他们之间的关系。
首先使用一维数组,如图。数组里面的值全部假设成-1,
下面来生成并查集,在叶子节点对应的位置填入根的下标,在根的位置数据大小减1。
一个位置是负数表示它是根,负数的绝对值是森林里面元素的个数,正数表示它的根的下标,一个并查集里面有几个负数就表示有几片森林。对应社交图上面就是有几个朋友圈。
并查集的合并:
接下来我们来看并查集的代码实现。(封装一个类来实现)
class UnionFindSet
{
private:
vector<int> ufs;
public:
UnionFindSet(int size)
{
ufs.resize(size, -1);
}//初始化并查集
};
统计负数的个数就能得到朋友圈的数量
int size()
{
int ret=0;
for (auto &i: ufs)
{
if (i<0)
{
ret++;
}
}
return ret;
}
得到一个值,值对应的下标,下标里面存放的值就是它的根的位置。用while循环可以找到最原始的根,因为在并查集里面叶子的根有时不止一个。
int FindRoot(int val)
{
int n = val;
while (ufs[n]>=0)
{
n = ufs[n];
}
return n;
}
并查集的合并:因为根相同的已经合并过了,负数的根是它本身。
void Union(int x1, int x2)
{
int Root1 = FindRoot(x1);
int Root2 = FindRoot(x2);
if (Root1 != Root2)
{
ufs[Root1] += ufs[Root2];
ufs[Root2] = Root1;
}
}
完整代码:
class UnionFindSet
{
private:
vector<int> ufs;
public:
UnionFindSet(int size)
{
ufs.resize(size, -1);
}
int size()
{
int ret=0;
for (auto &i: ufs)
{
if (i<0)
{
ret++;
}
}
return ret;
}
int FindRoot(int val)
{
int n = val;
while (ufs[n]>=0)
{
n = ufs[n];
}
return n;
}
void Union(int x1, int x2)
{
int Root1 = FindRoot(x1);
int Root2 = FindRoot(x2);
if (Root1 != Root2)
{
ufs[Root1] += ufs[Root2];
ufs[Root2] = Root1;
}
}
};