作用:
判断两个节点 是否在一个集合内/是否联通
数据结构:
数组id,id[i] 表示第i个元素的父元素的位置
上图表示所有单数节点 和双数节点各在一个集合中
主要算法:
第一种算法(quick find):
抽象数据结构如下,特点是最多只有双层,任何元素的父元素都是该集合的根元素
查找父元素:
int find(int p) {
return id[p];
}
quick find 使用O(1)的时间复杂度来找到父节点,但是相应的,需要接近O(n)时间来执行Union操作
union 操作:
bool unionElements(int p, int q) {
int pId = find(p);
int qId = find(q);
if (pId == qId) {
return;
}
else {
for (int i = 0; i < count; i++) {
if (id[i] == pId) {
id[i] == qId;
}
}
}
}
上面的代码中,为其中一个集合中的所有元素重新指定父节点,所以时间复杂度较大
第二种算法(quick union):
抽象数据结构如下,可以有多层结构,层数由实际问题决定,
查找根元素:
int find(int p) {
while (p != parent[p]) {
p = parent[p];
}
return p;
}
需要逐层向上查询父节点来找到根节点
union (quick union):
bool unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) {
return;
}
else {
parent[pRoot] = qRoot;
}
}
union 的时间复杂度与层数有关,层数越大,find的时间复杂度就越大,同时union的时间复杂度也越大
优化:
基于第二种算法的优化,减少层数
方法一:基于rank的优化
简单说就是在每次union的时候,总是选择将高度小的那个集合连接到高度大的集合的根节点上,只有当两个集合层数相同时,union后的集合层数才会增加
实现方法:
为每个根元素维护rank数组,表示元素所在的集合的高度
方法二:路径压缩
在find操作时,将元素的父节点设置为元素的父节点的父节点,以此来减少路径长度(集合高度),在最极端情况下,可以将所有元素都连接到根元素上