【C++】AVL树的4中旋转调整
文章目录
- 前提
- 一、AVL树的结构定义
- 二、AVL的插入(重点)
- 1. 插入的结点在较高左子树的左侧(右单旋)
- 2. 新节点插入较高右子树的右侧(左单旋)
- 3.新结点插入较高右子树的左侧(先右单旋再左单旋)
- 4. 新节点插入较高左子树的右侧(先左单旋再右单旋)
- 插入的整体代码
前提
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(-1、0、1),即可降低树的高度,从而减少平均搜索长度。
由此,该树被称为AVL树,即两位科学家名字的第一个字母。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树的高度差(简称平衡因子)的绝对值不超过1
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(logN),搜索时间复杂度O(logN)。
提示:以下是本篇文章正文内容,下面案例可供参考
一、AVL树的结构定义
树节点的结构创建:
template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv; //键值对来存储 K AND Vint _bf;//平衡因子//AVL树并没有规定必须要选择设计平衡因子,只是一个实现的选择,方便控制//构造函数AVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}};
树的框架创建:
template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node; //结点typedef
public:
//......
private:Node* _root = nullptr;
};
二、AVL的插入(重点)
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
(寻找位置->创建结点->插入节点->更新平衡因子->调整子树->形成AVL树)
1. 插入的结点在较高左子树的左侧(右单旋)
这样会造成parent的平衡因子变成-2, 当前节点(不是新增节点)的平衡因子变成-1
//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* pParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (pParent == nullptr){_root = subL;_root->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = subL;}else pParent->_right = subL;subL->_parent = pParent;}// 更新平衡因子parent->_bf = subL->_bf = 0;}
2. 新节点插入较高右子树的右侧(左单旋)
这样会造成parent的平衡因子变成2,当前节点(不是新增节点)的平衡因子变成1
//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}Node* pParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (pParent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = subR;}else pParent->_right = subR;subR->_parent = pParent;}//更新平衡因子subR->_bf = parent->_bf = 0;}
3.新结点插入较高右子树的左侧(先右单旋再左单旋)
会造成parent的平衡因子变成2, 当前节点(不是新增节点)平衡因子变成-1
void RotateRL(Node* parent){Node* subR = parent->_right; //左子树60Node* subRL = subR->_left;// 右子树的左子树90int bf = subRL->_bf;// 记录SubRLd 平衡因子// 先以SubR为轴进行右单旋RotateR(parent->_right);// 再进行左单旋RotateL(parent);if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else assert(0);}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else assert(0);}
4. 新节点插入较高左子树的右侧(先左单旋再右单旋)
这样会造成parent的平衡因子变成-2, 当前结点的平衡因子变成1
void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else assert(0);}
插入的整体代码
bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;//parent是cur的父节点Node* cur = _root;//cur往下走while (cur){if (cur->_kv.first > kv.first)//我比你小,往左找{parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first)//我比你大,往右找{parent = cur;cur = cur->_right;}else{return false;//AVL树不允许有重复值}}//走到这里就表示找到我们要插入kv值的正确位置了,准备插入节点..........cur = new Node(kv);if (parent->_kv.first < kv.first)//如果new的节点比父节点大,那么父节点的右指针指向new节点{parent->_right = cur;cur->_parent = parent;}else//如果new的节点比父节点小,那么父节点的左指针指向new节点{parent->_left = cur;cur->_parent = parent;}//开始更新平衡因子while (parent)//更新到根节点才算更新完平衡因子{//1、如果是右子树新增结点,那么父节点的_bf就加一//2、如果是左子树新增结点,那么父节点的_bf就减一//+1和-1大家可以自己决定,只要是对的,怎么都行!if (cur == parent->_right){parent->_bf++;}else{parent->_bf--;}// 是否继续更新依据:子树的高度是否变化// 1、parent->_bf == 0说明之前parent->_bf是 1 或者 -1// 说明之前parent一边高一边低,这次插入填上矮的那边,parent所在子树高度不变,不需要继续往上更新// 2、parent->_bf == 1 或 -1 说明之前是parent->_bf == 0,两边一样高,现在插入一边更高了,// parent所在子树高度变了,继续往上更新// 3、parent->_bf == 2 或 -2,说明之前parent->_bf == 1 或者 -1,现在插入严重不平衡,违反规则// 就地处理--旋转// 旋转:// 1、让这颗子树左右高度不超过1// 2、旋转过程中继续保持他是搜索树// 3、更新调整孩子节点的平衡因子// 4、让这颗子树的高度跟插入前保持一致//如果新增节点cur,使得父节点parent的平衡因子变成了0,那么表示该插入节点对整棵树的平衡因子没有影响//不用向上判断,可以直接退出if (parent->_bf == 0){break;}//如果新增cur使得父节点parent的平衡因子变成了1或者-1,那么我们要继续向上判断是否对上面的节点的//说明之前的平衡被打破,子树的高度变化了,有可能会造成父节点的平衡因子出现问题else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//当平衡因子出现2 or -2 的时候就需要调整子树else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == 2 && cur->_bf == 1)//左旋{RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1)//右旋{RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1)//左右旋{RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1)//右左旋{RotateRL(parent);}else{assert(false);}break;//旋转完一次就可以退出了,因为旋转的时候我们已经向上判断了,除非新插入,否则树就是avl树}else{assert(false);//这里直接报错,走到这里树就已经不是AVL树了}}return true;
}
相关文章:

