C++ AVL树底层实现原理

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:C++知识分享⏪

🚚代码仓库:C++高阶🚚

🌹关注我🫵带你学习更多C++知识
  🔝🔝



目录

前言

AVL 树

1.1 AVL树的概念

     1.2 AVL树节点的定义     

​编辑

1.3左旋 

 1.3右旋 

1.4双旋 


 

前言

C++ set&&map​​​​​​ 这篇从了解到使用map,map也是搜索二叉树,因为搜索二叉树会出现歪脖子树的情况,本篇就讲如何解决歪脖子树。记住口令 旋转 旋转 旋转 !!! 重要的事说三边。 搜索二叉树加入平衡因子 旋转 就成了传说之中的AVL树

AVL

1.1 AVL树的概念

二叉搜索树虽可以缩短查找的效率,但 如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下
因此,两位俄罗斯的数 G.M.Adelson-Velskii和E.M.Landis
1962 年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过 1( 需要对树中的结点进行调整 ) ,即可降低树的高度,从而减少平均搜索长度。
一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
  • 它的左右子树都是AVL
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
  • 如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
  • $O(log_2 n)$,搜索时间复杂度O($log_2 n$)

 

这里需要强调一下:AVL树不一定有平衡因子, 还有

递归更新高度:在插入或删除节点后,AVL树会递归地更新从该节点到根节点的所有祖先节点的高度。这是必要的,因为平衡因子是基于节点的高度来计算的。通过递归更新高度,AVL树能够准确地维护每个节点的平衡因子

这里我们用平衡因子来实现,因为比较好理解!!!

     1.2 AVL树节点的定义     

 我们先把AVL节点定义出来

template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf;AVLTreeNode(const pair<K,V)& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}};
template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
private:Node* _root = nullptr;
};

插入代码 

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;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;
}
插入之后 根据下图的规则,我们更新_bf

//更新平衡因子while (parent){if (cur == parent->_right){parent->_bf++;}elseparent->_bf--;if (parent->_bf == 1 || parent->_bf == -1){//继续更新parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 0){break;}

1.3左旋 

对上图新增插入10 这时右树的平衡因子 8 7 就变成了2 绝对值大于1,已经失衡了! 

这时就要通过旋转来调节平衡高度。

  这里强调代码不是重点 画图理解才是重点,前面有二叉树基础,代码非常好写,画图才能理清这里的关系!!!

这里我们从上面得出几个结论:

平衡因子发生变化 决定是否更新父亲节点,而爷爷节点更新也是取决于父亲节点的平衡因子是否发生变化 变了就继续往上更新,不变则不更新。

那就有3种情况:

  • parent-> bf == 1 || parent-> bf == -1 parent的子节点发生变化,继续更新。

       因为 插入之前 parent-> bf == 0 插入之后要么在左、要么在右 说明高度变了

  • parent-> bf ==2 || parent-> bf == -2 这种情况 parent的子树明显不平衡,需要旋转处理
  • parent-> bf == 0 这种情况 说明插入之前的parent这个节点是一高一低的,插入后刚好填到矮的那一边,两边子树的高度平衡不需要处理。

 那既然 parent的bf是2或者-2这两个值需要旋转处理。那什么情况左旋转?

我先说结论:右边的子树高 就左旋转。看图

图画出来就好办了,把图用代码实现。 

else if (parent->_bf == 2 || parent->_bf == -2){//旋转if (parent->_bf == 2 && cur->_bf == 1) //左旋{RotateL(parent);}

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

 1.3右旋 

else if (parent->_bf == -2 && cur->_bf == -1) //右旋{RotateR(parent);}
void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;subL->_right = parent;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* pparent = parent->_parent;parent->_parent = subL;if (pparent == _root){_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;}

1.4双旋 

双旋一共有两种情况:

  • 先右旋,再左旋。
  • 先左旋,再右旋 。

那什么时候先右旋,再左旋? 什么时候又先左旋,再右旋?先说结论:

  • 新增节点插入较高左子树的右侧,那么就先左单旋再右单旋。
  • 新增节点插入较高右子树的左侧,那么就先右单旋再左单旋。

先来讲解左右双旋

 

那如果我们复用之前的左右函数就会出现一个问题,parent 和sub*这两个节点的_bf设置为0 如上图 parent的_bf是1 ,subl是0。这时又有三种情况。

第一种情况就入上图所示。

第二种情况 新增节点插入到C这个子树 那么parent的_bf就是0 subl的_fd为-1。

第三种情况 如果h等于0那60就是新增节点,它们的_bf为0没错。

else if (parent->_bf == -2 && cur->_bf == 1) //先左再右{RotateLR(parent);}

void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}

