并查集是一个常用的数据结构,用于处理不交集的合并与查询问题。以下是一个基础的并查集的代码模板及详细注释:
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 不属于同一个集合
常见题型:
- 冗余连接:在一棵树(即,一个无环的无向图)中,有些边被多余地连接了两个节点。给出一个图的边列表,返回这些多余的边。
- 朋友圈:存在一个学生之间的朋友关系矩阵,其中M[i][j] = 1表示第i个和第j个学生互为直接朋友,否则不是。求学校中的朋友圈个数。
- 岛屿数量:给定一个由'1'(陆地)和'0'(水)组成的二维网格,计算岛屿的数量。
- 账户合并:给定一个列表,每个列表中的第一个元素表示用户ID,其余的元素是该用户的邮箱。现在,我们给出这样的用户列表,我们需要合并这些帐户。
这些题目都可以用并查集进行求解,通过使用并查集的find和union操作,可以高效地解决这些问题。