笔记-AVL树

AVL树是带有平衡条件的二叉查找树。

为什么需要平衡捏?

之前看的二叉查找树,在进行大量插入与删除操作时,会出现左子树越来越深(可以理解为元素节点越来越多,比右子树多很多)的情况,因为删除时经常把右子树中的节点拿出来替代删除的节点,这样在对树操作时会降低效率。因为操作时大多会往左检索,而需要检索的节点会越来越多。

所以,如果使树维持在左右两部分大致差不多的情况,在差不多的数据量下,效率明显更高。

一棵AVL树,其每个节点的左右子树的高度最多相差1。

如果一个节点的左右子树的高度相差超过1,我现在称它为不平衡。

为了保证每个节点的左右子树的高度最多相差1。

首先对删除操作实行懒惰删除。即当要删除一个节点上,只是对其进行标记,并不真的将其删除,其任然留在树中。这特别是在有重复项时很常用,因为此时记录出现频率数的域可以减1。如果树中的实际节点数和“被删除”的节点数相同,那么树的深度预计只上升一个小的常数。如果被删除的项是重新插入的,那么分配一个新单元的开销就避免了。

对插入操作,我们需要更新通向根节点路径上那些节点的所有平衡信息,判断是否让树平衡被打破,即树中某一节点的左右子树的高度最多相差超过1。如果平衡被破坏,则需要修正,称其为旋转。

简单来说,会破坏平衡的操作(插入)有四种:

1左左–左;2左左–右;3右右–左;4右右–右。1和4镜像对称,2和3镜像对称。

1和4需要一次单旋转,2和3则需要复杂些的双旋转。

单旋转

对于什么时候单旋转,即1和4的情况,我觉得可以这样理解:

即插入的打破平衡的新元素(A),并不在被打破平衡的节点(B)(该节点在插入新元素后左右子树的高度最相差2>1),与B到A的路路径的儿子节点(C)之间。

1左(B)左(C)–左(A):A比B和C都小。

4右(B)右(C)–右(A):A比B和C都大。

(2和3的情况就可以简单的理解为A在B和C之间,双旋转)

(备注:C是B的儿子节点,A是C的子节点,但A不一定是C的儿子节点,A可能距离B很远,但是它的插入却会使B不平衡)

此时进行单旋转。单旋转怎么旋转捏?

谁被打破,旋转谁,即改变谁。可以很明显看出应该旋转B,即用B的儿子C替换B,

对1左(B)左(C)–左(A)的情况就是 :C原本是B的左儿子,让其替换到B的位置,C成为父节点,B成为其右儿子。

对4右(B)右(C)–右(A))的情况就是:C原本是B的右儿子,让其替换到B的位置,C成为父节点,B成为其左儿子。

此时树就又平衡了。(//没有图,真是不知道怎么解释,如果有图,应该一目了然)

双旋转

双旋转实际就是两次单旋转

对于2和3的情况,单旋转不能使树平衡。(可以画图试试)。所以我们先旋转一次,使其成为1和4的情况。

此时被改变的任然是B,因为它不平衡了嘛。而此时不是用B的儿子C来代替B,而是用C的儿子节点来替换B。

若A就是C的儿子节点,则直接用A代替B,若A不是C的儿子节点,则再引入C的一个儿子节点D来进行讨论。

若A是C的左子节点(即A在C的左子树上),则D为C的左儿子节点。同理,若A是C的右子节点(即A在C的右子树上),则D为C的右儿子节点。

此时用D来代替B。

再让树满足二叉查找树的基本条件后,树就右平衡了。

本篇不敢标记为转载,因为不是照打书上的类容,上面均是个人理解。勿盲信!

伸展树

  按照前面所说,为了避免二叉树太容易变得很深,提出了AVL树。但是现实操作中,二叉树并不是那么容易变的极端深(你不能每次插入都往左节点去吧)。也就说我们可能并不需要频繁的去进行旋转操作。   伸展树的基本想法是,当一个节点被访问,它就要经过一系列的AVL树的旋转被推到根上。

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