并查集
- 并查集应用
- 并查集原理
- 并查集实现
1.并查集应用
在很多笔试题目中,经常需要将n个不同元素划分成不相交的集合,通过一定的条件和关系让一些集合合并。在此过程中经常需要判断该元素是否属于某个集合,或查询该集合中元素的个数。适用于解决这类问题的数据结构类型称为并查集。
题目1:.547. 朋友圈
题目2:990. 等式方程的可满足性
2.并查集原理
通过数组下标标识元素本身,数组内容标识其集合上层节点。举个例子
在班里有三波人是互相熟悉的,他们都有分别都有一个老大。现在给这些人进行编号{0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
用一个数组来存储表示集合,数组下标就是同学编号,数组中的数字代表这个集体有多少个人。(负号后面解释)
三个小团体分别是:team1={0,3,5} team2={1,4,7,9} team3={2,6,8,10}
小团队分好伙后就变成了
从上图可以观察到编号3,5属于team1,编号4,7,9属于team2,编号6,8,10属于team3。它们坐标数组中的值是其老大的坐标。
可以得到一下结论
- 数组的下标对应集合中元素的编号
- 数组中如果为负数,负号代表根,数字代表这个集合中的元素个数
- 数组中如果为非负数,代表该元素父节点在数组中的下标。
过了一阵时间,老大0和老大1相互认识了,因此也介绍它们的小弟相互认识,因此他们也就成了同一个集合
现在0集合就有7个人,2集合就有4个人。变成了两个集合
通过这个例子就能了解到并查集能解决的问题。
-
查找元素属于哪个集合
根据元素数组中数据,沿着一直查找到数据为负数的节点。
-
查看两个元素是否属于一个集合
如果他们的根节点相同,他们就属于同一个集合
-
可以将两个集合合并成一个集合
找到两个集合的根节点,将其中一个根节点名称改为另一个根节点。
另一个根节点加上当前集合元素个数。
-
集合的个数
遍历数组,查看数组元素为负数的个数,就是集合的个数。
3.并查集的实现
#include<iostream>
#include<vector>
using namespace std;
class UnionFindSet
{
private:
vector<int> _ufs;
public:
//构造函数
UnionFindSet(int size)
:_ufs(size, -1)
{}
//找到一个元素所在根节点
int FindRoot(int index)
{
while (_ufs[index] >= 0)
{
index = _ufs[index];
}
return index;
}
//将两个集合合并
bool Union(int set1, int set2)
{
int root1 = FindRoot(set1);
int root2 = FindRoot(set2);
_ufs[root1] += _ufs[root2];
_ufs[root2] = root1;
return true;
}
//统计集合元素
int Count() const
{
int count = 0;
for (const auto& e : _ufs)
{
if (e < 0)
++count;
}
return count;
}
//遍历按序输出各集合元素
void Print()
{
int num = Count();
for (int i = 1; i <= num; i++)
{
int index = 0;
for (int i = 0; i < num; i++)
{
int temp = FindRoot(index);
int flag = 0;
for (int j = index; j < _ufs.size(); j++)
{
if (FindRoot(j) == temp)
{
cout << j << " ";
}
else if (flag == 0)
{
//保证从小到大输出
index = j;
flag = 1;
}
}
if (i != num)
cout << endl;
}
}
}
};