C++实现并查集算法 _ 路径压缩与按秩合并优化【源码】

浅浩君_7960

浅浩君_7960

2026-04-13

876人浏览

原创

c++实现并查集最稳妥的方式是用vector存储parent和rank数组,下标为节点编号,值为父节点下标或秩;需先find找根再合并,配合路径压缩与按秩合并才能保证o(α(n))效率。

c++实现并查集算法 _ 路径压缩与按秩合并优化【源码】

为什么直接用 vector 存 parent 数组比指针/动态分配更稳妥

很多初学者会为每个节点 new 一个结构体,或者用 vector<node></node>,结果在合并时搞乱指针、忘记释放、或触发浅拷贝问题。C++ 实现并查集最稳的起点是 vector<int></int> 存根索引——下标即节点编号(0-based 或 1-based 一致即可),值为父节点下标。

这样做既避免内存管理开销,又天然支持随机访问,路径压缩时循环改父指针毫无压力。按秩合并用的 rank 数组也同理,vector<int></int> 足够。

  • 别用 map<int int></int> 模拟 parent:稀疏节点看似灵活,但 find() 平均变慢,且破坏了“连续编号→O(1)访问”的前提
  • 如果节点编号不连续(比如 ID 是大整数),先离散化再映射到 0~n-1,而不是硬套 map
  • vector 初始化大小必须 ≥ 最大可能节点数;若运行中动态加点,需预留或用 reserve() 减少重分配

find() 里递归 vs 迭代实现路径压缩的区别

递归写法简洁,但深树可能栈溢出(比如 10⁵ 节点链状结构);迭代虽多几行,但安全可控,且更容易看出压缩逻辑:一路向上找根,再倒回来统一设父为根。

关键不是“压不压缩”,而是“是否所有经过节点都直连根”。有些代码只改了当前节点的父,漏掉中间层,那就没真正压缩。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • 错误示范:parent[x] = find(parent[x]); return parent[x]; —— 看似递归压缩,但 C++ 中未加记忆化时可能重复调用,且部分编译器优化后行为不可靠
  • 推荐迭代:int root = x; while (root != parent[root]) root = parent[root]; while (x != root) { int next = parent[x]; parent[x] = root; x = next; }
  • 别在 find() 里修改 rank:rank 只在 unionSet() 中更新,且只更新根节点的值

unionSet() 中按秩合并的“秩”到底该存什么

这里的“秩”不是真实树高,而是**上界估计值**,用于控制合并方向。它只在两子树根秩相等时才对新根 +1,其余情况不更新。误把它当实际高度去维护(比如每次合并后重算),反而破坏 O(α(n)) 复杂度保证。

常见误区是把 rank 和节点数量(size)混用。按大小合并(union-by-size)也可行,但和按秩合并(union-by-rank)是两种独立优化策略,不要在一个实现里同时改 rank 和 size 又不区分用途。

  • 按秩合并:初始化 rank[i] = 0;合并时,让 rank 小的根挂到 rank 大的根下;相等时任选其一为根,并对其 rank++
  • 按大小合并:初始化 size[i] = 1;合并时,小 size 树挂到大 size 树下,后者 size += former_size
  • 二者不能混用:比如用 rank 数组却按 size 比较,会导致压缩失效或复杂度退化

合并操作前忘了 find() 查根,导致逻辑错误

这是最隐蔽也最高频的 bug:直接对两个原始输入节点 x 和 y 做 unionSet(x, y),但没先调 find(x) 和 find(y) 找各自所在集合的根。结果可能把两个本就同属一棵树的节点再次合并,或者挂错层级,破坏树结构。

正确流程永远是:先 find 根,再比较是否同根(是则跳过),否则按秩/大小合并两根。

  • 错误写法:if (rank[x] —— 此时 <code>x 和 y 很可能不是根,rank[x] 完全无意义
  • 正确骨架:int rx = find(x), ry = find(y); if (rx == ry) return; if (rank[rx] rank[ry]) parent[ry] = rx; else { parent[ry] = rx; rank[rx]++; }
  • 调试技巧:打印每次 unionSet 前后的 find(x) 和 find(y),确认它们是否真为根;跑小样例(如 4 个点连成环)立刻暴露问题
路径压缩和按秩合并必须配合使用才能稳定达到反阿克曼函数级别效率;单独用任一个,最坏仍是线性。而真正容易被忽略的,是每次 unionSet 都要基于根节点操作——这个前提一旦松动,所有优化都白搭。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.25

354

5

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

2025.06.09

4714

18

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

2025.07.04

4871

25

c语言union的用法
c语言union的用法

c语言union的用法是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型,union的使用可以帮助我们节省内存空间,并且可以方便地在不同的数据类型之间进行转换。使用union时需要注意对应的成员是有效的,并且只能同时访问一个成员。本专题为大家提供union相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.27

709

5

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

6359

3

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.08.29

3085

6

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

2025.08.29

3988

10

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2025.08.29

2765

10

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

359

31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习