一轮
AVL 树:看高度差,失衡就旋转。
红黑树:看颜色冲突,优先变色,必要时旋转。


一、AVL 树调整策略

AVL 树要求:

任意节点的左右子树高度差不能超过 1

通常定义平衡因子:

BF = 左子树高度 - 右子树高度

所以合法范围是:

BF ∈ {-1, 0, 1}

如果某个节点的 BF 变成:

BF = 2   或   BF = -2

就说明该节点失衡,需要旋转。


1. AVL 插入后的调整

插入新节点后,从新节点一路向上回溯,找到第一个失衡节点。

设这个失衡节点为 z,它的较高子节点为 y,新插入节点位于 y 的某一侧。

AVL 插入失衡只有四种情况:


情况 1:LL 型

也叫:

左左型

含义:

新节点插入到了 z 的左子树的左边

结构大概是:

      z
     /
    y
   /
  x

此时:

BF(z) = 2
BF(y) >= 0

调整方法:

对 z 右旋

旋转前:

      z
     /
    y
   /
  x

右旋后:

    y
   / \
  x   z

情况 2:RR 型

也叫:

右右型

含义:

新节点插入到了 z 的右子树的右边

结构大概是:

z
 \
  y
   \
    x

此时:

BF(z) = -2
BF(y) <= 0

调整方法:

对 z 左旋

旋转前:

z
 \
  y
   \
    x

左旋后:

    y
   / \
  z   x

情况 3:LR 型

也叫:

左右型

含义:

新节点插入到了 z 的左子树的右边

结构大概是:

      z
     /
    y
     \
      x

此时:

BF(z) = 2
BF(y) = -1

调整方法是两步:

先对 y 左旋
再对 z 右旋

旋转过程:

      z
     /
    y
     \
      x

先对 y 左旋:

      z
     /
    x
   /
  y

再对 z 右旋:

    x
   / \
  y   z

情况 4:RL 型

也叫:

右左型

含义:

新节点插入到了 z 的右子树的左边

结构大概是:

z
 \
  y
 /
x

此时:

BF(z) = -2
BF(y) = 1

调整方法是两步:

先对 y 右旋
再对 z 左旋

旋转过程:

z
 \
  y
 /
x

先对 y 右旋:

z
 \
  x
   \
    y

再对 z 左旋:

    x
   / \
  z   y

2. AVL 插入调整速查表

失衡类型判断方式结构方向调整方法
LLBF(z)=2 且新节点在左孩子的左边左左对 z 右旋
RRBF(z)=-2 且新节点在右孩子的右边右右对 z 左旋
LRBF(z)=2 且新节点在左孩子的右边左右先左旋左孩子,再右旋 z
RLBF(z)=-2 且新节点在右孩子的左边右左先右旋右孩子,再左旋 z

3. AVL 删除后的调整

AVL 删除比插入稍微麻烦,因为删除一个节点后,可能导致多个祖先节点高度变化。

删除后仍然从被删除位置向上回溯,检查每个祖先节点的平衡因子。

核心判断仍然是四类:


删除后左边太高

也就是:

BF(z) = 2

说明 z 的左子树比右子树高太多。

设 y = z.left。

如果

BF(y) >= 0

这是 LL 型。

调整:

对 z 右旋

如果

BF(y) < 0

这是 LR 型。

调整:

先对 y 左旋
再对 z 右旋

删除后右边太高

也就是:

BF(z) = -2

说明 z 的右子树比左子树高太多。

设 y = z.right。

如果

BF(y) <= 0

这是 RR 型。

调整:

对 z 左旋

如果

BF(y) > 0

这是 RL 型。

调整:

先对 y 右旋
再对 z 左旋

4. AVL 删除调整速查表

失衡节点 z子节点 y 的 BF类型调整方法
BF(z)=2BF(y)>=0LL对 z 右旋
BF(z)=2BF(y)<0LR先对 y 左旋,再对 z 右旋
BF(z)=-2BF(y)<=0RR对 z 左旋
BF(z)=-2BF(y)>0RL先对 y 右旋,再对 z 左旋

插入和删除的旋转类型是一样的。区别是:

插入:通常调整第一个失衡节点后就结束
删除:调整后可能还要继续向上检查

二、红黑树调整策略

红黑树的核心规则是:

  1. 每个节点要么红,要么黑;
  2. 根节点必须是黑色;
  3. 所有 NIL 空叶子视为黑色;
  4. 红节点不能有红孩子;
  5. 从任意节点到其所有后代 NIL 叶子的路径上,黑节点数量相同。

红黑树不像 AVL 那样严格控制高度差,而是通过颜色和旋转保证大致平衡。


1. 红黑树插入后的调整

红黑树插入规则:

新插入的节点默认染成红色

原因是:插入红节点不会立刻改变黑高,比较容易修复。

插入后主要可能破坏这一条规则:

红节点不能有红孩子

也就是出现:

父节点是红色,新节点也是红色

设:

