红黑树删除后自平衡的四种情形由replacement的兄弟节点颜色及子节点结构触发:case1兄弟为红;case2兄弟黑且两子均黑;case3兄弟黑、左子红右子黑;case4兄弟黑、右子红。

红黑树删除后自平衡的四种情形怎么触发
红黑树删除节点后,真正需要调整的不是“删了谁”,而是“删完后哪条路径少了一个黑节点”。只要被删节点是黑色,且它的替代节点(replacement)也是黑色(含 nullptr),就会破坏黑高平衡——这时才进入修复逻辑。四种情形全由 replacement 的兄弟节点(sibling)颜色、子节点结构决定,和原删除节点值无关。
常见错误是把“删除节点本身是否为红色”当作判断依据——其实红节点删除后无需修复;真正关键的是:删掉一个黑节点 → 黑高失衡 → 从 replacement 向上修复。
rbtree_delete_fixup 中 case 1 到 case 4 的区分逻辑
修复函数通常以 node(即 replacement)为起点向上迭代,每轮检查其父节点 parent 和兄弟节点 sibling。四种情形按如下顺序判断(顺序不能错):
- case 1:
sibling是红色 → 必须先变色+旋转,把sibling变黑,让新兄弟变黑,才能进入 case 2–4 - case 2:
sibling黑,且两个子节点都黑(或为空)→ 把sibling染红,node上推至parent,继续循环 - case 3:
sibling黑,sibling->left红、sibling->right黑 → 先右旋sibling,交换颜色,转为 case 4 - case 4:
sibling黑,sibling->right红 → 左旋parent,sibling取代parent位置,染成parent原色,parent染黑,sibling->right染黑 → 修复完成
注意:sibling->left 和 sibling->right 是否为 nullptr 要用指针比较(如 sibling->right == nullptr),不能只判颜色,否则空指针访问。
为什么删除实现比插入更难调试
插入最多引发一次双红冲突,路径唯一、方向明确;而删除可能连续触发多轮修复(case 2 可能循环多次),且 node 在过程中可能从叶子升到根,甚至变成 nullptr(当删的是根且无子时,replacement 为 nullptr)。调试时容易卡在:
-
node == nullptr却还去取node->color→ 访问违规 - 旋转后没更新
parent或root指针 → 树断裂 - 忘记在 case 1 后重新获取
sibling(因为父节点变了,兄弟也变了) - 染色顺序错:比如在 case 4 中先染黑
parent再染黑sibling->right,但此时sibling->right已是新节点的左孩子,颜色语义已变
C++ 实现中必须处理的边界与 RAII 细节
标准实现里,nullptr 节点不分配内存,用静态常量节点(如 static const Node s_nil)代替,所有叶子指向它。这意味着:
- 所有对
left/right的访问前,必须用!= &s_nil判断,而非!= nullptr - 删除后释放节点内存时,绝不能 delete
&s_nil—— 它是 static 对象 - 旋转函数中若传入
&s_nil作参数,需确保不对其parent、left、right成员赋值(否则改写只读内存) - 修复循环结束条件是
node->color == RED或node == &s_nil,后者表示整棵树黑高已补平
最易忽略的是:当 node == &s_nil 且进入 case 2 时,sibling 染红后,node 赋值为 node->parent,而 &s_nil->parent 是未定义行为——所以实际代码中,必须在循环开头加 if (node == &s_nil) break;,或统一用哨兵节点并保证其 parent 始终有效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











