转载请注明出处:https://www.cnblogs.com/morningli/p/16033733.html

AVL树是带有平衡条件的二叉查找树,其每个节点的左子树和右子树的高度最多相差1。为了保持AVL树始终平衡,每次插入和删除都需要进行额外的平衡操作。

上面两个二叉搜索树,A是AVL树,而B不是。

为什么需要平衡二叉树?

二叉搜索树一定程度上可以提高搜索效率,但是因为二叉树没有对树的形状进行限制,很容易就退化成了一个链表,搜索效率降低为 O(n)。

这里说明会导致二叉搜索树退化的两种原因:

1、插入的数据是有序地,比如先后插入1,2,3,4,5,会产生下面的二叉搜索树:

2、因为二叉搜索树的删除操作总是用右子树的节点替换被删除的节点,所以在不断的插入删除后,左子树会比右子树更深。现在已经证明,如果交替插入和删除O(N2)次,那么树的期望深度将是O(√N)。

为了避免二叉查找树搜索效率的恶化,我们需要对二叉树的深度进行限制,避免过深的二叉搜索树。

一种解决办法是要有一个称为平衡(balance)的附加结构条件:任何节点的深度均不可以过深。AVL树就是这样的加了平衡条件的最古老的平衡查找树。

另一种办法是不对树的深度做限制,但是每次操作都对树做一些调整,使后面的操作效率更高,保证连续M次操作在最坏的情况下花费O(MlogN)的时间。这种数据结构叫伸展树(splay tree)。这种树我们平时较少遇到,可以有兴趣再去研究。

时间复杂度

AVL 树的只读操作涉及执行与在不平衡二叉搜索树上执行的操作相同的操作,但修改必须观察和恢复子树的高度平衡。

平衡系数(balance factor)

在二叉树中,节点 X 的平衡因子定义为它的两个子树的高度差:

如果不变量

对于每个节点成立,二叉树被定义为AVL 树。BF(X) < 0 的节点被称为`left-heavy`,BF(X) > 0 称为 `right-heavy`,BF(X) = 0 简单称为 `balanced`。

最小失衡子树

每次插入新节点后,只有那些从插入点到根节点的路径上的节点的平衡有可能被改变,因为只有这些节点的子树可能发生变化。在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。一棵失衡的树,是有可能有多棵子树同时失衡的。可以证明,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

添加元素

我们把必须重新平衡的节点叫做 α。由于任意节点最多有两个儿子,因此出现高度不平衡就需要 | BF(α) | = 2。容易看出,这种不平衡可能出现在下面的4种情况中:

1. 对 α 的左儿子的左子树进行一次插入

2. 对 α 的左儿子的右子树进行一次插入

3. 对 α 的右儿子的左子树进行一次插入

4. 对 α 的右儿子的右子树进行一次插入

情形1和4是关于 α 点的镜像对称,情形2和3也是关于 α 点的镜像对称。理论上只有两种基本情况。

第一种是发生在“外边”的情况(即左左和右右的情况),这种情况只需要做一次单旋转(single rotation)可以完成调整。第二种情况是发生在“内部”的情况(即左右和右左的情况),这种情况通过稍微复杂的双旋转(double rotation)来处理。

动画显示了将几个元素插入到 AVL 树中。它包括左,右,左右和右左旋转。

单旋转

上图显示单旋转如何调整情形1。左边是旋转前,右边是旋转后。节点k2不满足AVL平衡性质,因为他的左子树比右子树深2层,RF(k2) = -2 。该图描述的只是情形1的一种可能的情况,在插入之前k2满足AVL性质,但是在插入之后这种性质被破坏了。子树X已经长出一层,这使得他比子树Z深出2层。下面分析Y可能所处的层数:

  1. Y不可能与新X在同一水平上,因为这样k2在插入之前已经失去平衡了。
  2. Y也不可能与Z在同一层,因为那样k1就会是在通向根的路径上破坏AVL平衡条件的第一个节点。

为了使树恢复平衡,我们需要把X上移一层,并把Z下移一层。此时二叉树已经不符合AVL树的要求,我们需要重新安排节点形成一棵等价的树,如图右边的树所示。

