尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C++ | AVLTree
云深麋鹿专栏C | 用C语言学数据结构 | Java回顾上一篇我们结束了 mapset的使用接下来这篇文章让我们进入到 AVLTree 的学习体会新的设计思路吧~放个目录一 AVL树的概念二 实现2.1 AVLTreeNode2.2 AVLTree2.3 Insert2.3.1 插入流程1新增结点2更新平衡因子3旋转2.3.2 平衡因子更新1更新原则2停止更新条件3代码实现2.3.3 旋转1旋转原则2右单旋3 左单旋4左右双旋5右左双旋2.3.4 Insert完整代码2.4 AVL树的平衡检测2.5 AVL树的查找2.6 简单测试一下一 AVL树的概念AVL树是一棵高度平衡二叉查找树左右子树高度差的绝对值不超过1。每个结点有一个平衡因子balance factor这不是必须的。有些情况下高度差不为0。二 实现2.1 AVLTreeNodetemplateclassK,classVstructAVLTreeNode{pairK,V_kv;AVLTreeNodeK,V*_left;AVLTreeNodeK,V*_right;AVLTreeNodeK,V*_parent;int_bf;// balance factorAVLTreeNode(constpairK,Vkv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}};这里的Node存储的是一个pair键值对应用场景更广一些。需要额外记录每个Node的_parent后面要用到。实现的是带平衡因子后面都简称bf的AVL树。2.2 AVLTreetemplateclassK,classVclassAVLTree{typedefAVLTreeNodeK,VNode;public:// ...private:Node*_rootnullptr;};搭个框架。2.3 Insert2.3.1 插入流程1新增结点按二叉搜索树规则来。具体在这篇博客 C | 二叉搜索树 第三大块。代码boolInsert(constpairK,Vkv){if(_rootnullptr){_rootnewNode(kv);returntrue;}Node*parentnullptr;Node*cur_root;while(cur){if(cur-_kv.firstkv.first){parentcur;curcur-_right;}elseif(cur-_kv.firstkv.first){parentcur;curcur-_left;}else{returnfalse;}}curnewNode(kv);if(parent-_kv.firstkv.first){parent-_leftcur;}else{parent-_rightcur;}cur-_parentparent;// ...}2更新平衡因子新增结点更新 新增节点 - 根节点 路径上的bf。具体后面展开讲。3旋转更新bf的过程中出现不平衡左右子树高度差的绝对值超过1需要旋转不平衡子树。平衡后就不用继续往上更新bf了完毕。2.3.2 平衡因子更新1更新原则平衡因子 右子树高度 - 左子树高度子树高度变化影响当前结点平衡因子。插入结点parent更新新结点在左–在右若parent的bf不为零则往上更新。2停止更新条件更新后parent的bf为 零表示parent所在子树高度不变。则parent的parent结点的bf不会受到影响更新结束。更新后parent的bf为 1 或 -1表示parent所在子树原先平衡更新后高度改变。则parent的parent结点的bf也会受到影响往上继续更新。更新后parent的bf为 2 或 -2表示parent所在子树已经不平衡了需要旋转处理。不断更新到rootbf为 1 或 -1 停止。3代码实现先套一个框架新增节点 的 parent 存在时进入循环体。while(parent){//...}更新 parent 的 bfif(parent-_kv.firstkv.first){parent-_bf--;}else{parent-_bf;}判断 parent 的 bf 是否为零if(parent-_bf0){break;}elseif(abs(parent-_bf)1){// continuecurparent;parentparent-_parent;}elseif(abs(parent-_bf)2){// rotatebreak;}else{// errorexit(-1);}接下来进入rotate上面第二个else if。2.3.3 旋转旋转共有四种左旋转、右旋转、左右双旋 和 右左双旋。1旋转原则旋转后保持搜索树的特性。让旋转的树从不平衡变平衡其次降低旋转树的高度。2右单旋左子树太高了。即如图中a子树新增一个结点 高h1太高了。①过程subL的右子树subLR变为parent结点的左子树subL结点到parent结点的位置上。parent上面还有parentParent后面会用到。最终实现结果②代码实现框架voidRotateR(Node*parent){//...}注意修改结点位置也要修改它的_parent。记录各个结点根据传参parentNode*subLparent-_left;Node*subLRsubL-_right;Node*parentParentparent-_parent;处理parent和subLRparent-_leftsubLR;if(subLR){subLR-_parentparent;}存在subLR为nullptr的情况。处理subL和parentsubL-_rightparent;parent-_parentsubL;处理parentParent和subLif(parentParent){if(parentParent-_leftparent){parentParent-_leftsubL;}else{parentParent-_rightsubL;}subL-_parentparentParent;}else{_rootsubL;_root-_parentnullptr;}涉及parentParent存在不存在的问题。若存在涉及parentParent存在于哪个分支的问题。记得更新bfparent-_bfsubL-_bf0;3 左单旋右子树太高了。即如图中a子树新增一个结点后 高h1①过程subR的左子树subRL变为parent结点的右子树subR结点到parent结点的位置上。最终实现结果②代码实现跟右单旋代码类似voidRotateL(Node*parent){Node*subRparent-_right;Node*subRLsubR-_left;Node*parentParentparent-_parent;parent-_rightsubRL;if(subRL){subRL-_parentparent;}subR-_leftparent;parent-_parentsubR;if(parentParent){if(parentParent-_leftparent){parentParent-_leftsubR;}else{parentParent-_rightsubR;}subR-_parentparentParent;}else{_rootsubR;_root-_parentnullptr;}parent-_bfsubR-_bf0;}4左右双旋左子树高了左子树的右子树高了。即图中 e子树 新增结点后 高h。subL 总高h2跟c子树高度差超过1了。①过程subL左单旋 parent右单旋。实现结果②代码实现框架:voidRotateRL(Node*parent){//...}记录各个结点Node*subLparent-_left;Node*subLRsubL-_right;Node*parentParentparent-_parent;额外记录一下bfintbfsubLR-_bf;旋转RotateL(parent-_left);RotateR(parent);这个直接复用写过的代码。重点 更新bfif(bf0){subL-_bf0;subLR-_bf0;parent-_bf0;}elseif(bf-1){subL-_bf0;subLR-_bf0;parent-_bf1;}elseif(bf1){subL-_bf-1;subLR-_bf0;parent-_bf0;}else{exit(-1);}演示一下这几种情况5右左双旋右子树高了右子树的左子树高了。即图中 e子树 新增结点后 高h。①过程subR右单旋 parent左单旋。结果②代码实现跟左右双旋类似直接上代码voidRotateRL(Node*parent){Node*subRparent-_right;Node*subRLsubR-_left;Node*parentParentparent-_parent;intbfsubRL-_bf;RotateR(subR);RotateL(parent);if(bf0){subR-_bf0;subRL-_bf0;parent-_bf0;}elseif(bf-1){subR-_bf1;subRL-_bf0;parent-_bf0;}elseif(bf1){subR-_bf0;subRL-_bf0;parent-_bf-1;}else{exit(-1);}}2.3.4 Insert完整代码加上省略掉的rotate部分boolInsert(constpairK,Vkv){if(_rootnullptr){_rootnewNode(kv);returntrue;}Node*parentnullptr;Node*cur_root;while(cur){if(cur-_kv.firstkv.first){parentcur;curcur-_right;}elseif(cur-_kv.firstkv.first){parentcur;curcur-_left;}else{returnfalse;}}curnewNode(kv);if(parent-_kv.firstkv.first){parent-_leftcur;}else{parent-_rightcur;}cur-_parentparent;// Update balance factorwhile(parent){if(parent-_kv.firstkv.first){parent-_bf--;}else{parent-_bf;}if(parent-_bf0){break;}elseif(abs(parent-_bf)1){// continuecurparent;parentparent-_parent;}elseif(abs(parent-_bf)2){// rotateif(parent-_bf-2){if(cur-_bf-1){RotateL(parent);}elseif(cur-_bf1){RotateLR(parent);}}elseif(parent-_bf2){if(cur-_bf1){RotateR(parent);}elseif(cur-_bf-1){RotateRL(parent);}}break;}else{// errorexit(-1);}}returntrue;}2.4 AVL树的平衡检测框架boolIsBalanceTree(Node*root){// ...}处理root为空的情况if(rootnullptr){returntrue;}记录左右子树高度intleftHeight_Height(root-_left);intrightHeight_Height(root-_right);计算高度差即为 root 的实际bfintdifferencerightHeight-leftHeight;检查 difference 是否合理if(abs(difference)2){coutroot-_kv.firstabnormal height differenceendl;returnfalse;}检查已存储bf是否正确if(difference!root-_bf){coutroot-_kv.firstabnormal balance factorendl;returnfalse;}不能直接检查bf因为bf可能更新错。递归往root左右判断returnIsBalanceTree(root-_left)IsBalanceTree(root-_right);再开放一个public接口供外部使用boolIsBalanceTree(){return_IsBalanceTree(_root);}2.5 AVL树的查找没啥好说的跟二叉搜索树一样Node*Find(constKkey){Node*cur_root;while(cur){if(cur-_kv.firstkey){curcur-_right;}elseif(cur-_kv.firstkey){curcur-_left;}else{returncur;}}returnnullptr;}2.6 简单测试一下AVLTree定义在定义域wyzy里了。wyzy::AVLTreeint,intt;inta[]{4,2,6,1,3,5,15,7,16,14};for(autoe:a){t.Insert({e,e});}t.Print();coutendl;coutt.IsBalanceTree()endl;定义了一个Print函数用于输出classAVLTree{publicvoidPrint(){_Print(_root);}private:void_Print(Node*root){if(rootnullptr){return;}_Print(root-_left);coutroot-_kv.first root-_bfendl;_Print(root-_right);}}这个递归老生常谈了就不多说了。运行这么一棵树AVLTree 的学习就到这里下一篇我们再上一个新的树 红黑树 很快bushi会更出来~
RELATED

相关推荐

NVIDIA Atlas一文读懂:2.5B参数天气生成式AI如何几分钟产出全球中期集成预报(新手入门完全指南)

NVIDIA Atlas一文读懂:2.5B参数天气生成式AI如何几分钟产出全球中期集成预报(新手入门完全指南)

【免费下载链接】atlas-era5 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/atlas-era5 点击查看 免费下载 NVIDIA Atlas 是一款 2.5B 参数的天气生成式AI模型,它能对全球 0.25 网格上的 ERA5 变量做自回归预测,几分钟内即可产出一次…

📅 2026/10/22 1:41:33
模拟位置死活不动?免ROOT虚拟定位,拖个摇杆就走

模拟位置死活不动?免ROOT虚拟定位,拖个摇杆就走

模拟位置死活不动?免ROOT虚拟定位,拖个摇杆就走 【免费下载链接】GoGoGo 一个基于 Android 调试 API 百度地图实现的虚拟定位工具,并且同时实现了一个可以自由移动的摇杆 项目地址: https://gitcode.com/GitHub_Trending/go/GoGoGo 做…

📅 2026/10/22 1:41:33
Modbus RTU读从站超时,但RS485接线没问题:我排查到的3个非典型原因

Modbus RTU读从站超时,但RS485接线没问题:我排查到的3个非典型原因

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/22 1:41:33
MORE NEWS

更多资讯

📰

QtXlsx在VS2019下的编译配置与Excel读写实战

上个月做内部设备管理工具,客户要求把运行报表导出成Excel。桌面端技术栈很早就定死了:Qt 5.15.2配VS2019,UI部分两天收工,结果Excel读写这块硬生生卡了我大半天。QtXlsx从源码编译、VS2019工程挂接、链接报错、调试闪退&#xff…

📰

六大开源Agent框架深度拆解:任务规划、工具调用与记忆管理选型指南

1. 为什么我要花两周时间逐个拆解这六个Agent代码库过去大半年,Agent这个词被反复提起,几乎每隔几天就能看到新的开源项目冒出来。但真正上手跑过一圈之后你会发现,能跑通Demo的和能扛住真实任务的,完全是两回事。我前后花了将近两…

📰

安全帽佩戴检测毕设全流程:从YOLO数据准备到部署答疑

现在想想,我接这个毕设题目的时候心里其实挺没底的。安全帽佩戴检测,听起来就是一个目标检测任务,把YOLO拉起来跑一遍数据就好了,但真等到自己从零把数据集、标注、训练、验证、部署整条链路走完,才发现这里面到处都是…

📰

NUMA架构下PHP-FPM性能优化:从原理到绑核实操全解析

1. 先搞懂NUMA:从一次线上性能抖动说起前几年我接手过一套跑在双路服务器上的PHP接口服务,应用逻辑本身并不复杂,就是典型的PHP-FPM加MySQL加Redis的组合。可每到高峰期,总会冒出一批延迟在800毫秒到1.2秒之间的慢请求&#xff0c…

📰

8位单片机实现模糊控制洗衣机的工程实践

简介:本资源是一套面向自动化控制与智能家电方向初学者及课程设计者的模糊控制洗衣机教学实践代码包,聚焦于模糊逻辑在洗涤参数自适应调节中的工程实现。压缩包共23个文件,含10个MATLAB脚本(.m)、6个FIS模糊推理系统文…

📰

SpringBoot+Vue前后端分离:小区物业管理系统源码实战解析

1. 项目核心价值与整体功能拆解先聊两句大实话。很多刚入行的朋友拿到“XX管理系统源码”这种项目,第一反应往往是先跑起来再说,跑起来之后又不知道下一步该干嘛,最后就把一堆代码扔在硬盘里吃灰。这套小区物业管理系统其实是一个很典型的前后…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