(d2)AVL树:插入

官方信息技术老师·19 页·深入(追求细节与边界)·0 次浏览·1 天前
AVL树平衡旋转插入操作平衡因子

AVL树:插入

看完你能徒手推演插入后平衡因子的变化并选定正确的旋转

按 空格/→ 演示下一步

1 / 19 页

全部页面点击任意一页,跳回舞台从这页播放

AVL树平衡旋转插入操作平衡因子

AVL树:插入

看完你能徒手推演插入后平衡因子的变化并选定正确的旋转

1第 1 页 · AVL树:插入

AVL树平衡因子

插入新节点后,每个节点的「身高差」可能发生变化。我们需要一个数值来量化这种不平衡——这就是平衡因子。

平衡因子 BF
某节点左子树高度 减去 右子树高度的差值
向上回溯更新
插入后从新节点往上,每层祖先重算高度
AVL 平衡条件
所有节点的 |BF| ≤ 1,否则视为失衡
失衡节点
|BF| = 2 的节点就是要旋转修复的位置
跷跷板两边的身高差对应 →节点的平衡因子

左高减右高所得的差,就是「偏」了多少

BF(v)=hLhR;BF1BF(v) = h_L - h_R \quad ; \quad |BF| \leq 1
2第 2 页 · AVL树平衡因子

插入操作的基本流程

AVL 的插入看似只是多了一步平衡检查,实际是一环扣一环的递归回溯。

1
按BST插入
从根比较大小一路向下,找到空位放下新节点
2
回溯更新BF
沿祖先链向上,每个祖先的平衡因子都要重算
3
定位失衡点
第一个出现 |BF|>1 的祖先就是失衡点,停下
4
判断旋转类型
结合失衡点与孩子的BF,分 LL/RR/LR/RL 四种
5
旋转恢复
旋转后子树高度回到插入前,失衡不再向上传播
3第 3 页 · 插入操作的基本流程

插入后失衡的判断

新节点已经入位,但这棵树到底有没有被'压歪'?今天讲清楚怎么判断:从插入点沿父指针向上回溯,定位那个平衡因子刚变成 ±2 的祖先节点。

失衡判定标准
某祖先节点的平衡因子从 ±1 跳到 ±2
向上回溯路径
从新节点出发,沿父指针一路向上检查每个祖先
最近的失衡祖先
回溯过程中第一个平衡因子为 ±2 的节点
只修这一处即可
旋转后该子树高度恢复原值,上层祖先自动平衡
跷跷板加重对应 →定位失衡祖先

从加重位置往上数,最近那块倾斜超限的支点就是修复点

4第 4 页 · 插入后失衡的判断

四种失衡类型总览

看失衡节点沿路径两次走的方向,决定失衡类型与修复方法

图解渲染中…
ll失衡节点的左孩子的左子树插入了新节点rr失衡节点的右孩子的右子树插入了新节点lr失衡节点的左孩子的右子树插入了新节点rl失衡节点的右孩子的左子树插入了新节点
5第 5 页 · 四种失衡类型总览

单旋的数学原理

前面我们看到,失衡分四种,每种用一次旋转修复。但有个疑问:祖先节点会不会也跟着失衡?要回答这个问题,得算清楚旋转前后子树的高度变化。

插入前的高度锚点
设子树插入前高度为 h₀,新节点插入后变 h₀+1
失衡时的强约束
以 LL 为例:C 高 h、B 高 h+1,A 右孩子必为 h-1
旋转后高度回落
右旋后新 A 高度 h、新 B 高度 h+1,等于插入前 h₀
传播到此为止
子树高度等于插入前,祖先 BF 不变,无需再旋
跷跷板对应 →单旋高度还原

一侧坐太重板压低,把人挪到另一侧,板面回到原位高度

h(B)=max(h(C),h(A))+1=h+1=h0h'(B) = \max(h(C), h'(A)) + 1 = h + 1 = h_0
6第 6 页 · 单旋的数学原理

