天天看点

纸上谈兵: AVL树

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!

纸上谈兵: AVL树

深度为n的二叉树

纸上谈兵: AVL树

深度为log(n)的二叉树

我们将处于同一深度的节点归为一层。如果除最后一层外的其他层都被节点填满时,二叉树有最小深度log(n)。

n和log(n)的时间复杂度意味着什么呢?时间复杂度代表了完成算法所需要的运算次数。时间复杂度越小,算法的速度越快。

纸上谈兵: AVL树

可以看到,随着元素的增加,log(n)的时间复杂度的增长要远小于n。所以,我们自然希望二叉搜索树能尽可能保持log(n)的深度。在上面深度为n的例子中,我们发现,每个节点只有左节点被填满。树的每一层都有很多空位。能不能尽可能减少每一层的空位呢? (相应的,减少树的深度)

纸上谈兵: AVL树

“紧致”的树

一种想法是先填满一层,再去填充下一层,这样就是一个完全二叉树(complete binary tree)。这样的二叉树实现插入算法会比较复杂。我们将介绍一种思路相似,但比较容易实现的树状数据结构——AVL树。

AVL树是根据它的发明者G. M. Adelson-Velskii和E. M. Landis命名的。它是一种特殊的二叉搜索树。AVL树要求: 任一节点的左子树深度和右子树深度相差不超过1

(空树的深度为0。注意,有的教材中,采用了不同的深度定义方法,所以空树的深度为-1)

下面是AVL树:

纸上谈兵: AVL树

AVL树

AVL树的特性让二叉搜索树的节点实现平衡(balance):节点相对均匀分布,而不是偏向某一侧。因此,AVL树的搜索算法复杂度是log(n)的量级。

纸上谈兵: AVL树

插入2: 破坏AVL树

观察节点5,它的左子树深度为2,右子树深度为0,所以左右两个子树深度相差为2,不再是AVL树。由于2的加入,从节点6,1,5,3到2的层数都增加1。6, 1, 5节点的AVL性质都被破坏。如果从节点2向上回溯,节点5是第一个被破坏的。从节点3开始的子树深度加1,这是造成6, 1, 5的AVL性质被破坏的本质原因。我们将5和3之间的路径画成虚线(就好像挂了重物,边被拉断一样)。

我们可以通过单旋照(single rotation),调整以5为根节点的子树,来修正因为插入一个元素而引起的对AVL性质的破坏。如下:

纸上谈兵: AVL树

Single rotation: 左侧超重,向右转

通过单旋转,3成为新的根节点,2,5称为3的左右子节点。子树重新成为AVL树。该子树的深度减小1,这将自动修正2带给节点6,1的“超负荷”。

单旋转效果如下:

纸上谈兵: AVL树

 向右单旋转

特别要注意的是,为了保持二叉树的性质,子树B过继给了节点5。

向左单旋转与之类似。作为练习,可以尝试绘制向左单旋转的示意图。

但如果插入的节点不是2,而是4,会是如何呢?

纸上谈兵: AVL树

插入4

尝试单旋转,会发现无法解决问题。以5为根节点的子树向右单旋转后,树将以3为根节点,4,5为子节点。4比3大,却是3的左子节点,显然,这依然不符合二叉搜索树的性质。但基于和上面相似的原则(调整以5为根节点的树),我们发现有一个简单的解决方式:

纸上谈兵: AVL树

double rotation

上面的操作被称作双旋转(double rotation)。双旋转实际上是进行两次单旋转: 4为根节点的子树先进行一次向左的单旋转,然后将5为根节点的子树进行了一次向右的单旋转。这样恢复了树的ACL性质。

对于AVL树,可以证明,在新增一个节点时,总可以通过一次旋转恢复AVL树的性质。

当我们插入一个新的节点时,在哪里旋转?是用单旋转还是双旋转?

纸上谈兵: AVL树

我们按照如下基本步骤进行:

1. 按照二叉搜索树的方式增加节点,新增节点称为一个叶节点。

2. 从新增节点开始,回溯到第一个失衡节点(5)。

    (如果回溯到根节点,还没有失衡节点,就说明该树已经符合AVL性质。)

3. 找到断的边(5->3),并确定断弦的方向(5的左侧)

4. 以断边下端(3)为根节点,确定两个子树中的哪一个深度大(左子树还是右子树)。

    (这两棵子树的深度不可能相等,而且深度大的子树包含有新增节点。想想为什么)

5. 如果第2和第3步中的方向一致(都为左或者都为右),需要单旋转以失衡节点为根节点的子树。

    否则,双旋转以失衡节点为根节点的子树。

下面是AVL树的插入算法实现如下:

输出如下:

root: 18

depth: 2

root: 5

depth: 3

root->lchild: 2

root->lchild: 3

root->lchild->lchild: 2

(空行是额外加的)

总结:

AVL树: 平衡,深度相差不超过1

单旋转,双旋转

继续阅读