AVL树是有平衡条件的二叉搜索树。这个平衡条件必须容易保持,而且需要保证树的深度是O(logN)。

  • AVL=BBST

  作为二叉搜索树的最后一部分,我们来介绍最为经典的一种平衡二叉搜索树:AVL树。回顾此前的几节,我们首先介绍的是二叉查找树BST。然而我们也能感受到,尽管从同时兼顾高效的静态操作

  和动态操作的角度讲,BST相对此前简单的向量和链表已经具有某种优势和潜质,但是毕竟它并不能保证这一点。其原因在于 它的高度,无论是从平均情况 还是最坏情况都不能保证做到足够的低,具体来说也就是做到logN以下当然——在BST中的确存在一种特殊的类型:也就是所谓的Complete Binary Tree它的高度可以达到严格的最小——也就是logN。然而相对于整体的BST这类BST的数量极少。而且将任何一棵树转化为一棵完全二叉树所需要的成本也太高,也正因如此,我们的建议是:或许应该适当地放松所谓“平衡”的标准。也就是说,只需考察某一类在渐近意义下不超O(logN)高度的树即可。而这样一类树,也就是我们所说的平衡二叉搜索树Balanced Binary Search Tree,BBST。

本节涉及概念及其关系

  比如我们这一节将要介绍的AVL树就是在这种意义下的一种BBST。以AVL树为代表的这些BBST

  首先并没有放弃渐近意义logN的复杂度底线。同时正因为它已经适度地放松了平衡的标准,所以通过精巧地设计,它们都可以具有这样一种属性:具体来说,对于任何一棵这样意义下的BBST在其生命期内即便在某次操作之后它不再满足BBST的条件, 游离到BBST这个范畴之外,我们也可以通过上节所介绍的等价变换迅速地将其重新转化为一棵等价的BBST。也就是说可以通过极小的代价就使之重新归入BBST的范畴。而这种极小的代价是多少呢?不出你的意料——依然是不超过logN。

  令刚刚失衡的搜索树重新恢复为一棵BBST的过程也称作重平衡Rebalance。而对于包括AVL树在内的各种BBST而言,其核心的技巧无非两条:

  • 如何来界定一种“适度的平衡”标准
  • 其次则是一整套重平衡的技巧和算法

  以下我们就以AVL树为例,具体地讲解如何完成这两项任务。

  先不管具体AVL树的要求,先来想想,如果我们自己来设计平衡标准,该如何考虑呢?最简单的想法是,要求左右子树具有相同的高度,那这种想法不要求树的高度尽量低。但是……极端情况下可能出现两条左右分叉的单链,这就很心碎了。所以这个标准是远远不够的。

  再想想有没有更好的办法,(可达鸭眉头一皱,发现事情并不简单hhhh)于是我们想到了另一种平衡条件——要求每个节点都必须有同高的左右子树。可是这样一来又会出问题:因为空树-1,那就只有具有2^k-1个节点的理想平衡树(Perfect Binary Tree)满足了,这个适用性就太狭窄了……很扎心。因此,虽然这个条件保证了树的高度很低,但是太苛刻了,我们还要再放宽点条件。

  后来,凶暴死宅的计算机科学家们(误)想到了一个绝妙的点子,而且我这里地方够大,写得下hhhhh。 用这个条件来限制:“让每个节点的左右子树高度最多差1”。可以证明,大致来讲一个AVL树的高度<=1.44log(N+2)-1.328。

  这棵树的左子树是高度为7且节点数最少的AVL树,右子树是高度为8且节点数最少的AVL树。可以看出在高度为h的AVL树中,最少节点数S(h)=S(h-1) + S(h-2) + 1。对于h=0,S(h) = 1;h=1,S(h)=2。函数S(h)与斐波那契数密切相关,由此可以推出上面的关于AVL树的高度上界。

  有了上面的感性认识后,我们来详细讨论、解释AVL树的各处细节,将上面给出的若干浓缩表述一一诠释。

  首先,给出在AVL的意义下,什么叫做适度的平衡。这要引入一个概念——平衡因子,凭它来判断一棵树是否是在AVL意义下的适度平衡

