【C++】AVL树
目录
1 简介
2 实现
2.1 框架构建
2.2 插入操作
2.2.1 平衡因子的更新
2.2.2 平衡因子异常时树的调整
3 检验
1 简介
AVL树基于二叉搜索树之上,又对其提出了平衡的要求,即:当向二叉搜索树插入新节点后,保证每个节点的左右子树高度之差的绝对值不超过1
AVL树具有如下性质:
1、它的左右子树都是二叉搜索树。
2、左右子树高度之差(简称平衡因子 = 右子树高度 - 左子树高度)的绝对值不超过1。
AVL树有多种方法来实现,使用平衡因子的方式只是其中一种,接下来讲解实现过程。
2 实现
2.1 框架构建
#pragma once
#include<iostream>template<class K, class V>
struct AVLTreeNode
{std::pair<K, V> _kv;AVLTreeNode<K, V>* _left; //左指针AVLTreeNode<K, V>* _right; //右指针AVLTreeNode<K, V>* _parent; //父指针int _bf; //balance factor 平衡因子
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public://
private:Node* _root = nullptr;
};
2.2 插入操作
2.2.1 平衡因子的更新
//1、更新平衡因子转换成代码
//这里注意:最坏情况下,平衡因子要持续更新到根节点后停止while (parent){if (cur == parent->_left)--parent->_bf;else++parent->_bf;if (parent->_bf == 0)break;else if(parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}else if(parent->_bf == 2 || parent->_bf == -2){//调整树来减小平衡因子}else assert(false);}
2.2.2 平衡因子异常时树的调整
对于如何调整树,我们引入AVL树的旋转操作,AVL树的旋转分为4种
而旋转最终的目的:
1、让这颗子树左右高度差不超过1
2、旋转过程中让它继续保持是搜索树
3、更新调整孩子节点的平衡因子
4、让这棵树的高度跟插入前保持一致
情况1:新节点插入较深右子树的右侧---右右:左单旋
步骤:1、将值为60的节点的左子树移到值为30的节点的右指针下
2、再将以值为30的节点的树移到值为60的节点的左指针下
void RotateL(Node* parent){Node* sub = parent->_right;Node* subL = sub->_left;if (subL)subL->_parent = parent;Node* ppnode = parent->_parent;parent->_right = subL;sub->_left = parent;parent->_parent = sub;if (ppnode == nullptr){_root = sub;_root->_parent = nullptr;}else{if (ppnode->_right == parent)ppnode->_right = sub;else if (ppnode->_left == parent)ppnode->_left = sub;sub->_parent = ppnode;}//重新更新平衡因子sub->_bf = 0;parent->_bf = 0;}
情况2:新节点插入较深左子树的左侧---左左:右单旋
步骤:1、将值为30的节点的右子树移到值为60的节点的左指针下
2、再将以值为60的节点的树移到值为30的节点的右指针下
代码与左单旋类似
void RotateR(Node* parent){Node* sub = parent->_left;Node* subR = sub->_right;if (subR)subR->_parent = parent;Node* ppnode = parent->_parent;parent->_left = subR;sub->_right = parent;parent->_parent = sub;if (ppnode == nullptr){_root = sub;_root->_parent = nullptr;}else{if (ppnode->_left == parent)ppnode->_left = sub;else if (ppnode->_right == parent)ppnode->_right = sub;sub->_parent = ppnode;}sub->_bf = 0;parent->_bf = 0;}
情况3:新节点插入较高左子树的右侧---左右:先左单旋再右单旋 -- 左右双旋
步骤:先以30为轴进行左单旋,再以60为轴进行右单旋
void RotateLR(Node* parent){Node* sub = parent->_left;Node* subR = sub->_right;int bf = subR->_bf; //记录subR的_bf来判断是左插入还是右插入...RotateL(parent->_left);RotateR(parent);if (bf == -1) //subR左子树新增{sub->_bf = 0;parent->_bf = 1;subR->_bf = 0;}else if (bf == 1) //subR右子树新增{parent->_bf = 0;sub->_bf = -1;subR->_bf = 0;}else if (bf == 0) //subR自己就是新增{parent->_bf = 0;sub->_bf = 0;subR->_bf = 0;}elseassert(false);}
情况4:新节点插入较高右子树的左侧---右左:先右单旋再左单旋 -- 右左双旋
代码与左右双旋类似
void RotateRL(Node* parent){Node* sub = parent->_right;Node* subL = sub->_left;int bf = subL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){parent->_bf = -1;sub->_bf = 0;subL->_bf = 0;}else if (bf == 0){parent->_bf = 0;sub->_bf = 0;subL->_bf = 0;}else if (bf == -1){parent->_bf = 0;sub->_bf = 1;subL->_bf = 0;}elseassert(false);}
综上可得到AVL数插入节点的整体过程:
bool insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}elsereturn false;}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}//更新平衡因子while (parent){if (cur == parent->_left)--parent->_bf;else++parent->_bf;if (parent->_bf == 0)break;else if (parent->_bf == -1 || parent->_bf == 1){cur = parent;parent = parent->_parent;}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);break;}}return true;}
3 检验
要检验一棵树是否为AVL树,可以先检验是否为二叉搜索树,再检验是否平衡树
如下附上代码:
//按照中序遍历打印,若为有序则是二叉搜索树
void _inorder(Node* root) {if (root == nullptr)return;_inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_inorder(root->_right);}
//检验是否为平衡二叉树
int getHeight(Node* root){if (root == nullptr)return 0;int lh = getHeight(root->_left);int rh = getHeight(root->_right);return lh > rh ? lh + 1 : rh + 1;}bool _isBalanced(Node* root){if (root == nullptr)return true;int lh = getHeight(root->_left);int rh = getHeight(root->_right);if (rh - lh != root->_bf)cout << "平衡因子异常" << endl;if (abs(lh - rh) > 2)return false;return _isBalanced(root->_left)&& _isBalanced(root->_right);}
本文着重讲解AVL数的整体构建过程,并未涉及到迭代器和其他等接口的设计,这些内容会在之后讲解红黑树一起加入。
感谢阅读
相关文章:

