手撕AVL树(map和set底层结构)(1)

troop主页
今日鸡汤:Action may out always bring happiness;but there is no happiness without action.
行动不一定能带来快乐,但不行动一定不行
C++之路还很长

手撕AVL树

  • 一 AVL树概念
  • 二 模拟实现AVL树
    • 2.1 AVL节点的定义
  • 三 插入
    • 更新平衡因子(重点)
  • 四 旋转
    • 1.左单旋
    • 1.1 左单旋完整代码
    • 2 右单旋
    • 2.2 右单旋完整代码
    • 3 双旋一(左+右)
    • 3.2左右双旋完整代码
    • 4 双旋二(右+左)
    • 4.2 右左双旋完整代码
  • 旋转总结
  • 五 验证AVL树的正确性

一 AVL树概念

二叉搜索树虽然可以缩短查找的效率,但是当数接近有序或者二叉搜索数接近单支树,查找的效率就相当于在顺序表中查找。所以为了解决这种极端环境,,两位俄罗斯的数学家G.M.Adelson-Velskii
和E.M.Landis在1962年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树的性质

  1. 他的左右子树都是AVL树
  2. 左右子树的高度差(平衡因子)的绝对值不超过1 在这里插入图片描述
    注(以下代码中,平衡因子=|右子树高度-左子树高度|

二 模拟实现AVL树

2.1 AVL节点的定义

template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance factorpair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}
};

三 插入

我们这里AVL只写插入,插入就可以让我们很好的了解AVL的底层实现了。
AVL树也是二叉搜索树,只是在此基础上增加了平衡因子的调整。所以我们的插入就分成了两部分。

  1. 按照二叉搜索树的规则插入
  2. 更新平衡因子
	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;

这个就是我们前面已经说过的二叉搜索树的插入规则。现在我们重点看如何更新平衡因子。

更新平衡因子(重点)

平衡因子更新原则:

  • cur插入在parent的左边 平衡因子减减
  • cur插入在parent的右边 平衡因子加加

思考:插入节点后会影响哪些节点的平衡因子?
会影响新插入节点的部分祖先。
是否影响爷爷节点取决于parent的高度是否有变化
首先父亲节点一定会被影响,其次重点考虑的应该是爷爷节点所受的影响。这里比较抽象我们需要借助图像来把每一种可能写出来。
第一种更新后p->_bf0
在这里插入图片描述
这种就是更新之前p的高度为1or-1,新节点插入在了比较矮的那一端,左右平衡。
第二种更新后p->_bf
1or-1
在这里插入图片描述

更新之前,p的高度平衡,cur插入在一侧,p不平衡了,这里p的高度变化爷爷节点也受到了影响,就需要向上更新。
第三种更新后p->_bf==2or-2
在这里插入图片描述
违反了AVL树的规则要进行旋转。
我们现在总结一下,什么情况下更新就结束了。

1.插入后父亲的平衡因子为0,更新结束
2.向上更新到,cur=root的位置时,更新结束
3.违反规则需要旋转,旋转之后,更新结束

		//调整平衡因子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 = 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{RotateRL(parent);}break;}//插入树之前这个树就不符合AVL树else{// 插入之前AVL树就有问题assert(false);}

这部分代码还比较简单,下面就是本篇核心(旋转)

四 旋转

旋转的目的

  • 保持搜索树规则
  • 当先树由不平衡转变为平衡
  • 降低树的高度

1.左单旋

在这里插入图片描述
根据上面的图我们写出下代码。

Node* subR=panret->_right;
Node* subRL=subR->_left;parent->_right=subRl;
subRL->_parent=parent;subR->_left=parent;
parent->_parent=subR;

这里还有几个细节问题需要注意
第一点:subRL可能为空,那subRL->_parent=parent;就会有问题。我们要加一个条件判断。
第二点:subR的父亲节点还没有被重新指向,这就会导致下图
在这里插入图片描述
我们就要先保存parent的父亲节点,这又有了一个新的问题,就是parent是不是根节点,所以左单旋的完整代码如下

1.1 左单旋完整代码

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

2 右单旋

右单旋就是左旋的变形,理解好左旋,右旋就很好理解了。
在这里插入图片描述
看图写出代码,再根据左旋的注意事项,补全代码的逻辑。

2.2 右单旋完整代码

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}subL->_bf = 0;parent->_bf = 0;}

3 双旋一(左+右)

在这里插入图片描述