LL型旋转图解

LL型失衡做右单旋,本质是让左子B顶上来,把失衡节点A压到右边。

1
找到失衡节点A
A就是平衡因子变成+2的那个根
2
找到A的左子B
B是顶替A的候选人,它将承担最高位置
3
B上位,A下放
位置互换:B成新根,A降为B的右子
4
B原右子C过继
C从B右侧移到A左侧,避免子树断裂丢失
7第 7 页 · LL型旋转图解

RR型旋转图解

RR型失衡通过一次左单旋修复,过程是LL型的镜像翻转。

1
识别RR失衡
新节点插在A右孩子的右子树,A平衡因子变-2
2
B上位
B取代A,成为这棵子树的新根
3
BL转给A
B原来的左子树BL,挂到A的右孩子位置
4
A下降
A变成B的左孩子
5
更新高度
从下往上重新计算A和B的节点高度
8第 8 页 · RR型旋转图解

LL与RR旋转对比

LL 与 RR 互为镜像,但旋转方向与判别条件容易记混。这一页把它们并排对照。

LL型 · 右旋
  • 失衡节点 BF=+2,左孩子 BF=1 或 0
  • 顺时针方向,整体右旋
  • 原左孩子升为新根,原根变右孩子
RR型 · 左旋
  • 失衡节点 BF=-2,右孩子 BF=-1 或 0
  • 逆时针方向,整体左旋
  • 原右孩子升为新根,原根变左孩子
左偏选 LL(右旋),右偏选 RR(左旋)。判断依据是失衡方向,不是子树高度差的绝对值。
9第 9 页 · LL与RR旋转对比

双旋的数学原理

单旋能一次修好 LL/RR 的直线型偏斜。但 LR/RL 的失衡路径在根节点处拐了弯——只把高层子节点拉上来,深枝仍会挂在错误方向,所以要做两次旋转。

单旋的隐含假设
高层子树的偏斜方向与外层一致,深枝在同侧
LR 型的拐弯路径
根向右偏,但深枝藏在右孩子的左子树里
单旋后的剩余失衡
新根左高 = h_D+1,右高 = h_E,差值仍 ≥2
先局部再整体
先在 C 内部右旋把 D 翻上来,再对 A 左旋
两次旋转后高度复原
新根高度回到 h(A)-1,所有祖先自动平衡
折弯的树枝对应 →LR/RL 型失衡

弯折处的关节先反向折一下拉直,再整体摆正——对应先局部旋再整体旋

$h_D \geq h_E + 1 \Rightarrow (h_D+1) - h_E \geq 2$
10第 10 页 · 双旋的数学原理

LR型旋转图解

LR型失衡要把中间节点C分两次上提:先对B左旋把C摆到能继续上提的位置,再对A右旋完成平衡。

1
识别LR结构
失衡节点A,左孩子B,新节点在B的右子树C
2
对B左旋
把C提升到B的位置,LR形态转化为LL形态
3
对A右旋
把C提升到A的位置,B和A分列左右子树,恢复平衡
11第 11 页 · LR型旋转图解

RL型旋转图解

RL 型失衡分两步:先对右孩子右旋,再对失衡节点左旋。

1
对 Y 做右旋
Z 升到 Y 的位置,Y 落为 Z 的右孩子
2
对 X 做左旋
Z 再升到 X 的位置,X 落为 Z 的左孩子
3
子树重挂完成
T2 改挂 X 右、T3 改挂 Y 左,整树高度恢复
12第 12 页 · RL型旋转图解

LR与RL旋转对比

LR 与 RL 是双旋的两种镜像情形,旋转方向一正一反,最容易记混或做反。

LR 型双旋
  • 失衡位置:新节点在 A 右孩子的左子树(路径 R→L)
  • 旋转顺序:先对 B 左旋变成 RR 型,再对 A 右旋
  • 结构示意:A—右—B—左—C,旋转后 C 升为新根