平衡因子(Balanced  Factor)就是左子树的高度与右子树高度之差,BalFac(v)=height(lc(v))- height(rc(v))。结合上面发明者的定义

  

  那么很显然,这是一颗AVL树(通过校验每个节点的balFac,每处都上上下下不超过1):

  

  AVL树只考虑左右子树的高度,所以只有所有节点满足全局的单调性即可,并不需要关心具体的数值。我们应该把更多的关注点集中于每个节点的平衡因子上。从这个例子可以看出,AVL树未必是CBT,反过来说,如此定义的AVL树,是否适度平衡呢?

  • AVL=适度平衡

  可以证明,AVL树的确是适度平衡的——也就是说 一棵规模为N的AVL树高度在渐近意义下是不超过logN的。

  

  实际上,为了证明规模固定的AVL树高度不会超过某个上限,我们可以等价地证明:在高度固定的情况下,一棵AVL树的节点也不至于太少。具体来说,可以证明这样一个事实:对于高度固定为h的AVL树其中所包含的节点数,至少是与h呈fibonacci数关系。

  

  为了得到这个关系式,我们需要借助递推式。具体而言可以证明这样一个递推式:S(h)=S(h-1) + S(h-2) + 1,这也就是上面所给出的那个递推式。如果我们将高度为h的AVL树的规模下限定义为S(h)的话,那么S(h)与S(h-1)以及S(h-2)之间就满足这么样一个叠加的关系。

  为此,我们来考察那棵高度为h同时规模达到最小的AVL树。

  

  既然它的规模要达到最少,所以它的左子树和右子树的规模也应该尽可能少,那么在AVL树的定义下,可变化的余地充其量不过其中一棵子树比另一棵子树高一层。不失一般性,假设左子树比右子树高出一层,因为它的高度为h-1,所以它的规模下限自然也就是S(h-1)。同理,作为高度为h-2的右子树,它的规模下限自然也就是S(h-2)。当然——还不要忘了这里的树根节点。这也就是为什么我们还要再附加上一个单位1。

  这个递推式是我们所有分析的核心,而以下只不过是一些简单的数学技巧而已。为此,我们不妨对它做一个等价变换——在左右各加一个1

 S(h)+1=[S(h-1)+1 ]+ [S(h-2) + 1]

  接下来,如果我们将S(h)+1定义为一个新的函数T(h),就会发现递推式的右侧变成T(h-1) +T(h-2)。这种形式是fibonacci数所特有的递推形式。所以我们可以断定它应该是等于fibonacci的某一项。那么具体的是从 h前后位移多少项呢?

  我们只需考察对应的边界情况即可:首先考察规模为1高度为0的AVL树。此时的T(h)应该等于1+1,这是fibonacci数的第三项。再来考察高度为1的AVL树,其规模最小也不至低于2,也就是左子树为一个节点、右子树为空的一棵AVL树,此时的T(h)应该是2+1,这是fibonacci数的第四项。

  由此可见 这里的T(h),只不过是fibonacci数向前位移了三位。我们知道fibonacci数,大致是呈Φ的指数形式增长,由此我们也得到了n关于高度h的一个下界。因此反过来等价地 n的对数,也就构成了h的一个上界。而这一点正是BBST所谓适度平衡的要求——这就意味着我们的AVL树的确是适度平衡的。

  好了,至此我们也就完成了第一项使命——也就是给出AVL意义下的,适度平衡标准。下一节将给出具体的重平衡(旋转操作)细节和插入新节点的算法。

