红黑树删除后修复的核心是处理「双黑」状态而非直接重平衡,因其源于用黑色叶子替换节点导致黑高减少;修复通过将双黑沿父链上传或借红侄子染黑来消除。

红黑树删除后的修复逻辑不是靠死记六种情形,而是靠理解「双黑」传播路径和兄弟节点的结构分类;直接硬背 case 容易在 rotate 方向、color 更新时机、以及 parent 指针归属上出错。
为什么删除后要处理「双黑」而不是直接重平衡
红黑树删除非叶子节点时(比如删一个有两个子节点的节点),实际替换的是它的中序后继(或前驱),而这个后继几乎总是黑色叶子(TNULL)。一旦用它替换原节点,原位置就「少了一层黑高」——表现为该位置临时拥有「双重黑色」标记(概念性,不真存两个 black)。修复目标就是把这层多余的黑向上/向旁转移,直到:① 到达根节点(可直接去掉双黑),或 ② 找到一个红色侄子来染黑补位,或 ③ 兄弟是红,先转成黑兄弟再继续。
- 「双黑」不是真实颜色值,是修复过程中的状态标记,代码里通常用指针迭代 + 条件分支模拟
- 所有六种情形本质是「当前节点 x 是双黑,其父 p 和兄弟 w 的颜色组合 + w 孩子结构」的穷举
- 真正关键的分界点只有两个:
w->color == RED(case 1)和w->right->color == BLACK(决定是否先做一次旋转)
case 1(兄弟为红)必须先转再进 case 2–6
这是唯一需要先 rotate_left(或 rotate_right)的情形,目的不是平衡,而是把红兄弟变成黑兄弟,让父节点变红——从而让兄弟子树黑高不变,同时制造出「黑兄弟」这个后续可操作的前提。跳过这步直接进 case 2,会导致父节点颜色错乱、w 指针失效。
- 若
p->left == x,则对p做rotate_left,之后新兄弟是原w->left -
w->color和p->color必须交换,否则黑高失衡 - 旋转后不立即退出,而是继续用新兄弟进入 case 2 判断——很多实现漏掉这句
continue或误写成break
case 2(兄弟黑,两侄子黑)只能上推双黑
此时兄弟 w 黑、w->left 和 w->right 都黑,没法借色或借结构,唯一办法是把双黑往父节点推:将 w 染红,x 指向 p,继续向上修复。注意这里 x = x->parent 后,若 x 原来是根,则新 x 是 TNULL(哨兵),循环结束,最后强制设 x->color = BLACK 即可。
- 不能对 w 做任何旋转——它没红孩子,转了也没用
- 必须先设
w->color = RED再移动 x,否则父节点黑高会暂时多 1 - 如果 x 已经是根(
x == root),循环结束后要额外执行root->color = BLACK,否则根可能变红
case 3–6 的核心其实是「找一个红侄子来染黑并旋转收口」
case 3 和 case 4 处理兄弟黑、远侄子黑但近侄子红的情况;case 5 和 case 6 处理远侄子为红的情形。它们的共同动作是:通过一次旋转(case 3/5)+ 颜色翻转(case 4/6),让红侄子上升为新兄弟,并最终在 case 4 或 case 6 中,用它染黑来吸收双黑。
- case 3:w->left 红、w->right 黑 → 对 w
rotate_right,然后 w = w->parent,进入 case 4 - case 4:w->right 红 →
rotate_left(p),然后w->color = p->color,p->color = BLACK,w->right->color = BLACK,修复完成 - 所有旋转都以
p或w为轴,绝不能以x为轴——x 是双黑节点,没有子树可调度
真正难调试的不是逻辑分支,而是指针更新顺序:比如在 case 4 的 rotate_left(p) 后,w 可能已不再是 p->right,若后续还用旧 w 访问字段就会 crash;另外,所有涉及 color 修改的地方,必须确保操作对象非 TNULL,否则解引用空哨兵是常见段错误源。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











