数据结构与算法之平衡二叉树(AVL)的插入、旋转以及遍历

平衡二叉树的定义

平衡二叉树 (AVL,Balanced Binary Tree),简称平衡树 (AVL 树)-- 树上任一结点的左子树和右子树的高度之差不超过 1。

  1. 结点的平衡因子=左子树高 - 右子树高。
  2. 平衡二叉树结点的平衡因子的值只可能是 -1、0 或 1。
  3. 只要有任一结点的平衡因子绝对值大于 1,就不是平衡二叉树。

typedef struct AVLNode{
	int key;  //数据域
	int balance;  //平衡因子
	struct AVLNode *lchild,*rchild;
}AVLNode,*AVLTree;

平衡二叉树的插入

每次调整的“对象”都是最小不平衡二叉树, 如下图:

调整最小不平衡子树

  1. LL:在 A 的左孩子的左子树中插入导致不平衡
  2. RR:在 A 的右孩子的右子树中插入导致不平衡
  3. LR:在 A 的左孩子的右子树中插入导致不平衡
  4. RL:在 A 的右孩子的左子树中插入导致不平衡

LL

假定有如下子树,{A}、{B}、{AR}、{BL}、{BR},且假定所有子树的高度为 H(或者你认为是 1 也可以)。如下图

即:A 的平衡粒子为 1,B 为 0,AR 无子树
现在:以 LL(左孩子的左子树) 插入,操作如下图:

此时:B 的子树高度为 H+1(已做 LL 操作),AR 不变,那 A 的平衡因子为 2 了。此时就成 A 为最小不平衡子树

那么此时我们怎么去调整呢,使其满足二叉排序树的特性:
这里提一下二叉排序树的特性是:

  1. 左子树结点值<根节点值<右子树结点值
  2. 即:BL < B < BR < A < AR
解决办法 (右旋):

使 LL 平衡右旋 (右单旋转):由于在结点 A 的左孩子 (L) 的左子树 (L) 上插入了一个新节点,A 的平衡因子由 1 增至 2,导致以 A 为根的子树失去平衡,需要一次向右的旋转操作。将 A 的左孩子 B 向右上旋转代替 A 成为根节点,将 A 结点向右下旋转成为 B 的右子树的根节点,而 B 的原右子树则作为 A 结点的左子树。

RR

上述 相同,如下图:

此时 A 树为最小不平衡树。

  1. 二叉排序树的特性:左子树结点值<根结点值<右子树结点值
  2. AL<A<BL<B<BR
解决办法 (左旋):

RR 平衡旋转 (左单旋转)。优于在结点 A 的右孩子 ® 的右子树 ® 上插入了新结点,A 的平衡因子由 -1 减至 -2,导致以 A 为根的子树失去平衡,需要一次向左的旋转操作。将 A 的右孩子 B 向左上旋转代替 A 成为根节点,将 A 结点向左下旋转成为 B 的左子树的根节点,而 B 的原左子树则作为 A 结点的右子树。

代码示例

右旋


实现 f 向右下旋转,p 向右上旋转:
其中 f 是爹,p 为左孩子,gff 他爹

  1. f->lchild=p=->rchild;
  2. p->rchild=f;
  3. gf->lchild/rchild=p;

BL<B<BR<A<AR
左旋、右旋操作后可以保持二叉排序树的特性

左旋


实现 f 向左下旋转,p 向左上旋转:
其中 f 是爹,p 为右孩子,gff 他爹

  1. f->rchild=p->lchild;
  2. p->lchild=f;
  3. gf->lchild/rchild=p;

LR

这里考虑的是插入到 CR,即 C 的右子树的位置

LR 平衡旋转 (先左后右双旋转)。由于在 A 的左孩子 (L) 的右子树 ® 上插入新节点,A 的平衡因子由 1 增至 2,导致以 A 为根的子树失去平衡,需要进行两次旋转操作,先做旋转后右旋转。先将 A 结点的左孩子 B 的右子树的根节点 C 向左上旋转提升到 B 结点的位置,然后再把该 C 结点向右上旋转提升到 A 结点的位置

二叉树结点关系:BL<B<CL<C<CR<A<AR
此时左旋:

此时在右旋:

下图为插入结点是 CL 的处理过程

RL

新增结点可能是在 Cl 可能是在 CR,这里以 CL 为例

RL 平衡旋转 (先有后左双旋转)。由于在 A 的右孩子 ® 的左子树 (L) 上插入新节点,A 的平衡因子由 -1 减至 -2,导致以 A 为根的子树失去平衡,需要进行两次旋转操作,先有旋转后左旋转。先将 A 结点的右孩子 B 的左子树的根节点C 先右上旋转提升到 B 结点的位置,然后再把该 C 结点向左上 旋转提升到 A 结点的位置,过程如下图:

下面是插入结点是 CR 的处理过程:

总结

  1. LL
    1. 在 A 的左子树中插入导致不平衡
    2. 调整:A 的左孩子结点右上旋
  2. RR
    1. 在 A 的右孩子的右子树中插入导致不平衡
    2. 调整:A 的右孩子结点左上旋
  3. LR
    1. 在 A 的左孩子的右子树中插入导致不平衡
    2. 调整:A 的左孩子的右孩子先左上旋在右上旋
  4. RL
    1. 在 A 的右孩子的左子树中插入导致不平衡
    2. 调整:A 的右孩子的左孩子先右上旋后左上旋
      每次调整之后注意验证:是否符合左<根<右

旋转操作说明:

右旋操作

注意:只有左孩子可以右旋

实现 f 向右下旋转,p 向右上旋转:
其中 f 是爹,p 为左孩子,gff 他爹

  1. f->lchild=p=->rchild;
  2. p->rchild=f;
  3. gf->lchild/rchild=p;
左旋操作

注意:只有右孩子可以左旋

实现 f 向左下旋转,p 向左上旋转:
其中 f 是爹,p 为右孩子,gff 他爹

  1. f->rchild=p->lchild;
  2. p->lchild=f;
  3. gf->lchild/rchild=p;

练习

RR 型

RL 型

LR 型

3 查找效率分析

若树高为 h,则最坏情况下,查找一个关键字最多需要对比 h 次,即查找操作的时间复杂度不可能 超过 O(h);
平衡二叉树——树上任一结点的左子树和右子树的高度之差不超过 1。
假设以 n h n_h nh表示深度为 h 的平衡树中含有的最少结点数。则有 n 0 n_0 n0=0, n 1 n_1 n1=0, n 2 n_2 n2=0,并且有 n h n_h nh= n h − 1 n_{h-1} nh1+ n h − 2 n_{h-2} nh2+1。
可以证明含有 n 个结点的平衡二叉树的最大深度为 O l o g 2 n O_{log_2n} Olog2n,平衡二叉树的平均查找长度为 O l o g 2 n O_{log_2n} Olog2n

4 知识回顾与重要考点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

five-five

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值