AVL树 & 重平衡概念的更多相关文章

  1. AVL树(平衡二叉查找树)

    首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...

  2. 判断AVL树是否平衡

    AVL树是高度的平衡二插搜索树,其左子树和右子树的高度之差不超过1(树中的左子树和右子树都是AVL树),维持这个高度之差就要控制它的平衡因子.那么判断一颗AVL树是否平衡就需要判断它的左子树和右子树高 ...

  3. AVL树的平衡算法(JAVA实现)

      1.概念: AVL树本质上还是一个二叉搜索树,不过比二叉搜索树多了一个平衡条件:每个节点的左右子树的高度差不大于1. 二叉树的应用是为了弥补链表的查询效率问题,但是极端情况下,二叉搜索树会无限接近 ...

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

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

  5. AVL树 高度平衡的二叉查找树

    1.What is AVL tree? AVL tree 是一种特殊的二叉查找树,,首先我们要在树中引入平衡因子balance,表示结点右子树的高度减去左子树的高度差(右-左),对于一棵AVL树要么它 ...

  6. 自己动手实现java数据结构(七) AVL树

    1.AVL树介绍 前面我们已经介绍了二叉搜索树.普通的二叉搜索树在插入.删除数据时可能使得全树的数据分布不平衡,退化,导致二叉搜索树最关键的查询效率急剧降低.这也引出了平衡二叉搜索树的概念,平衡二叉搜 ...

  7. 高度平衡的二叉搜索树(AVL树)

    AVL树的基本概念 AVL树是一种高度平衡的(height balanced)二叉搜索树:对每一个结点x,x的左子树与右子树的高度差(平衡因子)至多为1. 有人也许要问:为什么要有AVL树呢?它有什么 ...

  8. AVL重平衡细节——插入

    话说这个系列鸽了好久,之前在准备语言考试,就没管博客了,现在暑假咱们继续上路! 每当我们进行一次插入之后,整棵AVL树的平衡性就有可能发生改变,为了控制整棵树的高度,我们需要通过一系列变换(重平衡)来 ...

  9. 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树

    一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...

随机推荐

  1. Python+selenium打开网页

    东西都安装好了,是不是都迫不及待的想要运行一个程序呢? 不过不幸的是,在正式编程打开网页之前,我们还需要做一件事:下载驱动. 据说,在很久之前的selenium1和2中,驱动是被内嵌在selenium ...

  2. C#编程命名规范推荐

    1.用Pascal规则来命名方法和类型. Pascal 大小写形式是指名称中的单词的第一个字母大写public class DataGrid{public void DataBind(){ }} 2. ...

  3. C#三步实现标准事件处理程序

    事件,MSDN解释:类或对象可以通过事件向其他类或对象通知发生的相关事情.发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”. 有关事件的理论与好处,在这里就不再废话了,感兴趣的 ...

  4. 基础教程:视图中的ASP.NET Core 2.0 MVC依赖注入

    问题 如何在ASP.NET Core MVC Views中注入和使用服务. 解 更新 启动 类来为MVC添加服务和中间件. 添加一项服务 添加一个Controller,返回 ViewResult. 添 ...

  5. 创建mysql快捷登录方式

    1.先找到mysql的bin目录,将Mysql.exe发送快捷方式到桌面,到这里还没有完成. 2.然后右键选择属性,将目标后面添加上 -uroot -p 我的完整目标如下: D:\install\my ...

  6. NuGet的使用、部署、搭建私有服务

    NuGet的使用.部署.搭建私有服务 前言 什么是NuGet? 为什么要使用NuGet NuGet的优点 使用 Get-Help NuGet Install-Package Get-Package U ...

  7. FreeRTOS 移植到WIN10

    背景 标题表述的不准确,大意是移植到WIN10的PC机,Intel I5. 最近因为项目接触了FreeRTOS 实时操作系统,想对这个操作系统有一个更深入的了解,所以决定下载源码看看,下面这个链接的随 ...

  8. Activity 的 4 种加载模式

    Activity 的 4 种加载模式 配置 Activity 时可指定 android:launchMode 属性,该属性用于配置该 Activity 的加载模式.该属性支持如下 4 个属性值. * ...

  9. Servlet之初始化参数和传递数据(ServletConfig,ServletContext )

    ServletConfig 容器初始化一个Servlet的时候,会为这个Servlet建一个唯一的Servletconfig的对象(Servlet的配置对象) 容器会从部署的描述文件(web.xml) ...

  10. O(N^2)最长上升子序列

    //最长上升子序列o(N^2)可以不连续的子序列, //状态为maxlen[i]表示以a[i]为终点最大上升子序列长度 #include<iostream> #include<cst ...