数据结构-5-平衡二叉树算法原理解析

平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

  很显然,平衡二叉树是在二叉排序树(BST)上引入的,就是为了解决二叉排序树的不平衡性导致时间复杂度大大下降,那么AVL就保持住了(BST)的最好时间复杂度O(logn),所以每次的插入和删除都要确保二叉树的平衡

  关于书上说的旋转操作的概念,一般人真的看不懂,也无法领会,下面我从几个简单的例子和别的方面来解惑。

 

关于“转”: 

旋转——>“两个结点的变换

如图:

《数据结构-5-平衡二叉树算法原理解析》

就拿第一个来说->100101的变换:

101占据点100的位置,点100换到了101的对面的位置,其他点的相对位置不变。

我们可以更直观的理解为:把101结点上提一下!

分析:101>100,所以100可以作为101的左孩子;

也就是在二叉排序树中,两个结点这样的变换操作是可行的,是符合二叉排序树的性质。

 

下边正式说这个图的四种不平衡情况(插入时)及操作:

首先还需要明白一个概念->最小不平衡子树的根结点:也就是当你进行插入操作时,找到该需要插入结点的位置并插入后,从该结点起向上寻找(回溯),第一个不平衡的结点即平衡因子bf变为-22

为什么要引入这个最小不平衡根结点的概念,因为在插入时,对该子树进行保持平衡操作后,其它的结点的平衡因子不会变,也就是整棵树又恢复平衡了。为什么呢?

你想想不平衡点的bf一定是-22吧,经过平衡操作后,他会把一边子树的一个结点分给另一边的子树,也就是一边的深度分给另一边,这样就平衡了!

比如,插入前:左边是深度1,右边深度是0;插入后左边深度是2,右边深度是0,经过平衡后左边深度是1,右边深度是1

那么你说插入前和插入后该根结点所领导的子树的深度变没??仍是1,显然没变!那么就仍保持了这棵树的平衡了!

 

下面即四种情况分别为:左左、右右、左右、右左,每种情况又有两个图:是该情况的最简单的图形,是该情况的一般的图形;

x为最小不平衡子树的根结点,y为刚插入的点

 

左左:

即在x的左孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图),即y可以是c,也可以是c的左孩子(如图),也可以是c的右孩子(不在画出)

             《数据结构-5-平衡二叉树算法原理解析》                     

就不用说了,结点x和结点a变换,则树平衡了;那么图就是树中的一般情况了a结点有右孩子d,那要进行xa变换,那么a的右孩子放哪啊?

很简单,如图放在x的左孩子上;分析:x>d,d>a,所以d可作为x的左孩子,且可作为a的右孩子中的孩子。下边这样的类似情况不再一一分析,自己分析分析~

实现:找到根结点x,与它的左孩子a进行交换即可使二叉树树再次平衡;

 《数据结构-5-平衡二叉树算法原理解析》

 

右右:

即在x的右孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图),即y可以是c,也可以是c的右孩子(如图),也可以是c的左孩子(不在画出)

实现:找到根结点x,与它的右孩子a进行交换即可使二叉树树再次平衡;

 

 

左右:

即在x的左孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图),即y可以是c,也可以是c的右孩子(如图),也可以是c的左孩子(不在画出)

 

 《数据结构-5-平衡二叉树算法原理解析》

这个左右和下边的右左,稍微复杂了点,需要进行两次交换,才能达到平衡,注意这时yc的右孩子,最终y作为x的左孩子;若yc的左孩子,最终y作为a

的右孩子,画图分析一下~~下边类似,不再敖述。

 

实现:找到根结点x,让x的左孩子ax的左孩子a的右孩子c进行交换,然后再让xx此时的左孩子c进行交换,最终达到平衡;

 

右左:

即在x的右孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图),即y可以是c,也可以是c的右孩子(如图),也可以是c的左孩子(不在画出)

 《数据结构-5-平衡二叉树算法原理解析》

实现:找到根结点x,让x的右孩子ax的右孩子a的左孩子c进行交换,然后再让xx此时的右孩子c进行交换,最终达到平衡;

 

一定要注意这个交换操作,比如ab交换(a在上,b在下),b一定要占据a的位置!什么意思?就是b要放在(覆盖)储存a的那块内存上,

再通俗点说,若ax的左孩子,则交换后b要做x的左孩子;这就是所谓的b占据a的位置!

 

如何找到最小不平衡子树的根结点x

 

插入一个结点时,我们首先找到需要插入的位置,并插入;数据结构上用的是递归,不要说递归太浪费时空,你想想一个含2^31个结点的平衡二叉树的深度大约是31吧,它递归再多也不就是31层!而且递归代码短小、精悍、富含艺术之美!所以我认为对于这个平衡二叉树,用递归很合适!

显然插入之后就要检查是否出现不平衡的结点,那么如何检查?

我们知道,你插入的时候用的是递归,一条线找到要插的位置,并插入;那么谁的平衡因子的有可能变呢?

不难想象,只有该条线上的结点的平衡因子有可能改变!那么我们在回溯的时候不就可以找到第一个不平衡的子树的结点?!

