并查集
什么是并查集?
实现一个共用的群组之集合的类,通过群主表示不同的群组,当且仅当群主是自己表示孤立节点或者群主节点,每个人都有一个主,修改了主只需要修改一个,在查的时候按照主的主来寻找最终的主,最开始每个人的主是自己。
核心操作
- Find:查找元素所属集合的代表元(根节点)
- Union:合并两个元素所在的集合
想象你有一堆人(节点),他们之间会互相认识(连接)。并查集就是用来:
- 快速判断两个人是否互相认识(直接或间接)
- 让两个不认识的人变成认识(把他们的朋友圈合并)
生活中的例子
假设班上有5个同学:
- 小明、小红、小刚、小丽、小强
初始状态:每个人只认识自己
第一步:建立朋友圈
- 小明和小红成为朋友 → 他们现在是一个朋友圈
- 小刚和小丽成为朋友 → 另一个朋友圈
- 小强自己一个人 → 单独的朋友圈
现在朋友圈情况:
- 朋友圈1:小明、小红
- 朋友圈2:小刚、小丽
- 朋友圈3:小强
第二步:朋友的朋友
小红认识了小刚 → 现在小红的朋友圈和小刚的朋友圈合并
现在朋友圈情况:
- 大朋友圈:小明、小红、小刚、小丽
- 单独朋友:小强
第三步:查询是否认识
- 小明认识小丽吗? → 是的(通过小红和小刚)
- 小明认识小强吗? → 不认识
代码实现(最简单版)
1 | class FriendCircle: |
关键点总结
- **找上级(find)**:一直向上找,直到找到终极老大(朋友圈代表)
- **交朋友(union)**:让两个人的终极老大变成同一个人
- **查关系(is_connected)**:看两个人的终极老大是不是同一个人
典型应用
- 动态连通性问题
- 图的连通分量检测
- 最小生成树算法(Kruskal算法)
- 网络连接判断
- 图像处理中的像素连通区域
优缺点:只适合维护连通性,不适合删除
时间复杂度分析
并查集的效率取决于 路径压缩(Find优化) 和 按秩合并(Union优化)。
操作 | 普通并查集 | 路径压缩 | 路径压缩 + 按秩合并 |
---|---|---|---|
初始化 | O(N) | O(N) | O(N) |
Find | O(N) | O(α(N)) | O(α(N)) |
Union | O(N) | O(α(N)) | O(α(N)) |
其中:
- α(N) 是 反阿克曼函数,增长极其缓慢(对于任何实际应用的N,α(N) ≤ 5)。
- 因此,**优化后的并查集操作接近 O(1)**。
并查集 vs. 其他数据结构对比
操作 | 并查集 | DFS/BFS | 动态图(Link-Cut Tree) |
---|---|---|---|
查询连通性 | ✅ O(α(N)) | ✅ O(V+E) | ✅ O(log N) |
查询路径 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
动态加边 | ✅ 高效 | ❌ 每次O(V+E) | ✅ 高效 |
动态删边 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
查询集合大小 | ⚠️ 需额外维护 | ❌ 不适合 | ⚠️ 需额外维护 |
遍历集合元素 | ❌ 不高效 | ✅ 可以 | ✅ 可以 |
3. 什么时候避免使用并查集?
- 需要查询具体路径 → 用 DFS/BFS。
- 需要频繁删除边 → 用 动态图算法(如 Link-Cut Tree)。
- 需要实时查询集合大小或元素 → 用 哈希表 + 并查集扩展。
- 需要切分集合 → 用 离线处理或高级数据结构。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Min的博客!
评论