如图,把k2左二子k1提升为新的根,这样左子树高度会减去1。二叉树的属性告诉我们k2>k1,所以在新树中k2应该是k1的右儿子,子树Y包含了大于k1小于k2的节点,把他放到k2的左儿子的位置上就可以满足二叉查找树的属性。通过这样的调整,新树称为了一棵等效的新的AVL树。因为X向上移动了一层,Y保持在原来的高度上,Z下移了一层。k2和k1不仅满足AVL树的要求,而且他们的子树恰好处在同一高度上。不仅如此,整棵树的新高度恰恰与插入前原树的高度相同,而插入操作却使得子树X升高了。因此,通向根节点的路径的高度不需要进一步的修正,因而也不需要进一步的旋转。

情形4代表的是对称的情形。情形1的调整是从左往右旋转,称为右旋,情形4需要反过来,称为左旋。

双旋转

上面描述的单旋转对于情形2和情形3无效,需要使用双旋转来解决。

上图表示了对于情形2进行单旋转的情形。单旋转只会让子树Y保持高度不变,不会减少Y的高度。图中Y已经有一个数据插入其中,可以保证子树Y非空,因此可以假设子树Y有一个根和两棵子树,如下图所示。

可以把整棵树看成是由三个节点连接的4棵子树。如图所示,恰好树B或者树C中有一棵比D深两层(除非他们都是空的),但是我们不能肯定是哪一棵。事实上这并不要紧。图里B和C都画得比D低了2层。为了重新平衡,我们看到不能再让k3做根了,而上面已经说明了在k1和k3之间的旋转解决不了问题,唯一的选择是把k2作为新的根。这迫使k1成为k2的左儿子,k3成为k2的右儿子,从而决定4棵树的位置,如上图3所示,最后得到的树满足AVL的性质,与单旋转类似,我们也把树的高度恢复到插入以前的水平,这就保证了所有重新平衡和高度更新是完善的。情形3也可以通过双旋转进行处理,方向跟情形2相反。

删除元素

AVL 树和二叉查找树的删除操作基本相同,只是在二叉查找树的删除逻辑后后需要重新检查平衡性并修正。删除操作与插入操作后的平衡修正区别在于,插入操作后只需要对路径上最深的一个非平衡节点进行修正,而删除操作需要修正路径上所有非平衡节点。

对于删除操作造成的非平衡状态的修正,可以这样理解:对左或者右子树的删除操作相当于对右或者左子树的插入操作,然后再对应上插入的四种情况选择相应的旋转就好了。

引用:

https://zhuanlan.zhihu.com/p/56066942

《数据结构与算法分析——C++语言描述(第四版)》

https://en.wikipedia.org/wiki/AVL_tree

