简单介绍一下AVL树
AVL树是一种自平衡的二叉搜索树(Balanced Binary Search Tree, BBST),由俄罗斯数学家G. M. Adelson-Velsky和E. M. Landis在1962年发明,因此以其名字首字母命名。AVL树通过保持任何节点的两个子树的高度最大差别为1来确保树的平衡,这种平衡性使得AVL树在插入、删除和查找操作中具有较好的性能。这里我讲解的是以引入平衡因子的AVL树的模拟实现。平衡因子 = 左子树高度 - 右子树高度。
AVL树为什么不能做到绝对平衡呢?因为实际使用中,数据可能是一次一次插入的。这样导致了没有办法维持绝对平衡的结构。
AVL的增删改查的时间复杂度是O(lgN),它相较于二叉搜索树来说,通过平衡树的高度来提高了时间复杂度的上限。
插入接口的模拟实现
在实现insert之前,简单把AVL树的基本模子整起来
下面正式介绍insert接口。这里我先将二叉搜索树的插入接口的逻辑部分先写出来。
接下来重点介绍一下引入平衡因子以维持AVL树的平衡特性。因为平衡因子 = 右子树高度 - 左子树高度。
所以,当新节点插入在左边时,新节点的父亲节点的平衡因子–。当新节点插入在右边时,新节点的父亲节点的平衡因子++。
如果更新完父亲节点的平衡因子后,恰好父亲的平衡因子等于0,此时,说明parent所在的子树的高度不变, 不会再影响祖先, 不用再继续沿着到root的路经往上更新。插入结束,直接返回true。
如果此时更新完平衡因子后,父亲节点的平衡因子为1或-1时,仍然需要向上更新祖先节点的平衡因子。因为可能祖先的平衡因子的绝对值已经为2了。此时,继续向上更新平衡因子。
更新后parent平衡因子 ==2 or -2, 说明parent所在的子树的高度变化且不平衡, 对parent所在子树进行旋转, 让他平衡。然后插入结束
还有一个比较特殊的边界情况,当更新平衡因子更新到根节点时,插入结束。
接下来介绍旋转部分的内容。
首先介绍一下左单旋转。
通过上图可以看到,左单旋转的主要实现逻辑就是,将cur的左子树连接到parent的右子树,然后,将parent连接到cur的左子树。然后修改cur、parent、curLeft的_parent。最后将平衡因子修改成0。
具体的细节有需要考虑curLeft是否为空,以及parent->_parent是否为空的问题。以及需要让cur连接parent->_parent。
左单旋参考代码如下
再来看右单旋的实现
通过上图可以看到,右单旋转的主要实现逻辑就是,将cur的右子树连接到parent的左子树,然后,将parent连接到cur的右子树。然后修改cur、parent、curLeft的_parent。最后将平衡因子修改成0。细节问题同左旋转一致,这里不做赘述。
右单旋参考代码如下
下面介绍一下右左单旋
我们需要先对90进行右单旋,让后对30进行左单旋。然后树的高度就平衡了
虽然左旋接口和右旋接口可以复用。但是左旋和右旋接口中,统一将平衡因子全部改为0,这不符合双旋后的平衡因子。修改平衡因子需要考虑三个情况。
情况一,新增节点的平衡因子为 0。那么此时将parent、cur和curLeft的平衡因子全部修改成0即可。
情况二,新节点插入在curleft的右子树诱发的双旋。双旋后,需要将parent的平衡因子修改成-1,cur和curleft的平衡因子为0。
情况三,新节点插入在curleft的左子树诱发的双旋。双旋后,需要将cur的平衡因子修改成1,parent和curleft的平衡因子为0。
右左双旋参考代码
左右双旋的情况如下
这里的更新平衡因子一样需要考虑三个情况,分别为当curright为新插入节点时,三个节点平衡因子都为0。以及当插入在b位置时 parent的平衡因子为 1, cur和curright平衡因子为0。 以及当插入在c位置时, parent和curright的平衡因子为0, cur的平衡因子为-1。
参考代码如下
完整insert接口参考代码如下
总结一下insert接口,就是在二叉搜索树的insert的基础上引入平衡因子以及旋转的概念。引入平衡因子为了让树的高度差绝对值小于2,这样让搜索树的结构的到保证。让增删改查效率为树的高度次,即O(lgN)。而旋转就是保证平衡因子能够保证高度差绝对值小于2的手段。旋转一共分为单旋和双旋。单旋分左单旋和右单旋,双旋分左右双旋和右左双旋。通过学习AVL树insert接口,可以感受到AVL树的高效性能以及性能高效背后的原因。
删除接口的介绍
删除接口的实现思路如下,首先,按照搜索树的规则进行节点的删除。然后,再删除节点位置向上更新平衡因子。最后,若平衡因子为2或者-2,进行相应旋转来平衡高度。
学习了插入接口,相应的删除接口也就很好理解它的原理。处于学习者的角度看,AVL树的结构理解了就行,并不是需要我们去造一个轮子。毕竟知识是无穷无尽的,咱的精力是 有限的。
总结
AVL树是一种复杂且性能强悍的数据结构。它通过平衡因子绝对值小于2来控制二叉搜索树的高度,以达到增删改查的高效。通过insert的模拟实现,明白了如何使用旋转操作保证AVL树的平衡因子绝对值小于2。当然,并不是所有的AVL树都是这么设计,不使用平衡因子也能够做到控制好树的绝对高度。但是,这么设计代码的可读性就没那么好了。