数据结构——树(11)——AVL树的实现(1)

重温AVL树

我们上篇的文章介绍过什么叫平衡二叉树,以及我们是怎么样实现二叉树的平衡的。其中AVL树就是其中的一种算法。现在回顾一下,我们说过导致树不平衡的插入方式有四种。现在我们用实例来说明,假设你要创建一个BST,其中里面的元素是元素周期表中的元素。
数据结构——树(11)——AVL树的实现(1)_第1张图片
现在按照元素周期表的顺序建立平衡二叉搜索树。我们先插入第一个元素H,这很简单,因为一开始树为空。接下来我们调用之前我们写过的addNode函数,向里面插入我们的He元素,因为He元素在H元素的后面,所以插入的结果如下:
数据结构——树(11)——AVL树的实现(1)_第2张图片
为了追踪所得的树是否为平衡树,AVL算法为每一个节点与某一个整数相关联,且这个整数的值为右子树的高度减去左子树的高度。这个值称为该节点的平衡因子(the balance factor of the node)
所以,对于节点H,右子树高度为1,左子树为0,平衡因子为1 - 0 = 1. 对于节点He来说,左右子树高度都为0,自然平衡因子为0.所以我们把这样的情况用下图表示:
数据结构——树(11)——AVL树的实现(1)_第3张图片
此时,这棵树是平衡的,因为左右子树的高度差小于2(即每个节点中的平衡因子的绝对值 < 2),那么我们继续往里面插入Li元素,我们查阅周期表我们可以得到Li元素在He元素之后,那么应该这样表示:
数据结构——树(11)——AVL树的实现(1)_第4张图片
此时,根节点不在平衡,因为它的右子树的高度为2,左子树高度为0.因此我们需要重构此树使得这棵树得以平衡。此时我们将He作为新的根节点,H作为左孩子,Li作为右孩子,得到下图就平衡了:
数据结构——树(11)——AVL树的实现(1)_第5张图片

那么,问题来了,你怎么知道,或者说你怎么知道这样的操作就能使得原本不平衡的树变得平衡呢?

单侧旋转

如果你认真看看上图的变换,就能发现,中间的He元素往上升高,变成新的根节点。而H元素想做下降称为He的左孩子,Li不动。
数据结构——树(11)——AVL树的实现(1)_第6张图片
参与旋转操作的两个节点称为旋转的轴。 在由元素H,He和Li组成的示例中,围绕H-He轴执行旋转。 由于此操作将节点向左移动,因此此图所示的操作称为左旋转(left rotation)。 如果树在相反方向上(即左边)不平衡,则可以应用称为右旋的对称操作,其中所有操作都简单地反过来过来。 例如,接下来的两个元素的符号(Be和B)分别被添加到树的左边。 要重新平衡树,必须围绕Be-H轴执行右旋,如下图所示:
数据结构——树(11)——AVL树的实现(1)_第7张图片
因此,单侧旋转可以记为,左侧右旋,右侧左旋。

双侧旋转

然而,单侧旋转的操作并不是总能将树重构成平衡二叉树。试想一下,如果我们在上述的树上插入C元素,此时树的状态是这样的:
数据结构——树(11)——AVL树的实现(1)_第8张图片
位于根部的He元素此时失衡,因为平衡因子的绝对值为2,如果此时你利用刚刚所说的单侧旋转,应该旋转He-Be杠杆,那么会得到下面的结果:
数据结构——树(11)——AVL树的实现(1)_第9张图片
旋转之后,所得到的二叉树仍与之前一样处于不平衡的状态,唯一的不同点在于根节点的不平衡状态此时出现在右子树。
为什么会出现这个问题呢?原因在于,参与旋转的节点具有相反符号的平衡因子。当出现这种情况的时候,一次的单侧旋转是不够的,为了解决这个问题,我们必须想办法使得参与旋转的两个节点的平衡因子的符号相同。那么怎么样使得参与旋转的两个节点的平衡因子的符号相同呢?很简单,我们想想,平衡因子的符号取决于右子树的高度减去左子树的高度,如果左右子树通过旋转对调,那么平衡因子的符号也相应取相反数。 因此在旋转不平衡节点之前,沿相反方向旋转其子节点。 这个操作让参与旋转的父母和孩子的平衡因素相同的符号,这也就意味着第二次旋转将会成功。所以实际上是进行两次单侧旋转。
以上图刚刚插入C元素为例子,我们可以看到,He元素的平衡因子与Be元素的平衡因子符号相反,那么我们就不能直接进行单侧旋转,我们就对Be子树进行调整,使得杠杆的符号一致。具体做法是:沿相反方向旋转其子节点。
本来我们应该对He-Be杠杆进行右旋的,那么我们就对其子节点进行左旋:
数据结构——树(11)——AVL树的实现(1)_第10张图片
此时旋转之后的结果是,这棵树依旧不处于平衡状态,但是我们已经做到了杠杆符号一致,那么此时再进行一次正常的单侧旋转,也就是右旋,得到平衡的二叉树AVL:
数据结构——树(11)——AVL树的实现(1)_第11张图片
因此,双侧旋转要两次旋转,一次调整杠杆符号,一次单侧旋转。

AVL树的特点

在描述这些树的论文中,Adelson-Velskii和Landis展示了他们的树平衡算法的以下特性:

  • 如果你向一棵AVL树插入新节点中,则始终可以通过执行至多一次操作来恢复其平衡,该操作可以是单次旋转或双次旋转。
  • 完成旋转操作后,旋转轴上子树的高度始终与插入新节点之前的高度相同。 此属性可确保树中任何更高级别的平衡因素都不会发生变化。

你可能感兴趣的:(数据结构与算法深入,AVL树,平衡二叉树,单侧旋转,双侧旋转)