当前位置: 首页 > news >正文

AVL树的实现

一. AVL的概念

        

        AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的 左右⼦树都是AVL树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树,通过控制⾼度差去控制平衡。

        AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis是两个前苏联的科学家,他们在1962年的论⽂《An algorithm for the organization of information》中发表了它。

        AVL树实现这⾥我们引⼊⼀个平衡因⼦(balance factor)的概念,每个结点都有⼀个平衡因⼦,任何 结点的平衡因⼦等于右⼦树的⾼度减去左⼦树的⾼度,也就是说任何结点的平衡因⼦等于0/1/-1, AVL树并不是必须要平衡因⼦,但是有了平衡因⼦可以更⽅便我们去进⾏观察和控制树是否平衡,就像⼀个⻛向标⼀样。

        思考⼀下为什么AVL树是⾼度平衡搜索⼆叉树,要求⾼度差不超过1,⽽不是⾼度差是0呢?0不是更好的平衡吗?画画图分析我们发现,不是不想这样设计,⽽是有些情况是做不到⾼度差是0的。⽐如⼀棵树是2个结点,4个结点等情况下,⾼度差最好就是1,⽆法做到⾼度差是0

        AVL树整体结点数量和分布和完全⼆叉树类似,⾼度可以控制在 ,那么增删查改的效率也可

以控制在 O(logN),相⽐⼆叉搜索树有了本质的提升。

        这颗树是平衡的。

        

        此时插入了一个13,此时这棵树就失衡了,失衡点是10。

二. AVL树的实现

        2.1 AVL树的结构

        

        这是我们实现的树结构。

2.2 AVL树的插⼊

2.2.1 AVL树插⼊⼀个值的⼤概过程

1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊。

2. 新增结点以后,只会影响祖先结点的⾼度,也就是可能会影响部分祖先结点的平衡因⼦,所以更新 从新增结点->根结点路径上的平衡因⼦,实际中最坏情况下要更新到根,有些情况更新到中间就可 以停⽌了,具体情况我们下⾯再详细分析。

3. 更新平衡因⼦过程中没有出现问题,则插⼊结束

4. 更新平衡因⼦过程中出现不平衡,对不平衡⼦树旋转,旋转后本质调平衡的同时,本质降低了⼦树的⾼度,不会再影响上⼀层,所以插⼊结束。

2.2.2 平衡因⼦更新

更新原则:

平衡因⼦ = 右⼦树⾼度-左⼦树⾼度

只有⼦树⾼度变化才会影响当前结点平衡因⼦。

插⼊结点,会增加⾼度,所以新增结点在parent的右⼦树,parent的平衡因⼦++,新增结点在

parent的左⼦树,parent平衡因⼦--

parent所在⼦树的⾼度是否变化决定了是否会继续往上更新

更新停⽌条件:

更新后parent的平衡因⼦等于0,更新中parent的平衡因⼦变化为-1->0 或者 1->0,说明更新前

parent⼦树⼀边⾼⼀边低,新增的结点插⼊在低的那边,插⼊后parent所在的⼦树⾼度不变,不会

影响parent的⽗亲结点的平衡因⼦,更新结束。

更新后parent的平衡因⼦等于1 或 -1,更新前更新中parent的平衡因⼦变化为0->1 或者 0->-1,说

明更新前parent⼦树两边⼀样⾼,新增的插⼊结点后,parent所在的⼦树⼀边⾼⼀边低,parent所

在的⼦树符合平衡要求,但是⾼度增加了1,会影响parent的⽗亲结点的平衡因⼦,所以要继续向

上更新。

更新后parent的平衡因⼦等于2 或 -2,更新前更新中parent的平衡因⼦变化为1->2 或者 -1->-2,说

明更新前parent⼦树⼀边⾼⼀边低,新增的插⼊结点在⾼的那边,parent所在的⼦树⾼的那边更⾼

了,破坏了平衡,parent所在的⼦树不符合平衡要求,需要旋转处理,旋转的⽬标有两个:1、把

parent⼦树旋转平衡。2、降低parent⼦树的⾼度,恢复到插⼊结点以前的⾼度。所以旋转后也不

需要继续往上更新,插⼊结束。

不断更新,更新到根,跟的平衡因⼦是1或-1也停⽌了。

        我们了解了上面的之后,可以再来看一下是怎么完成++操作的。

        

        我们先完成了bf的++/--操作,接下来我们就要完成旋转操作了。

