背景

AVL 树是一棵平衡的二叉查找树,于 1962 年,G. M. Adelson-Velsky 和 E. M. Landis 在他们的论文《An algorithm for the organization of information》中发表。

所谓的平衡之意,就是树中任意一个结点下左右两个子树的高度差不超过 1。(本文对于树的高度约定为:空结点高度是 0,叶子结点高度是 1。)

那 AVL 树和普通的二叉查找树有何区别呢?如图,如果我们插入的是一组有序上升或下降的数据,则一棵普通的二叉查找树必然会退化成一个单链表,其查找效率就降为 O(n)。而 AVL 树因其平衡的限制,可以始终保持 O(logn) 的时间复杂度。

具体实现与代码分析

在我们进行完插入或删除操作后,很可能会导致某个结点失去平衡,那么我们就需要把失衡结点旋转一下,使其重新恢复平衡。

经过分析,不管是插入还是删除,它们都会有四种失衡的情况:左左失衡,右右失衡,左右失衡,右左失衡。因此每次遇到失衡时,我们只需判断一下是哪个失衡,再对其进行相对应的恢复平衡操作即可。

好,下面以插入操作为例,来看下这四种失衡的庐山真面目。(以下统一约定:红色结点为新插入结点,y 结点为失衡结点)

(1)左左失衡


所谓的左左,即 "失衡结点" 的左子树比右子树高 2,左孩子下的左子树比右子树高 1。

我们只需对 "以 y 为根的子树" 进行 "左左旋转 (ll_rotate)" 即可。一次旋转后,恢复平衡。

Node * AVL::ll_rotate(Node * y)

{

Node * x = y->left;

y->left = x->right;

x->right = y;

y->height = max(get_height(y->left), get_height(y->right)) + 1;

x->height = max(get_height(x->left), get_height(x->right)) + 1;

return x;

}

(2)右右失衡

所谓的右右,即 "失衡结点" 的右子树比左子树高 2,右孩子下的右子树比左子树高 1。

我们只需对 "以 y 为根的子树" 进行 "右右旋转 (rr_rotate)" 即可。一次旋转后,恢复平衡。

Node * AVL::rr_rotate(Node * y)

{

Node * x = y->right;

y->right = x->left;

x->left = y;

y->height = max(get_height(y->left), get_height(y->right)) + 1;

x->height = max(get_height(x->left), get_height(x->right)) + 1;

return x;

}

(3)左右失衡


所谓的左右,即 "失衡结点" 的左子树比右子树高 2,左孩子下的右子树比左子树高 1。

观察发现,若先对 "以 x 为根的子树" 进行 "右右旋转 (rr_rotate)",此时 "以 y 为根的子树" 恰好符合 "左左失衡",所以再进行一次 "左左旋转 (ll_rotate)"。两次旋转后,恢复平衡。

Node * AVL::lr_rotate(Node * y)

{

Node * x = y->left;

y->left = rr_rotate(x);

return ll_rotate(y);

}

(4)右左失衡

所谓的右左,即 "失衡结点" 的右子树比左子树高 2,右孩子下的左子树比右子树高 1。

观察发现,若先对 "以 x 为根的子树" 进行 "左左旋转 (ll_rotate)",此时 "以 y 为根的子树" 恰好符合 "右右失衡",所以再进行一次 "右右旋转 (rr_rotate)"。两次旋转后,恢复平衡。

Node * AVL::rl_rotate(Node * y)

{

Node * x = y->right;

y->right = ll_rotate(x);

return rr_rotate(y);

}


插入操作

插入成功后,在递归回溯时依次对经过的结点判断是否失衡,若失衡就需要对其进行对应的旋转操作使其恢复平衡,在这期间,原先作为一棵子树的根结点就会因为旋转被替换,因此设置insert_real( )返回的是新根结点,这样就可以实时更新根结点。

插入操作实现代码如下:

int AVL::get_height(Node * node)

{

if (node == nullptr)

return 0;

return node->height;

}

int AVL::get_balance(Node * node)

{

if (node == nullptr)

return 0;

return get_height(node->left) - get_height(node->right);

}

Node * AVL::insert_real(int key, Node * node)

{

if (node == nullptr)

return new Node(key);

if (key < node->key)

node->left = insert_real(key, node->left);

else if (key > node->key)

node->right = insert_real(key, node->right);

else

return node;

node->height = max(get_height(node->left), get_height(node->right)) + 1;

int balance = get_balance(node);

// 左左失衡

if (balance > 1 && get_balance(node->left) > 0)

return ll_rotate(node);

// 右右失衡

if (balance < -1 && get_balance(node->right) < 0)

return rr_rotate(node);

// 左右失衡

if (balance > 1 && get_balance(node->left) < 0)

return lr_rotate(node);

// 右左失衡

if (balance < -1 && get_balance(node->right) > 0)

return rl_rotate(node);

return node;

}