N = 当前节点,也就是新插入节点
P = 父节点
G = 祖父节点
U = 叔叔节点

情况 0:父节点是黑色

如果:

P 是黑色

那就没有违反红黑树性质。

不需要调整。


情况 1:父节点红,叔叔节点红

结构大概是:

      G(B)
     /   \
   P(R)  U(R)
   /
 N(R)

或者对称情况。

问题:

P 和 N 连续红色

修复方法:

P 染黑
U 染黑
G 染红

然后把 G 当作新的当前节点,继续向上检查。

示意:

      G(B)
     /   \
   P(R)  U(R)
   /
 N(R)

变成:

      G(R)
     /   \
   P(B)  U(B)
   /
 N(R)

如果 G 是根节点,最后还要把根染黑。

记忆:

父叔都红:只变色,往上递归

情况 2:父节点红,叔叔节点黑,LL 型

结构:

        G(B)
       /
     P(R)
     /
   N(R)

也就是:

当前节点在祖父节点的左子树的左边

修复方法:

对 G 右旋
P 染黑
G 染红

旋转前:

        G(B)
       /
     P(R)
     /
   N(R)

旋转后:

      P(B)
     /   \
   N(R)  G(R)

记忆:

左左:右旋祖父

情况 3:父节点红,叔叔节点黑,RR 型

结构:

    G(B)
       \
       P(R)
          \
          N(R)

也就是:

当前节点在祖父节点的右子树的右边

修复方法:

对 G 左旋
P 染黑
G 染红

旋转前:

    G(B)
       \
       P(R)
          \
          N(R)

旋转后:

      P(B)
     /   \
   G(R)  N(R)

记忆:

右右:左旋祖父

情况 4:父节点红,叔叔节点黑,LR 型

结构:

        G(B)
       /
     P(R)
       \
       N(R)

也就是:

当前节点在祖父节点的左子树的右边

修复方法分两步:

先对 P 左旋
再按 LL 型处理

过程:

        G(B)
       /
     P(R)
       \
       N(R)

先对 P 左旋:

        G(B)
       /
     N(R)
     /
   P(R)

这就变成了 LL 型。

然后对 G 右旋,并调整颜色:

      N(B)
     /   \
   P(R)  G(R)

记忆:

左右:先左旋父,再右旋祖父

情况 5:父节点红,叔叔节点黑,RL 型

结构:

    G(B)
       \
       P(R)
      /
    N(R)

也就是:

当前节点在祖父节点的右子树的左边

修复方法分两步:

先对 P 右旋
再按 RR 型处理

过程:

    G(B)
       \
       P(R)
      /
    N(R)

先对 P 右旋:

    G(B)
       \
       N(R)
          \
          P(R)

这就变成了 RR 型。

然后对 G 左旋,并调整颜色:

      N(B)
     /   \
   G(R)  P(R)

记忆:

右左:先右旋父,再左旋祖父

2. 红黑树插入调整速查表

父节点叔叔节点类型调整策略
黑色任意无冲突不调整
红色红色变色型父、叔染黑,祖父染红,然后继续向上检查
红色黑色LL祖父右旋,父染黑,祖父染红
红色黑色RR祖父左旋,父染黑,祖父染红
红色黑色LR先父左旋,转成 LL,再祖父右旋
红色黑色RL先父右旋,转成 RR,再祖父左旋

插入修复最后一定要做:

根节点染黑

3. 红黑树删除后的调整

红黑树删除比插入复杂,关键是看被删除节点的颜色。

删除时一般先按二叉搜索树删除:

  1. 如果删除节点有两个孩子,通常用它的中序后继或前驱替代;
  2. 最终真正被删除的节点,通常会变成一个最多只有一个孩子的节点。

重点看:

真正被删除的节点是什么颜色?

情况 1:删除的是红色节点

如果真正删除的是红节点,通常不需要调整。

原因:

删除红节点不会改变任何路径上的黑节点数量

直接删除即可。


情况 2:删除的是黑色节点,替代节点是红色

如果删除的是黑节点,但它的替代节点是红色:

删除黑节点后,路径少了一个黑色

修复方法:

把替代节点染黑

这样黑高恢复。


情况 3:删除的是黑色节点,替代节点也是黑色或 NIL

这是最麻烦的情况。

可以理解为出现了一个:

双重黑色 double black

意思是:某条路径上少了一个黑节点,需要向上修复。

设:

X = 当前双重黑节点
P = X 的父节点
S = X 的兄弟节点

还要看兄弟节点的两个孩子。

为了方便描述,先假设 X 是父节点 P 的左孩子:

      P
     / \
    X   S
       / \
     SL   SR

其中:

S  = sibling,兄弟节点
SL = 兄弟的左孩子,近侄子
SR = 兄弟的右孩子,远侄子

如果 X 是右孩子,所有左右情况对称。


删除修复情况 3.1:兄弟节点是红色

结构:

      P(B)
     /   \
    X    S(R)
        /   \
      SL(B) SR(B)