可是我们如何判断该结点的平衡因子是否应该改变,显然要看它被插入结点的一边的深度是否增加;

如何看它被插入结点的一边的深度是否增加?

《数据结构-5-平衡二叉树算法原理解析》

如上图,如何看x的右孩子a(即被插入结点的一边)的深度增加?我们知道在a的右孩子上插入了结点y那么abf是一定要减1

那么x结点的bf?可根据abf决定是否改变!

a:bf=-11,那么a之前一定为0,表示a的深度增加了,那么xbf可根据ax哪一边的孩子决定+1-1

a:bf=0,那么a之前一定为-11,表示a的深度每增加,那么不仅xbf就不用变,该条线上的所有结点的bf都不用变,直接返回即可;

 

当然了,那么找到最小不平衡子树的根结点x了,如何判断它属于哪种不平衡呢?

根据上边的几种情况,我们需要知道两个方向,在回溯时可以记录一下该结点到下一个结点的方向0:左、1:右为第二个方向,传递给上一层中,那么上层中的方向就是一个方向,有了这两个方向就能确定是哪种不平衡了。

还就上边的图说吧~可以定义一个全局变量secdirection(第二个方向),也可在递归中定义一个局部变量,返回给上一层。在回溯到a中时,secdirection=1,到x的时候

x->a的方向也为1,定义firdirection=1;而这时x:bf=-2;那么就找到了最小不平衡子树的根结点x,又知道了两个方向,那么进行相应的平衡操作不就行了。

其实我代码中的就是按照写的,可是刚又想了,其实不用用个变量记录第二个方向,可以根据abf确定它的第二个方向,a:bf=-1说明右孩子的深度增加,y加到右孩子上;

a:bf=1;说明左孩子的深度增加,y加到左孩子上;

 

多多指教!

 

下边一一列出(插入操作中)变换后bf改变的结点及值:

左左:前a->bf=1  x->bf=0,a->bf=0;右右:前a->bf=-1 x->bf=0,a->bf=0;显然左左与右右的xa变换后的bf都为0

左右、右左中结点bf的改变要根据之前cbf

左右:若c->bf=1,x->bf=-1,a->bf=0,c->bf=0;若c->bf=-1,x->bf=0,a->bf=1,c->bf=0;若c->bf=0,x->bf=0,a->bf=0,c->bf=0

右左:若c->bf=1,x->bf=0,a->bf=-1,c->bf=0;若c->bf=-1,x->bf=1,a->bf=0,c->bf=0;若c->bf=0,x->bf=0,a->bf=0,c->bf=0

可以发现,当左右、右左的c->bf相同时xabf刚好取相反的值。

 

好了,到现在插入一个结点的二叉树终于平衡了,相应的平衡因子也修改了!插入算是完成了!!

 

删除时:

删除类似插入的操作,蛋又不同,删除会有一些特殊情况需要特殊处理,当然核心操作保持平衡是不变的!

 

删除时少一个结点,也就是该结点所在的子树深度可能会减小,而插入时多一个结点,该结点所在的子树深度可能会增加,

所以递归删除一个结点时,回溯时找到最小不平衡子树的根结点时,要向相反的方向去找属于哪种情况;

如图:

y为要删除的结点;

《数据结构-5-平衡二叉树算法原理解析》

y结点删除后,回溯到x结点x:bf=-1变为x:bf=-2;则需从相反方向即从x的右孩子的方向向下检查属于哪种情况,显然第一个方向为1:右;

第二个方向看a:bf的值——若为1时,那就相当于插入时右左的情况;若为-1时,那就相当于插入时左左的情况;可现在a:bf既不是1也不是-1

而是0,这就是删除的特殊情况了!我们不妨试试对他进行类似于插入时的右右操作,看怎么样~    如上图,经过变换后该子树平衡了!但是因子的

修改就跟插入时的右右不一样了!此时变为:x:bf=-1,a:bf=1;所以我们不妨就把a:bf=0也归纳为删除的右右左左(如图,不再敖述)操作;

那么删除时因子的改变需在插入时因子的改变中添加上:

左左:前a:bf=0 x:bf=1,a:bf=-1; 右右:前a:bf=0 x:bf=-1,a:bf=1;其他不变!

 

插入时结束结点平衡因子的修改,直接返回(也就是该树已经平衡了):

回溯时发现儿子结点的平衡因子为0(当发现不平衡结点,并进行平衡操作后,平衡后根结点的bf一定为0,也结束了)

但是删除时结束结点平衡因子的修改,直接返回,就与插入时不一样了:回溯时发现儿子结点的平衡因子为-11

再删除操作中,平衡一个子树后,该子树的深度不一定不变,而只有上边那种特殊情况该子树的深度不变,其他都会变!

可以想象,其实是很简单的道理:除了特殊情况其他都与插入的情况一模一样,说白了就是把深度大的子树(根结点的其中一个)向深度小子树贡献一个深度,

那么这样一来,该子树(对于根结点所领导的树)的深度是不是比原来的小1了?!所以要继续向上一个一个进行检索,直到根结点为止!

 

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/loadingsong/article/details/39098947
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