初识C++ · AVL树(1)

目录

前言:

1 AVL树的创建

2 部分成员函数

2.1 查找

2.2 中序遍历

2.3 插入

2.4 左旋转

2.5右旋转


前言:

上文,上上文提到了map set,二叉搜索树,其实都是为了近两文做铺垫的,虽然map的底层是红黑树,但是也为AVL树的学习打下了基础,那么什么是AVL树呢?由谁发明的呢?
两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis发明的,名字就是发明家的首字母咯,发明是用来干什么的呢?发明出来是为了解决当树的结构趋近于单链表时候,效率接近O(N)的问题,只要能有效降低高度,那么就能提高速度,所以AVL树,诞生了。

创建AVL树有很多种方式,我们本次介绍的是使用的平衡因子的概念,也就是每个节点都有个平衡因子,绝对值不能超过1,也就是取值范围是0 1 -1,如果超过2,也就代表树失衡了,需要进行旋转调整。

这里旋转右子树高度减左子树高度的值作为平衡因子的大小,当然,AVL树的满足条件就是,左右子树都是AVL树,并且每个节点的平衡因子都小于2。


1 AVL树的创建

AVL树的创建和二叉平衡搜索树是一样的,无非是每个节点的区别而已,前言,上文提及,节点涉及到的内容有,平衡因子,左右指针,key或者是key-value,这里我们使用key-value模型实现。

但是!当我们旋转的时候,涉及到的节点势必有某个节点的父节点,所以还有一个成员变量,指向父节点的指针,所以AVL树实际上是三叉链的结构:

template<class T, class V>
struct AVLTreeNode
{AVLTreeNode<T>* _left;AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;pair<T, V> _kv;int _bf;//平衡因子AVLTreeNode(const pair<T,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

这就是节点的创建。

那么AVL树的大体如下:

template<class T>
class AVLTree
{
public:typedef AVLTreeNode<T, V> Node;private:Node* _root = nullptr;
};

2 部分成员函数

这里要实现的成员函数有左旋,右旋,以及查找,插入(实现一半),以及中序遍历。

但是部分函数其实变化不打,改的都是细枝末节的东西,这里直接给代码即可:

2.1 查找

Node* Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;
}

2.2 中序遍历

	void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

2.3 插入

插入和二叉搜索树都是一样的,但是呢,插入完成之后,平衡因子怎么改变呢?

我们现在不妨来分析一下,平衡因子在插入后会有哪些改变?

当我们在8的左边插入数据后,8的平衡因子变为了0,此时,父节点的平衡因子就不用更新了,因为作为7的右子树来说,8这个子树的高度没有变。

当我们在6的任意部分插入数据,6的平衡因子变为了1或者是-1,此时,需要往上更新数据,7的平衡因子被更新为了0,那么不用往上了,所以此时我们就可以发现一个规律,基于原有的AVL树的基础上,插入数据之后平衡因子变为0的,都不用继续遍历了,因为平衡了,但是要注意是 基于原来的树就是AVL树。

当我们在9的右边插入数据,此时9的平衡因子变为了1,往上更新,8的平衡因子变为了2,那么就需要旋转了,此时是完全的右子树高,所以需要左旋转。

当我们在9的右边插入数据,此时9的平衡因子变为了1,往上更新,8的平衡因子变为了2,那么就需要旋转了,此时是完全的右子树高,所以需要左旋转。

当我们在0的左边边插入数据,此时0的平衡因子变为了-1,往上更新,1的平衡因子变为了-2,那么就需要旋转了,此时是完全的左子树高,所以需要右旋转。

所以插入这里要干的事是更新平衡因子,一直往上更新,直到出现0或者是更新到根节点位置,更新到根节点也就说明了父节点为空,就可以结束了,为0也可以直接结束:

bool Insert(const pair<T,V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node* root = _root;Node* parent = nullptr;//判断部分while (root){if (kv.first > root->_kv.first){parent = root;root = root->_right;}else if (val < root->_kv.first){parent = root;root = root->_left;}else{return false;}}Node* newnode = new Node(kv);//连接部分 开始判断大小关系if (parent->_key > kv.first){parent->_left = newnode;}else{parent->_right = newnode;}//更新平衡因子newnode->_parent = parent;while (parent){if (parent->_left == newnode){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){newnode = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//右单旋if (parent->_bf == -2 || newnode->_bf == -1){RotateR(parent);}//左单旋else if (parent->_bf == 2 || newnode->_bf == 1){RotateL(parent);}else if (parent->_bf == 2 || parent->_bf == -1){}else{}}else{//理论而言不可能出现这种情况assert(false);}}return true;
}

为了保险期间,出现了超过2的情况就要报错了。

现在的问题就是,面对完全的右子树高或者是完全的左子树高我们应该怎么操作?

2.4 左旋转

