【数据结构】五、树:6.平衡二叉树AVL

2.平衡二叉树AVL

2.1定义

平衡二叉树(Self-Balancing Binary Search Tree 或 Height-Balanced Binary Search Tree),是由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树。

  1. 平衡二叉树(AVL树),它是 “平衡二叉搜索树” 的简称,它是一种二叉排序树

    它或者是一颗空树,或者是具有以下性质的二叉排序树:

    1. 它的左子树和左子树的高度之差(平衡因子)的绝对值不超过1
    2. 且它的左子树和右子树又都是一颗平衡二叉树。

    平衡因子(BF, Balance Factor):我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子

    那么平衡二叉树上所有结点的平衡因子只可能是 -1、0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

在这里插入图片描述

追求更好的平衡二叉树,可以得到更好的二叉排序树,提高排序和查询的效率,不至于让一边的树的深度太大。

2.2存储结构

// 平衡二叉树存储结构
typedef struct AVLNode{
    int data;		//数据域
    int balance;	//平衡因子
    struct AVLNode *lchild, *rclild;
}AVLNode,*AVLTree;

2.3查找

在平衡二叉树上进行查找的过程与二叉排序树的相同。因此,在查找过程中,与给定值进行比较的关键字个数不超过树的深度。

假设以 n h n_h nh 表示深度为 h 的平衡树中含有的最少结点数。

显然,有 n 0 = 0 , n 1 = 1 , n 2 = 2 n_0=0,n_1=1,n_2=2 n0=0,n1=1,n2=2,并且有 n h = n h − 1 + n h − 2 + 1 n_h=n_{h-1}+n_{h-2}+1 nh=nh1+nh2+1

可以证明,含有 n 个结点的平衡二叉树的最大深度为 O ( l o g 2 n ) O(log2n) O(log2n),因此平衡二叉树的平均查找长度为 O ( l o g 2 n ) O(log2n) O(log2n) 如下图所示:

在这里插入图片描述

2.4插入(保持平衡)

二叉排序树保证平衡的基本思想:每当在二叉排序树中插入(或删除)一个结点时,首先检查其插入路径上的结点是否因为此次操作而导致了不平衡。若导致了不平衡,则先找到插入路径上离插入结点最近的平衡因子的绝对值大于 1 的结点A,再对以A为根的子树(最小不平衡子树),在保持二叉排序树特性的前提下,调整各结点的位置关系,使之重新达到平衡。

【注意】每次调整的对象都是最小不平衡子树

最小不平衡子树:以插入路径上离插入结点最近的平衡因子的绝对值大于 1 的结点作为根的子树。下图中的虚线框内为最小不平衡子树:

在这里插入图片描述

平衡二叉树的插入过程的前半部分与二叉排序树相同,但在新结点插入后,若造成查找路径上的某个结点不再平衡,则需要做出相应的调整。可将调整的规律归纳为下列4种情况:

2.4.1 LL平衡旋转(右单旋转)

在结点A的**左孩子(L)左子树(L)**上插入了新结点,导致了不平衡。

LL平衡旋转(右单旋转)。由于在结点A的左孩子(L)的左子树(L)上插入了新结点,使得(图a->b)A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。

将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则作为A结点的左子树。(见下面动图所示)

如下图所示,结点旁的数值代表结点的平衡因子,而用方块表示相应结点的子树,下方数值代表该子树的高度。

在这里插入图片描述

在这里插入图片描述

CODE:

//f是父结点A,p是左孩子B,gf是父结点的父结点A的父结点
f->lchild = p->rchild;	//把B的左孩子BL放到B的位置
p->rchild = f;		//B和A右旋,A变成B的右孩子
gf->lchild/rchild = p;	//A的父结点现在指向B
2.4.2 RR平衡旋转(左单旋转)

在结点A的**右孩子®右子树®**上插入了新结点,导致了不平衡。

RR平衡旋转(左单旋转)。由于在结点A的右孩子®的右子树®上插入了新结点,使得(图a->b)A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。

将A的右孩子B向左上旋转代替A成为根结点,将A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树。

在这里插入图片描述

在这里插入图片描述

CODE:

//f是父结点A,p是左孩子B,gf是父结点的父结点A的父结点
f->rchild = p->lchild;
p->lchild = f;		//B和A右旋
gf->lchild/rchild = p;	//A的父结点现在指向B
2.4.3 LR平衡旋转(先左后右双旋转)

在A的**左孩子(L)右子树®**上插入新结点,导致了不平衡。

LR平衡旋转(先左后右双旋转)。由于在A的左孩子(L)的右子树®上插入新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。

先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置(即进行一次RR平衡旋转(左单旋转)),然后再把该C结点向右上旋转提升到A结点的位置(即进行一次 LL平衡旋转(右单旋转) )。

