【C++】AVL树模拟实现插入功能

news2024/7/4 6:02:24

AVL树模拟实现插入

  • 前言
  • 正式开始
    • 树节点
    • insert
    • 旋转
      • 左单旋
      • 右单旋
      • 左右双旋
      • 右左双旋
    • 用旋转来平衡树
    • 测试

在这里插入图片描述

前言

本篇主要介绍AVL树的插入功能。其中就包含了最重要的旋转。

通过旋转来使得树平衡,是学习AVL树的一个重点,也是也是一个难点。

正式开始

先简单介绍一下AVL树。

AVL树在二叉搜索树的基础上添加了一项规定,就是保证每个结点的左右子树高度之差的绝对值不超过1,如果能够实现的话,那么就能生成一棵接近满二叉树的二叉搜索树。这样就能使得搜索的效率直线上升,这样生成出来的树就是AVL树,也叫高度平衡二叉搜索树。

如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

那么:如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)

写代码之前先看图:
在这里插入图片描述

注释:

  1. 每个节点跟前蓝色标记为 平衡因子 (balance factor),用来记录当前节点的做右子树高度差。代码实现时,我会以 bf 来表示某个节点的平衡因子。

当左树和右树高度相同时,记为0。
当左树比右树高一个节点时,记为-1。
当右树比左树高一个节点时,记为1。

bf 的取值只有上述三种情况,没有其他。因为要按照AVL标准去实现,左右子树高度差的绝对值不超过1。

  1. 因为插入一个节点,只会影响其祖宗节点的 bf,所以当插入一个节点后,要不断更新其祖先节点的 bf,因此我们可以在树节点中添加一个指向父节点的指针来方便我们更新祖先节点的 bf。

祖先节点是指从当前节点到树的根节点的所有节点,包含自身,比如说上图中2的祖先节点就是2,1,3,5

  1. 更新祖先节点 bf 的规律如下:

当新增节点在某节点的左侧时,该节点的 bf 减一。
当新增节点在某节点的右侧时,该节点的 bf 加一。
如图:
在这里插入图片描述
在这里插入图片描述

插入节点后,父节点 bf 变为-1 或 1,说明原来为0,即左右子树高度相同,插入后有一边变高,parent高度变了,需要继续往上更新。
如图:
在这里插入图片描述

插入节点后,父节点 bf 变为0,说明原来为 -1 或者 1,即左右子树一高一低,插入后两边一样高,插入填上了矮的那一棵子树,所以parent所在的子树高度不变,其父节点的平衡因子就不需要更新,所以不需要继续往上更新了。
如上图中节点7,插入节点在其左侧,其平衡因子减一变为0,说明做右子树此时高度相同,不需要再继续向上更新了。

插入节点后,父节点 bf 变为 2 或 -2 ,说明原来为 -1 或者 1,即已经达到平衡的临界值了,插入后变为 2 或 -2,打破了平衡,所以parent所在子树需要旋转处理。
如图:
在这里插入图片描述

插入节点后,父节点 bf > 2 或 < -2 ,这是不可能出现的情况,若出现说明插入前就不是AVL树,需先检查之前操作的问题。

旋转等会再细讲,相信各位看了半天了理论已经有些腻了,下面来点代码:

树节点

本篇是以key/value模型来实现的。

节点存放内容如下:
在这里插入图片描述

构造函数:
在这里插入图片描述

初始情况下,就一个节点就够了:
在这里插入图片描述

然后来写insert,其实插入的时候就是按照二叉搜索树那样插入的,只是遇到了某节点平衡因子变为2时,就要对该节点进行旋转就好。

insert

先来写出二叉搜索树的插入:
在这里插入图片描述

上面我故意把旋转的情况留了下来,那么下面我们就来细说旋转。

旋转

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

分别对应四种特殊情况。其实就两个,单旋会一个另一个单旋就会了,双旋也是。都很相似。

先说简单点的单旋。

左单旋

当插入节点在较高右子树的右侧。即右右。

对应的抽象图:
在这里插入图片描述

我来解释解释:

上方a代表a整棵子树,b、c同理。h代表树的高度,h >= 0。

