并查集

简介

假想这样一个事实,一群人和他们之间的几对交际关系,使他们间共有的关系抽象为交际圈(如果A不认识B,A的朋友也不认识B,那么我们在这里说A与B属于两个交际圈)

对这个问题进行思考:将人抽象为元素,交际关系抽象为关系Ri,人际圈抽象为集合Xi。可以将这个事实表述为:对于∈Ri,所有满足条件的x,y的集合是Xi。

给定一些(人)元素和它们间的数对(人际)关系,该以何种数据结构表示出这个(交际圈)集合Xi呢?并查集作为一种数据结构可以方便地合并若干个不重叠的集合,快捷地查询元素所属集合。

储存,查找与合并

并查集是结构上包含了数个集合;在功能上能对这些集合进行操作的数据结构。那么首先势必要用某种方法表示集合——在并查集中,我们使用“代表元”的方法来表示一个集合,即为一个集合选取当中的一个元素作为它的“代表”:
输入n个元素,当建立并查集时,将每一个元素看成一个集合。

for (int i=1;i<=n;i++)
f[i]=i;

我们可将每个集合看成一个树形结构,这样一棵树的根节点即是集合的代表元(根节点的父节点是它本身)。在上述代码中,初始化每个结点f[i]的父节点是i,这就是说所有元素的父节点都是它本身,所以每个元素自成一个集合。
设若将n个集合中的两个合并为一个集合。

f[a]=f[b];

这行代码表示元素f[a]的父节点变成了f[b],它们组成了一棵根节点为f[b]的树。因为代表元表示着一个集合,有着相同代表元的f[a]和f[b]此时属于同一集合,我们便完成了对两个集合的合并操作。

如果我们想要查询某个元素所属集合

int get(int x){//输入元素x
if(f[x]==x) return x;//如果父节点等于本身,即根节点,亦为代表元。
return f[x]=get(f[x]);//否则查找父节点是否为根节点,并将访问过的每一个结点指向根节点。
}

因为并查集中数据的层次结构并不重要,当我们查询每个节点所属代表元时,可以顺带将访问过的每个节点指向代表元。这种方法被称为“路径压缩”。

如果我们想要合并都含多个元素的两个集合,使用f[a]=f[b]=......=f[n]这样的形式过于累赘。使用merge操作可以简便清晰地合并两集。

void merge(int x,int y){//将x所属集合并入y集
f[get(x)]=get(y);//将x所属代表元的父节点设为y的代表元。则代表元相同的两集成为一集。
}

回到开头的例子中,因为两个人只要直接或间接相识,都属于同一个交际圈。我们只需要将符合∈R的x,y元素的代表元进行merge操作。就可以构建出一个或数个交际圈。

你可能感兴趣的:(并查集,数据结构,c++)