void AVL::insert(int key)

{

header->left = insert_real(key, header->left);

}


查找操作

Node * AVL::find_real(int key, Node * node)

{

if (node == nullptr)

return nullptr;

if (key < node->key)

return find_real(key, node->left);

else if (key > node->key)

return find_real(key, node->right);

else

return node;

}

Node * AVL::find(int key)

{

return find_real(key, header->left);

}

删除操作

删除操作的四种失衡情况和插入操作一样,读者可以参考前文。下面是删除操作的实现代码:

Node * AVL::erase_real(int key, Node * node)

{

if (node == nullptr)

return node;

if (key < node->key)

node->left = erase_real(key, node->left);

else if (key > node->key)

node->right = erase_real(key, node->right);

else

{

if (node->left && node->right)

{

// 找到后继结点

Node * x = node->right;

while (x->left)

x = x->left;

// 后继直接复制

node->key = x->key;

// 转化为删除后继

node->right = erase_real(x->key, node->right);

}

else

{

Node * t = node;

node = node->left ? node->left : node->right;

delete t;

if (node == nullptr)

return nullptr;

}

}

node->height = max(get_height(node->left), get_height(node->right)) + 1;

int balance = get_balance(node);

// 左左失衡

if (balance > 1 && get_balance(node->left) >= 0) // 需要加等号

return ll_rotate(node);

// 右右失衡

if (balance < -1 && get_balance(node->right) <= 0) // 需要加等号

return rr_rotate(node);

// 左右失衡

if (balance > 1 && get_balance(node->left) < 0)

return lr_rotate(node);

// 右左失衡

if (balance < -1 && get_balance(node->right) > 0)

return rl_rotate(node);

return node;

}

void AVL::erase(int key)

{

header->left = erase_real(key, header->left);

}

完整代码

/**

*

* author : 刘毅(Limer)

* date   : 2017-08-17

* mode   : C++

*/

#include <iostream>

#include <algorithm>

using namespace std;

struct Node

{

int key;

int height;

Node * left;

Node * right;

Node(int key = 0)

{

this->key = key;

this->height = 1;

this->left = this->right = nullptr;

}

};

class AVL

{

private:

Node * header;

private:

Node * ll_rotate(Node * y);

Node * rr_rotate(Node * y);

Node * lr_rotate(Node * y);

Node * rl_rotate(Node * y);

void destroy(Node * node);

int get_height(Node * node);

int get_balance(Node * node);

Node * insert_real(int key, Node * node);

Node * find_real(int key, Node * node);

Node * erase_real(int key, Node * node);

void in_order(Node * node);

public:

AVL();

~AVL();

void insert(int key);

Node * find(int key);

void erase(int key);

void print();

};

Node * AVL::ll_rotate(Node * y)

{

Node * x = y->left;

y->left = x->right;

x->right = y;

y->height = max(get_height(y->left), get_height(y->right)) + 1;

x->height = max(get_height(x->left), get_height(x->right)) + 1;

return x;

}

Node * AVL::rr_rotate(Node * y)

{

Node * x = y->right;

y->right = x->left;

x->left = y;

y->height = max(get_height(y->left), get_height(y->right)) + 1;

x->height = max(get_height(x->left), get_height(x->right)) + 1;

return x;

}

Node * AVL::lr_rotate(Node * y)

{

Node * x = y->left;

y->left = rr_rotate(x);

return ll_rotate(y);

}

Node * AVL::rl_rotate(Node * y)

{

Node * x = y->right;

y->right = ll_rotate(x);

return rr_rotate(y);

}

void AVL::destroy(Node * node)

{

if (node == nullptr)

return;

destroy(node->left);

destroy(node->right);

delete node;

}

int AVL::get_height(Node * node)

{

if (node == nullptr)

return 0;

return node->height;

}

int AVL::get_balance(Node * node)

{

if (node == nullptr)

return 0;

return get_height(node->left) - get_height(node->right);

}

Node * AVL::insert_real(int key, Node * node)

{

if (node == nullptr)

return new Node(key);

if (key < node->key)

node->left = insert_real(key, node->left);

else if (key > node->key)

node->right = insert_real(key, node->right);

else

return node;

node->height = max(get_height(node->left), get_height(node->right)) + 1;

int balance = get_balance(node);

// 左左失衡

if (balance > 1 && get_balance(node->left) > 0)

return ll_rotate(node);

// 右右失衡

if (balance < -1 && get_balance(node->right) < 0)

return rr_rotate(node);

// 左右失衡

if (balance > 1 && get_balance(node->left) < 0)

return lr_rotate(node);

// 右左失衡

if (balance < -1 && get_balance(node->right) > 0)

return rl_rotate(node);

return node;

}

