双红冲突指插入节点与其父节点同为红色,必须通过变色优先、旋转次之的局部修复解决,且最终强制根节点染黑。

双红冲突修复不能只靠记忆旋转口诀,必须理解变色与旋转的触发条件和作用边界——否则插入 50 个节点后必在某个祖父节点上卡死。
什么是双红冲突?它只发生在插入路径上
双红冲突指新插入的 node 与其父节点 parent 同时为红色,违反红黑树“红节点子节点必为黑”的规则。注意:它不检查叔节点颜色,也不关心祖父是否为根——只要 parent->color == RED && node->color == RED 就触发修复。
常见错误现象:insert(10); insert(5); insert(15); insert(3); insert(7); insert(12); insert(17); insert(1); 执行后树结构合法但颜色错乱,后续查找或删除报段错误。
- 只在
insert_fixup中处理,删除时不出现双红冲突 - 冲突一定出现在从新节点向根回溯的某一层,且仅需修复当前局部(最多影响祖父、叔、曾祖父)
- 叔节点
uncle为nullptr时视为黑色,不是“不存在就跳过”
变色优先于旋转:四种 case 的本质是“叔节点颜色 + 插入方向”组合
标准教材把修复分为 4 种 case,本质只有两个判断维度:uncle != nullptr && uncle->color == RED(变色分支),否则进入旋转分支。旋转分支再按 node 相对于 parent、parent 相对于 grandparent 的左右关系拆解。
关键细节:
- Case 1(变色):
uncle为红 → 父、叔变黑,祖父变红;然后将grandparent设为当前节点继续向上检查 - Case 2/3(旋转):
uncle为黑或空 → 若node是parent的右孩子而parent是grandparent的左孩子,则先左旋parent,再把node指向parent(即变成 Case 3) - Case 3(最终旋转):此时
node和parent在同一侧 → 右旋grandparent,再交换parent和grandparent颜色
示例(Case 2 → Case 3 转换):
G(B) G(B) P(B) / \ / \ / \ P(R) U(B) → N(R) U(B) → N(R) G(R) / / \ N(R) P(R) U(B)
旋转操作必须原子更新 parent 指针,否则 parent->left/right 悬空
左旋 rotate_left(x) 不只是交换 x 和 x->right,核心是三步指针重连:x->right->left 挂到 x 右子树,x 挂到 x->right 左子树,x->right->parent 改为 x->parent。漏掉任意一步都会导致后续访问 nullptr->left。
易踩坑点:
- 旋转前未保存
x->right(即y),后续用x->right->left时若x->right已被改写,行为未定义 - 未更新
y->parent:若x是根,y->parent应设为nullptr;否则要根据x是左/右孩子设置y在祖父中的位置 -
rotate_right中y->right非空时,必须设y->right->parent = x,否则该子树脱离控制
根节点强制染黑是兜底动作,不是修复逻辑的一部分
所有 insert_fixup 流程结束后,必须执行 root->color = BLACK。这不是可选优化,而是因为变色过程中祖父可能被染红,而根没有父节点来触发上层修复。
这个动作容易被忽略,尤其在递归实现或提前 return 的分支里。测试时可用以下最小反例验证:
- 插入单个节点 → 根应为黑;若没强制染黑,
root->color == RED,直接违反红黑树定义 - 插入
5, 3, 7, 2后,根仍为黑;但插入5, 3, 7, 2, 1后,若无最后染黑,根可能为红
真正的难点不在旋转方向判断,而在于每一步指针更新是否覆盖全部关联节点——少一个 parent 赋值,整棵树就失去自平衡能力。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