右左双旋 

 

第一种情况就入上图所示。

第二种情况 新增节点插入到b这个子树 那么parent的_bf就是0 subR的_fd为1。

第三种情况 如果h等于0那60就是新增节点,它们的_bf为0没错。

else if (parent->_bf == 2 && cur->_bf == -1) //先右再左{RotateRL(parent);}

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

代码 我是单独拆分了,直接复制可能会报错,为了大家好理解我做成模块化!

这里强调的是 其实AVL树,我们根本就不需要手撕,前人已经帮我们造好轮子了,我们自己根本就不需要自己造轮子。主要是画图理解旋转的过程。 

要看代码的完整性可以去看我的码云 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/810998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[大模型]Qwen1.5-7B-Chat FastApi 部署调用

Qwen1.5-7B-Chat FastApi 部署调用 环境准备 在 Autodl 平台中租赁一个 3090 等 24G 显存的显卡机器&#xff0c;如下图所示镜像选择 PyTorch–>2.0.0–>3.8(ubuntu20.04)–>11.8&#xff08;11.3 版本以上的都可以&#xff09;。 接下来打开刚刚租用服务器的 Jupyt…

vivado 使用 ILA 默认仪表板

使用 ILA 默认仪表板 ILA 仪表板 &#xff08; 请参阅下图 &#xff09; 是给定 ILA 核相关的所有状态和控制信息的集中显示位置。刷新硬件器件并首次检测到 ILA 核时 &#xff0c; 将自动打开该核的默认 ILA 仪表板。如果需要手动打开或重新打开此仪表板 &#xff0c; 只…

java之编译型多态

在Java中方法重载就属于编译型多态&#xff0c;又称静态多态。 举个例子&#xff0c;动物园中饲养员需要知道每个动物应该吃什么饲料&#xff0c;例如熊猫吃竹子&#xff0c;狗吃骨头&#xff0c; 对于饲养员而言他只要对程序输入动物名&#xff0c;程序输出该动物吃什么即可…

性能优化 - 你知道CSS有哪些优化方案吗

难度级别:中高级及以上 提问概率:70% CSS是前端开发工作中必不可少的技能之一,同时也是网页开发中必不可少的重要元素之一。但很多人所开发的项目本身对性能要求并不高,再加上项目周期紧张,久而久之,也就容易养成不考虑细节的习惯,觉得C…

jquery 正则自整理

常用正则表达式大全&#xff01;&#xff08;例如&#xff1a;匹配中文、匹配html&#xff09; 匹配中文字符的正则表达式&#xff1a; [u4e00-u9fa5]   评注&#xff1a;匹配中文还真是个头疼的事&#xff0c;有了这个表达式就好办了   匹配双字节字符(包括汉字在内)&…

C++ 的内存安全与效率

在C编程中&#xff0c;内存安全和效率是两个至关重要的考虑因素。 内存安全涉及确保程序在分配和使用内存时不会发生错误&#xff0c;如内存泄漏、悬挂指针、越界访问、空指针解引用等&#xff1b; 效率则关注如何有效地使用内存资源&#xff0c;减少不必要的内存分配和释放操…

前端说你的API接口太慢了,可是真的有几千万条数据啊!怎么办?

当有千万条海量数据时,前端调取接口发现接口响应的太慢,前端这时让你优化一下接口,你说有几千万条数据,觉得自己尽力了,前端觉得你好菜,别急,读完这篇文章,让前端喊你一声:大佬,厉害!!! 常用的方法总结 通过合理的分页加载、索引优化、数据缓存、异步处理、压缩…

每日OJ题_01背包①_牛客DP41 【模板】01背包(滚动数组优化)

