并查集优化有五种方法:一、递归路径压缩+按秩合并;二、迭代路径压缩+按大小启发式合并;三、懒惰单层压缩;四、模板化键类型支持;五、断言驱动的压缩有效性验证。

如果您在实现 C++ 并查集(Union-Find)结构时发现路径压缩未生效、合并逻辑错误或 find 操作时间退化,可能是由于压缩时机不当、递归/迭代实现缺陷或 union 判定条件误用。以下是几种可独立验证的优化思路与对应源码实现:
一、递归路径压缩 + 按秩合并实现
该方法在 find 过程中通过递归回溯一次性将整条路径上的节点直接挂载到根节点下,同时 union 时依据秩(rank)较小树挂入较大树,避免树高增长过快。
1、定义并查集类,包含 parent 和 rank 数组,初始化每个节点父节点为自身,rank 初始化为 0。
2、find 函数采用递归:若 x 不是根,则执行 parent[x] = find(parent[x]),再返回 parent[x]。
立即学习“C++免费学习笔记(深入)”;
3、unionSet 函数先 find(x) 和 find(y),若根不同,则比较 rank[rootX] 与 rank[rootY];秩小者根指向秩大者根;若相等,任选其一作为新根,并将其 rank 加 1。
二、迭代路径压缩 + 启发式合并实现
规避递归调用栈开销与潜在栈溢出风险,使用显式循环完成路径遍历与父指针重写;启发式合并不依赖秩数组,改用子树大小(size)作为合并依据,提升实际性能稳定性。
1、声明 parent 和 size 数组,初始化 size[i] = 1。
2、find 函数使用 while 循环定位根节点,再用第二轮循环将路径上所有节点的 parent 直接设为根。
3、unionSet 中先获取两节点根,若不同,将 size 较小的根的 parent 设为 size 较大的根,并更新后者 size 值。
三、带压缩标记的懒惰路径压缩实现
适用于大规模动态图且 find 调用频次远高于 union 的场景,仅在 find 返回前对当前查询路径做单层压缩(即只将 x 的父节点设为父节点的父节点),不立即递归到底,降低单次 find 开销,后续多次访问自然收敛。
1、find 函数内不递归,仅判断 parent[x] != parent[parent[x]] 时执行 parent[x] = parent[parent[x]]。
2、重复执行该跳转操作直至 parent[x] == parent[parent[x]] 或 parent[x] == x。
3、unionSet 保持基础版本:直接令 parent[rootX] = rootY,不做秩或大小判断。
四、模板化支持任意键类型的并查集封装
突破 int 索引限制,通过 std::map 或 std::unordered_map 映射键值到内部整型 ID,使字符串、pair 等类型可直接参与并查集操作,同时维持路径压缩有效性。
1、维护 map
2、find(const Key& k) 先 ensureId(k) 分配 ID,再对对应 id 执行标准递归路径压缩 find(id)。
3、unionSet(const Key& a, const Key& b) 转换为对应 id 后调用整型 unionSet,ensureId 必须在每次 find/union 前调用以保证 ID 存在。
五、断言驱动的压缩有效性验证模块
在调试模式下插入运行时检查,确保每次 find 后路径长度 ≤ 2,且根节点 parent[root] == root,用于判定路径压缩是否真正生效而非逻辑空转。
1、定义 validatePathLength(int x) 函数,沿 parent 链计数至根,若长度 > 2 则 assert(false)。
2、在 find 返回前调用该验证函数,仅在 _DEBUG 或 ENABLE_VALIDATION 宏定义时启用。
3、添加 printTreeStructure() 辅助函数,输出每个节点的深度与父节点,供人工比对压缩前后差异。


















