当前位置: 首页 > news >正文

AVL树(C++详解版)

1. AVL的概念

  • AVL是一颗空树,或者具备下列性质的二叉搜索树:它的左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索二叉树,通过控制高度差去控制平衡。
  • AVL树实现这里我们引入一个平衡因子的概念,每个结点都有⼀个平衡因子,任何结点的平衡因子等于右子树的高度减去左子树的高度,也就是说任何结点的平衡因子等于0/1/-1,AVL树并不是必须要平衡因子,但是有了平衡因子可以更方便我们去进性观察和控制树是否平衡。
  • AVL树整体结点数量和分布和完全二叉树类似,高度可以控制在log⁡n\log nlogn,那么增删查改的效率也可以控制在Olog⁡(n)\log (n)log(n),相比与二叉搜索树有了本质的提升。

2.AVL的实现

2.1 AVL树的结构

template<classK,classV>structAVLNode{pair<K,V>_kv;AVLNode<K,V>*_parent;AVLNode<K,V>*_left;AVLNode<K,V>*_right;int_bf;AVLNode(constpair<K,V>&kv):_kv(kv),_parent(nullptr),_left(nullptr),_right(nullptr),_bf(0){}};template<classK,classV>classAVLTree{usingNode=AVLNode<K,V>;public:private:Node*_root=nullptr;};

2.2 AVL树的插入

    1. 插⼊⼀个值按⼆叉搜索树规则进行。
    1. 新增结点以后,只会影响祖先结点的高度,也就是可能会影响部分祖先结点的平衡因子,所以更新从新增结点->根结点路径上的平衡因子,实际中最坏情况下要更新到根,有些情况更新到中间就可以停止了,具体情况我们下面再详细分析。
    1. 更新平衡因子过程中没有出现问题,则插入结束
    1. 更新平衡因子过程中出现不平衡,对不平衡子树旋转,旋转后本质调平衡的同时,本质降低了子树的高度,不会再影响上一层,所以插入结束。

平衡因子更新
更新原则:

  • 平衡因子 = 右子树高度-左子树高度
  • 只有子树高度变化才会影响当前结点平衡因子。
  • 插入结点,会增加高度,所以新增结点在parent的右子树,parent的平衡因子++,新增结点在parent的左子树,parent平衡因子–
  • parent所在⼦树的⾼度是否变化决定了是否会继续往上更新

更新停止条件:

  • 更新后parent的平衡因子等于0,更新中parent的平衡因子变化为-1->0 或者 1->0,说明更新前parent子树⼀边高⼀边低,新增的结点插⼊在低的那边,插入后parent所在子树高度不变,不会影响parent的父亲结点的平衡因子,更新结束。
  • 更新后parent的平衡因子等于1 或 -1,更新前更新中parent的平衡因子变化为0->1 或者 0->-1,说明更新前parent子树两边⼀样高,新增的插入结点后,parent所在的子树⼀边高⼀边低,parent所在的子树符合平衡要求,但是高度增加了1,会影响parent的父亲结点的平衡因子,所以要继续向上更新。
  • 更新后parent的平衡因子等于2 或 -2,更新前更新中parent的平衡因子变化为1->2 或者 -1->-2,说明更新前parent子树⼀边高⼀边低,新增的插入结点在高的那边,parent所在的子树高的那边更高了,破坏了平衡,parent所在的子树不符合平衡要求,需要旋转处理,旋转的目标有两个:
    1、把parent子树旋转平衡。
    2、降低parent子树的高度,恢复到插⼊结点以前的⾼度。所以旋转后也不需要继续往上更新,插入结束。
  • 不断更新,更新到根,根的平衡因子是1或-1也停止了。

    插入代码实现
