(d1)AVL树:重平衡

官方信息技术老师·20 页·深入(追求细节与边界)·1 次浏览·1 天前
数据结构二叉搜索树自平衡

AVL树:重平衡

看完你能根据失衡形态,推出对应的修复旋转

按 空格/→ 演示下一步

1 / 20 页

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

数据结构二叉搜索树自平衡

AVL树:重平衡

看完你能根据失衡形态,推出对应的修复旋转

1第 1 页 · AVL树:重平衡

什么是AVL树

上一节我们讲了'重平衡',那什么是AVL树?它是最早的自平衡二叉搜索树,1962年由两位苏联科学家发明,'AVL'三个字母就取自他们的姓氏。

自平衡BST
二叉搜索树 + 自动维持平衡约束
最早提出
1962年发表,比红黑树早约十年
命名由来
Adelson-Velsky 与 Landis 姓氏首字母
平衡条件
任意节点左右子树高度差 ≤ 1
失衡就调
破坏条件时通过旋转恢复平衡
按身高排队的方阵对应 →AVL树

左右两列最高的人,身高差不能超过1,否则调整站位

hLhR1|h_L - h_R| \leq 1
2第 2 页 · 什么是AVL树

AVL树与BBST的关系

认识了 AVL 树,我们再往外看一眼:它不是孤例,而是「平衡二叉搜索树」这个大家族中的明星成员。这页帮你看清它在整个家族里的位置。

BBST 定义
在 BST 基础上,附加「高度受控」的约束,防止退化成链表
家族成员
AVL、红黑树、伸展树等都是 BBST 的不同实现方案
AVL 的特色
平衡条件最严:左右子树高度差 ≤ 1,查询最快但维护代价高
交通工具家族对应 →BBST 家族

汽车、火车、飞机都是交通工具,正如 AVL、红黑树、伸展树都是 BBST

3第 3 页 · AVL树与BBST的关系

平衡因子的定义

前页说 AVL 追求左右子树「高度差不多」——但「差不多」太模糊。工程师需要一把精确的尺子,把每个节点「偏多少」量化成一个数字。

平衡因子定义
某节点 BF = 左子树高度 − 右子树高度
取值范围
每个节点 BF 必须落在 {-1, 0, 1} 内
每个节点都要查
不是只看根节点,整棵树每个节点都要满足
跷跷板两端对应 →平衡因子

左端翘起记正、右端翘起记负,差值就是平衡因子

BF(v)=h(vL)h(vR),BF{1,0,1}BF(v) = h(v_L) - h(v_R), \quad BF \in \{-1, 0, 1\}
4第 4 页 · 平衡因子的定义

什么是适度平衡

上页平衡因子 BF = hL - hR 只能取 -1、0、1。听起来是三个值,但关键在于:为什么不强制 BF = 0、也就是走完美平衡路线?答案藏在维护代价中。

定义
左右子树高度差绝对值不超过1
比完美平衡松
允许差1,不必严格相等
调整更省
插入删除时不必每次都旋转
仍O(log n)
松绑后高度仍是对数级
天平称重对应 →AVL适度平衡

完美平衡要指针严格归零;适度平衡允许差一格,调整更省

5第 5 页 · 什么是适度平衡

适度平衡 vs 完美平衡

左右两条链分别代表 AVL 适度平衡与完美平衡,最后汇于权衡节点。

图解渲染中…
a1平衡因子绝对值 ≤ 1b2⌊log₂(n+1)⌋,最矮高度a3单次失衡只旋转 O(1) 节点b3失衡需全局重建,成本 O(n)
6第 6 页 · 适度平衡 vs 完美平衡

平衡因子的计算示例

每节点旁标注 BF = 左高 - 右高。从叶子向上逐层回代,找到 |BF|=2 的失衡节点。

图解渲染中…
a1根节点 BF = 3 - 3 = 0b2BF=-2,越界触发再平衡c1/d1/d2叶子节点 BF=0(空子树高 0)
7第 7 页 · 平衡因子的计算示例

AVL树的基本操作接口

前面我们看到AVL树靠平衡因子判定倾斜程度。落到接口层,哪些操作和普通BST一样,哪些必须额外维护平衡?这是动手前要先分清的问题。

搜索接口
与普通BST完全相同,只比较键值决定走向,不改变树形
插入接口
BST插入新节点,再回溯更新沿途平衡因子,失衡则旋转修复
删除接口
BST删除节点,再回溯更新沿途平衡因子,失衡则旋转修复
读写不对称
搜索只读不破坏平衡;插入/删除改变树形,必须额外维护
查户口vs迁户口对应 →AVL搜索vs写操作

查户口只读不改,搜索同理;迁户口会改记录,写操作需维护平衡

8第 8 页 · AVL树的基本操作接口

插入/删除操作的流程

AVL的插入和删除都遵循:先做BST基本操作,再向上修复失衡。

1
执行BST插入或删除
按普通BST规则找到插入点或目标节点并完成操作
2
向上回溯更新BF
从操作点向上逐节点重算高度和平衡因子
3
定位失衡节点
插入只需找最近的;删除路径上可能有多个
4
判定旋转模式
根据失衡节点和较高子节点的BF方向确定LL/RR/LR/RL
5
旋转并向上检查
插入旋转一次即停;删除要一路修复到根
9第 9 页 · 插入/删除操作的流程

失衡的判定

上页走完了插入/删除的主流程,但其中有一环没展开:怎么判定哪些节点失衡需要修?答案很简洁——看平衡因子是否越界。

判定阈值
|BF| > 1 即失衡;BF 必为整数,取值只在 -1/0/1(平衡)或 ±2 起(失衡)
回溯起点
从刚被修改的节点出发,沿父指针向上逐个检查
关键节点
路径上首个 |BF| > 1 的祖先节点——也是离修改点最近的那个失衡位
楼梯的台阶高度差对应 →节点的平衡因子

