并查集
定义
并查集(Union-Find Sets)也叫不相交集合(Disjoint Sets),是一种用于维护若干个不相交元素所构成的集合的一种树型数据结构。并查集常用来处理一些不相交元素的合并与查询问题,在使用中常常用森林来表示。
主要操作
一个并查集数据结构维护了一个不相交动态集的集合。我们用一个代表(根节点)来标识每个集合,它是这个集合的某个成员。在一些应用中,我们不关心哪个成员被用来作为代表,仅仅关心的是2次查询动态集合的代表中,如果这些查询没有修改动态集合,则这两次查询应该得到相同的答案。其他一些应用可能会需要一个预先说明的规则来选择代表,比如选择集合中最小的成员。
对于一个并查集,我们希望支持三个操作:
1.初始化(Initialize)
用于建立一个新的集合,假设它的唯一成员(因而为代表)是x。因为各个集合是不相交的,故x不会出现在别的某个集合中。
2.合并(Union)
Union(x,y)将包含x和y的两个动态集合合并成一个新的集合,即这两个集合的并集。合并后的新集合的代表可以是原来两个集合中的任意一个成员。由于我们要求各个集合之间不相交,故要“消除”原有的两个集合。实际上,我们经常把其中一个集合的元素并入另一个集合中,来代替删除操作。
3.查找(Find)
Find(x)返回一个指针,这个指针指向包含x的(唯一)集合的代表(根节点)。
因此,并查集的数据结构定义如下(也可以使用单链表结构来定义并查集):
#ifndef UNIONFIND_H
#define UNIONFIND_H
#include <iostream>
class UnionFind {
private:
int *id;//记录分量
int cnt;//记录连通分量的数量
int n;
public:
UnionFind (int N);//初始化分量数组
~UnionFind ();//析构函数
int Count (void);//返回连通分量的数量
bool Connected (int p, int q);//判断两分量是否在同一集合中
int Find (int p);//查找分量
void Union (int p, int q);//合并两个分量
};
#endif
并查集的许多应用之一是确定无向图的连通分量,如图所示(图G的顶点集用G.V表示,边集用G.E表示):
并查集的操作伪码如下:
//判断连通性并决定是否合并
Conneted-Components(G)
for each vextex v∈G.V
Initialize(v)
for each edge(u,v)∈G.E
if Find(u)≠Find(v)
Union(u,v)
//判断两顶点是否在同一连通分量
Same-Components(u,v)
if Find(u) == Find(v)
return true
else
return false
Conneted-Components开始时,将每个顶点v放在它自己的集合中,然后对每条边(u,v),它将包含u和v的集合进行合并。处理完所有的边之后,两个顶点在相同的连通分量当且仅当与之对应的对象在相同的集合中。因此Conneted-Components以这种方式算出集合,使得过程Same-Components能确定两个顶点是否在相同的连通分量中。过程如上图所示。
在该连通分量算法的实际实现中,图和并查集的表示需要相互引用。也就是说,一个表示顶点的对象会包含一个指向与之对应的不相交集合对象的指针;反之亦然。
Union-Find算法
本文讨论的Union-Find算法均为不需要给出具体路径的算法,而给出具体路径的算法需要基于DFS,写在另外的博文。
动态连通性
先给一张图
假设我们输入了一组整数对,即上图中的(4, 3) (3, 8)等等,每对整数代表这两个points/sites是连通的。那么随着数据的不断输入,整个图的连