【C++】AVL树
目录 1 简介 2 实现 2.1 框架构建 2.2 插入操作 2.2.1 平衡因子的更新 2.2.2 平衡因子异常时树的调整 3 检验 1 简介 AVL树基于二叉搜索树之上,又对其提出了平衡的要求,即:当向二叉搜索树插入新节点后,保证每个节点的左右…...

Mybatis源码(2) - SqlSessionTemplate的介绍及创建过程
0. 前言1. Spring对SqlSessionTemplate的管理1.1. SqlSessionTemplate的创建:1.2. MapperProxy中sqlSession的来源:2. SqlSessionInterceptor中的getSqlSession0. 前言 众所周知😏:MyBatis通过SqlSessionFactory 创建SqlSession去调用Executo…...

女生做大数据有发展前景吗?
当前大数据发展前景非常不错,且大数据领域对于人才类型的需求比较多元化,女生学习大数据也会有比较多的工作机会。大数据是一个交叉学科涉及到的知识量比较大学习有一定的难度,女生比较适合大数据采集和大数据分析方向的工作岗位。 大数据采…...

Git实用指令记录
config 用例:对git最先要做的一个操作就是配置用户名和邮箱,否则无法commit查看所有可以config的条目,非常之多$ git config --list core.symlinksfalse core.autocrlftrue core.fscachetrue color.interactivetrue color.uiauto help.forma…...

复杂美公链技术重要特色:平行公链架构
复杂美公链技术Chain33从11月开源至今,获得众多合作方的认可,其中首创的平行公链架构被百度、阿里、360等机构认可并跟进研究,这也说明了平行公链或许是区块链普及应用的重要解决方案之一。 平行公链(以下简称平行链)是…...
Java——进制转换的一些内容
Java——进制转换的一些内容1.16进制字符串String转字节数组byte[]2.16进制字符串String转10进制数字int3.字节数组byte[]转字符串String4.16进制字符串String-->byte[]-->String(使用ByteBuffer转换)5.字节数组byte[]转字符数组char[]6.字节byte转…...

使用 Nodejs、Express、Postgres、Docker 在 JavaScript 中构建 CRUD Rest API
让我们在 JavaScript 中创建一个 CRUD rest API,使用:节点.js表达续集Postgres码头工人码头工人组成介绍这是我们将要创建的应用程序架构的架构:我们将为基本的 CRUD 操作创建 5 个端点:创造阅读全部读一个更新删除我们将使用以下…...

