目录
并查集概念
合并
查找集合的数量
并查集类代码实现
并查集概念
并查集和堆一样,都是通过数组来实现树的节点映射,不过并查集作用是,把一堆数据分为不同的几个小集合
不过并查集是森林的概念,并查集的学习可以帮助我们去更好的学习图这个数据结构
这里我们举一个例子:
假设我们有一个旅游团,一共有十个人,编号是0~9;
其中 s1 = {0,6,7,8} 这几个人来自湖北
s2 = {1,4,9} 这几个人来自四川
s3 = {2,3,4} 这几个人来自北京
上面我们把十个人分为了不同几个小组,他们来自不同的地方,吃的口味自然不同,于是就分别让0,1,2这三个人担任组长,带领各自的队伍去吃饭。
队长可以抽象的理解为头结点,组员理解为各自的孩子结点
数组的初始化规则定义如下:
1.每一个下标存放自己的双亲结点下标
2.如果该结点是双亲结点,存放一个负数,我们可以顺便存放这棵树的结点个数
特点:
- 一个位置是负数,那他就是树的根,这个绝对值就是棵树的结点个数
- 一个位置值是负数那他就是双亲的下标
合并
我们假设上面的s1和s2发生了合并,咋办呢?
步骤:
- 先找到两个需要合并集合的双亲结点
- 再把集合2的根下标存放的值+=集合1的根下标,在把集合2的根下标存放的值置为集合1的根
查找集合的数量
我们只需要将整个数组遍历一遍,找到为负数的位置有多少个,即集合的数量
并查集类代码实现
class UnionFindSet
{
public:
UnionFindSet(size_t n)
:_ufs(n, -1)
{}
void Union(int x1, int x2)
{
// 1.先找根
int root1 = FindRoot(x1);
int root2 = FindRoot(x2);
//如果在一个集合就没有必要去合并
if (root1 == root2)
{
return;
}
//将集合2合并到集合1
_ufs[root1] += _ufs[root2];
_ufs[root2] = root1;
}
// 找根/双亲
int FindRoot(int x)
{
int parent = x;
while (_ufs[parent] >= 0)
{
parent = _ufs[parent];
}
return parent;
}
bool InSet(int x1, int x2)
{
return FindRoot(x1) == FindRoot(x2);
}
size_t SetSize()
{
size_t size = 0;
for (size_t i = 0; i < _ufs.size(); ++i)
{
if (_ufs[i] < 0)
{
++size;
}
}
return size;
}
private:
vector<int> _ufs;
};