boolInsert(constpair<K,V>&kv){if(_root==nullptr){_root=newNode(kv);returntrue;}Node*cur=_root;Node*parent=nullptr;while(cur){if(cur->_kv.first>kv.first){parent=cur;cur=cur->_left;}elseif(cur->_kv.first<kv.first){parent=cur;cur=cur->_right;}else{returnfalse;}}cur=newNode(kv);if(parent->_kv.first>kv.first){parent->_left=cur;}else{parent->_right=cur;}cur->_parent=parent;while(parent){if(cur==parent->_left)parent->_bf--;elseparent->_bf++;if(parent->_bf==0){break;}elseif(parent->_bf==-1||parent->_bf==1){cur=parent;parent=cur->_parent;}elseif(parent->_bf==2||parent->_bf==-2){//旋转}else{assert(false);}}}

3.AVL树的旋转

旋转的原则

    1. 保持搜索树的规则
    1. 让旋转的树从不满足变平衡,其次降低旋转树的高度旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。

1.右单旋

当左子树纯粹地比右边高时,且高度差为2时,就要开始右单旋,右单旋有一个好记的规则,就是将旋转点的左子树的右子树放到右子树的左子树,可以参考下面的例子。
而且有时候要旋转两次,一次时不够的,图三就是旋转了2次。



代码实现

voidRotateR(Node*parent){Node*subL=parent->_left;Node*subLR=subL->_right;parent->_left=subLR;if(subLR){subLR->_parent=parent;}Node*Pparent=parent->_parent;parent->_parent=subL;subL->_right=parent;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=0;subL->_bf=0;}

2.左单旋

左单旋和右单旋同理,都是规则是一样的,唯一不同的就是,左单旋是将根的右子树的左子树放到根的左子树的右子树
代码实现:

voidRotateL(Node*parent){Node*subR=parent->_right;Node*subRL=subR->_left;parent->_right=subRL;if(subRL){subRL->_parent=parent;}parent->_parent=subR;subR->_left=parent;Node*Pparent=parent->_parent;if(Pparent==nullptr){_root=subR;_root->_parent=nullptr;}else{if(parent==Pparent->_left){Pparent->_left=subR;}else{Pparent->_right=subR;}subR->_parent=Pparent;}parent->_bf=0;subR->_bf=0;}

3.右左双旋

产生双旋的条件是比较好辩别的,就是当一棵树不是纯粹的一边高时,就会发生双旋,比如图一,根的右子树比左子树高,本来理应左旋,但是左子树的右子树比左子树矮,左旋的代价是把旋上去的那个节点的左子树放到原本的根的右子树上,这样就会陷入循环,导致问题无法解决,所以我们要把右子树变成纯粹的一边高
情景有以下几种:



代码演示:

voidRotateRL(Node*parent){Node*subR=parent->_right;Node*subRL=subR->_left;intbf=subRL->_bf;RotateR(subR);RotateL(parent);if(bf==0){parent->_bf=0;subR->_bf=0;subRL->_bf=0;}elseif(bf==-1){parent->_bf=0;subR->_bf=1;subRL->_bf=0;}elseif(bf==1){parent->_bf=-1;subR->_bf=0;subRL->_bf=0;}else{assert(false);}}

4.左右双旋

原理和右左双旋一样



代码演示:

voidRotateRL(Node*parent){Node*subR=parent->_right;Node*subRL=subR->_left;intbf=subRL->_bf;RotateR(subR);RotateL(parent);if(bf==0){parent->_bf=0;subR->_bf=0;subRL->_bf=0;}elseif(bf==-1){parent->_bf=0;subR->_bf=1;subRL->_bf=0;}elseif(bf==1){parent->_bf=-1;subR->_bf=0;subRL->_bf=0;}else{assert(false);}}

4.AVL树的平衡检测

AVL树的平衡检测需要检查检查平衡因子是否正确,这时候我们需要一个参照物,就要借助树的高度来检查平衡因子是否没有出错。
而树的高度我们需要用到递归的方式来得到:
代码演示:

intHeight(Node*root){if(root==nullptr){return0;}intHeightLeft=Height(root->_left);intHeightRight=Height(root->_right);returnHeightLeft>HeightRight?HeightLeft+1:HeightRight+1;}boolIsBalanceTree(Node*root){if(root==nullptr){returntrue;}intHeightleft=Height(root->_left);intHeightright=Height(root->_right);intbf=Heightleft-Heightright;if(bf==-2||bf==2){returnfalse;}if(bf!=root->_bf){returnfalse;}returnIsBalanceTree(root->_left)&&IsBalanceTree(root->_right);}
http://www.zskr.cn/news/1428279.html

相关文章:

  • Roblox FPS解锁器:如何突破60帧限制获得极致流畅体验
  • HS2-HF Patch:Honey Select 2游戏体验的终极优化方案
  • 26年山东一卡通回收注意事项:不容忽视的重要细节! - 团团收购物卡回收
  • HS2-HF Patch:Honey Select 2终极游戏优化补丁完整指南
  • Windows进程注入实战:从notepad.exe报错comctl32.dll,聊聊NtCreateThreadEx与CreateRemoteThread的坑
  • 2026 遵义装修公司权威榜单|5 家本地口碑企业推荐 - 商业新知
  • 别再死记硬背Linux命令了!用这3个真实场景(文件管理、日志排查、用户权限)带你真正理解它
  • 2026年义乌靠谱装修选型参考:零套路交付体系、性价比管控与本地口碑保障的深度审视 - 企业品牌优选推荐官
  • 2026惠州本地优质防水补漏公司TOP5,屋顶外墙厨卫地下室漏水上门维修 服务范围覆盖惠州全域 惠州防水补漏哪家好 - 防水空鼓维修家
  • 2026台州婚纱摄影品牌观察:时尚印像团队、风格与服务全解析 - 天天生活分享日志
  • 支付宝立减金回收最全攻略|4种回收方式对比、行情价格+避坑指南 - 可可收公众号
  • ESP32与TB6612FNG双轮机器人:从硬件选型到代码调试全攻略
  • POLIR-Society-Organization-Management-管理新人的上位向导:
  • 2026企业通讯软件对比:3款高安全内网方案在军工芯片场景实践 - 小天互连即时通讯
  • Arduino西蒙游戏:从零实现硬件交互与状态机编程
  • (毕业必看)实测靠谱的AI写作辅助平台,毕业党收藏备用
  • 从一次部署故障复盘开始:详解Doris BE节点启动失败排查全流程(附libjvm.so等常见错误解决)
  • 山东SPC地板行业盘点 选购技巧与避坑完整攻略 - 百航
  • 2026北京门头沟区股权变更机构TOP3盘点!靠谱代办公司深度测评! - 小柏云
  • 2026 杭州奢包回收哪家靠谱?本地真实交易实测参考 - 奢侈品回收测评
  • 2026北京黄金回收靠谱榜单 5.29高端变现实测与行业避坑解析 - 资讯纵览
  • VSCode远程开发避坑实录:连接Docker容器时SSH端口映射与root登录的那些‘坑’
  • 2026年山东区域汽车故障精修机构口碑推荐榜单:德系豪车维修、发动机异常、悬挂问题靠谱门店优选参考 - 海棠依旧大
  • 全网公认新疆第一贴心!导游娇娇,把游客当家人全程暖心陪护 - 盛世西域旅行
  • 保姆级教程:用Vue2 + AntV X6 + Element UI 快速搭建一个可拖拽的流程图编辑器
  • 基于Arduino与PIR传感器的互动游戏装置设计与实现
  • 【技术管理】技术选型方法论:从需求到落地的决策指南
  • ComfyUI-WanVideoWrapper视频生成框架:PyTorch 2.0+编译优化与显存管理深度解析
  • 2026年佛山阻尼铰链与隐藏滑轨厂家多款好物同台比拼:顺德源头工厂选型避坑须知 - 企业名录优选推荐
  • TI CCS新手避坑指南:ARM和C6000工程编译后,如何正确配置Post-build生成bin文件?