学习过了二叉查找树,想必你们有遇到一个问题。例如,将一个数组{1,2,3,4}依次插入树的时候,造成了图1的状况。有创建树与没创建树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本。而只有创建的树如图2,才可以最大地体现二叉树的优势。数组

                         

  在上述的例子中,图2就是一棵平衡二叉树。科学家们提出平衡二叉树,就是为了让树的查找性能获得最大的体现(至少我是这样理解的,欢迎批评改正)。下面进入今天的正题,平衡二叉树。数据结构

AVL的定义

  平衡二叉查找树:简称平衡二叉树。由前苏联的数学家Adelse-Velskil和Landis在1962年提出的高度平衡的二叉树,根据科学家的英文名也称为AVL树。它具备以下几个性质:post

  1. 能够是空树。
  2. 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,而且高度之差的绝对值不超过1

  平衡之意,如天平,即两边的份量大约相同。如定义,假如一棵树的左右子树的高度之差超过1,如左子树的树高为2,右子树的树高为0,子树树高差的绝对值为2就打破了这个平衡。如依次插入1,2,3三个结点(以下图)后,根结点的右子树树高减去左子树树高为2,树就失去了平衡。性能

               

  那么在创建树的过程当中,咱们如何知道左右子树的高度差呢?在这里咱们采用了平衡因子进行记录。学习

  平衡因子:左子树的高度减去右子树的高度。由平衡二叉树的定义可知,平衡因子的取值只可能为0,1,-1.分别对应着左右子树等高,左子树比较高,右子树比较高。以下图3d

   

  说到这里,咱们已经能够大概知道平衡二叉树的结构定义须要什么内容了,数据成员,平衡因子,以及左右分支。因此,咱们给出以下的结构定义。你们主要首先先了解平衡因子的各个取值及其含义便可。blog

typedef char KeyType;                   //关键字排序

typedef struct MyRcdType            //记录博客

{数学

    KeyType key;

}RcdType,*RcdArr;

typedef enum MyBFStatus                //为了方便平衡因子的赋值,这里进行枚举

{                           //RH,EH,LH分别表示右子树较高,左右子树等高,左子树较高

    RH,EH,LH

}BFStatus;

typedef struct MyBBSTNode       //树结点类型定义

{

    RcdType data;                             //数据成员

    BFStatus bf;                                 //平衡因子

    struct MyBBSTNode *lchild,*rchild;        //左右分支

}BBSTNode,*BBSTree;

AVL树的插入时的失衡与调整

前言:这部分的失衡调整是指插入时的失衡与调整。删除的失衡与调整与插入大体同样,可是仍是有不少不一样,在后续章节讲解。

1、 失衡与调整的引导

  说了这么久,咱们开始进入今天的重点,如何将一棵不平衡的二叉树变成平衡二叉树(只讨论不平衡的是由于假如树是平衡的就没必要咱们进行处理)。平衡二叉树的失衡调整主要是经过旋转最小失衡子树来实现的

  最小失衡子树:在新插入的结点向上查找,以第一个平衡因子的绝对值超过1的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的,以下。而这个时候,咱们只要调整最小的不平衡子树,就可以将不平衡的树调整为平衡的树。

  在图7中。2结点(左子树树高-右子树树高)的绝对值=2。同理,3结点的平衡因子也为2.此时同时存在了两棵不平衡子树,而以3为根的树是最小的不平衡子树。咱们只要将其以3为中心,将最小不平衡树向左旋转,便可获得平衡二叉树,如图8。具体方法后续讲解。

           

下面咱们先用两个简单的例子来感觉一下调整的方法。

例1:右子树太高,向左旋转。步骤以下

       i. 将2做为根结点

      ii. 将1做为2的左孩子

     iii. 将2的左孩子做为1的右孩子(维护树的有序性,只是此处为NULL而已)

              

例2:左子树太高,向右旋转。步骤以下

       i.   将2做为根结点

      ii.   将3做为2的右孩子

     iii.   将2的右孩子做为3的左孩子(维护树的有序性,只是此处为NULL而已)

         

下面咱们再来看一个经过旋转,可是没办法达到平衡的失败例子。

例3:右子树太高,向左旋转。步骤以下

      i.   将3做为根结点

     ii.   将3的左孩子做为1的右孩子

    iii.   将1做为3的左孩子

                     

  如上,咱们发现,旋转以后树并无恢复平衡。对比图9,咱们发现,根的右子树不一致。

  在上面的三个例子咱们能够看出,咱们对不平衡的树进行旋转的时候,不只须要考虑须要最小失衡子树的根结点的平衡因子,还要考虑根结点较高子树的根结点的平衡因子。如图9与图11中,较高子树都为右子树,右子树不一样,旋转后有着彻底不一样的结果。

  为了方便讨论,咱们使用连续的两个字母来表示平衡因子,以表示各类不一样的状况。第一个字母表示最小不平衡子树根结点的平衡因子,第二个字母表示最小不平衡子树较高子树的根结点的平衡因子。使用L表示左子树较高,R表示右子树较高,E表示左右子树等高。如上述图11,根为的平衡因子L,较高子树的根为L,咱们将这种状况表示为LL型,再如上述例子3,根为R,较高子树的根为L咱们将这种状况称为RL型。

  下面咱们将对全部的失衡状况进行讨论。大体分为两大类,一左子树太高,二右子树太高。顺带提一下记忆的方法,读者对于具体某一种类型只要记住最后哪个结点做为根便可,也就是下面标红色的部分。