Node * AVL::find_real(int key, Node * node)

{

if (node == nullptr)

return nullptr;

if (key < node->key)

return find_real(key, node->left);

else if (key > node->key)

return find_real(key, node->right);

else

return node;

}

Node * AVL::erase_real(int key, Node * node)

{

if (node == nullptr)

return node;

if (key < node->key)

node->left = erase_real(key, node->left);

else if (key > node->key)

node->right = erase_real(key, node->right);

else

{

if (node->left && node->right)

{

// 找到后继结点

Node * x = node->right;

while (x->left)

x = x->left;

// 后继直接复制

node->key = x->key;

// 转化为删除后继

node->right = erase_real(x->key, node->right);

}

else

{

Node * t = node;

node = node->left ? node->left : node->right;

delete t;

if (node == nullptr)

return nullptr;

}

}

node->height = max(get_height(node->left), get_height(node->right)) + 1;

int balance = get_balance(node);

// 左左失衡

if (balance > 1 && get_balance(node->left) >= 0) // 需要加等号

return ll_rotate(node);

// 右右失衡

if (balance < -1 && get_balance(node->right) <= 0) // 需要加等号

return rr_rotate(node);

// 左右失衡

if (balance > 1 && get_balance(node->left) < 0)

return lr_rotate(node);

// 右左失衡

if (balance < -1 && get_balance(node->right) > 0)

return rl_rotate(node);

return node;

}

void AVL::in_order(Node * node)

{

if (node == nullptr)

return;

in_order(node->left);

cout << node->key << " ";

in_order(node->right);

}

AVL::AVL()

{

header = new Node(0);

}

AVL::~AVL()

{

destroy(header->left);

delete header;

header = nullptr;

}

void AVL::insert(int key)

{

header->left = insert_real(key, header->left);

}

Node * AVL::find(int key)

{

return find_real(key, header->left);

}

void AVL::erase(int key)

{

header->left = erase_real(key, header->left);

}

void AVL::print()

{

in_order(header->left);

cout << endl;

}

int main()

{

AVL avl;

// test "insert"

avl.insert(7);

avl.insert(2);

avl.insert(1); avl.insert(1);

avl.insert(5);

avl.insert(3);

avl.insert(6);

avl.insert(4);

avl.insert(9);

avl.insert(8);

avl.insert(11); avl.insert(11);

avl.insert(10);

avl.insert(12);

avl.print(); // 1 2 3 4 5 6 7 8 9 10 11 12

// test "find"

Node * p = nullptr;

cout << ((p = avl.find(2)) ? p->key : -1) << endl;   //  2

cout << ((p = avl.find(100)) ? p->key : -1) << endl; // -1

// test "erase"

avl.erase(1);

avl.print(); // 2 3 4 5 6 7 8 9 10 11 12

avl.erase(9);

avl.print(); // 2 3 4 5 6 7 8 10 11 12

avl.erase(11);

avl.print(); // 2 3 4 5 6 7 8 10 12

return 0;

}

起初构造的 AVL 树为下图:

总结

和二叉查找树相比,AVL 树的特点是时间复杂度更稳定,但缺点也是很明显的。

插入操作中,至多需要一次恢复平衡操作,递归回溯的量级为 O(logn)。有一点需要我们注意,在对第一个失衡结点进行恢复平衡后,递归回溯就应该立即停止(因为失衡结点的父亲及其祖先们肯定都是处于平衡状态的)。

但让 "递归的回溯" 中途停止,不好实现,所以我上面的编码程序都不可避免的会继续回溯,直到整棵树的根结点,而这些回溯都是没有必要的。(谢谢 LLL 的提醒,若在结点中增设父亲结点,就可以解决递归回溯的问题)

删除操作中,若存在失衡,则至少需要一次恢复平衡操作,递归回溯的量级亦为 O(logn)。与插入操作不同,当对第一个失衡结点恢复平衡后,它的父亲或者是它的祖先们也可能是非平衡的(见下图,删除 1),所以删除操作的回溯很有必要。

没有参照物对比的探讨是没有意义的,所以此文就止于此吧,有兴趣的朋友可以看下我后面《红黑树》及《AVL 树与红黑树的对比》的文章。

参考文献

维基百科. AVL 树.

GeeksforGeeks. AVL Tree | Set 1 (Insertion).

GeeksforGeeks. AVL Tree | Set 2 (Deletion).

