# 第 1 版基于 quick-find 实现
重点提示:基于
id
的思想类似于:给每个元素改名字,名字一样,就表示在同一个集合中。
- 优点:查询两个元素是否在一个集合中很快。
- 缺点:把两个集合合并成一个集合较慢。
这一版「并查集」并不常用,仅作为了解。
并查集可以使用数组表示。这个数组我们命名为 id
。初始化的时候每一个元素的值都是不一样的。如果值一样的,表示是属于同一个集合内的元素。
基于 id
的 quick-find 的思路:设置 id
数组,数组的每个元素是分量标识。
从 quick-find 这个名字上看,我们这一版的实现,对于 find(int p)
这个操作来说是高效的,但对于 union(int p, int q)
这个操作是低效的,因为需要遍历整个并查集。find(int p)
这个操作的时间复杂度是 union(int p, int q)
这个操作的时间复杂度是
例如上面的表格中,如果我们要将第 0
和 1
执行 union(int p, int q)
操作,有两种方式:
- 第 1 种方式:把第 1 行的
0
,2
,4
,6
,8
的值全改成1
; - 第 2 种方式:把第 1 行的
1
,3
,5
,7
,9
的值全改成 0。
可以看到,union(int p, int q)
的操作和问题的规模成正比,所以 quick-find 思想(基于 id
)的 union(int p, int q)
操作时间复杂度是
Java 代码:
public class UnionFind1 implements IUnionFind {
private int[] id; // 分量 id
private int count; // 连通分量的数量
public UnionFind1(int n) {
this.count = n;
// 初始化分量 id 数组
id = new int[n];
for (int i = 0; i < n; i++) {
id[i] = i;
}
}
@Override
public String versionName() {
return "并查集的第 1 个版本,基于 id 数组,quick-find";
}
// 以常数时间复杂度,返回分量的标识符,与并查集的规模是无关的,这一步很快
// 因此我们称这个版本的并查集是 quick-find
@Override
public int find(int p) {
return id[p];
}
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 因为需要遍历数组中的全部元素,所以这个版本其实效率并不高
@Override
public void union(int p, int q) {
int pid = find(p);
int qid = find(q);
// 如果 p 和 q 已经在相同的分量之中,则什么都不做
if (pid == qid) {
return;
}
// 将 p 的分量重新命名为 q 的名称
for (int i = 0; i < id.length; i++) {
if (find(i) == pid) {
id[i] = qid;
}
}
// 每次 union 以后,连通分量减 1
count--;
}
}
使用数组 id
在进行 union(int p, int q)
的时候,要遍历整个数组中的结点,这种方式效率不高,因此,我们换一种思路:由于 union(int p, int q)
慢,所以我们就想办法让 union(int p, int q)
快起来。
作者:liweiwei1419 链接:https://suanfa8.com/union-find/quick-find 来源:算法吧 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。