【算法基础】并查集
并查集
一、并查集的功能
- 将两个集合进行合并。
- 查询两个元素是否属于同一个集合。
- 将两个元素并到成一个集合。
以基本
二、并查集的实现
2.1 实现的基本原理
并查集基本使用用树来维护的,用一个
-
每个独立的集合都是一棵树,在树中,树根的编号就是集合的编号。
-
每个节点存的都是父节点;
存储 的父节点。
2.2 如何判断树根、求得编号
- 如何判断是不是树根
如果上述条件是满足的,就说明
- 如何求得编号是多少
因为
- 如何合并两个集合
此时,有
2.3 路径压缩
在并查集中,最费时间复杂度的一个操作是求得集合编号。
不带路径压缩的情况,是我们的左图情况,在左图中我们们想求得某个点的集合,需要一层一层上去,非常费时间。所以我们要把并查集进行路径压缩成右图的样子,即每个点的父节点就是祖宗节点。这样的话就是
三、并查集实现代码
3.0 并查集初始化
for(int i = 1; i <= n; i++) p[x] = i;
p = [x for x in range(n)]
3.1 判断根节点
if(x == p[x]) return true;
if x == p[x]: return True;
3.2 找元素的集合编号(带路径压缩)
int find(x){
while(x != p[x])
x = p[x] = p[p[x]];
return x;
}
def find(x):
while x != p[x]:
p[x] = p[p[x]]
x = p[p[x]]
return x
# 递归写法
def find(x):
if x != p[x]: p[x] = find(p[x]);
return p[x]
3.3 判断两个元素是否在同一个集合里面
return find(x) == find(y);
3.4 合并两个元素
p[find(x)] = find(y);
3.5 Jiangly DSU板子
3.5.1 带维护集合数量的并查集
/** 并查集(DSU)
* 2023-08-04: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63239142
**/
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
3.5.2 带删除的并查集 (DSU With Rollback)
/** 可撤销并查集(DSU With Rollback)
* 2024-09-17: https://qoj.ac/submission/569639
**/
struct DSU {
std::vector<int> siz;
std::vector<int> f;
std::vector<std::array<int, 2>> his;
DSU(int n) : siz(n + 1, 1), f(n + 1) {
std::iota(f.begin(), f.end(), 0);
}
int find(int x) {
while (f[x] != x) {
x = f[x];
}
return x;
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
if (siz[x] < siz[y]) {
std::swap(x, y);
}
his.push_back({x, y});
siz[x] += siz[y];
f[y] = x;
return true;
}
int time() {
return his.size();
}
void revert(int tm) {
while (his.size() > tm) {
auto [x, y] = his.back();
his.pop_back();
f[y] = y;
siz[x] -= siz[y];
}
}
};
四、并查集维护一些值
在并查集的习题中不一定只会问你存不存在、是不是一个集合,通常要并查集再维护一些值。
- 集合中最大值
- 集合中节点数量
- 集合最小值.......
这里一般都是在合并集合的时候做操作,我们此处以个数为例。
在合并集合的时候,大家肯定会知道,把
如果我们开个
但在此之前,我们需要判断,假如
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
可见,在哥哥的板子中,他已经都封装好了带集合数量的维护。
def merge(x, y):
x = find(x)
y = find(y)
if x == y: return False
cnt[x] += cnt[y] # 将 y 合并到 x 集合中
f[y] = x;
return True
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具