AVL平衡二叉搜索树 - 历史上第一个自平衡BST

062AVL树:一场平衡之舞

📰 5W1H 发明者故事

Who(何人)- 发明者是谁?

发明者:格奥尔基·阿杰尔松-韦利斯基(Georgy Adelson-Velsky,1922-2014)和叶夫根尼·兰迪斯(Evgenii Landis,1921-1997)
背景:

  • Adelson-Velsky:苏联数学家,莫斯科国立大学教授,后移民以色列,在以色列理工学院工作;他还参与了世界上第一个计算机国际象棋程序Kaissa的开发
  • Landis:苏联数学家,以微分方程研究闻名,与Adelson-Velsky合作研究数据结构的数学性质
  • 两人均是应用数学家而非纯粹的计算机科学家,这也解释了AVL树严格数学分析的风格

When(何时)- 什么时候发明的?

时间:1962年,论文"An Algorithm for the Organization of Information"发表于苏联科学院院刊(Doklady Akademii Nauk SSSR)
时代背景:

  • 刚好在Hibbard(1962)发表BST删除算法的同一年
  • 冷战时期,苏联和西方计算机科学研究相互隔绝,AVL树在西方被重新发现时已是1970年代
  • 苏联在这一时期有世界级的数学和计算机科学研究,但成果传播受政治限制

Where(何地)- 在哪里发明的?

地点:苏联,莫斯科国立大学(Московский государственный университет)
环境:

  • 冷战高峰期,苏联政府大力资助基础科学研究
  • 莫斯科大学数学系是世界顶级研究机构,聚集了大批优秀数学家
  • 论文以俄语发表,后被翻译为英语,使西方学者在数年后才了解这一成果

What(何事)- 发明了什么?

数据结构:AVL树(以两位发明者姓名首字母命名:Adelson-Velsky and Landis)
核心性质(AVL条件):对树中每一个节点,其左子树和右子树的高度差(平衡因子)不超过1
关键突破:

  • 首次证明:维护平衡因子为{-1,0,1}的BST,树高度始终保持在 1.44 log₂N 以内
  • 定义了四种旋转操作(LL/RR/LR/RL)来恢复平衡,且每次旋转只需O(1)时间
  • 证明任意插入/删除操作后,至多需要 O(log N) 次旋转即可恢复平衡

四种旋转类型:

LL旋转(右旋):   RR旋转(左旋):   LR旋转(先左后右):   RL旋转(先右后左):
    z                 z                    z                      z
   / \               / \                  / \                    / \
  y   T4           T1   y               y   T4                T1   y
 / \                   / \             / \                        / \
x   T3               T2   x          T1   x                     x   T4
                                         / \
                                        T2  T3

Why(何因)- 为什么发明?

要解决的问题:

  1. 普通BST在最坏情况下(如顺序插入)退化为链表,查找退化为O(N)
  2. 需要一个保证最坏情况O(log N)的动态查找结构,而非平均情况保证
  3. 随机化方案(如跳表)引入概率,不能保证严格的最坏情况

理论依据:

  • AVL条件保证树高度 h <= 1.44 log₂(N+2) - 0.328
  • 这意味着所有操作(查找、插入、删除)的最坏情况均为O(log N)
  • 相比BST的O(N)最坏情况,这是质的飞跃

当时的挑战:

  • 平衡维护的正确性需要严格的数学证明(苏联数学家的强项)
  • 四种旋转情况的分类和实现容易出错
  • 删除操作比插入更复杂(可能需要从删除点到根的全路径旋转)

How(何果)- 如何实现?有什么影响?

插入流程:

1. 按BST规则插入新节点
2. 从新节点向上回溯到根,更新每个祖先的平衡因子
3. 找到第一个失衡节点(平衡因子变为±2)
4. 根据失衡类型执行对应旋转(LL/RR/LR/RL)
5. 旋转后该子树高度恢复,停止回溯

历史影响:

  • AVL树开创了"自平衡搜索树"这一重要研究方向
  • 直接启发了红黑树(1972年,Rudolf Bayer)、B树(1970年)等后续结构
  • 红黑树(std::set/std::map的基础)是AVL树的工程优化版本(旋转次数更少)
  • Java的TreeMap、C++ STL的set/map都基于红黑树(AVL树的精神继承者)

今天的使用:

  • 数据库内存索引(某些实现用AVL树,因其查找比红黑树更快)
  • 实时系统(AVL树高度更严格,查找更稳定)
  • 操作系统内核(OpenBSD内核的某些数据结构使用AVL树)