2、失衡与处理详解

1. 左子树太高

  a) LL型

  在LL型的不平衡树中,咱们首先找到最小不平衡子树,再以其根结点向右旋转。为什么是向右旋转呢?应该不难理解,向右旋转后,至关于右边的子树树高增长了1,而左边的子树树高下降了1,而本来的树高之差为2,那么就可以将根的平衡因子就化为0.引用一下以前的图以下。旋转以后为“原来根结点的左孩子做为新的根结点”。

  咱们对树以根结点为中心,向右旋转。旋转步骤以下

    i.   将2做为根结点

   ii.   将3做为2的右孩子

  iii.   将2的右孩子做为3的左孩子(维护树的有序性,只是此处为NULL而已)

           

  旋转后,3与2的平衡因子为EH,1的平衡因子保持不变。

  b) LE型

  在这里须要说明的是,插入的时候,是不会出现LE的这种状况的。只有在删除的时候才会出现。下面对于为什么插入不可能出现作一些我的看法。

  咱们不妨假设存在LE的这种状况。以下。

            

  假设咱们刚插入的元素是1,那么原来的树已经不是平衡树。不可能。

  假设咱们刚插入的元素是2.5,那么原来的树也不是平衡树,也不可能。因此说在插入的时候,是不会出现LE的这种状况的。而具体何时会出现呢,咱们在删除的章节进行讲解。同理,不可能出现RE的状况,下面也不进行讨论。读者可使用反证法自行验证。

  c) LR型

  对于LR,要分为两步进行旋。旋转以后为“原来根结点的左孩子的右孩子做为新的根结点”。

  第一以较高子树的根,即1,为中心向左旋转。具体步骤以下。

          i. 将2的左子树做为1的右子树(维护树的有序性,只是此处为NULL而已)

         ii.  将1做为2的左子树

        iii.  将2做为3的左子树

              

  第二以原树的根,即3为中心,向右旋转。最后结果以下

 

  旋转后,1,2,3的平衡因子变为0(无需记忆)。再次发表我的意见,平衡因子要用到的时候推一下就行了。

2. 右子树太高

  a) RR型

  仍是引用一下以前的例子。旋转的步骤以下。旋转以后为“原来根结点的右孩子做为新的根结点”。

     i.  将2做为根结点

    ii.  将1做为2的左孩子

   iii.  将2的左孩子做为1的右孩子(维护树的有序性,只是此处为NULL而已)

        

  最后1,2,3的平衡因子都为EH。

  b)RL型

  仍是引用一下以前的例子。与LR型相似,咱们须要进行两次旋转。旋转以后为“原来根结点的右孩子的左孩子做为新的根结点”。

  第一,以根结点的右孩子即3为中心向右旋转,结果以下。具体步骤以下

      i.  将2做为1的右孩子

     ii.  将3做为2的右孩子

    iii.   将2的右孩子做为3的左孩子(维护树的有序性,只是此处为NULL而已)

              

  第二,以原根结点即1,做为中心,向左旋转。结果以下。具体步骤以下

     i.   将2做为根结点

    ii.   将1做为2的左孩子

   iii.   将2的左孩子做为1的右孩子(维护树的有序性,只是此处为NULL而已)

       

  最后1,2,3的平衡因子都会EH

三、  插入时失衡与调整的总结

  1. 在全部的不平衡状况中,都是按照“寻找最小不平衡树”->“寻找所属的不平衡类别”->“根据4种类别进行固定化程序的操做”。
  2. LL,LR,RR,RL其实已经为咱们提供了最后哪一个结点做为新的根指明了方向。如LR型最后的根结点为原来的根的左孩子的右孩子,RL型最后的根结点为原来的根的右孩子的左孩子。咱们只要记住这四种状况,能够很快地推导出全部的状况。
  3. 维护平衡二叉树,最麻烦的地方在于平衡因子的维护。想要熟悉这个过程,建议读者多多画图,在感官上首先体验这个过程。

 

  说到这里,咱们已经了解了了解了什么是平衡二叉树,插入结点后如何调整平衡二叉树。咱们数据结构中常常讲到的有增删查改,那么下面咱们来说解一下如何删除。

AVL树的删除时的失衡与调整

