在一些应用问题中,需要将n个不同的元素划分为一组不相交的集合,开始时,每个元素自成一个单元素集合,然后按一定规律将归于同一组元素的集合合并。在此过程中要反复用到查询某个元素属于哪个集合的运算。适合于描述这类问题的抽象数据类型的数据结构称之为并查集(union_find set)
并查集需要两种数据类型的参数:集合类型和集合元素的类型,在许多情况下,可以用整数作为集合名。如果集合中有n个元素,可以用0~n-1以内的整数来表示元素。实现并查集的一个典型方法是采用树形结构来表示元素及其所属子集的关系。
每个集合以一棵树表示,树的每一个结点代表集合的一个单元素。所有各个集合的全集合构成一个森林,并用树与森林的父指针表示来实现。其下标代表元素名。第i个数组元素代表集合元素i的树结点。树的根节点的下标代表集合名称,根节点的父为-1,表示集合中的元素个数。
【基本操作】
1、初始化(将每个节点初始化为-1),通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度为O(N)
UFSets(size_t size)//构造函数,将并查集中size个元素初始化为size个只有一个单元的子集合,这里初始化为-1(方便之后的操作)
:_array(size,-1)
{
//_array.resize(size, -1);//方法2利用vector函数
//_array._assign(size, -1);//方法3
}
2、查找--->查找元素所在的集合,即根节点
int FindRoot(int data)//搜索单元素data所在的集合,并返回集合的名字
{
while (_array[data] >= 0)
{
data = _array[data];
}
return data;
}
3、合并--->将两个元素所在的集合合并为一个集合(合并之前,先判断两个集合是否在同一个集合)
void Union(int data1, int data2)//把子集合root1并入root2集合中,要求root1与root2不相交,否则不执行合并(因为本身就是一个集合啊)
{
int root1 = FindRoot(data1);
int root2 = FindRoot(data2);
if (root1 != root2)
{
_array[root1] += _array[root2];
_array[root2] = root1;
}
}
4、集合的总数---->统计根节点的个数(所有元素中为负数的元素个数)
size_t Size()//这时候该合并的都已经合并,剩下的元素中仍为负数就是集合,定义一个计数器n,将集合的个数统计起来
{
size_t n = 0;
for (size_t i = 0; i < _array.size(); i++)
{
if (_array[i] < 0)
{
n++;
}
}
return n;
}
【全部代码】
实现代码:
#include<iostream>
using namespace std;
#include<vector>
template<class T>
class UFSets
{
public:
UFSets(size_t size)
:_array(size,-1)
{
//_array.resize(size, -1);//利用vector函数
//_array._assign(size, -1);
}
void Union(int data1, int data2)
{
int root1 = FindRoot(data1);
int root2 = FindRoot(data2);
if (root1 != root2)
{
_array[root1] += _array[root2];
_array[root2] = root1;
}
}
int FindRoot(int data)
{
while (_array[data] >= 0)
{
data = _array[data];
}
return data;
}
size_t Size()
{
size_t n = 0;
for (size_t i = 0; i < _array.size(); i++)
{
if (_array[i] < 0)
{
n++;
}
}
return n;
}
private:
vector<int> _array;
};
测试代码:
int main()
{
UFSets<int> un(6);
un.Union(1,2);
un.Union(2,3);
un.Union(4,5);
printf("一共有%d个集合\n", un.Size()-1);
//因为0号位置虽然初始化了,但是没有数字集合在该集合中,所以减去1
system("pause");
return 0;
}