红黑树删除节点为什么比插入复杂?如何调整?
简化版
删除先按 BST 规则转化成「删一个至多一个孩子的节点」。删红节点不影响黑高,直接删完事;删黑节点会让那条路径少一个黑节点,破坏「黑高相同」——这就是复杂的根源,叫「双黑(double black)」问题。修复要看兄弟节点及其孩子的颜色分多种情况,通过旋转 + 变色把缺失的黑色「补回来」或「向上转移」,最坏调整到根。红黑树删除最多 3 次旋转。
详细版
删除流程:
-
BST 删除:有两个孩子时用中序后继替换,转化为删除一个「至多一个孩子」的节点 X。
-
看被删节点/替身的颜色:
- 删的是红节点 → 直接删,黑高不变,无需修复。
- 删的是黑节点、且有一个红孩子顶替 → 把顶替的孩子染黑即可补上缺失的黑。
- 删的是黑节点、且没有红孩子顶替 → 出现**「双黑」**,需要复杂修复。
-
双黑修复:设双黑节点为 X、其兄弟为 S,按 S 及 S 的孩子颜色分情况(简述):
- S 红:旋转父节点、交换父与 S 颜色,转化成 S 为黑的情况。
- S 黑,且 S 的两个孩子都黑:把 S 染红,把「双黑」上移到父节点,继续向上处理。
- S 黑,且 S 靠外的孩子红:旋转 + 变色,一次消除双黑(终止情况)。
- S 黑,且 S 靠内的孩子红、外侧黑:先旋转 S 转成上一种,再处理。
完整版教学
一、为什么删除比插入复杂
插入新节点染红,最多破坏「红不相邻」,修复方式相对统一。而删除的麻烦在于:如果删掉的是一个黑色节点,那条路径的黑节点数就少了 1,破坏了「性质 5:黑高相同」。黑高是全局对齐的约束,少一个黑节点意味着「这条路径比其他路径少了一层黑」,必须想办法补回来——要么从别处「借」一个黑,要么把「缺黑」的问题往上转移。这种「某节点额外背负一个需要偿还的黑色」的状态就是双黑(double black),它的情况分类比插入更多、更细。
二、先化简:转成删除至多一个孩子的节点
和 BST 删除一样,删除有两个孩子的节点时,用中序后继(右子树最小节点)的值替换,然后转为删除那个后继。后继至多一个孩子。所以最终真正「物理删除」的,总是一个至多一个孩子的节点,这简化了后续的颜色讨论。
三、三种收尾情况
对那个至多一个孩子的待删节点 X:
- X 是红色:直接删除。红节点不计入黑高,删了不影响性质 5,且它的父子都是黑(红不相邻),也不产生双红。最简单。
- X 是黑、但有一个红孩子:让红孩子顶替 X 的位置,并染黑。这个孩子从红变黑,正好补上 X 被删后缺失的那个黑,黑高恢复。
- X 是黑、且孩子也是黑(NIL):删掉后这条路径凭空少一个黑,孩子(NIL)背上「双黑」,进入修复流程。
四、双黑修复的核心思想
双黑修复的目标是消除那个多出来的黑色债务,手段是观察兄弟 S能否「贡献一个黑色」:
- 如果兄弟的孩子里有红色,就能通过旋转把这个红色转过来染黑,补上缺失的黑——问题就地解决(终止情况,最多再旋转 1~2 次)。
- 如果兄弟和它的孩子全黑,无处可借,就把兄弟染红(让兄弟那侧也「减一个黑」以保持两侧平衡),然后把双黑上移到父节点,继续向上修复——这类似插入时「叔叔红」的向上传播。
- 如果兄弟是红,先旋转把它变成黑兄弟的情形再处理。
最坏情况下双黑一路上移到根,此时直接把根的多余黑色「吸收」掉即可(根的黑高对所有路径一视同仁),修复结束。
五、复杂度与工程取舍
- 时间 O(log n):删除下降 + 双黑最多上移 O(log n) 层。
- 旋转次数:最多 3 次(仍是常数级;对比 AVL 删除最坏 O(log n) 次旋转)。
- 变色可能沿路径 O(log n) 次,但都是 O(1)。
正因为红黑树删除的旋转次数是常数上界(≤3),而 AVL 删除可能一路旋转到根,红黑树在「增删频繁」的通用容器里更受青睐。删除逻辑虽然分类繁琐,但记住「删红无害、删黑生双黑、双黑靠兄弟借黑或上移」这条主线即可。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 删除红节点 | 通常直接删,不破坏黑高 |
| 删除黑节点 | 可能造成黑高亏损,需要双黑修复 |
| 修复手段 | 兄弟颜色、侄子颜色决定变色或旋转 |
delete black node -> double black
case sibling red: rotate + recolor
case sibling black: recolor or rotate based on nephews
红黑树删除复杂,核心原因是删除黑节点会破坏每条路径黑节点数相同。
- 误区:删除和插入的修复难度差不多。 插入主要解决红红冲突,删除黑节点还要处理黑高亏损,情况更多。
- 误区:删除红节点也一定要复杂修复。 红节点不贡献黑高,删除叶子红节点通常不会破坏黑高性质。
- 误区:双黑是一个真实颜色。 双黑是修复过程中的抽象状态,表示某条路径少了一个黑节点。
- 追问:为什么先转成删除至多一个孩子的节点? 和 BST 删除一样,用后继/前驱替换后,真正删除的位置更容易处理。
- 追问:兄弟为红时为什么先旋转? 旋转和变色把局面转成兄弟为黑的标准情况,再继续按侄子颜色处理。
- 追问:复杂度是多少? 修复沿祖先路径向上,最多 O(log n),旋转次数有上界但变色可能向上传播。
七、加强记忆
红黑树删除比插入复杂,因为删黑节点会破坏「黑高相同」、产生双黑。流程:BST 删除(两孩子用中序后继替换)→ 删红直接删;删黑有红孩子就把孩子染黑补上;删黑无红孩子则双黑。双黑修复看兄弟 S:兄弟孩子有红 → 旋转变色就地解决;兄弟全黑 → 兄弟染红、双黑上移;兄弟红 → 先转成黑兄弟。最多 3 次旋转、O(log n)。主线:删红无害、删黑生双黑、双黑向兄弟借黑或上移。