算法笔记 / A LEAF IN THE GARDEN
并查集进阶
算法笔记 · 数据结构 / 集合 / 并查集。来自 Daily Learning 的学习笔记。
维护连通块信息
核心
将每个并查集(即连通块)的信息存储在根节点上,并在合并时更新。
常见
假设合并p[rx] = ry;
-
连通块大小
sz[ry] += sz[rx]; -
块内最大值(最小值同理)
mx[ry] = max(mx[ry], mx[rx]); -
块内和
sum[ry] += sum[rx];……
带权并查集
核心
在普通并查集的基础上增加权值数组,记录每个节点到父节点(或根节点)的权值。
作用
处理一些需要维护元素间关系的场景,如距离、差值、互斥等。
操作
: a到b的权值 : 父节点 parent : 根节点 root : 权值 weight
初始化
// p: 父节点数组,w: 权值数组
vector<int> p(n), w(n, 0);
// 父节点初始化为自己,权值初始化为0
for(int i = 0; i < n; i++) p[i] = i;
Find
- 更新逻辑
- 新父节点 = 根节点 更新为 权值
- 原为 权值
- 递归栈结构,保证已更新为 权值
- 权值
- 更新顺序
- 先更新权值,此时父节点还是原来的父节点
- 后路径压缩,父节点更新为根节点
int find(int x){
if(p[x] != x){
// 找到根节点
int t = find(p[x]);
// 先更新权值(保证父节点有效)
w[x] += w[p[x]];
// 后路径压缩
p[x] = t;
}
return p[x];
}
Union
更新逻辑
为 权值 将父节点指向 更新为 权值
- =
- =
// w0为y到x的权值
bool unite(int x, int y, int w0){
// 找到根节点
int rx = find(x);
int ry = find(y);
// 已在同一集合,检查权值合法
if(rx == ry) return w[y] - w[x] == w0;
// 不在同一集合,先合并集合
// 这里也可以加入优化,如按秩合并
p[ry] = rx;
// 更新权值
w[ry] = w[x] - w[y] + w0;
return true;
}
补充: 这里只演示了加性权值的情况,实际上权值迁移可以是乘、异或等任意运算。
离线并查集
核心
- 先记录所有操作
- 将分离转换为逆向合并
- 或重排序规避分离操作
作用
处理需要动态修改关系、分离集合的场景,如动态连通性、动态图等。
扩展域并查集
核心
- 给每个状态分配一个为 的域
- 元素 的状态 为
- 合并操作时,将两个元素的相同状态域合并
一般取为元素个数
本质
设有 个元素,每个元素有 个状态维度
- 需要维护 个集合,每个集合中元素的状态全部相同
- 这 个集合称为 个域
- 是一种状态编码,通过 访问状态
- 也存在其他编码方式,如维护一个状态数组、或用权值异或传导(仅支持2维)
作用
处理维护元素互斥状态的场景,如组合、染色等。