数据结构--平衡树--普通平衡树

普通平衡树

一般说“普通平衡树”, 应该指的就是最早发明的平衡二叉树 “AVL树”。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

为什么发明了平衡树

当我们用二叉树维护信息的时候,要进行插入/删除/查找操作。
二叉树体现了一种二分的思想,因此操作信息的期望复杂度在 log级。
但是二叉树的形态受操作顺序的影响,例如我按照1,2,3…,n的顺序把这n个数插入到二叉树中,按照二叉树的性质,会形成下面的状态:
在这里插入图片描述
这个时候二叉树就变成了链表,操作的时间复杂度退化到了O(n),这是人们不希望看到的。其实可以想到,我们不希望二叉树的各个子树的深度相差的特别大,否则就会出现这样看上去 “极不协调” 的样子。
相同结点个数的情况下,我们希望树的形态尽可能的接近满二叉树
这里有一个细节,对于满二叉树的定义,国内和国外有很大出入,具体见百度百科,我这里引用的是国内的定义。
例如下面,两棵树维护的信息实际上是相同的,但是右边的树的效率显然更高。
在这里插入图片描述
红色的是本不存在的虚点。左边的图也有但是数量太多就不画了(2^5-5个)。能看出来左图虚点很多,离理想的满二叉树也很远。

平衡树的平衡因子

前面写道,人们不希望二叉树的各个子树深度相差过大,因为这样会降低操作的效率。即满二叉树是一种最理想的状态,它的各个子树的深度完全相等。但是实际使用当中,受到结点数目的限制,一棵二叉树的各个子树的深度不可能时时刻刻相等。在平衡树中,引入了一种标记信息叫做“平衡因子”,来衡量一棵二叉树的平衡程度。
平衡因子定义为一个结点的左子树的深度减去右子树的深度。
在 “无法保证实际当中的二叉树总是满二叉树” 的情况下,我们允许一个结点的左右子树深度最多相差1,即一个结点的平衡因子的绝对值大于1,则判定该结点失衡。

树旋转操作

插入和删除结点都会改变某些结点的平衡因子,也就有可能会导致失衡。
下面以插入结点为例介绍树旋转操作。
当新结点插入时,从根节点插入结点 这条路径上的结点的平衡因子会改变,从插入的结点向上找,找到的第一个失衡结点 (如果有的话) 则称这个失衡结点为根的子树为最小失衡子树
下面记最小失衡子树的根结点为Rs

对于一个引起失衡的新插入结点,他的位置只能有四种情况:

  1. Rs的左儿子的左子树。(记为LL,下同)
  2. Rs的左儿子的右子树。(LR)
  3. Rs的右儿子的右子树。(RR)
  4. Rs的右儿子的左子树。(RL)
    在这里插入图片描述

当有结点发生失衡时应当如何调整呢。
下面给出一个例子,在一个空树中依次插入1,2,3 三个点。
首先1作为根结点,2比1大,插入1的右子树,3比1和2大,插入2的右子树。
在这里插入图片描述
这样结点1的平衡因子变成了-2,明显是失衡了。
我们想到,所谓“2比1大”,其实反过来说就是 “1比2小”,所以其实可以把1放在2的左子树上,这样其实也是符合二叉搜索树的定义的。而且这样做似乎不会产生失衡。
在这里插入图片描述
看上去就像1号结点围绕2号结点,逆时针旋转了一些角度。
这个操作叫 “左旋”,很明显这个例子中,插入结点在最小失衡子树根节点的右儿子的右子树上(RR),这种情况下可以用一次左旋来维持平衡。
下面是其他失衡情况的维持平衡操作:

  1. LL型失衡:以最小失衡子树的根节点为待旋转结点,进行一次右旋。
  2. RR型失衡:以最小失衡子树的根节点为待旋转结点一次左旋。
  3. LR型失衡:先以最小失衡子树根节点的左儿子为待旋转结点,进行一次左旋;再以最小失衡子树的根节点为待旋转结点进行一次右旋。
  4. RL型失衡:先以最小失衡子树根节点的右儿子为待旋转结点,进行一次右旋;再以最小失衡子树的根节点为待旋转结点进行一次左旋。

注:LR型失衡,在进行进行一次左旋后会变成LL型,所以再进行右旋会回到平衡,RL型与之对称。

旋转的一般形式

上面是一个左旋的简单例子,下面给出旋转的一般形式:
左旋:

  1. 待旋转结点的右儿子的左子树(如果有的话)连接在待旋转结点的右子树上。
  2. 用待旋转结点的右子树,代替待旋转结点的位置,将待旋转结点变成原先它的右儿子的左子树。

右旋:

  1. 待旋转结点的左儿子的右子树(如果有的话)连接在待旋转结点的左子树上。
  2. 待旋转结点的左子树,代替待旋转结点的位置, 将待旋转结点变成原先它的左儿子的右子树。

给一个标准的RL例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

写在后面

平衡树(没有特指AVL)我本来想好好写写来着,但是(特指AVL)这个普通平衡树,真正写的时候才发现,这个东西真的很难写明白。有些地方,不像前几篇博客,我能对 “为什么是这样操作” 给出一个起码能把自己说服的说法。
还有就是对于AVL这个数据结构本身,作为 “最早发明的平衡二叉查找树” ,它的价值自然是巨大的。但是随着时代发展,AVL渐渐被其他后出现的,更加简洁的平衡树所取代,用B站里某位大佬的话说,唯一能看到AVL的地方可能只有大学教材了。包括 洛谷P3369 那道题,整整12页的题解,竟然只有一篇是 “老老实实” 地用AVL写的,而且好像还用了面向对象编程(我也不太懂这个)。我企图用AVL过掉此题,断续有三四天吧,最后还是没成功,和别人照猫画虎,写了个 Splay 过了。有两种可能,一是这个东西是真的没法玩了,二是…我太菜了

普通平衡树例题

真·AVL模板:HNUST OJ 1812 算法9-9~9-12:平衡二叉树的基本操作.
伪·AVL模板:洛谷P3369 【模板】普通平衡树.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值