电子招标采购系统源码之什么是电子招投标系统?
随着互联网时代的到来,各行业都受到不同的影响,其中招投标行业也不例外。为了顺应互联网潮流的发展,电子招投标逐渐取代传统的纸质的招投标方式,给招标方、投标方、招标代理等各方也带来了前所未有的机遇与挑战。那么什么是电子招…...
匹配文件名称模块glob和fnmatch
匹配文件名称模块glob 1.概述 glob模式规则与re模块的正则表达式规则不大相同,glob模块遵循标准的UNIX路径扩展规则。 fnmatch模块用于根据glob模式比较文件名 2.glob表达式匹配文件名 2.1.测试文件 介绍glob配置规则前,先使用下面的代码创建测试文…...
day12_oop
今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、作业 二、继承 三、重写 四、this和super 五、访问修饰符 零、 复习昨日 局部变量和成员变量什么区别 位置,作用域,初始值,内存位置,生命周期 构造方法…...
在 Flutter 中使用 webview_flutter 4.0 | js 交互
大家好,我是 17。 已经有很多关于 Flutter WebView 的文章了,为什么还要写一篇。两个原因: Flutter WebView 是 Flutter 开发的必备技能现有的文章都是关于老版本的,新版本 4.x 有了重要变化,基于 3.x 的代码很多要重…...

嵌入式ARM工业边缘计算机BL302的CAN总线接口如何设置?
CAN 接口如图所示,输入如下命令: ifconfig -a //查看所有网卡 如果 FlexCAN 驱动工作正常的话就会看到 CAN 对应的网卡接口,如图。从图中可 以看出,有一个名为“can0”的网卡,这个就是 BL302 板上的 CAN1 接口对应的 c…...

Win11系统如何安装Ubuntu20.04(WSL版本)并安装docker
终于还是下定决心去换电脑了……这次采用轻量级的WSL,发现虽然没有占内存的GUI界面,但是编码和阅读文档还是非常nice的 1、首先开启Win11的虚拟机服务 2、下载你期望的Ubuntu服务器(这里以20.04为例) 安装成功后,发现…...

Elasticsearch和Solr的区别
背景:它们都是基于Lucene搜索服务器基础之上开发,一款优秀的,高性能的企业级搜索服务器。(是因为他们都是基于分词技术构建的倒排索引的方式进行查询)开发语言:java语言开发诞生时间:Solr2004年…...
如何在北京买房
首先我陈述一点,如果为了买房后再卖掉赚取差价,我这篇文章也许不适合,我这篇文章为整体愿景的发展而设计,为可操作房产的买卖而操作。 买房的愿景: 首先,我们要以一种心态来买房。那就是以始为终的态度&am…...

使用Proxifier+burp抓包总结
一、微信小程序&网页抓包 1. Proxifier简介 Proxifier是一款功能非常强大的socks5客户端,可以让不支持通过代理服务器工作的网络程序能通过HTTPS或SOCKS代理或代理链。 2. 使用Proxifier代理抓包 原理:让微信相关流量先走127.0.0.1:80到burp。具体…...
安装华为aab包的处理方式
1、转换 aab包 为 apks 说明: 1、bundletool-all-1.11.2.jar 转换文件的工具 2、a.aab aab源文件 3、xxx.apks 导入的文件以及路径(例如:D:\Android\xxx.apks) 4、–ksxxxx.jks 该aab打包所需的jsk文件 5、三条命令为 jsk打包所…...
Word处理控件Aspose.Words功能演示:使用 C++ 将 RTF 文档转换为 PDF
Aspose.Words 是一种高级Word文档处理API,用于执行各种文档管理和操作任务。API支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft Word。此外,API支持所有流行的Word处理文件…...

【Java|多线程与高并发】进程与线程的区别与联系
文章目录什么是进程什么是线程上下文切换多线程一定比串行执行快吗进程与线程的区别与联系什么是进程 进程的定义:进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存&a…...
K8s手工创建kubeconfig
我们通过 kubectl 命令行连接 k8s apiserver 时需要依赖 kubeconfig 文件。 kubeconfig 文件通常包含了 context(上下文)列表,每个 context 又会引用 cluster 和 user,最后通过 current-context 指定当前 kubeconfig 使用哪个 con…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

麒麟系统使用-进行.NET开发
文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的,如果需要进行.NET开发,则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET,所以要进…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...