AVL树的理解以及编写(C++)

AVL树

AVL树的定义

AVL树是一种高度平衡的二叉搜索树,对每一个节点而言它的左子树右子树高度差最多为1。一棵有n个节点的AVL树高度为O(lgn).

AVL树基本操作

AVL树基本操作和一棵普通二叉树是一样的。一棵普通二叉树实现如下
二叉搜索树
而一棵AVL树满足二叉树的所有性质。所以譬如在寻找最大值(Maximum)、最小值(Minimum)时这类函数没有任何改动。
然而,从AVL的描述种我们可以看出,AVL树的节点比起普通二叉树,多了一个h值(节点高度)。所以,我们需要一个可以在我们进行了改变树高度的操作之后,对树高进行维护的函数。

InsertHeightFixup

InsertHeightFixup应该是一个通用函数,最初我是为Insert操作设计的;后来逐渐发现,所有改变高度的操作都可以由这个函数进行高度维护。
假设我们在一棵AVL树上插入一个节点,那么这个节点一定在树的最低端(正如同一棵普通的二叉搜索树);也就是,这个节点高度为1。那么,可能被这个节点影响高度的,应该是,从这个节点到树根路径上的所有点。
并且,一个节点的高度应该是他的左节点右节点中高度的最大值+1,既x.h=max(x.left.h,x.right.h)+1。所以,在插入一个节点后,为了维护所有的h值,我们只需要将上述操作对所有x.p至root执行即可。如果出现了x.left.h==x.right.h,遍历可以提前终止。
我已经提到,这个函数在改进的过程中可以适用于所有改变了树高度的操作。
所以,对于左旋\右旋这个函数同样适用。
《AVL树的理解以及编写(C++)》
在上图的右旋操作中,很明显,abc三棵树树高不受影响。受到影响的应该是A、B两个节点的高度。所以,将x.h=max(x.left.h,x.right.h)+1这个操作,针对于B到root路径上所有节点使用即可。(同样的,可以提前终止)
那么,在Delete操作后,原理也很简单。如果Delete操作针对的是只有左孩子或者右孩子的节点,将x.h=max(x.left.h,x.right.h)+1这个操作对于替代了删除节点toDelete的节点toReplace以及toReplace到root路径上所有节点使用即可;另一种情况就是toReplace并不是toDelete的孩子节点,而是toDelete的Successor,那么你只需要将那个操作针对取代了toReplace位置的节点(以及它到root路径上的所有点)使用就可以了。

Balance

当我们插入一个节点

Balance函数是整棵AVL树的难点所在,Balance维护了AVL树“左右子树高度差小于等于1”的性质。Balance是通过左旋右旋的操作来实现的,并且可以维护可能因为Insert或者是Delete改变的AVL树结构。
首先考虑Insert可能带来的高度变化。如果在插入了某个节点,AVL树的性质受到了破坏;那么如果我们要修复这种破坏,我们必须要找到树种从下往上第一个不AVL的节点x,其中|x.left.h-x.right.h|==2。我们可以假设,x.left.h-x.right.h==2。首先需要强调的一点是,基于该树AVL性质的考虑,在Insert之前,x.left.h-x.right.h==1。所以,该树存在下图两种可能:
《AVL树的理解以及编写(C++)》
这里含k的表达式代表相应高度关系的树。
对于情况1,我们很容易发现,通过对x进行一次右旋,整个树就重新AVL了。
那么对于情况2呢?
我们需要继续与k+1的那棵树进行讨论:是左子树高,还是右子树高。
然后我发现,如果这时候x.right>x.left,那么,我们如果对x.left进行一次左旋,这棵树就会变成情况1。既然有了情况1,我们继续进行一次右旋,即可AVL。
《AVL树的理解以及编写(C++)》
在这里很容易发现的一个事实就是:如果x.p.left.h>x.p.right.h&&x.left.h>x.right.h,通过对x.p进行一次右旋,可以使得x.p.right.h>x.p.left.h。也就是说,我们从上文种找到的最低的x开始向下寻找,只要有一个节点y的左右高度关系和y.p的左右高度关系一致(y.left.h-y.right.h)*(y.p.left.h-y.p.right.h)>0(在关系不一致时我们沿着较大高度的一边向下,期间左子树和右子树轮流做更高的子树)我们就可以通过旋转对y.p的左右高度相对值进行调整。一直重复这个旋转过程直到x被我们旋转取代,整棵树重新满足AVL。

当我们删掉一个节点

进行Delete操作时,和Insert有相似之处。如果你随意地想一想,就会觉得维护的过程和Insert是一样的:找到第一个不AVL的节点,一直向下拆分比较高的子树,直到某个节点左右子树高度关系与它的父节点做右子树高度关系相同……然而,Delete有时候会造成一种特殊情况。假如x.left.h=k,x.right.h=k+2,有可能会存在x.right.right.h==x.right.left.h==k+2。当然,如果不不出现这种情况整个过程和Insert的Balance并没有区别;一旦出现其实也很简单,只需要直接对x进行左旋就好了。左旋之后,又变成左高右低咯。

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