parent[x] = find(parent[x]) 是危险写法,因C++不保证赋值右操作数先求值,可能导致读取旧值、中间节点未更新、路径压缩失效;必须拆为两步:int root = find(parent[x]); parent[x] = root; return root。

为什么 parent[x] = find(parent[x]) 是危险写法
C++ 标准不保证赋值表达式右边先求值,parent[x] = find(parent[x]) 可能导致 parent[x] 被读取后、find 执行前,parent[x] 已被其他路径修改(尤其在多线程或复杂调用链中),更关键的是:即使单线程,该写法也**无法保证递归返回后才完成赋值**,压缩逻辑可能被跳过。
常见错误现象:find 返回正确根,但中间节点的 parent 未更新,下次查同一节点仍走长链;性能测试显示均摊时间未改善,甚至退化。
- 必须显式拆成两步:
int root = find(parent[x]); parent[x] = root; return root; - 递归终点必须是
parent[x] == x,不能只靠!=判断,避免空指针或越界(尤其未初始化时) - 若担心栈溢出(节点 > 10⁵),改用迭代版:
find先循环找根,再第二遍循环设父指针
unionSet 里必须检查 find(a) != find(b)
合并前不判连通性,会导致 count 错误递减、size 累加失真、甚至 parent[root] = root 自环(虽不影响功能,但污染结构)。
使用场景:Kruskal 算法中重复边、社交网络中冗余好友请求、岛屿合并时相邻格子已属同一连通块。
立即学习“C++免费学习笔记(深入)”;
- 正确流程:
int ra = find(a), rb = find(b); if (ra == rb) return false; - 按大小合并优先于按秩合并:
size始终精确,支持get_size(x)查询;rank仅作上界估计,压缩后失效 - 合并时只动根节点:
if (size[ra]
find 必须承担全部路径压缩责任
路径压缩不是“可选优化”,而是每次 find(x) 的副作用。它不能推迟到 unionSet 里做——因为合并只碰两个根,对路径上非根节点完全不可见。
典型错误:while 版 find 只找根但没改沿途 parent;或递归版漏掉 return parent[x] 导致压缩结果丢失。
- 迭代安全写法:第一遍找根,第二遍从
x出发,用临时变量存next,逐个设parent[x] = root - 递归简洁写法:
if (parent[x] != x) parent[x] = find(parent[x]); return parent[x];—— 注意不是return find(...) - 别在
unionSet里调find后再手动压路径,纯属重复劳动且易遗漏节点
容易被忽略的边界和调试陷阱
实际编码时最常踩的坑不在算法逻辑,而在细节处理:
- 初始化
parent和size必须严格对应:若用size,初始值全为1,不是0或未定义 - 索引越界:
find(-1)或find(n)不做校验,直接访问parent数组 → 段错误或静默数据污染 - 内联缺失:
find和unionSet必须加inline,否则函数调用开销在高频查询下显著 - 别用
map存稀疏编号——除非真有动态插入/删除需求,否则vector预分配 + 连续内存访问快一个数量级


