此时,这棵AVL树就不平衡了,那么我们可以从一个有趣的角度来看,7是当家的,8的二当家,6是三当家,当家的不管事,二当家一不小心做大做强了,那么当家的就得易主了是吧?所以7要下位了,此时8的左子树就应该指向7,那么7的右子树就应该指向8的左子树,空也没有关系。那么旋转完成了吗?没有,现在不够形象,换个结构,我们这样看:

旋转的核心就是这两条线的指向,二号指向7,一号指向7.5,这个过程大家脑部一下,就十分形象了。

但是不要忘了,AVL树实际上是三叉链结构,所以还有父节点需要更新,二号的节点如果不为空,父节点应该指向的是7,7如果不是根节点,那么我们还要用7的父节点作为连接的下一步,判断相对位置,让7的父节点连8,如果是根节点,那么根节点易位就可以了。此时连接的差不多了,但是涉及到的8的父节点还要连接,可能是空,也可能是7的父节点,这里代码给上,结合文字的说明,代码写的也是有区域性的,结合相对来说好理解许多:

void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;subR->_left = parent;parent->_right = subRL;parent->_parent = subR;//可能为空,比如接近单链表if (subRL){subRL->_parent = parent;}//旋转的时候判断是不是根节点if (parent == _root){_root = subR;subR->_parent = nullptr;}else{//根据位置进行连接Node* pparent = parent->_parent;if (parent == _root->_left){_root->_left = subR;}else{_root->_left = subRL;}//三叉链记得更新完subR->_parent = _pparent;}//更新平衡因子parent->_bf = subR->_bf = 0;
}

2.5右旋转

右旋转的是同理的,就直接给代码了:

void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;subL->_right = parent;parent->_left = subLR;parent->_parent = subL;if (subLR)subLR->_parent = parent;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{Node* pparent = parent->_parent;if (parent == _root->_left){_root->_left = subL;}else{_root->_right = subL;}subL->_parent = pparent;}//更新平衡因子parent->_bf = subL->_bf = 0;
}

以上两种情况是完全的左右子树高,所以只需要一个左旋或者是右旋,下篇是,复合旋转!

至于为什么平衡因子旋转之后一定为0,请见下文~


感谢阅读!

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

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

相关文章

openFeign配置okhttp

原来的项目出现了性能问题&#xff0c;老大不知道怎么的&#xff0c;让我改openFeign线程池为okhttp&#xff0c;说原生的不支持线程池性能比较差。 原openFeign配置文章地址 一、pom文件 <dependency><groupId>org.springframework.cloud</groupId><arti…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的跳格子游戏(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

手写spring简易版本,让你更好理解spring源码

首先我们要模拟spring&#xff0c;先搞配置文件&#xff0c;并配置bean 创建我们需要的类&#xff0c;beandefito&#xff0c;这个类是用来装解析后的bean&#xff0c;主要三个字段&#xff0c;id&#xff0c;class&#xff0c;scop&#xff0c;对应xml配置的属性 package org…

第二讲:NJ网络配置

Ethernet/IP网络拓扑结构 一. NJ EtherNet/IP 1、网络端口位置 NJ的CPU上面有两个RJ45的网络接口,其中一个是EtherNet/IP网络端口(另一个是EtherCAT的网络端口) 2、网络作用 如图所示,EtherNet/IP网络既可以做控制器与控制器之间的通信,也可以实现与上位机系统的对接通…

MySQL --- 表的操作

在对表进行操作时&#xff0c;需要先选定操作的表所在的数据库&#xff0c;即先执行 use 数据库名; 一、创建表 create table 表名( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎 ; 说明&#xff1a…

从零入门 AI for Science(AI+药物) #Datawhale AI 夏令营

使用平台 我的Notebook 魔搭社区 https://modelscope.cn/my/mynotebook/preset 主要操作 运行实例&#xff0c;如果有时长尽量选择方式二&#xff08;以下操作基于方式二的实例实现&#xff09; 创建文件夹&#xff0c;并重命名为 2.3siRNA 上传两个文件 到文件夹&#…

BGP路由反射器

原理概述 缺省情况下&#xff0c;路由器从它的一个 IBGP对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体&#xff0c;这个原则称为BGP水平分割原则&#xff0c;该原则的根本作用是防止 AS内部的BGP路由环路。因此&#xff0c;在AS内部&#xff0c;一般需要每台…

【Android】数据存储方案——文件存储、SharedPreferences、SQLite数据库用法总结

文章目录 文件存储存储到文件读取文件 SharedPreferences存储存储获取SharedPreferences对象Context 类的 getSharedPreferences() 方法Activity 类的 getPreferences() 方法PreferenceManager 类中的 getDefaultSharedPreferences() 方法 示例 读取记住密码的功能 SQLite数据库…

4.Java Web开发模式(javaBean+servlet+MVC)