2.3 旋转

 2.3.1 旋转的原则

1. 保持搜索树的规则

2. 让旋转的树从不满⾜变平衡,其次降低旋转树的⾼度

旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。

说明:下⾯的图中,有些结点我们给的是具体值,如10和5等结点,这⾥是为了⽅便讲解,实际中是什么值都可以,只要⼤⼩关系符合搜索树的性质即可。

        

2.3.2 右单旋

        

本图1展⽰的是10为根的树,有a/b/c抽象为三棵⾼度为h的⼦树(h>=0),a/b/c均符合AVL树的要

求。10可能是整棵树的根,也可能是⼀个整棵树中局部的⼦树的根。这⾥a/b/c是⾼度为h的⼦树,

是⼀种概括抽象表⽰,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体图2/图3/图4/

图5进⾏了详细描述。

在a⼦树中插⼊⼀个新结点,导致a⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平

衡因⼦从-1变成-2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树左边太⾼了,需要

往右边旋转,控制两棵树的平衡。

旋转核⼼步骤,因为5 < b⼦树的值 < 10,将b变成10的左⼦树,10变成5的右⼦树,5变成这棵树新 的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2,符合旋转原

则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了。

                                                                   图  1 

                                                             图  2 

        

                                                                图 3 

        如果感觉上面太抽象的话,可以看图二和图三,我来讲解一下。

        我们可以发现图二失衡了,此时我们就需要旋转了,旋转的要求就是还是保持搜索二叉树的,此时是失衡节点的左子树的左子树,是LL型的,此时我们需要右旋,右旋就是让10变成5的右孩子,如果5本身就有右孩子的话,就是图三的情况,我们需要让10变成5的右孩子,再让5的原本的右孩子变成10的左孩子,此时还是满足搜索二叉树的,其他的节点不要动,此时就完成了平衡的操作。

        我们下面用代码写一下右旋的操作吧。

        

        我们通过插入函数中的这个if语句先找到失衡的节点,随后再看它的失衡类型,此时是右旋,我们可以通过图二看到,失衡节点的平衡因子是-2,它的左子树的平衡因子是-1,此时是LL型的,需要右旋,下面我们来完成一下这个右旋操作,首先我们先来看一下右旋之后的情况吧。

        

        我们可以拿这个树来理一下思路。

        首先我们右旋成下面这样。

        

        我们来看一下它都需要改变哪些指向,我们的parent指向失衡节点,cur指向失衡节点的左孩子,我们先来看一下,首先就是我们的cur->right=parent,让parent变成了cur的右孩子,我们知道让parent->left=cur->right,图中cur没有右孩子,此时parent->left原来是指向cur的,此时就指向空了,但是我们的cur是可能有右孩子的,所以这里需要判断一下,存在我们还得改变这个右孩子的指向,就是cur->right->parent=parent了,这样就完成了一部分操作,还有就是parent->parent=cur,此时5变成10的父母了,所以要改变,最后就是5的父母了,我们知道5是有可能有父母的,所以这里还得判断一下5是否是根节点如果是就指向空,不是就指向10原来的父母了,下面是代码的书写。

        

        这样我们就完成了右旋操作了。

2.3.3 左单旋

        

我们了解了右旋转之后,此时就要看一下左旋转了,我们直接看一个例子吧。
此时我们发现上图这棵树失衡了,失衡节点是5,此时是失衡节点的右子树的右子树失衡的,是RR型。此时需要左旋了,左旋就是让5作为10的左孩子,10原来的左孩子成为5的右孩子,此时还是保持搜索二叉树的,此时也完成了平衡。
旋转完就是这样了,这个和右旋转的思路是一样的只需要注意是左孩子还是右孩子即可,下面我们直接来看代码。
就是这样的,大家可以好好看看思考一下。

