数据结构与算法 -> 并查集
一、并查集概念
- 并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的合并及查询问题。 它支持两种操作:
- 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 O(α(n)),即查询元素p和元素q是否属于同一组
- 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 O(α(n)),即合并元素p和元素q所在的组
- 以下是并查集的常用模板,需要熟练掌握。其中:
- n 表示节点数
- p 存储每个点的父节点,初始时每个点的父节点都是自己
- size 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
- find(x) 函数用于查找 x 所在集合的祖宗节点
- union(a, b) 函数用于合并 a 和 b 所在的集合
二、并查集模板
1、模板一
// 并查集类。需要维护一个数组和两个方法,find()和union()
class UnionFind {
constructor(n) {
this.arr = []
// 初始默认为每个人是独立圈子,则他的父级就是他自身
for (let i = 0; i < n; i++) {
this.arr[i] = i
}
}
// 直到arr[x] === x,停止向上搜索
find(x) {
let arr = this.arr
while (arr[x] !== x) {
x = arr[x]
}
return arr[x]
}
// 路径压缩
union(x, y) {
let xFather = this.find(x)
let yFather = this.find(y)
if (xFather !== yFather) {
this.arr[xFather] = yFather
}
}
}
2、模板二
class UnionFind {
constructor(size) {
this.fa = []
this.size = size
this.init()
}
// 初始化 每个元素的父节点为自身
init() {
for(let i = 0; i < this.size; i++) {
this.fa[i] = i
}
}
// 递归找到根节点,同时进行路径压缩
find(x) {
if(x === this.fa[x]) {
return x
}
this.fa[x] = this.find(this.fa[x])
return this.fa[x]
}
// 合并 x, y 直到各自的根节点, 其中一个的指向另一个
merge(x, y) {
let fx = this.find(x)
let fy = this.find(y)
if(fx !== fy) {
this.fa[fx] = fy
}
}
// 获取集合数量
getCount() {
let count = 0
for(let i = 0; i < this.size; i++) {
if(this.fa[i] === i) {
count++
}
}
return count
}
}
3、模板三
// 这个并查集使用了一种叫做路径压缩的优化策略,可以有效减少查找操作的时间复杂度
class UnionFind {
constructor(n) {
this.count = n;
this.parent = [];
this.rank = [];
for (let i = 0; i < n; i++) {
this.parent[i] = i;
this.rank[i] = 1;
}
}
// 查找元素 p 所在的集合编号
find(p) {
if (p !== this.parent[p]) {
this.parent[p] = this.find(this.parent[p]);
}
return this.parent[p];
}
isConnected(p, q) {
return this.find(p) === this.find(q);
}
// 将元素 p 和元素 q 所在的集合合并
unionElements(p, q) {
const pRoot = this.find(p);
const qRoot = this.find(q);
if (pRoot === qRoot) {
return;
}
if (this.rank[pRoot] < this.rank[qRoot]) {
this.parent[pRoot] = qRoot;
} else if (this.rank[qRoot] < this.rank[pRoot]) {
this.parent[qRoot] = pRoot;
} else {
this.parent[pRoot] = qRoot;
this.rank[qRoot] += 1;
}
}
}
三、力扣例题
- 1971. 寻找图中是否存在路径 - 力扣(LeetCode)
- 130. 被围绕的区域 题解 - 力扣(LeetCode)
- 1361. 验证二叉树 题解 - 力扣(LeetCode)
- 785. 判断二分图 题解 - 力扣(LeetCode)
- 695. 岛屿的最大面积 题解 - 力扣(LeetCode)
四、PDF参考资料
百度网盘:https://pan.baidu.com/s/1FzPwQKEEoSeEWFX-7A104A?pwd=hcfr