名言:Knuth在TAOCP中写道:“AVL树是平衡搜索树中最优雅的结构,它以最少的约束(平衡因子至多为1)换取了最强的保证(高度至多为1.44logN)。”


📝 自然语言需求定义

需求名称:实现AVL树,支持插入和查找,保证任意时刻高度 ≤ 1.44 log₂N

功能需求(用精确的中文描述)

  1. 插入(含平衡维护):向AVL树插入值,插入后自动通过旋转恢复平衡

    • 输入:根节点指针的指针、整数值
    • 操作:BST插入 → 向上回溯更新高度 → 检测失衡 → 执行旋转(LL/RR/LR/RL)
    • 输出:无(就地修改,返回新根)
  2. 查找:在AVL树中查找值(与BST查找相同,利用BST性质)

    • 输入:根节点指针、目标值
    • 输出:找到返回节点指针,未找到返回NULL
  3. 四种旋转操作(内部函数):

    • 右旋(LL情况):将左孩子提升为新根
    • 左旋(RR情况):将右孩子提升为新根
    • 左右旋(LR情况):先对左孩子左旋,再对当前节点右旋
    • 右左旋(RL情况):先对右孩子右旋,再对当前节点左旋
  4. 高度查询:返回AVL树高度(空树为-1)

  5. 中序遍历:按升序遍历(与BST相同,用于验证有序性)

  6. 释放内存:后序遍历释放所有节点

约束条件

  • 每个节点维护height字段(而非balance_factor,以简化实现)
  • 平衡因子 = 左子树高度 - 右子树高度,|平衡因子| <= 1
  • 不实现删除(删除更复杂,单独成一个高级话题)
  • 重复值忽略
  • 10个节点插入后树高度必须 <= 4(1.44 × log₂(10) ≈ 4.78,实际AVL树会更低)

验收标准(必须可验证)

编号 测试场景(自然语言描述) 预期结果 验证方式
1 顺序插入1,2,3(会触发RR旋转) 树高度=1,根为2 断言height(root)<=1
2 插入3,2,1(触发LL旋转) 树高度=1,根为2 断言height和root->data
3 插入3,1,2(触发LR旋转) 树高度=1,根为2 断言height和root->data
4 插入3,5,4(触发RL旋转) 树高度=1,根为4 断言height和root->data
5 插入10个元素后树高度 高度 <= 4 断言height(root) <= 4
6 插入后中序遍历有序 中序遍历结果严格递增 断言数组各相邻元素
7 查找存在的值 返回非NULL节点 断言
8 查找不存在的值 返回NULL 断言
9 顺序插入1到20,高度 <= 1.44*log2(20)+1 高度 <= 6 断言

AI 生成提示

基于以上需求和验收标准,用标准C语言实现AVL树(含四种旋转,不实现删除)。

要求:
1. 使用标准C99,gcc -Wall无警告
2. 节点结构体:int data, int height, AVLNode* left, AVLNode* right
3. 高度维护:每次插入后递归更新height字段
4. 实现四种旋转:rotate_right, rotate_left, rotate_left_right, rotate_right_left
5. avl_insert返回新根指针
6. 完整测试框架:tests_passed/tests_failed计数
7. main最后返回 tests_failed > 0 ? 1 : 0

核心函数:
- avl_insert(root, value) - 递归插入,返回新根指针
- avl_search(root, value) - 查找,返回节点指针
- avl_height(root) - 树高度(空树-1)
- avl_inorder(root, arr, &cnt) - 中序遍历
- avl_free(root) - 释放内存
- rotate_right(y) / rotate_left(x) - 基本旋转

💻 C语言实现文件

对应文件: avl_tree.c

编译运行:

gcc -std=c99 -Wall -o avl_tree_test avl_tree.c
./avl_tree_test

# 内存泄漏检测
valgrind --leak-check=full ./avl_tree_test

核心函数:

  • avl_insert(root, value) - 递归插入并自动旋转,返回新根
  • avl_search(root, value) - 查找,返回节点指针或NULL
  • avl_height(root) - 返回树高度(空树返回-1)
  • avl_inorder(root, arr, &cnt) - 中序遍历填数组
  • avl_free(root) - 后序遍历释放所有节点
  • rotate_right(y) / rotate_left(x) - LL/RR基本旋转
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