任何一级与下一级高度差超过 1 就异常,要从最低的异常台阶开始修

10第 10 页 · 失衡的判定

LL型失衡与单右旋

左图为LL型失衡(A左-左路径过深),右图为单右旋后的平衡结果。重点看A、B角色互换及T3归属变化。

图解渲染中…
a1失衡祖先,BF=+2,旋转轴心a3新插入节点,使B左子树增高rot单右旋操作:B被提为新根b5原T3从B右孩子改挂到A左孩子
11第 11 页 · LL型失衡与单右旋

RR型失衡与单左旋

右子树的右侧过深,执行一次左旋即可恢复

RR型失衡与单左旋
右子树的右侧过深,执行一次左旋即可恢复
12第 12 页 · RR型失衡与单左旋

LR型失衡与左右双旋

三阶段展示:失衡态→对Y左旋→对X右旋,每阶段呈现子树结构。

图解渲染中…
X失衡节点,平衡因子+2,左高YX的左孩子,平衡因子-1,右高Z新插入节点,位于Y的右子树
13第 13 页 · LR型失衡与左右双旋

RL型失衡与右左双旋

从左到右读图: 顺着箭头看RL失衡经两次反向旋转, 转化为平衡树的过程。

图解渲染中…
T1A右倾BF=-2, C左倾BF=+1, 异号组合即RLT3右旋C后, 树形转化为RR型失衡T5左旋A后, D成为新根, BF归零
14第 14 页 · RL型失衡与右左双旋

重平衡的完整流程

重平衡不是一次旋转,而是从发现到回溯的完整闭环。

1
失衡发现
沿修改路径向上检查平衡因子,定位首个|bf|>1的祖先节点
2
确定类型
根据失衡节点与较重子树的方向,匹配LL/RR/LR/RL四种模式
3
执行旋转
调用对应单旋或双旋,让该节点恢复|bf|≤1
4
向上回溯
旋转后子树高度可能变化,需继续向父节点检查是否引发新失衡
15第 15 页 · 重平衡的完整流程

旋转的本质:中序遍历不变性

四种旋转看完,你有没有想过:旋转之后这棵树还是 BST 吗?答案藏在中序遍历里——旋转只改父子指针,不动节点里的键值。

中序 = 升序
BST 性质:左 < 根 < 右子树,所以中序遍历必然得到键的升序序列
旋转不动键
所有键留在原节点,旋转只是重新连接父子链,不交换、不增删
局部序保持
被旋转的 2~3 个节点中谁大谁小没变,重新连接后仍满足 BST 约束
仅改拓扑
旋转 = 改父子链 + 不改中序序列,所以旋转后必仍是 BST
学生按高矮换座位对应 →BST 旋转

键对应人、节点对应座位;只调换座位不动人,整体「从矮到高」的相对顺序不变

InOrder(Rotate(T))=InOrder(T)\text{InOrder}(\text{Rotate}(T)) = \text{InOrder}(T)
16第 16 页 · 旋转的本质:中序遍历不变性

旋转的代码实现要点

以右旋(LL型)为模板,五行赋值即完成一次旋转。沿箭头读:每个节点是一个中间状态,边上是那一步的指针操作。

图解渲染中…
a1P 是失衡根节点,L 是其左孩子(也是旋转后的新根)a2temp 暂存 L 的引用,避免后续赋值把它覆盖掉a5双向:原 P 的 parent 改指 L;L 的 parent 改指原 P 的 parent
17第 17 页 · 旋转的代码实现要点

自测:失衡类型判断

点击作答

已知AVL树中某节点A失衡(平衡因子为+2,左偏重),其左孩子为B,新插入节点位于B的右子树中,使B的平衡因子为-1。则A属于哪种失衡类型,应采用哪种旋转?

18第 18 页 · 自测:失衡类型判断

本章要点回顾

  • 局部高度差一旦越过2,失衡形态由较高侧子结点的路径决定
  • AVL以子树高度差受限换取对树高和操作复杂度的控制
  • LL、RR先单旋,LR、RL先反向旋至同向,再单旋
  • 旋转重接局部结点,修复高度关系并保持中序遍历序列
  • 插入和删除都要沿访问路径回溯,在最低失衡处完成修复
延伸主题:删除引发的连锁失衡AVL删除修复实现红黑树:另一种平衡策略
19第 19 页 · 本章要点回顾

课后思考

先自己拿笔算一算,再下滑对照参考答案,看思路是否对得上。

1为什么AVL树要把平衡因子严格控制在[-1, 1]?如果放宽到[-2, 2],最坏情况下查找复杂度会退化到什么量级?

参考答案退化为O(n)线性查找。±1的约束保证插入/删除后至多一次旋转即可恢复平衡,树高始终维持在log(n)。放宽后失衡可能沿路径累积,最终退化成链表,整套对数级保证就崩了。

2旋转操作除了保持中序遍历序列不变,还守住了哪些结构不变量?为什么这些不变量对AVL的正确性同样关键?

参考答案还守住BST的左小右大关键字序,以及被旋转子树高度的严格下降。前者保证find语义不变;后者才是rotate的真正目的——高度不下降,旋转就成了死循环。

3高度为h的AVL树至少需要多少个节点?设这个最小节点数为N(h),请推出N(h)的递推式,它和斐波那契数列有什么关系?

参考答案N(0)=1, N(1)=2, N(h)=1+N(h-1)+N(h-2)。递推与Fibonacci同构(差常数1),由此推出h ≤ c·log_φ(n),AVL查找的常数因子约为log₂φ ≈ 1.44,介于O(log n)与log₂n之间。

20第 20 页 · 课后思考