【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…...
FPGA SDIO模式读写SD卡程序功能说明
FPGA以SDIO模式读写SD卡源码,可移植到任何FPGA中。 在SDIO模式下,SD卡读写速率50Mbps以上。 文件里包含tb和说明文档,已经下板验证通过。一、程序核心功能概述 本程序实现了FPGA通过SDIO模式对SD卡进行高速读写操作的完整解决方案,…...
Hammerspoon:macOS桌面自动化的终极Lua脚本工具
Hammerspoon:macOS桌面自动化的终极Lua脚本工具 【免费下载链接】hammerspoon Staggeringly powerful macOS desktop automation with Lua 项目地址: https://gitcode.com/gh_mirrors/ha/hammerspoon Hammerspoon是一款让macOS桌面自动化变得简单而强大的工具…...
[特殊字符]️cv_resnet101_face-detection_cvpr22papermogface实战教程:从模型加载到JSON坐标提取完整流程
MogFace 极速智能人脸检测工具实战教程:从模型加载到JSON坐标提取完整流程 1. 引言:为什么你需要一个强大的人脸检测工具? 想象一下这个场景:你手头有一堆活动照片,需要快速统计每张照片里有多少人。或者,…...
深入 Java 垃圾回收调优:从底层原理到落地实战,攻克性能瓶颈
🔥 本文系统梳理Java垃圾回收(GC)调优的核心知识、实战技巧与典型案例,帮你从「会用JVM」到「精通GC调优」,精准解决内存泄漏、GC频繁、响应延迟等核心问题。 在Java开发中,GC(垃圾回收&#x…...
银河麒麟V10 SP1离线环境搭建全攻略:从Java8到Node.js的避坑指南
银河麒麟V10 SP1离线环境搭建全攻略:从Java8到Node.js的避坑指南 在国产操作系统日益普及的今天,银河麒麟V10 SP1作为一款安全可靠的操作系统,正被越来越多的企业和开发者采用。然而,在离线环境下搭建完整的开发环境,尤…...
LaTeX科研提案模板定制指南:从Overleaf选模板到个性化排版实战
LaTeX科研提案模板定制指南:从Overleaf选模板到个性化排版实战 在学术写作领域,一份格式规范、排版精美的科研提案(Research Proposal)往往能给人留下专业的第一印象。对于经常需要申请基金或项目的研究人员来说,掌握LaTeX模板的定制能力&…...
从医疗到工业:模拟与数字电路隔离在不同场景下的最佳实践
从医疗到工业:模拟与数字电路隔离在不同场景下的最佳实践 在电子系统设计中,模拟与数字电路的隔离问题就像一场精心编排的舞蹈——每个动作都需要精确协调,任何失误都可能导致整个表演失败。对于医疗设备工程师来说,这可能意味着心…...
FreeRTOS任务优先级反转实战:如何用互斥量解决STM32项目中的调度难题
FreeRTOS任务优先级反转实战:互斥量在STM32中的高效解决方案 1. 优先级反转现象的本质剖析 在嵌入式实时系统中,任务调度器的核心职责是确保高优先级任务能够及时抢占低优先级任务。然而,当多个任务共享临界资源时,可能会出现一种…...
3月前端面试了十来个前端开发,全是菜鸡!!
都说了现在除了大厂,基本不问八股了,都在刷场景 例如: “如何一次性渲染十万条数据还能保证页面不卡顿?”(虚拟列表实现,还要支持动态高度) “当QPS达到峰值时,前端该如何处理&am…...
告别单调界面:foobox-cn如何为foobar2000带来专业级音乐播放体验
告别单调界面:foobox-cn如何为foobar2000带来专业级音乐播放体验 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 你是否厌倦了千篇一律的音乐播放器界面?foobox-cn作为专为foo…...
