「并查集」专题 2:并查集第 1 版(quick-find 实现)


「并查集」专题 2:并查集第 1 版(quick-find 实现)

重点提示

基于 id 的思想类似于:给每个元素改名字,名字一样,就表示在同一个集合中。

  • 优点:查询两个元素是否在一个集合中很快。

  • 缺点:把两个集合合并成一个集合较慢。

这一版「并查集」并不常用,仅作为了解。

并查集可以使用数组表示。这个数组我们命名为 id 。初始化的时候每一个元素的值都是不一样的。如果值一样的,表示是属于同一个集合内的元素。

基于 id 的 quick-find 的思路:设置 id 数组,数组的每个元素是分量标识。

从 quick-find 这个名字上看,我们这一版的实现,对于 find(int p) 这个操作来说是高效的,但对于 union(int p, int q) 这个操作是低效的,因为需要遍历整个并查集。find(int p) 这个操作的时间复杂度是 $O(1)$,而 union(int p, int q) 这个操作的时间复杂度是 $O(n)$,要全部遍历并查集中的元素,把其中一个分量标识的所有结点的编号更改为另一个一个分量标识。

![高级数据结构:并查集-4](https://liweiwei1419.gitee.io/images/algorithms/union-find-set/基于 id 并查集算法的例子.jpg)

例如上面的表格中,如果我们要将第 $1$ 行的 01 执行 union(int p, int q) 操作,有两种方式:

第 1 种方式:把第 1 行的 02468 的值全改成 1

第 2 种方式:把第 1 行的 13579 的值全改成 0。

可以看到,union(int p, int q) 的操作和问题的规模成正比,所以 quick-find 思想(基于 id)的 union(int p, int q) 操作时间复杂度是 $O(n)$。

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
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 liweiwei1419 !
评论
 上一篇
「并查集」专题 3:并查集第 2 版基于 parent 的并查集(非最终版本)(非最终版本) 「并查集」专题 3:并查集第 2 版基于 parent 的并查集(非最终版本)(非最终版本)
「并查集」专题 3:并查集第 2 版基于 parent 的并查集(非最终版本)(非最终版本) 重点提示:这一版「并查集」代码是最基本的「并查集」,我们需要学习思想,核心思想是「代表元法」,以「树」的「根结点」作为代表元。 后续我们介绍这一版
下一篇 
「并查集」专题 1:并查集简介 「并查集」专题 1:并查集简介
「并查集」专题 1:并查集简介重点提示 「并查集」是一种建立在「数组」上的树形结构,并且这棵树的特点是孩子结点指向父亲结点; 「并查集」主要用于解决「动态连通性」问题,重点关注的是连接问题,并不关注路径问题; 「并查集」是树,所以优化的策
  目录