未插入节点时,对于30而言,右侧的子树是比左侧的子树高1的。

插入节点之后,也就是插入节点在较高右子树的右侧,此时对于30而言右侧的子树是比左侧的子树高2的,所以此时就要对树进行调整,让30的右侧指向60的左侧,让60的左侧指向30。看起来就像以30的右侧子树节点为轴心,向左旋转,从而使得整个树得到平衡,所以此步也就叫做左单旋。

我们来画几个具体的树看看:
在这里插入图片描述
画了三棵树,我已经画不下去了,太麻烦了,方法都是一样的,这里只是把h具体化了出来,如果还有同学没看懂的话,可以自己画画试试,摸索摸索。

这个可以单独写一个函数出来。函数名就是RotateL。

旋转时,可以显示指出两个节点,一个为subR,就是当前parent->_right;一个为subRL,就是当前parent->_right->left。

看图:
在这里插入图片描述

写的时候就是parent->_right = subRL,然后subR->left = parent。

然后就得到平衡树了:
在这里插入图片描述

但是还要注意原先30的父节点,如果30本来就是root的话,就要让root指向60,但是不是root的话,就要让30原先的父节点指向60。

记得同时更新变化的节点的父指针。

代码如下:
在这里插入图片描述

右单旋

新节点插入较高左子树的左侧。即左左。

在这里插入图片描述

右单旋跟上面的左单旋情况非常相似。

简单说说,未插入新节点时,对于60而言,左侧子树高度比右侧子树高度高1,在30左侧插入节点之后,左侧子树高度比右侧子树高度高2,此时就要进行旋转调整,让30的右给60的左,30的左指向60。看起来就像以30为轴心,向右旋转,从而使得整个树得到平衡,所以此步也就叫做右单旋。

具体的图解就不给了,就和上面左单旋的非常像。不是很理解的同学可以自己画画。

写代码的话,还是显示支出三个节点,一个原树根的父节点ppNode,一个左子树的根subL,一个左子树的右根subRL。

画一下抽象图:
在这里插入图片描述

代码如下:
在这里插入图片描述

左右双旋

新节点插入再较高左子树的右侧。即左右。

这里就有点讲究了。

首先,插入左子树的右侧还要分更细致一点。

在这里插入图片描述

那些字母的意思跟上面的左单旋、右单旋的一样。

但是这里要分三种情况。
第一种,往b树下面插新节点。
在这里插入图片描述

第二种,往c树下面插新节点。
在这里插入图片描述

第三种,h == 0,60就是新插入的节点。
在这里插入图片描述

就上面三种情况,前两种是抽象的,不懂的同学可以给h一个具体值来自己画画。三种情况下最终根的左右子节点的平衡因子是要分情况讨论的。

而想要分情况讨论的话,就要先找出能够区分出三者的前提条件,这里前提条件就是插入节点之后,也就是旋转之前60的平衡因子。

第一种,往b树下面插新节点,60的平衡因子为-1。
第二种,往c树下面插新节点,60的平衡因子为1。
第三种,h == 0,60的平衡因子为0。

写代码时要显示指出四个节点:subL、subLR。
在这里插入图片描述
左旋和右旋的时候只要复用RotateL和RotateR就行了。

所以就可写出如下代码:
在这里插入图片描述
上面双旋是让subLR的左右分别给到subL,parent,然后再让subL,parent做subLR的左右护法。

右左双旋

新节点插入在较高右子树的左侧。即右左。

在这里插入图片描述

这就跟上面的一样了,就不过多赘述了。也是三种情况,各位自己画画图看你能搞出来不。

我就直接给代码了:
在这里插入图片描述

用旋转来平衡树

上面四种旋转情况已经写完了,下面就要用旋转来平衡树了。

续着旋转前insert的代码,我们写到了:
在这里插入图片描述
此时就要用到旋转了。

我们可以通过父子节点的平衡因子来判断一棵子树是该左单、右单、左右双、右左双。

上面四种情况中,父与子的bf分别为:

  1. 左左是-2,-1
  2. 右右是2,1
  3. 左右是-2,1
  4. 右左是2,-1

根据bf写出如下代码:
在这里插入图片描述