RL 型双旋
  • 失衡位置:新节点在 A 左孩子的右子树(路径 L→R)
  • 旋转顺序:先对 B 右旋变成 LL 型,再对 A 左旋
  • 结构示意:A—左—B—右—C,旋转后 C 升为新根
LR 和 RL 是严格镜像——把 LR 的图水平翻转就得到 RL。掌握 LR 后反过来套用即可,关键是先旋孩子、别把两次旋转方向都做反。
13第 13 页 · LR与RL旋转对比

旋转选择决策树

从失衡节点出发,先看自身BF正负,再看重侧孩子的BF,两步定位旋转方法。

图解渲染中…
CBF = 左高 - 右高;+2 表示左偏,-2 表示右偏FLL:新节点落在左孩子的左子树GLR:新节点落在左孩子的右子树IRL:新节点落在右孩子的左子树
14第 14 页 · 旋转选择决策树

失衡类型判断

点击作答

AVL树插入新节点后,失衡节点A的平衡因子为+2,其左孩子B的平衡因子为-1,新节点位于B的右子树中。该失衡属于哪种类型,应如何修复?

15第 15 页 · 失衡类型判断

插入算法框架

前面我们看了四种旋转的样子,现在退一步看全貌:插入其实只有两个阶段——向下找到位置放新节点,向上回溯时顺手检查并修复失衡。把这两段拼起来,就是完整的插入算法。

递归下降
按BST规则向下找空位,新节点一定落在叶子层
逐层回溯
从插入点向上返回,逐层更新height和平衡因子
检测失衡
每层检查 |平衡因子|>1 就触发修复
旋转选型
根据失衡节点的平衡因子和子节点方向选一种旋转
返回新根
旋转后新子树根向上传递,让上层继续向上检查
快递分拣中心对应 →AVL递归插入

包裹落到末端→逐层向上汇报重量→某层超载就拆分重组

向下递归BST 插入  +  向上回溯更新 h + 旋转\underbrace{\text{向下递归}}_{\text{BST 插入}} \;+\; \underbrace{\text{向上回溯}}_{\text{更新 \,h\, + \,旋转}}
16第 16 页 · 插入算法框架

AVL树插入完整代码

c

含四种旋转的 AVL 树插入完整 C 实现,可直接编译。

代码高亮加载中…

插入回溯时先 updH 再取 bf;按 bf 与 key 方向落入 LL/RR/LR/RL 四种旋转,单旋一次完成,双旋先旋子树再旋根。

17第 17 页 · AVL树插入完整代码

关键要点回顾

  • 平衡因子=左高−右高,超±1即失衡
  • LL与RR单旋,LR与RL双旋,均为O(1)
  • 旋转后子树高度复原,整树插入O(log n)
  • 失衡判首个不平衡祖先,类型看新节点路径
延伸主题:AVL树删除与再平衡与红黑树的对比旋转的迭代版实现
18第 18 页 · 关键要点回顾

课后思考

先自己默想 30 秒,再点开参考答案对照思路。

1插入新节点后,从新节点往上回溯到第一个失衡祖先才旋转,而不是从根节点一路旋转下来?

参考答案因为旋转会恢复从该节点向上的整条路径的平衡因子,只需处理最近的那个失衡点即可,再往上必然已经平衡。

2删除节点时 AVL 树也需要旋转保持平衡,最多需要几次旋转?为什么?

参考答案可能 O(log n) 次。因为删除可能让多个祖先失衡,且旋转后未必能修复上层,必须一路向上回溯修复。

3红黑树也用旋转保持平衡,但它规定路径长度差不超过 2 倍而不是严格平衡。AVL 树严格平衡换来什么、付出什么?

参考答案换来查找稳定 O(log n) 且树高最小;付出是插入删除需频繁旋转,写操作代价更高,节点额外存平衡因子。

19第 19 页 · 课后思考