并查集概念
在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。
并查集数据结构表示
常用的并查集实现有两种方法。
使用数组
我们使用数组来表示这样的数据结构。
比如,我们知道这个集合最大元素个数为 10^5 个,也就是 1 ~ 10^5。那么我们可以定义一个数组来记录每个元素的父节点。
const int MAXN=1e5+4;
int parent[MAXN];
memset(parent, -1, sizeof(parent));
如上定义,元素 i 的父节点保存在对应的 parent[i] 中。例如:
parent[i]=-1,表示节点 i 没有父节点。
parent[3]=5,表示节点 3 的父节点为 5。我们拥有的集合如图所示,。
parent[1]=7,表示节点 1 的父节点为 7。我们拥有的集合如图所示,。
parent[7]=5,表示节点 7 的父节点为 5。我们拥有的集合如图所示,。
使用 STL 的 map 来实现
数组的问题是数组下标不能为负数。当我们元素出现负数的时候,使用 map 更加方便。
map<int, int> ds;
这样就可以表示一个并查集。含义和数组定义并查集是一样的。
并查集操作
并查集的操作主要有两个。查集和并集。
查集
主要作用是查询两个集合是否相交。比如,我们有两个集合 {A} 和 {B},集合 {A} 中有元素 x,集合 {B} 中有元素 y。find_root() 用来查询集合 {A} 和集合 {B} 是否相交。
int x_root=find_root(x);
int y_root=find_root(y);
if (x_root==y_root) {
集合相交
} else {
集合不相交
}
并集
主要作用是将两个集合是否合并成为一个新的集合。比如,我们将元素 x 的父亲设置为 y,则就将这两个元素变成一个新的集合。
int x_root=find_root(x);
int y_root=find_root(y);
if (x_root!=y_root) {
parent[x]=y;
}
并查集举例
元素从 1 ~ 8。我们用数组 parent[9] 来表示。
初始状态
图形表示为,这样,我们有 8 个独立的集合。
对应的 parent 数组值为
parent[1]=-1;
parent[2]=-1;
parent[3]=-1;
parent[4]=-1;
parent[5]=-1;
parent[6]=-1;
parent[7]=-1;
parent[8]=-1;
这样表示,每个元素的父节点都是自己,也就是每个元素都是独立的集合。
合并 3 和 4
集合的图像变为。
对应的 parent 数组值为
parent[1]=-1;
parent[2]=-1;
parent[3]=4;
parent[4]=-1;
parent[5]=-1;
parent[6]=-1;
parent[7]=-1;
parent[8]=-1;
我们将元素 3 的父节点指向 4,这样 3 和 4 就在同一个集合中。
合并 1 和 2
集合的图像变为。
对应的 parent 数组值为
parent[1]=2;
parent[2]=-1;
parent[3]=4;
parent[4]=-1;
parent[5]=-1;
parent[6]=-1;
parent[7]=-1;
parent[8]=-1;
我们将元素 1 的父节点指向 2,这样 1 和 2 就在同一个集合中。
查询 1 和 5 是否在同一个集合
通过查询,我们知道元素 1 的父节点是 2,元素 5 的父节点是 5。这样说明 1 和 5 不在同一集合。
查询 3 和 4 是否在同一个集合
通过查询,我们知道元素 3 的父节点是 4,元素 4 的父节点是 4。这样说明 3 和 4 在同一集合。
合并 1 和 3
集合的图像变为。
我们查询元素 1 的父节点,得到 2;查询元素 3 的父节点,得到 4。对应的 parent 数组值为
parent[1]=2;
parent[2]=4;
parent[3]=4;
parent[4]=-1;
parent[5]=-1;
parent[6]=-1;
parent[7]=-1;
parent[8]=-1;
合并 5 和 8
集合的图像变为。
对应的 parent 数组值为
parent[1]=2;
parent[2]=4;
parent[3]=4;
parent[4]=-1;
parent[5]=8;
parent[6]=-1;
parent[7]=-1;
parent[8]=-1;