当兄弟是红色时,说明父节点一定是黑色,兄弟的孩子也一定是黑色。

修复方法:

S 染黑
P 染红
对 P 左旋

旋转后,原来的红兄弟变成新的父节点。

这个操作的目的不是直接结束,而是把情况转换成:

兄弟节点是黑色

方便继续处理。

记忆:

兄弟红:先旋转换兄弟

删除修复情况 3.2:兄弟黑,兄弟的两个孩子都黑

结构:

      P
     / \
    X   S(B)
       /   \
    SL(B) SR(B)

修复方法:

S 染红

含义是:把兄弟这边也减少一个黑色,使两边黑高重新一致。

但是这样会让父节点 P 所在路径少一个黑色,所以:

  • 如果 P 是红色,把 P 染黑,修复结束;
  • 如果 P 是黑色,把 P 当成新的双重黑节点,继续向上修复。

记忆:

兄黑侄黑:兄弟染红,问题上移

删除修复情况 3.3:兄弟黑,近侄子红,远侄子黑

对于 X 是左孩子的情况:

      P
     / \
    X   S(B)
       /   \
    SL(R) SR(B)

这里 SL 是近侄子,SR 是远侄子。

修复方法:

SL 染黑
S 染红
对 S 右旋

这样会转换成情况 3.4:远侄子红。

记忆:

近红远黑:先旋兄弟,转成远红

删除修复情况 3.4:兄弟黑,远侄子红

对于 X 是左孩子的情况:

      P
     / \
    X   S(B)
       /   \
    SL   SR(R)

修复方法:

S 染成 P 的颜色
P 染黑
SR 染黑
对 P 左旋

修复结束。

记忆:

远侄子红:旋父节点,修复结束

4. 红黑树删除调整速查表

假设 X 是左孩子,S 是右兄弟:

兄弟 S兄弟孩子情况调整策略
红色必有黑孩子S 染黑,P 染红,对 P 左旋
黑色两个孩子都黑S 染红,问题上移到 P
黑色近侄子红,远侄子黑近侄子染黑,S 染红,对 S 右旋
黑色远侄子红S 染成 P 的颜色,P 染黑,远侄子染黑,对 P 左旋

如果 X 是右孩子,就把表里的左右全部反过来:

兄弟 S兄弟孩子情况调整策略
红色必有黑孩子S 染黑,P 染红,对 P 右旋
黑色两个孩子都黑S 染红,问题上移到 P
黑色近侄子红,远侄子黑近侄子染黑,S 染红,对 S 左旋
黑色远侄子红S 染成 P 的颜色,P 染黑,远侄子染黑,对 P 右旋

三、AVL 和红黑树调整策略对比

对比点AVL 树红黑树
维护目标严格高度平衡近似平衡
判断依据平衡因子 BF节点颜色、黑高
插入后主要问题某个节点高度差超过 1出现红红冲突
删除后主要问题高度减少导致祖先失衡黑高减少导致双重黑
常见调整旋转为主变色为主,旋转辅助
插入旋转次数最多一次单旋或双旋最多两次旋转
删除调整可能一路向上调整可能一路向上调整
查询效率更稳定,树更矮略松,但调整成本低
常见应用读多写少、对查询性能敏感写入删除较多、工程实现常见

四、最实用的记忆方法

AVL 树记忆

AVL 只看方向:

LL:右旋
RR:左旋
LR:先左旋,再右旋
RL:先右旋,再左旋

可以记成:

外侧插入:单旋
内侧插入:双旋

所谓外侧:

LL、RR

所谓内侧:

LR、RL

红黑树插入记忆

红黑树插入时,先看父节点:

父黑:结束
父红:看叔叔

然后:

叔叔红:变色,上移
叔叔黑:旋转 + 变色

方向判断和 AVL 类似:

LL:右旋祖父
RR:左旋祖父
LR:先左旋父,再右旋祖父
RL:先右旋父,再左旋祖父

红黑树删除记忆

红黑树删除时,先看真正删除的节点颜色:

删红:直接删
删黑 + 替代红:替代节点染黑
删黑 + 替代黑:处理双重黑

双重黑时看兄弟:

兄弟红:旋转变成兄弟黑
兄弟黑且侄子全黑:兄弟染红,问题上移
兄弟黑且近侄红:先旋兄弟
兄弟黑且远侄红:旋父节点,修复结束

五、一张总表

树操作触发问题判断关键调整方式
AVL插入高度差超过 1LL/RR/LR/RL单旋或双旋
AVL删除高度差超过 1BF 和子节点 BF单旋或双旋,可能继续向上
红黑树插入红红冲突父节点、叔叔节点颜色叔红变色,叔黑旋转
红黑树删除黑高减少兄弟和侄子颜色变色、旋转、双重黑上移

最核心的区别可以压缩成一句话:

AVL 树通过“高度差”判断是否旋转;
红黑树通过“颜色关系”判断是变色还是旋转。