Java Web开发模式 一、Java Web开发模式 1.javaBean简介 JavaBeans是Java中一种特殊的类&#xff0c;可以将多个对象封装到一个对象&#xff08;bean&#xff09;中。特点是可序列化&#xff0c;提供无参构造器&#xff0c;提供getter方法和setter方法访问对象的属性。名称中…

JAVA代码审计JAVA0基础学习(需要WEB基础知识)DAY2

JAVA 在 SQL执行当中 分为3种写法&#xff1a; JDBC注入分析 Mybatis注入分析 Hibernate注入分析 JDBC 模式不安全JAVA代码示例部分特征 定义了一个 sql 参数 直接让用户填入id的内容 一个最简单的SQL语句就被执行了 使用安全语句却并没有被执行 Mybatis&#xff1a; #…

【MetaGPT系列】【MetaGPT完全实践宝典——多智能体实践】

目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent&#xff08;ProductManager&#xff09;2-3、多智能体系统介绍2-4、多智能体案例分析2-4-1、构建智能体团队2-4-2、动作/行为 定义2-4-3、角色/智…

【OpenCV C++20 学习笔记】调节图片对比度和亮度(像素变换)

调节图片对比度和亮度&#xff08;像素变换&#xff09; 原理像素变换亮度和对比度调整 代码实现更简便的方法结果展示 γ \gamma γ校正及其实操案例线性变换的缺点 γ \gamma γ校正低曝光图片矫正案例代码实现 原理 关于OpenCV的配置和基础用法&#xff0c;请参阅本专栏的其…

五、工厂方法模式

文章目录 1 基本介绍2 案例2.1 Drink 抽象类2.2 Tea 类2.3 Coffee 类2.4 DrinkFactory 抽象类2.5 TeaFactory 类2.6 CoffeeFactory 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Product ( 抽象产品 )3.1.2 ConcreteProduct ( 具体产品 )3.1…

生物信息学新突破:在英特尔 Gaudi 2 上实现 ProtST 蛋白质语言模型加速

引言 随着人工智能技术的快速发展&#xff0c;蛋白质结构预测和语言模型在生物信息学领域扮演着越来越重要的角色。ProtST作为一种新兴的蛋白质语言模型&#xff0c;其性能在英特尔 Gaudi 2 加速器的助力下得到了显著提升。本文将探讨如何利用英特尔 Gaudi 2 加速 ProtST 模型…

哈希表相关的力扣题和讲解和Java、C++常用的数据结构(哈希法)

20240725 一、什么时候适用什么样的结构。1.java中1.1 HashSet&#xff1a;1.2 TreeSet&#xff1a;1.3 LinkedHashSet&#xff1a;1.4 HashMap&#xff1a;1.5 TreeMap&#xff1a;1.6 LinkedHashMap&#xff1a;1.7 总结 2. c中2.1 std::unordered_set&#xff1a;2.2 std::s…

项目实战——外挂开发(30小时精通C++和外挂实战)

项目实战——外挂开发&#xff08;30小时精通C和外挂实战&#xff09; 外挂开发1-监控游戏外挂开发2-秒杀僵尸外挂开发3-阳光地址分析外挂开发4-模拟阳光外挂开发5-无限阳光 外挂开发1-监控游戏 外挂的本质 有两种方式 1&#xff0c;修改内存中的数据 2&#xff0c;更改内存中…

谷粒商城实战笔记-54-商品服务-API-三级分类-拖拽效果

文章目录 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果1&#xff0c;el-tree控件加上允许拖拽的属性2&#xff0c;是否允许拖拽3&#xff0c;完整代码 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果 本节的主要内容是给三级分类树形结构加上拖拽功能&#…

Mysql 集群搭建 05

文章目录 1. Mysql主从复制集群搭建1.1 主库配置1.2 从库配置 2. 分库分表2.1 拆分策略2.2 实现技术2.2.1 MyCat概述2.2.2 MyCat入门2.2.3 配置 schema.xml 3. 双主双从4. 双主双从读写分离 1. Mysql主从复制集群搭建 主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志…

VMware Cloud Foundation ESXi 主机

一、准备嵌套 ESXi 主机环境# 1)物理 ESXi 主机信息 本次准备用于部署 VCF 嵌套实验环境的物理宿主机的配置信息如下图所示。其实,部署 VCF 环境主要对内存的大小要求比较高,部署完整的管理域相关组件下来差不多就要占用 200 GB左右内存,而对 CPU 和存储的需求可以根据实…

Pytorch使用教学8-张量的科学运算

在介绍完PyTorch中的广播运算后&#xff0c;继续为大家介绍PyTorch的内置数学运算&#xff1a; 首先对内置函数有一个功能印象&#xff0c;知道它的存在&#xff0c;使用时再查具体怎么用其次&#xff0c;我还会介绍PyTorch科学运算的注意事项与一些实用小技巧 1 基本数学运算…