在这里插入图片描述

2.4.4 RL平衡旋转(先右后左双旋转)

在A的**右孩子®左子树(L)**上插入新结点,导致了不平衡。

RL平衡旋转(先右后左双旋转)。由于在A的右孩子®的左子树(L)上插入新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先右旋转后左旋转。

先将A结点的右孩子B的左子树的根结点C向右上旋转提升到B结点的位置(即进行一次LL平衡旋转(右单旋转)),然后再把该C结点向左上旋转提升到A结点的位置(即进行一次RR平衡旋转(左单旋转))。

在这里插入图片描述

【注意】LR和RL旋转时,新结点究竟是插入C的左子树还是插入C的右子树不影响旋转过程。

二叉排序树还有另外的平衡算法,如**红黑树(Red Black Tree)等,与平衡二叉树(AVL树)**相比各有优势。

2.4.5题解

例子:

假设关键字序列为 15 , 3 , 7 , 10 , 9 , 8 通过该序列生成平衡二叉树的过程如下图所示:

在这里插入图片描述

插入步骤

  1. 找到最小不平衡子树的根节点;
  2. 判断旋转方式;
  3. 进行旋转(根节点改变子树迁移);
  4. 检查:是否符合左 < 根 < 右

【RR】R子树根节点替代最小不平衡子树的根节点。

在这里插入图片描述

【LR】根节点的左子树L --变成–> 父结点的右子树R
根节点的右子树R --变成–> 爷结点的左子树L
根节点 --变成–> 爷爷结点

在这里插入图片描述

【RL】根节点的右子树R --变成–> 父结点的左子树L
根节点的左子树L --变成–> 爷结点的右子树R
根节点 --变成–> 爷爷结点

在这里插入图片描述

2.5性能分析

  • 查找效率分析

若树高为 h,则最坏情况下,查找一个关键字最多需要对比 h 次,即查找操作的时间复杂度不可能超过O(h)。

因为平衡二叉树的左右子树之间高度差不会超过1,

所以假设 n h n_h nh 表示高度为 h 的平衡二叉树的含有的最少的结点数

则有:
n 0 = 0 n 1 = 1 n 2 = 2 那么可以推出 : n h = n h − 1 + n h − 2 + 1 含义为 : 左子树的最少结点数 + 右子树的最少结点数 + 根节点 那么就有 : n 3 = 4 ; n 4 = 7 ; n 5 = 12 ; n 6 = 20... n_0 = 0\\ n_1 = 1\\ n_2 = 2\\ 那么可以推出:\\ n_h = n_{h-1} + n_{h-2} + 1\\ 含义为:左子树的最少结点数 + 右子树的最少结点数 + 根节点\\ 那么就有:\\ n_3=4;n_4=7;n_5=12;n_6=20... n0=0n1=1n2=2那么可以推出:nh=nh1+nh2+1含义为:左子树的最少结点数+右子树的最少结点数+根节点那么就有:n3=4;n4=7;n5=12;n6=20...
那么,如果知道了结点数 n,就可以推断出整棵树的最大高度 h。

eg: 这棵树有n=9个结点,求最大高度h.

那么因为 n 4 = 7 ; n 5 = 12 n_4=7;n_5=12 n4=7;n5=12,而 7 < 9 < 12.

想要高度为 5,至少需要12个结点,所以这棵树的最大高度 h 为4.

那么知道高度了,就能够得到时间复杂度O(4)。

可以证明含有 n 个结点的平衡二叉树的最大深度为 O(log2 n),平衡二叉树的平均查找长度为 O(log2 n)

2.6删除

平衡二叉树的删除操作删除结点后,也要保持二叉排序树的特性不变(左<中<右)。若删除结点导致不平衡,则需要调整平衡。

平衡二叉树的删除操作具体步骤:

  1. 删除结点 (方法同“二叉排序树”);

    1. 叶子结点:直接删除。
    2. 仅有左或右子树的结点:删除后,让被删除结点的**直接后继(子树)**接替它的位置。
    3. 左右子树都有的结点:删除后,用右子树的最小结点,即右子树的中序第一个子女来接替它的位置,然后再删除这个最小的子女。
  2. 一路向上找到最小不平衡子树,找不到就说明平衡,完结撒花return;

  3. 找最小不平衡子树下,高度最高的儿子、孙子;

  4. 根据孙子的位置,调整平衡(LL/RR/LR/RL);

    1. 孙子在LL:儿子右单旋。
    2. 孙子在RR:儿子左单旋。
    3. 孙子在LR:孙子先左旋,再右旋。
    4. 孙子在RL:孙子先右旋,再左旋。
  5. 如果还不平衡向上传导,继续②。

    对最小不平衡子树的旋转可能导致树变矮,从而导致上层祖先不平衡(不平衡的向上传递)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值