一文读懂 AVL 树相关推荐

  1. B+树 范围查询_一文读懂B树和B+树

    目前大多数数据库系统及文件系统都采用 B-Tree 或其变种 B+Tree 作为索引结构.B+ 树中的 B (balance)代表平衡,而不是二叉.B+ 树是从最早的平衡二叉树演化而来的.B+ 树是由 ...

  2. 【一文读懂】Mysql索引原理之环环相扣

    MySQL为什么要使用索引? MySQL读写比例一般是10:1,大部分性能瓶颈在查询,所有优化性能的关键点都在查询,而索引可以有效的提升MySQL的查询性能. 为什么索引能提升查询性能? 索引通过特殊 ...

  3. 腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

    1.引言 我们常常会听说,某个互联网应用的服务器端系统多么牛逼,比如QQ.微信.淘宝.那么,一个大型互联网应用的服务器端系统,到底牛逼在什么地方?为什么海量的用户访问,会让一个服务器端系统变得更复杂? ...

  4. python随机森林变量重要性_推荐 :一文读懂随机森林的解释和实现(附python代码)...

    原标题:推荐 :一文读懂随机森林的解释和实现(附python代码) 作者:WilliamKoehrsen:翻译:和中华:校对:李润嘉 本文约6000字,建议阅读15分钟. 本文从单棵决策树讲起,然后逐 ...

  5. 一文读懂 MySQL Explain 执行计划

    一.前言 上周老周的一个好朋友让我出一篇教你读懂 SQL 执行计划,和我另一位读者反馈的面试题如何排查慢 SQL 的强相关,索性先出一篇一文读懂 MySQL Explain 执行计划.Explain ...

  6. JVM(一)一文读懂Java编译全过程

    一文读懂Java编译全过程 java代码首先要通过前端编译器编译成.class字节码文件,然后再按一定的规则加载到JVM(java 虚拟机)内运行,有三种运行方式,解释模式(javac).编译模式(C ...

  7. 一文读懂BERT(原理篇)

    一文读懂BERT(原理篇) 2018年的10月11日,Google发布的论文<Pre-training of Deep Bidirectional Transformers for Langua ...

  8. 一文读懂:什么是区块链

    今天写一个能够一文读懂区块链的文章,以后谁再问我区块链是什么东西,我就把这篇文章发给他. -------- 个人技术公众号:解决方案工程师 欢迎同领域的朋友关注.相互交流. -------- 区块链技 ...

  9. “一文读懂“系列:Android中的硬件加速

    浅谈 前几天有个朋友问我"了不了解关于手机硬件加速方面的知识?",嗯?其实我也想知道... 于是笔者就去网上搜罗了文章再结合自己对源码的理解,总结了这篇关于硬件加速的理解. 关于屏 ...

最新文章

  1. BZOJ 3669: [Noi2014]魔法森林( LCT )
  2. 【数学与算法】牛顿法的两种应用:求根和最优化
  3. 年运行10万台服务器数据中心方案设计公示
  4. windows和linux-JDK环境变量设置
  5. brew 更换国内源(镜像)
  6. 【渝粤题库】国家开放大学2021春2246社会工作概论题目
  7. js操作table中tr的顺序,实现上移下移一行的效果
  8. mysql批量写入100万数据_Mysql数据库实践操作之————批量插入数据(100万级别的数据)-阿里云开发者社区...
  9. 利用维纳滤波编码实现给定的运动模糊图像恢复
  10. Cisco路由器安全配置必用的10条命令
  11. 面对 10 亿数据量的挑战,如何对系统进行性能优化?
  12. 大学毕业4年-回顾和总结(6)-技术研发-重构之法
  13. Python学习week7_映射
  14. ssh协议(git和github gitee 之间的传输协议)
  15. ballerina 学习二十八 快速grpc 服务开发
  16. Tiny4412 LCD驱动(DRM+设备树)
  17. EXCEL的Ctrl键神操作续集,Ctrl+字母=高效
  18. 呕心沥血60道Vue、Vue-Router、Vuex面试题及详解!
  19. 虚继承是什么意思_huaxiazhihuo
  20. 安超OS为企业数字化转型构建坚实的云基座

热门文章

  1. 【.NET Core 跨平台 GUI 开发】第三篇:Gtk# 表格布局与事件处理
  2. 深入Dapper.NET源码
  3. .NET分布式大规模计算利器-Orleans(一)
  4. 用pythonnet为计算机视觉做图像整理
  5. 给 asp.net core 写个中间件来记录接口耗时
  6. AddMvc 和 AddMvcCore 的区别
  7. .NET Core 3.0 linux 部署小贴士
  8. 西安活动 | 2019年1月13号 拥抱开源, 又见.NET 线下交流活动报名进行中
  9. 用 Docker Machine 创建 Azure 虚拟主机
  10. JWT【JSON Web Token】 简述