测试

先写个中序遍历来测试一下:
在这里插入图片描述
测试:
在这里插入图片描述

但是光遍历的话不具有说服力。
我们再来写判断是否平衡的函数:
在这里插入图片描述

测试:
在这里插入图片描述

再来一组:
在这里插入图片描述

然后再来一组10000个随机数的:

跑出来10000个数,结果正确。

到这里插入的就都讲完了,本篇就讲这一个功能,删除的话不讲,各位有对删除感兴趣的可以看看其他教学。查找什么的功能都很简单,AVL重在旋转,其他的都是小case,把旋转掌握好,AVL树就拿下了。

到此结束。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/857915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虹科方案 | 汽车总线协议转换解决方案

汽车总线&#xff1a; 汽车总线是一种用于在车辆电子系统中传输数据和控制信息的通信系统。它允许不同的电子控制单元&#xff08;ECU&#xff09;在车辆中相互通信&#xff0c;协调各个系统的操作&#xff0c;以实现功能的集成和协同工作。 在现代汽车中&#xff0c;综合通信…

提高办案效率:公检系统引入自动校对技术

引入自动校对技术到公检系统中可以有效提高办案效率。自动校对技术结合公检系统的特点&#xff0c;可以在以下方面提高办案效率&#xff1a; 1.节省时间&#xff1a;自动校对技术可以快速检测和修正法律文书中的语法、拼写和标点符号等错误。与手动校对相比&#xff0c;自动校对…

dfs+回溯做题笔记

题目链接&#xff1a;t矩阵中的路径_牛客题霸_牛客网 参考代码&#xff1a; import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param matrix char字符型二维数组 * param …

虹科方案 | 汽车总线协议转换解决方案(二)

上期说到&#xff0c;虹科的PCAN-LIN网关在CAN、LIN总线转换方面有显著的作用&#xff0c;尤其是为BMS电池通信的测试提供了优秀的解决方案。假如您感兴趣&#xff0c;可以点击文末相关链接进行回顾&#xff01; 而今天&#xff0c;虹科将继续给大家带来Router系列在各个领域的…

个推消息推送专项运营提升方案,基于AIGC实现推送文案智能生成

个推消息推送专项运营提升方案自今年3月份发布以来&#xff0c;已应用于游戏社交、影音资讯、电商购物等多个行业。现个推消息推送专项运营提升方案又实现了推送策略的智能化和推送流程的自动化&#xff0c;助力APP进一步提升消息推送的效率和效果。 丰富推送策略组合&#xf…

Tomcat10.1源码安装与部署

安装JDK 1、下载jdk17 [rootmysql80 ~]# wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz[rootmysql80 ~]# ll -h jdk-17_linux-x64_bin.tar.gz -rw-r--r--. 1 root root 174M Mar 18 03:53 jdk-17_linux-x64_bin.tar.gz2、安装目录[rootmysql8…

第三方电容笔怎么样?apple pencil的平替笔

在当今世界&#xff0c;高科技已经成为推动电子产品迅速发展的重要动力。无论是工作&#xff0c;还是学习&#xff0c;iPad平板都很方便。iPad平板电脑将会和我们的生活联系在一起&#xff0c;不管是现在还是未来。iPad配上一支简单的电容笔&#xff0c;不仅提高了工作效率&…

数据库管理-第九十七期 以一当十的数据库路线(20230810)

第九十七期 以一当十的数据库路线&#xff08;20230810&#xff09; 距离上一期已经过去了整整9天了&#xff0c;相较于前几个月的“生产队的驴”&#xff0c;确实慢了很多&#xff0c;归根结底有几点&#xff1a;一是19c OCM的相关内容暂时告一段落&#xff0c;少了一半内容&…

从NPM注册中心获取包

目录 1、搜索和选择要下载的包 1.1 为什么使用 1.2 工作原理 1、质量 2、维护 3、受欢迎程度 4、名气 1.1、开始搜索包 2、在本地安装下载和安装软件包 2.1 安装未限定作用域的包 2.2 安装有作用域的公共包 2.3 安装私有包 2.4 测试包安装 2.5 已安装的软件包版本…