注意看图,先对subL进行了左单旋,再对整棵树进行了右单旋。
注意:双旋对比单旋多了旋转结束之后平衡因子不是固定的,我们要风分情况把所有的可能性都写出来。
在这里插入图片描述
观察上图,我们发现subRL的平衡因子不同分别为:-1 1 0,这就是我们的解决方案。我们再画出旋转之后的图片。
在这里插入图片描述
分析完之后,就到了最简单的代码环节

3.2左右双旋完整代码

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

4 双旋二(右+左)

在这里插入图片描述

还是先画出一般图观察,对subR进行右旋,再对整体左旋。
下面的分析与上面的分析类似,我就把图片放在下面,供大家参考。
在这里插入图片描述

4.2 右左双旋完整代码

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

现在再去看上面的更新平衡因子的代码就比较清晰了。

旋转总结

左旋:新节点插入了较高子树的
右旋:新节点插入了较高子树的
双旋:
左+右:新节点插入了较高子树的
右+左:新节点插入了较高子树的

总之一句话:理解旋转我们一定要自己去画图,一定要自己动手,才会理解深刻。

五 验证AVL树的正确性

我们要写一个函数来判断这颗树符不符合AVL树。

	bool _IsBalance(Node* root, int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0, rightHeight = 0;if (!_IsBalance(root->_left, leftHeight)|| !_IsBalance(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout << root->_kv.first << "不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;return true;}

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

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

相关文章

SpringBoot学习之Kafka下载安装和启动【Mac版本】(三十三)

一、配置Java环境变量 在启动Kafka之前,你需要先正确配置好你的Java环境变量。可以在终端输入java -version检查java环境变量是否配置正确,在Mac上如何配置java环境变量,请读者自行网上搜索操作之,此处不赘叙。 二、下载安装Kafka 1、下载Kafka:Apache Kafka,这两个版本…

四川赢涟电子商务有限公司深耕抖音电商服务

在当今数字化、网络化高速发展的时代&#xff0c;电子商务行业异军突起&#xff0c;成为推动经济增长的重要力量。四川赢涟电子商务有限公司凭借其敏锐的市场洞察力和创新精神&#xff0c;专注于抖音电商服务&#xff0c;致力于为广大消费者提供便捷、高效、个性化的购物体验&a…

Paddle 1.8 与 Paddle 2.0 API 映射表

安装2.6的paddlepaddle之后总是报fluid的错误&#xff0c;查询得知这个接口已经弃用了&#xff0c;但是一直找不到替换接口&#xff0c;偶然查询报错信息的时候找到了映射表&#xff0c;转存一下。 Paddle 1.8 与 Paddle 2.0 API 映射表

如何优雅地Spring事务编程

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 在开发中&#xff0c;有时候我们需要对 Spring 事务的生命周期进行监控&#xff0c;比如在事务提交、回滚或挂起时触发特定的逻辑处理。那么如何实现这种定制化操作呢&am…

直播报名 | 科技出海新势力,遥感+AI助力一带一路

遥感技术的出海之路顺畅吗&#xff1f; 国内外遥感应用的关注点相同吗&#xff1f; 目前珈和主要辐射哪些海外国家&#xff1f; … 上周数据赋农季第三期《科技出海&#xff0c;遥感AI赋能“一带一路”提升种植园规模效益》直播预告一出&#xff0c;小伙伴们纷纷来咨询珈和的海…

CentOS安装htop工具

启用 EPEL Repository 安装Htop 首先启用 EPEL Repository: yum -y install epel-release启用 EPEL Repository 后, 可以用 yum 直接安裝 Htop: 安装htop yum -y install htop安装成功 输入htop使用工具 htop安装glances工具 yum install glances

Springboot+Vue项目-基于Java+MySQL的企业客户管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

前端零代码开发实践:页面嵌套+逻辑连线0开发扩展组件,实现切换开关控制扇叶转动。能无代码封装扩展组件,有别于常规的web组态或低代码平台

前言&#xff1a; 官网:http://www.uiotos.net/ 什么是 UIOTOS&#xff1f; 这是一款拥有独创专利技术的前端零代码工具&#xff0c;专注于解决前端界面开发定制难题&#xff0c;原型即应用&#xff01;具有页面嵌套、属性继承、节点连线等全新特性&#xff0c;学习门槛低…

网络安全之SQL注入及防御(下篇)

目录 什么是SQL注入&#xff1f; 一&#xff0c;SQL注入会导致什么呢&#xff1f; 二&#xff0c;SQL注入思想与步骤 三&#xff0c;SQL注入的绕过 四&#xff0c;sqlmap工具的使用 五&#xff0c;sql注入的防御方法 总结 什么是SQL注入&#xff1f; SQL注入&#xff08;…

第二证券|股票做短线要关注什么?

在股市中短线交易因其快速的盈利时机而招引了众多投资者&#xff0c;但做短线想要挣钱也不是那么容易的。对于股票做短线要重视什么&#xff0c;第二证券下面就为我们具体介绍一下。 短线交易需重视&#xff1a; 1、商场短期趋势。短线投资者首先需要重视的是全体商场趋势&am…

tokio多任务绑定cpu(绑核)

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f; 首先我们先写一段简单的多任务程序。 use tokio; use tokio::runtime; use core_affinity;fn tokio_sample() {let rt runtime::Builde…

【软考】UML中的关系

目录 1. 说明2. 依赖3. 关联4. 泛化5. 实现 1. 说明 1.UML中有4种关系&#xff1a;依赖、关联、泛化和实现2.这 4种关系是 UML,模型中可以包含的基本关系事物。它们也有变体&#xff0c;例如&#xff0c;依赖的变体有精化、跟踪、包含和延伸 2. 依赖 1.依赖(Dependency)。2.…

【InternLM实战营---第六节课笔记】

一、本期课程内容概述 本节课的主讲老师是【樊奇】。教学内容主要包括以下三个部分&#xff1a; 1.大模型智能体的背景及介绍 2. Lagent&AgentLego框架介绍 3.Lagent&AgentLego框架实战 二、学习收获 智能体出现的背景 智能体的引入旨在克服大模型在应对复杂、动态任…

【设计模式】使用中介者模式优化表单交互

我们想象一下机场的指挥塔&#xff0c;如果没有指挥塔的存在&#xff0c;每一架飞机要和方圆 100 公里内的所有飞机通信&#xff0c;才能确定航线以及飞行状况&#xff0c;后果是不可想象的。现实中的情况是&#xff0c;每架飞机都只需要和指挥塔通信。指挥塔作为调停者&#x…

93页 | 数据中台标准技术体系方案(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 数据中台标准技术体系方案 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT解决方案&#…

文件上传漏洞-白名单检测

如何确认是否是白名单检测 上传一张图片与上传一个自己构造的后缀&#xff0c;如果只能上传图片不能上传其它后缀文件&#xff0c;说明是白名单检测。 绕过技巧 可以利用 00 截断的方式进行绕过&#xff0c;包括 %00 截断与 0x00 截断。除此之外如果网站存在文件包含漏洞&…

ElasticSearch总结二

正向索引和倒排索引&#xff1a; 正向索引&#xff1a; 比方说我这里有一张数据库表&#xff0c;那我们知道对于数据库它一般情况下都会基于i d去创建一个索引&#xff0c;然后形成一个b树。 那么你根据i d进行检索的速度&#xff0c;就会非常的快&#xff0c;那么这种方式的…

智慧水生态系统的架构设计与优化:内蒙古硕达智水百数低代码平台的实践

随着业务的不断扩展&#xff0c;传统的项目管理方式已无法满足现代水生态项目的需求。为提高项目管理和决策效率&#xff0c;内蒙古硕达智水生态科技决定引入百数作为其数字化转型的合作伙伴。 内蒙古硕达智水生态科技有限公司&#xff1a; 内蒙古硕达智水生态科技有限公司&a…

用于割草机器人,商用服务型机器人的陀螺仪

介绍一款EPSON推出适用于割草机器人&#xff0c;商用服务型机器人的高精度陀螺仪模组GGPM61&#xff0c;具体型号为GGPM61-C01。模组GGPM61是一款基于QMEMS传感器的低成本航向角输出的传感器模组&#xff0c;它可以输出加速度、角速度及姿态角等信息&#xff0c;为控制机器人运…

人工智能基础-Python之Pandas库教程

文章目录 前言一、Pandas是什么&#xff1f;二、使用步骤1.引入库2.数据读取2.1 数据类型2.2 数据读取1.常见操作2.txt读取 3.pandas的数据结构3.1 Series1.属性2.创建Series3.查询 3.2 DataFrame1.创建DataFrame 4.查询数据4.1 data.loc 根据行列标签值进行查询1.使用单个labe…