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

server/2024/9/24 7:14:58/

2.平衡二叉树AVL

文章目录

    • 2.平衡二叉树AVL
      • 2.1定义
      • 2.2存储结构
      • 2.3查找
      • 2.4插入(保持平衡)
        • 2.4.1 LL平衡旋转(右单旋转)
        • 2.4.2 RR平衡旋转(左单旋转)
        • 2.4.3 LR平衡旋转(先左后右双旋转)
        • 2.4.4 RL平衡旋转(先右后左双旋转)
        • 2.4.5题解
      • 2.5性能分析
      • 2.6删除

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. 如果还不平衡向上传导,继续②。

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


http://www.ppmy.cn/server/99769.html

相关文章

http的发展历史,各版本的差异点,以及和https的区别

### HTTP的发展历史及各版本的差异点 HTTP/0.9 - **发布时间**&#xff1a;1991年 - **特点**&#xff1a; - 最初的HTTP协议版本&#xff0c;非常简单。 - 只支持GET方法&#xff0c;不支持请求头和响应头。 - 响应仅为纯文本&#xff0c;无法传输图片、音频等多媒体资…

llama factory 训练 TensorBoard 可视化

首先需要在 yaml 里设置两个参数&#xff1a; output_dir: /home/wangguisen/projects/LLaMA-Factory/weights/tensbox_demoreport_to: tensorboard logging_dir: /home/wangguisen/projects/LLaMA-Factory/weights/tensbox_demo/runs然后开始训练&#xff0c;在你的输出目录下…

LCM红外小目标检测

根据站内的matlab代码修改成python版本。 import numpy as np import matplotlib.pyplot as plt import cv2 from pylab import mpl# 设置中文显示字体 mpl.rcParams["font.sans-serif"] ["SimHei"]def LCM_computation(patch_LCM_in):row, col patch_L…

nodejs/node-sass/sass-loader三者版本对应关系(已解决)

基本前提&#xff1a;了解版本对应关系 示例&#xff1a; 我的nodejs&#xff1a;v14.21.3&#xff0c; 则package.json: "node-sass": "^4.14.1", "sass-loader": "^8.0.0",扩展&#xff1a; 查看node历史版本&#xff1a; Node.js…

java SPI实现类中注入spring bean对象

在项目中&#xff0c;用到了SPI来扩展一些功能&#xff0c;发现很多实现类中用到了bean对象&#xff0c;并且都是通过getBean的方式每次都去拿&#xff0c;感觉不是很方便&#xff0c;而且速度也没有直接使用对象快。 正好安排的工作就是优化那一块的代码&#xff0c;所以就改造…

统计回归与Matlab软件实现上(一元多元线性回归模型)

引言 关于数学建模的基本方法 机理驱动 由于客观事物内部规律的复杂及人们认识程度的限制&#xff0c;无法得到内在因果关系&#xff0c;建立合乎机理规律的数学模型数据驱动 直接从数据出发&#xff0c;找到隐含在数据背后的最佳模型&#xff0c;是数学模型建立的另一大思路…

HTTP的场景实践

HTTP的场景实践&#xff1a;任选一个浏览器&#xff0c;对于其涉及的请求中的缓存策略展开具体分析 1. 强缓存&#xff1a; Cache-Control用于指定缓存的最长有效时间。 Expires用于指定资源过期的日期。 2. 协商缓存&#xff1a; ETag用于标识资源的唯一标识符&#xff0c;…

Python套接字综合应用(UDP篇)

Python套接字综合应用(UDP篇) 1、 主要功能 UDP客户端实现UDP服务端实现输出字体颜色控制响应捕获键盘CtrlC信号套接字异常捕获及处理通信报文16进制格式化输出 2、 Python UDP套接字应用 Windows程序在WinServer2022上验证运行&#xff0c;Linux程序在银河麒麟V10上验证运…