LeetCode算法递归类—两两交换链表中的节点

目录 24. 两两交换链表中的节点 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a;​编辑 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节…

线程池工作原理深入解析

目录 1. 线程正常的生命周期 2. 为什么要用线程池&#xff1f; 3. 线程池的核心原理 4. 怎样创建线程池&#xff1f; 5.线程池的代码实现 6. ThreadPoolExecutor 源码分析 7. ThreadPoolExecutor 工作原理展示&#xff08;重点&#xff09; 1. 线程正常的生命周期 我们知…

Pyinstaller 打包 django 项目如何将命令行参数加入?

起因 Pyinstaller 打包 django 项目&#xff0c;打包成 manage.exe 后用命令行 cmd manage.exe runserver 0.0.0.0:8001 --noreload 来运行感觉很不方便。 希望能够直接把命令行参数也打包进去&#xff0c;直接运行 exe 。我走了些弯路&#xff0c;但最终实现了。 弯路 我看…

Linux —— 基础I/O

一&#xff0c;背景介绍 狭义的文件存放在磁盘上&#xff0c;广义上在Linux下一切皆文件&#xff1b;磁盘上的文件一般为永久存储的外设&#xff0c;本质上对文件的操作&#xff0c;即为对外设的输入和输出&#xff08;简称I/O&#xff09;&#xff1b;空文件并不是不占磁盘文件…

Xamarin.Android实现手写板的功能

目录 1、背景说明2、实现效果3、代码实现3.1 整体思路3.2 核心绘画类-PaintView.cs3.3 对话框类-WritePadDialog.cs3.4 前端实现类-MainActivity3.5 布局文件3.5.1 write_pad.xml3.5.2 activity_main布局文件 4、知识总结5、代码下载6、参考资料 1、背景说明 在实际使用过程中…

【动态规划刷题 6】 买卖股票的最佳时机含冷冻期 买卖股票的最佳时机含手续费

买卖股票的最佳时机含冷冻期 链接: 买卖股票的最佳时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票…

【赠书活动|第四期《互联网广告系统:架构、算法与智能化》】

文章目录 内容简介作者简介读者对象大咖推荐抽奖方式 广告平台的建设和完善是一项长期工程。例如&#xff0c;谷歌早于2003年通过收购Applied Semantics开展Google AdSense 项目&#xff0c;而直到20年后的今天&#xff0c;谷歌展示广告平台仍在持续创新和提升。广告平台是负有…

2024软考系统架构设计师论文写作要点

一、写作注意事项 系统架构设计师的论文题目对于考生来说&#xff0c;是相对较难的题目。一方面&#xff0c;考生需要掌握论文题目中的系统架构设计的专业知识;另一方面&#xff0c;论文的撰写需要结合考生自身的项目经历。因此&#xff0c;如何将自己的项目经历和专业知识有机…

内网穿透:实现公网访问内网群晖NAS的方法

公网远程访问内网群晖NAS 7.X版 【内网穿透】 文章目录 公网远程访问内网群晖NAS 7.X版 【内网穿透】前言1. 在群晖控制面板找到“终端机和SNMP”2. 建立一条连接公网数据隧道3. 获取公网访问内网群晖NAS的数据隧道入口 前言 群晖NAS作为应用较为广泛的小型数据存储中心&#…

RabbitMQ学习——发布订阅/fanout模式 topic模式 rabbitmq回调确认 延迟队列(死信)设计

目录 引出点对点(simple)Work queues 一对多发布订阅/fanout模式以登陆验证码为例pom文件导包application.yml文件rabbitmq的配置生产者生成验证码&#xff0c;发送给交换机消费者消费验证码 topic模式配置类增加配置生产者发送信息进行发送控制台查看 rabbitmq回调确认配置类验…

Shopee虾皮买家号注册时需要注意什么问题

虾皮是一家在线购物平台&#xff0c;如果您打算在虾皮上注册一个买家账号&#xff0c;以下是一些需要注意的问题&#xff1a; 账号安全&#xff1a;确保您选择一个安全的密码&#xff0c;并定期更改密码&#xff0c;以保护您的账号免受未经授权的访问。 个人信息&#xff1a;…