2.3.4 左右旋转

        这个也很好理解,就是失衡节点的左子树的右子树导致的失衡,我们来看一个例子。

        这个就是左右旋转了,我们可以看到是失衡节点的左子树的右子树导致的,此时我们通过单旋是无法完成的,此时就需要用左右旋转了,我直接来说是怎么旋转的吧,我也教大家一个方法记一下,左子树的右子树导致失衡的,就是LR型的,L是左吗,所以就是先让失衡节点的左孩子左旋,再让失衡节点右旋即可。

        我们来看一下。

        先左旋。

        

        画的不好请见谅,左旋完就成这样了,此时就变成LL型了,再以失衡结点右旋一下就行了。

        

        此时就是这样了,我们发现完成了平衡。

        直接复用上面的代码即可。

        这样我们只是完成了平衡,但是平衡因子我们并没有给到它该有的值,因为我们的LR型是有多种情况的,下面我们来看一下。

        

        这是我们的第一种,此时我们通过旋转,我们的旋转的这三个的平衡因子都是0,此时是可以的。

        

        

        这就是我们通过两次旋转得到的树,此时我们只需要看我们旋转的这几个点的平衡因子即可,我们发现10的平衡因子不对,也就是我们原来的parent,所以还需要分情况讨论一下。

第三种情况

        

        

        这是我们旋转两次得到的平衡二叉树,我们发现它的平衡因子也是不对的。

综合上面三种情况,我们发现我们可以通过8的平衡因子来判断是哪种情况。

        下面我们来补全一下我们的代码。

        

        就是这样的,大家可以好好看看理解一下。

2.3.5  右左双旋

        上面我们说了有左右双旋,那么肯定也是存在右左双旋的,我们下面举个例子来看一下吧。

        

        这个就是一个右左双旋的例子,我们发现它是失衡结点的右子树的左子树导致的失衡,所以它是RL型的。

        直接旋转就行了,先让失衡结点的右孩子右旋再让失衡结点左旋即可完成平衡操作。

        

        就是这样的,代码也是复用上面的代码。

        

        这个同理也是三种情况,也是需要补充代码的,我就直接给结果了,大家可以模仿上面的自己试一下。

        这样就没有问题了,不好理解了,大家那张纸画画图就理解了。

三.测试

        在测试之前先完成一些必要的函数。

        3.1中序遍历

        

3.2 Height和Size

        

3.3 Find

        这些函数实现起来都很简单,不过多说明了。

3.4 测试函数

        

        这就是我们的测试函数。

        

        测试一下,如果程序没崩溃,说明代码没问题。

        

        我们发现是没有问题的。

四.结束语

        感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。  感谢大家的一键三连。

http://www.xdnf.cn/news/584749.html

相关文章:

  • 【线段树】P2846 [USACO08NOV] Light Switching G|LG4|普及+
  • 无人机集装箱箱号识别系统准确率如何?能达到多少?
  • 微服务架构中的 RabbitMQ:异步通信与服务解耦(一)
  • Linux探秘:驾驭开源,解锁高性能——基础指令(续集)
  • LeetCode 1340. 跳跃游戏 V(困难)
  • 【Harmony】【鸿蒙】List列表View如果刷新内部的自定义View
  • 力扣HOT100之二叉树: 236. 二叉树的最近公共祖先
  • vue3定于组件名字的几种方法
  • 杨校老师竞赛课之青科赛GOC5-6年级组模拟题
  • ISO 26262- 5 评估硬件度量值
  • 2025年中青杯赛题浅析-快速选题
  • 12kV 环保气体绝缘交流金属封闭开关设备现场交流耐压试验规范
  • Web前端开发(HTML、CSS快速入门)
  • 2024 年地理信息技术与应用技能大赛(选拔赛/初级)——实操试题
  • 部署Prometheus并通过Grafana展示界面
  • wx.getPrivacySetting接口needAuthorization一直返回false
  • vue element-plus 集成多语言
  • SQLynx:一款跨平台的企业级数据库管理工具
  • pdf图片导出(Visio和Origin)
  • 2025口语App实测Top5!练习口语app真实口碑
  • 可视化图解算法43:数组中的逆序对
  • 鸿蒙Flutter实战:24-混合开发详解-4-初始化Flutter
  • 鸿蒙Flutter实战:25-混合开发详解-5-跳转Flutter页面
  • [Flutter]Completer和compute
  • 计量单片机 RN8302:特性、使用与应用
  • 【人工智障生成日记1】从零开始训练本地小语言模型
  • 【无标题】西门子S7-1500PLC与西门子V90 PN伺服通讯控制项目程序项目程序,共有8轴,编码器信号直接输入到变频器内。
  • 系统架构设计(十八):ATAM
  • 《棒球百科》棒球运动规则·棒球1号位
  • 【竖排繁体识别】如何将竖排繁体图片文字识别转横排繁体,转横排简体导出文本文档,基于WPF和腾讯OCR的实现方案