【C++】AVL树的4中旋转调整
文章目录 前提一、AVL树的结构定义二、AVL的插入(重点)1. 插入的结点在较高左子树的左侧(右单旋)2. 新节点插入较高右子树的右侧(左单旋)3.新结点插入较高右子树的左侧(先右单旋再左单旋&#x…...

【MATLAB源码-第69期】基于matlab的LDPC码,turbo码,卷积码误码率对比,码率均为1/3,BPSK调制。
操作环境: MATLAB 2022a 1、算法描述 本文章介绍了卷积码、Turbo码和LDPC码。以相同的码率仿真这三种编码,并对比其误码率性能 信源输出的数据符号(二进制)是相互独立和等概率的; 信道是加性白高斯噪声信道&#…...
Java获取时间戳、字符串和Date对象的相互转换、日期时间格式化、获取年月日
获取时间戳(自1970年1月1日经历的毫秒数值) package org.example;import java.util.Date;public class Main {public static void main(String[] args) {Date date1 new Date(1699540662210L);System.out.println(date1.getTime());Date date2 new Dat…...
用c语言实现矩阵转置
下面是在 C 语言中实现矩阵转置的示例代码: #include <stdio.h> #define ROWS 3 #define COLS 3 void transpose(int matrix[ROWS][COLS]) { int temp; for(int i0; i<ROWS; i) { for(int j0; j<i; j) { temp matrix[i][j]; matrix[i][j] matrix[j]…...
蓝桥杯官网练习题(移动距离)
题目描述 X 星球居民小区的楼房全是一样的,并且按矩阵样式排列。其楼房的编号为 1,2,3, 当排满一行时,从下一行相邻的楼往反方向排号。 比如:当小区排号宽度为 6 时,开始情形如下: 1 2 3 4 5 6 12 …...

不止于“初见成效”,阿斯利康要让数据流转,以 AI 带动决策智能
“阿斯利康数字化成果在进博会上引人注目,令我感到非常高兴。”这是阿斯利康代表的感慨。 数字化建设目标是利用先进技术来提高企业运营效率,降低成本。在第六届进博会的7.2 B2-01展区,阿斯利康不仅展示了全球领先的生物医药和医疗器械成果&a…...

nav2 调节纯追踪算法
纯追踪算法 纯追踪基础 The core idea is to find a point on the path in front of the robot and find the linear and angular velocity to help drive towards it. 核心思想是在机器人前方的路径上找到一个点,并找到一个合适的线速度和角速度,以驱…...

安装RabbitMQ
安装RabbitMQ 下载需要的两个包 # 这直接就可以安装了,下面 ‘上传对应的rmp包’ 操作 [rootrabbitmq-1 ~]# curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash [rootrabbitmq-1 ~]# yum install erlang-21.3.8.2…...

Spring基础(1):两个概念
最近看了点Spring的源码,于是来稍微扯一扯,希望能帮一部分培训班出身的朋友撕开一道口子,透透气。 广义上的Spring指的是Spring整个项目,包含SpringBoot、SpringCloud、SpringFramework、SpringData等等, 本系列文章…...

国产化精密划片机已得到国内更多厂家青睐
国产化精密划片机在近年来得到了国内许多厂家的青睐,这是因为精密划片机在工业生产中有着重要作用。这种设备主要用于高精密切割加工,适用于多种材料,包括硅、石英、氧化铝、氧化铁等。 以精密晶圆划片机为例,这种设备采用了自主研…...

Voice Control for ChatGPT简单高效的与ChatGPT进行交流学习。
快捷又不失灵活性 日常生活中,我们与亲人朋友沟通交流一般都是喜欢语音的形式来完成的,毕竟相对于文字来说语音就不会显的那么的苍白无力,同时最大的好处就是能解放我们的双手吧,能更快实现两者间的对话,沟通便更高效…...

flutter生态一统甜夏 @Android @ios @windowse @macos @linux @Web
(愿景)G o o g l e 中 国flutter生态一统天下(IT) Web Android ios Windowse Macos Linux Google中国https://space.bilibili.com/64169458 https://pub-web.flutter-io.cn 构建 Flutter Web 应用 构建 Flutter Web 应用 - Flutter 中文文档 - Flutter 中文开发者网站 …...

计算机基础知识49
三板斧的使用(views.py) 三个方法:HttpResponse: 返回的是字符串render : 返回html文件redirect : 返回加载HTML页面的 def html(request):print(from html)# return HttpResponse(request) # 它返回的是字符串return render(request,html.html) # 返回html# ret…...

el-table给某一行加背景色
数据列表中总价大于100的一行背景色为红色,效果图如下: 代码示例: <template><div id"app"><!-- 测试区域!!!!!!!!&am…...

搭建 Makefile+OpenOCD+CMSIS-DAP+Vscode arm-none-eabi-gcc 工程模板
STM32F407-GCC-Template Arm-none-eabi-gcc MakefileOpenOCDCMSIS-DAPVscode工程模板 一、本次环境搭建所用的软硬件 1)Windows or Linux (本文以Windows为主) 2)JLink、Daplink、Wch-Link烧录器 3)GNU Arm Embedded Toolchain交叉编译…...
Unity场景ab包加载压缩(LZ4,LZMA)格式的测试
情况 最近场景越来越大,大概800M的场景加载时间可能长达40秒左右,所以需要测试看看发生了什么。 测试环境 测试环境Win10,21thI5-12600KF,32GRam , Nvidia GF RTX2060 32G Scene1大小:741M 加载代码 首…...

私有化部署大模型:5个.Net开源项目
从零构建.Net前后端分离项目 今天一起盘点下,10月份推荐的5个.Net开源项目(点击标题查看详情)。 1、BootstrapBlazor企业级组件库:前端开发的革新之路 BootstrapBlazor是一个用于构建现代Web应用程序的开源框架,它基…...

安卓系统手机便签app使用哪一款?
在现代快节奏的生活中,我们经常会遇到各种繁忙的事务和容易遗忘的备忘事项。为避免大家遗忘重要的事情,大家可以在常用的手机上安装记录备忘事项的工具,为了帮助安卓用户高效地记录和管理这些信息,今天我将向大家推荐一款功能强大…...

SpringCloud-Gateway无法使用Feign服务(2021.X版本)
Spring Cloud Gateway 2021.x版本,无法使用Feign调用其他服务接口。 问题原因: 在官网的 issue 里面找到了相关的问题。 How to call another micro-service on GatewayFilterFactory ? Issue #1090 spring-cloud/spring-cloud-gateway GitHubHel…...

基于SSM的建筑装修图纸管理平台
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...