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意义下的,适度平衡标准。下一节将给出具体的重平衡(旋转操作)细节和插入新节点的算法。