并查集的简单实现
我们把一个数组中的不同下标看做每一个不同的元素,每一个下标里面对应的值就是它的父亲的下标,为负数就代表是根。根对应的负数的绝对值就是这个集合元素的个数。
初始状态下,所有下标对应的值为 -1,表示每个元素自身为一个集合,集合内的个数为 1 。
构造:
class UnionFindSet
{
public:
UnionFindSet(size_t n)
:_ufs(n, -1)
{}
private:
vector<int> _ufs;
};
找某个元素根
我们只需要先判断当前节点是否为根,是就返回,不是就让它去判断它父亲是不是根,再没找到,就依次向上寻找即可。
// 找根
int FindRoot(int x)
{
int root = x;
// 先判断当前节点是否为根
while (_ufs[root] >= 0)
{
// 不是的话就让 root 为其父亲,依次向上寻找
root = _ufs[root];
}
return root;
}
合并集合
如果我们要将上面的图中的 3 和 7 合并,那么我们就需要先找到 3 的根和 7 的根,再将它们的根连接起来即可。
我们还要考虑到,如果要合并的两个元素本身就在一个集合中,那么我们就不需要合并了。
例如:上图中的 5 和 7,它们找根时发现根相同,那就不需要合并,直接返回即可。
// 合并
void Union(int x1, int x2)
{
// 分别找要合并的两个元素的根
int root1 = FindRoot(x1);
int root2 = FindRoot(x2);
// 如果两根相等,则它们本身就在一个集合,直接返回即可
if (root1 != root2)
{
// 让 root1 做根,root2 连接在 root1 上
_ufs[root1] += _ufs[root2];
_ufs[root2] = root1;
}
}
判断在不在同一个集合
我们只需要判断根是否相同即可。
// 在不在同一个集合
bool IsInSameSet(int x1, int x2)
{
return FindRoot(x1) == FindRoot(x2);
}
总共有几个集合
只需要遍历一遍数组,判断有几个下标对应的值为负数即可。
// 大小
size_t SetSize()
{
size_t ret = 0;
for (size_t i = 0; i < _ufs.size(); ++i)
{
if (_ufs[i] < 0)
ret++;
}
return ret;
}
感谢大家观看(・ω・)ノ