5分钟了解二叉树之AVL树的更多相关文章

  1. 二叉树与AVL树

    二叉树 什么是二叉树? 父节点至多只有两个子树的树形结构成为二叉树.如下图所示,图1不是二叉树,图2是一棵二叉树. 图1 普通的树                                    ...

  2. 二叉树,AVL树和红黑树

    为了接下来能更好的学习TreeMap和TreeSet,讲解一下二叉树,AVL树和红黑树. 1. 二叉查找树 2. AVL树 2.1. 树旋转 2.1.1. 左旋和右旋 2.1.2. 左左,右右,左右, ...

  3. 二叉树-二叉查找树-AVL树-遍历

    一.二叉树 定义:每个节点都不能有多于两个的儿子的树. 二叉树节点声明: struct treeNode { elementType element; treeNode * left; treeNod ...

  4. python常用算法(5)——树,二叉树与AVL树

    1,树 树是一种非常重要的非线性数据结构,直观的看,它是数据元素(在树中称为节点)按分支关系组织起来的结构,很像自然界中树那样.树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形 ...

  5. 二叉树之AVL树的平衡实现(递归与非递归)

    这篇文章用来复习AVL的平衡操作,分别会介绍其旋转操作的递归与非递归实现,但是最终带有插入示例的版本会以递归呈现. 下面这张图绘制了需要旋转操作的8种情况.(我要给做这张图的兄弟一个赞)后面会给出这八 ...

  6. 二叉树之AVL树

    高度为 h 的 AVL 树,节点数 N 最多2^h − 1: 最少N(h)=N(h− 1) +N(h− 2) + 1. 最少节点数n 如以斐波那契数列可以用数学归纳法证明: 即: N(0) = 0 ( ...

  7. 04-树4. Root of AVL Tree-平衡查找树AVL树的实现

    对于一棵普通的二叉查找树而言,在进行多次的插入或删除后,容易让树失去平衡,导致树的深度不是O(logN),而接近O(N),这样将大大减少对树的查找效率.一种解决办法就是要有一个称为平衡的附加的结构条件 ...

  8. AVL树和伸展树 -数据结构(C语言实现)

    读数据结构与算法分析 AVL树 带有平衡条件的二叉树,通常要求每颗树的左右子树深度差<=1 可以将破坏平衡的插入操作分为四种,最后通过旋转恢复平衡 破坏平衡的插入方式 描述 恢复平衡旋转方式 L ...

  9. 【数据结构】什么是AVL树

    目录 什么是AVL树 1. 什么是AVL树 2. 节点的实现 3. AVL树的调整 3.1 LL旋转 3.2 RR旋转 3.3 RL旋转 3.4 LR旋转 什么是AVL树 二叉查找树的一个局限性就是有 ...

  10. 数据结构中的树(二叉树、二叉搜索树、AVL树)

    数据结构动图展示网站 树的概念 树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>=1)个有限节点组成一个具有 ...

随机推荐

  1. .NET项目集成VS混淆加密

    在微软.NET体系下开发软件难免遇到加密类库的需要,从商业的角度出发,使用像Xenocode .NET.Dotfuscator专业版等混淆器肯定是最好的选择, 作为一般小项目,我倒是觉得简单.易用达到 ...

  2. VB.NET中图像处理的一些技巧以及其和C#图像处理的差距。

    早期的时候我使用的开发工具是VB6,VB6做图像处理的速度在我的软件Imageshop中有所体现,还是算可以的.目前,我已经改用C#来研究图像算法,C#中有指针,做图像处理起来效率确实要高不少.VB. ...

  3. 【leetcode❤python】 168. Excel Sheet Column Title

    class Solution(object):    def convertToTitle(self, n):        """        :type n: in ...

  4. html5中的beginPath与stroke

    名词解释: 定义和用法 beginPath() 方法在一个画布中开始子路径的一个新的集合. 语法 beginPath() 描述 beginPath() 丢弃任何当前定义的路径并且开始一条新的路径.它把 ...

  5. warning MSB3391

    1.用C#生成dll文件 提示 (warning MSB3391)"未包含任何可为 COM 互操作注销的类型"和 ( warning MSB3214)"不包含任何可为 C ...

  6. 交换机的端口状态是UP,但是查询该端口下的MAC地址为空

    (电脑已关机)电脑与交换机直连的端口状态 还是 UP ,但是 查询该端口下的 MAC地址为空. 初步怀疑原因: Wake-on-LAN(电脑关机,网卡还在工作) Wake-On-LAN简称WOL,是一 ...

  7. iOS之UIview动画

    一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持 执行动画所需要的工作由UIView类自动完成, ...

  8. 一天搞定CSS(扩展):CSS Hack

    做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我们会极不情愿的使用这个不太友好的方式来达到大家要求的页面表现.我个人是不太推荐使用hack的,要知道 ...

  9. 十三、java_GUI

    目录: 一.AWT 二.组件和容器 三.布局管理器 四.事件处理 五.java图形 六.window事件 一.AWT AWT(Abstract Window Toolkit)包括了很多类和接口,用于J ...

  10. 记录一则expdp任务异常处理案例

    环境:AIX 6.1 + Oracle 10.2.0.4 现象:在XTTS迁移测试阶段,遇到执行几个expdp的导出任务,迟迟没有返回任何信息,对应日志无任何输出,查看任务状态: SQL> se ...