目录 牛客DP41 【模板】01背包 问题一解析 问题二解析 解析代码 滚动数组优化代码 牛客DP41 【模板】01背包 【模板】01背包_牛客题霸_牛客网 #include <iostream> using namespace std;int main() {int a, b;while (cin >> a >> b) { // 注意 while 处…

软件杯 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 前言1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 该项目较为新颖&#xff0c;适合作为竞…

二叉树学习

树 树是n个结点的有限集合&#xff0c;当n0时为空树&#xff0c;在任意一颗非空的树中&#xff0c;有且只有一个特定的称为根的结点&#xff0c;当n>1时&#xff0c;其余结点又可以分为m个不相交的有限集&#xff0c;其中每一个集合又是一棵树&#xff0c;并且称为根的子树…

解决MySQL错误:`ERROR 1049 (42000): Unknown database ‘nonexistentdb‘`

在管理MySQL数据库的过程中&#xff0c;我们可能会遇到各种各样的错误信息&#xff0c;这些错误信息有助于我们快速定位并解决问题。本文将深入探讨一个特定的错误——ERROR 1049 (42000): Unknown database nonexistentdb&#xff0c;这个错误会在尝试连接到MySQL服务器上不存…

【Java探索之旅】从输入输出到猜数字游戏

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、输入输出1.1 输出到控制台1.2 从键盘输入 二、猜数字游戏2.1 所需知识&#xff1a…

《猎灵online》游戏完整源码(源码+客户端+服务端+文档+工具),云盘下载

《猎灵》是一款由国内知名开发运营开发的大型3D魔幻网游&#xff0c;《猎灵》研发团队突破诸多瓶颈&#xff0c;首创“全自由无限制PK”&#xff0c;让玩家拒绝无意义等待&#xff0c;自由作战不受任何束缚&#xff0c;真正的实现想战就战&#xff0c;游戏创建了六界神魔乱斗的…

Amazon SageMaker:让机器学习变得更简单、更强大

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 前言&#xff1a; 在大数据时代的浪潮中&#xff0c;数据不再只是…

Anaconda的常用指令

一、conda基础命令 ① 查看conda帮助信息 conda --help # 或者&#xff1a; conda -h ② 查看conda版本 conda --version ③ 更新conda conda update conda ④ 降级conda版本 conda install -n base conda4.6.7 ⑤ 升级conda和anaconda conda update conda conda up…

Python神器!WEB自动化测试集成工具 DrissionPage

案例 跟踪商品价格&#xff0c;降价自动推送消息到微信 咱买不起还等不起吗&#xff1f; from DrissionPage import * import re from time import sleep import csv import os import datetime#写入时间p MixPage() p.get(http://xxxxxxx) #快快买网址 p.to_iframe(iframe…

云服务器centos提示 Cannot prepare internal mirrorlist: No URLs in mirrorlist的解决办法

yum update -y CentOS-8 - AppStream 118 B/s | 38 B 00:00 Error: Failed to download metadata for repo AppStream: Cannot prepare internal mirrorlist: No URLs in mirrorlist 执行下面的命令就可…

算法-快速幂

算法-快速幂 时间复杂度 O(logk) //求 m^k mod p int qmul(int m,int k,int p) {int res1%p;while(k){if(k&1){res*m;res%p;}m*m;m%p;k>>1;}return res; }

出海业务的网络安全挑战

出海业务的扩展带来了巨大的市场机遇&#xff0c;同时也带来了不少网络安全挑战&#xff1a; 数据泄露与隐私保护&#xff1a;跨境数据传输增加了数据被截获和泄露的风险。地理位置限制和审查&#xff1a;某些地区的网络审查和地理位置限制可能阻碍企业正常开展业务。网络攻击…

【svg】—— java解析svg的宽高属性

SVG&#xff08;Scalable Vector Graphics&#xff09;是一种基于XML的图像格式&#xff0c;用于描述二维矢量图形。用户在网页上展示高质量的矢量图形&#xff0c;svg图形可以无限放大或缩小而不会失真&#xff0c;保持清晰的边缘和线条。 java对于svg的处理其实比较麻烦&…