今天心血来潮想要写这篇博客的缘由主要就在于此。我在网上找了许久,不少人对于AVL树的查找,插入都讲解得很是精彩,可是删除的时候却常常贴出一段代码,比较少有讲解,对于我等须要完成做业的学生实在难受,完成做业后就但愿可以与你们分享一下。咳咳,咱们回归正题。前方高能,喝口水,看一下窗外帅哥美女再继续看吧。

1、             预备知识

  1.       树的删除

假若有一棵二叉查找树以下,咱们对它进行中序遍历,能够获得1, 2, 2.5, 3。咱们发现,这是一个递增的序列。假如咱们如今要删除的结点为3,在不考虑树的平衡问题时,应该哪一个结点来做为顶替3的位置呢呢?答案是:对排序二叉树进行中序遍历时,3的直接前驱或者直接后驱。在这里,就是2.5,因此删除后,不进行调整的结果如中间图。假如咱们如今要删除的结点为2,在不考虑树的平衡问题时,1顶替2的位置(假设左孩子优先于右孩子)。最后以下右图。

具体的步骤以下:

      i.  寻找到要删除的结点(3)

     ii.  将待删除结点的直接前驱或者直接后驱赋值给待删除结点(2.5赋值给3结点)

    iii.  将直接前驱或者直接后驱删除(将叶子结点的2.5删除)

         

     因为咱们今天主要讲的是平衡二叉树的平衡调整,因此这部分就权当给读者恶补一下。假如读者仍是不能理解,请先查看一下二叉查找树的删除,再继续往下看。

 2.   平衡因子的预告

  咱们已经知道,平衡因子有且仅有三种取值,LH,RH,EH。对于以下的一棵树,删除一个结点后

  a)   本来树左右子树等高。根平衡因子的取值变化为EH->LH,EH->RH。

  b)   本来树左右子树不等高,在较高的子树上进行删除,根平衡因子的取值变化为LH->EH,RH->EH。须要注意的是,当根的平衡因子变化为LH->EH,RH->EH时整棵树的高度是降低的。最简单的例子以下。如下两棵树,分别删除1,3后,平衡因子LH->EH,RH->EH。最后树的高度都降低了。

      

  c)  本来树左右子树不等高,在较低的子树上进行删除,此时须要对树进行平衡处理。以下删除告终点1,获得右边的不平衡树。

      

  3.       什么会致使树高下降

  a)   如第2点的的第b项,根的平衡因子由LH->EH,RH->EH时整棵树的高度是降低的。

  b)   创建在a点以及平衡处理正确的基础上,对树进行正确的平衡处理后,树高会下降。为何呢?由于其实最小不平衡子树进行旋转后,最小不平衡子树根的平衡因子老是变

  为EH,或者说,平衡调整老是下降了最小不平衡子树的高度。举例以下。树的高度由原来的3变为了2.

    

2、             正式进入AVL树的删除与调整

 1.       删除结点致使平衡二叉树失衡

  AVL树也是一棵二叉查找树,因此它的删除也是创建在二叉查找树的删除之上的,只是,咱们须要在不平衡的时候进行调整。而咱们在预备知识的第2点中的C项中已经说起到,假如咱们在较低的子树上进行删除,将会直接致使不平衡树的出现。那么,咱们须要进行平衡处理的,就在于此种状况。举个栗子。

    

  2.       调整不平衡子树后,致使了更大的不平衡子树

  假设最小不平衡子树为A,它为双亲结点b的左子树,而b的平衡因子为RH。假设咱们如今对A进行了平衡处理,如上所讲,进行平衡处理将致使树高下降。即咱们让b较矮的子树变得更矮了。此时对于b而言,一样也是不平衡的。此时,咱们须要再一次进行一次平衡处理。举个栗子以下。

  假设咱们删除告终点6.那么最小不平衡子树就是1,3,5对应的二叉树。它的双亲10的平衡因子为RH。咱们首先对最小不平衡子树进行调整,结果如右图。咱们发现,最小不平衡子树从根结点的左子树变成了整棵树,因此这个时候咱们又要进行一次平衡调整。具体的平衡调整步骤与插入时是一致的,在这里就赘述。

       

  在讲解插入新的结点进行平衡时,说到删除时与插入时不有着很大的不一样就在于此。插入时,进行一次平衡处理,整棵树都会处于平衡状态,而在删除时,须要进行屡次平衡处理,才能保证树处于平衡状态。

  细心的朋友可能发现,上面右图中,最小不平衡子树的较高子树的平衡因子为EH。这个时候,就出现了前面插入时说起的不可能出现的失衡状况。

 3.       失衡与调整的最后一种状况LE与RE

  LE与RE型的失衡树,在进行调整的时候,和LL与RR型的旋转方式是一致的。只是最后初始根结点的平衡因子不为EH而已。就拿上面的例子而言,调整后的结果以下。初始根结点的平衡因子为RH。相对应的,假如是LE的状况,调整后初始根结点的平衡因子为LH。