【JavaScript数据结构】并查集

83 阅读2分钟

并查集是一个常用的数据结构,用于处理不交集的合并与查询问题。以下是一个基础的并查集的代码模板及详细注释:

class DisjointSet {
    constructor(n) {
        // 初始化 n 个单独的集合
        // parent 数组用于存储每个元素的父节点
        this.parent = new Array(n).fill(0).map((_, index) => index);
        // rank 数组用于存储每个集合的高度
        this.rank = new Array(n).fill(0);
    }

    // 查找方法:查找元素 x 所属的集合的代表元素(即根节点)
    find(x) {
        // 路径压缩:如果 x 不是其集合的代表元素,则递归地找到它的代表元素,并在回溯时更新 x 的父节点
        if (this.parent[x] !== x) {
            this.parent[x] = this.find(this.parent[x]);
        }
        return this.parent[x];
    }

    // 合并方法:将元素 x 和 y 所属的两个集合合并为一个集合
    union(x, y) {
        const rootX = this.find(x);
        const rootY = this.find(y);

        if (rootX === rootY) {
            // x 和 y 已经属于同一个集合
            return;
        }

        // 按秩合并:将高度较小的集合合并到高度较大的集合
        if (this.rank[rootX] > this.rank[rootY]) {
            this.parent[rootY] = rootX;
        } else if (this.rank[rootX] < this.rank[rootY]) {
            this.parent[rootX] = rootY;
        } else {
            // 当两个集合的高度相同,选择一个为新的根,高度增加 1
            this.parent[rootY] = rootX;
            this.rank[rootX]++;
        }
    }
}

// 使用示例:
const ds = new DisjointSet(5);  // 创建一个有 5 个元素的并查集

ds.union(0, 1);  // 合并 0 和 1 所属的集合
console.log(ds.find(0) === ds.find(1));  // 输出:true,因为 0 和 1 现在属于同一个集合

ds.union(1, 2);  // 合并 1 和 2 所属的集合
console.log(ds.find(0) === ds.find(2));  // 输出:true,因为 0、1 和 2 现在属于同一个集合

console.log(ds.find(2) === ds.find(3));  // 输出:false,因为 2 和 3 不属于同一个集合

常见题型:

  1. 冗余连接:在一棵树(即,一个无环的无向图)中,有些边被多余地连接了两个节点。给出一个图的边列表,返回这些多余的边。
  2. 朋友圈:存在一个学生之间的朋友关系矩阵,其中M[i][j] = 1表示第i个和第j个学生互为直接朋友,否则不是。求学校中的朋友圈个数。
  3. 岛屿数量:给定一个由'1'(陆地)和'0'(水)组成的二维网格,计算岛屿的数量。
  4. 账户合并:给定一个列表,每个列表中的第一个元素表示用户ID,其余的元素是该用户的邮箱。现在,我们给出这样的用户列表,我们需要合并这些帐户。

这些题目都可以用并查集进行求解,通过使用并查集的findunion操作,可以高效地解决这些问题。