C++进阶之路---手把手带你学习AVL树

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、AVL树的概念

       二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:

       当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

       一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

       1.它的左右子树都是AVL树

       2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

       如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在(log_2 n),搜索时间复杂度O(log_2 n)。 


二、AVL树的旋转

       如果在一颗原本平衡的AVL树插入新节点后,平衡因子可能会发生变化,从而使绝对值大于1,所以就需要旋转去调整树的结构,使之平衡化,而根据节点的不同,旋转就有4中情况。

1.左单旋

实现图解:

代码实现:

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

2.右单旋

实现图解:

代码实现:

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

3.左右双旋

       将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。

实现图解:

代码实现:

void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){// subLR自己就是新增parent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == -1){// subLR的右子树新增parent->_bf = 0;subLR->_bf = 0;subL->_bf = 1;}else if (bf == 1){// subRL的左子树新增parent->_bf = -1;subLR->_bf = 0;subL->_bf = 0;}else{assert(false);}
}

4.右左双旋

       具体实现参考左右双旋。

实现图解:

代码实现:

void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){// subRL自己就是新增parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == -1){// subRL的左子树新增parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 1){// subRL的右子树新增parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else{assert(false);}
}

5.旋转总结 

       假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑:

    1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR

       当pSubR的平衡因子为1时,执行左单旋

       当pSubR的平衡因子为-1时,执行右左双旋

    2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL

       当pSubL的平衡因子为-1是,执行右单旋

       当pSubL的平衡因子为1时,执行左右双旋

    旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。


三、AVL树的基本实现

1.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; // balance factorAVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};

2.AVL树的插入实现

class AVLTree
{typedef AVLTreeNode<K, V> Node;
public: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;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}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 = 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){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}// 1、旋转让这颗子树平衡了// 2、旋转降低了这颗子树的高度,恢复到跟插入前一样的高度,所以对上一层没有影响,不用继续更新break;}else{assert(false);}}return true;}
private:Node* _root=nullptr;
};

四、AVL树的性能

       AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log2n。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。


预告:AVL树固然nb,但是红黑树更强!如果说发明AVL树的人是周瑜,那么发明红黑树的人就是诸葛亮。下篇文章带你学习红黑树。


结语:C++关于如何实现AVL树的分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

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

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

相关文章

图像处理与视觉感知---期末复习重点(3)

文章目录 一、空间域和频率域二、傅里叶变换三、频率域图像增强 一、空间域和频率域 1. 空间域&#xff1a;即所说的像素域&#xff0c;在空间域的处理就是在像素级的处理&#xff0c;如在像素级的图像叠加。通过傅立叶变换后&#xff0c;得到的是图像的频谱&#xff0c;表示图…

【深度学习笔记】9_9 语义分割和数据集

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 9.9 语义分割和数据集 在前几节讨论的目标检测问题中&#xff0c;我们一直使用方形边界框来标注和预测图像中的目标。本节将探讨语义分…

深度学习基础知识之Atrous卷积(空洞卷积)

太久不看代码确实生疏了&#xff0c;盯着一堆不同的dilation&#xff0c;不知道有什么作用&#xff0c;论文中说是Atrous卷积&#xff0c;原来就是空洞卷积的意思。 Dilated/Atrous Convolution 空洞卷积&#xff08;膨胀卷积/扩张卷积&#xff09; 空洞卷积是一种不增加参数量…

Web 服务器-Tomcat

文章目录 Web服务器一、Tomcat简介二、基本使用三、在IDEA中创建Maven Web项目四、在IDEA中使用Tomcat Web服务器 一、Tomcat简介 二、基本使用 三、在IDEA中创建Maven Web项目 四、在IDEA中使用Tomcat

外包干了9天,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2018年我通过校招踏入了南京一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

【C#】.net core 6.0 使用第三方日志插件Log4net,日志输出到控制台或者文本文档

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

关于原型的一些总结

猛然发现太久没去复习了&#xff0c;于是复习了一些知识&#xff0c;顺便冒个泡。本次主要总结的知识点关于原型&#xff0c;再文章后半部分有原型相关的题&#xff0c;感兴趣的可直接观看。 一、原型 1.什么是原型 简单理解&#xff0c;原型就是一个对象&#xff0c;通过原…

Linux基础 想学好Linux请看这篇文章 Linux操作指令大全

当涉及学习 Linux 时&#xff0c;了解其基本原理和核心概念是至关重要的。Linux 是一种开源操作系统&#xff0c;广泛应用于服务器、嵌入式系统以及个人计算机中。它的灵活性、稳定性和安全性使得它成为许多 IT 专业人士和开发人员的首选。 第一步&#xff1a;了解基本概念和特…

CC连接过程

1、CC线连接过程 DFP和UFP会实时监控CC1和CC2引脚的电压&#xff0c;来评估DFP和UFP是否都已经在位。同时DFP可以根据电压确定自己所能提供的电流的大小 2、连接过程 Source端使用一个MOS管去控制Vbus&#xff0c;初始状态下&#xff0c;FET为关闭状态&#xff0c;Vbus不通。S…

苍穹外卖问题记录(持续更新)

Day01_3.2.4前后端联调 1. 前端无法登录 &#xff08;1&#xff09;确保nginx服务器已经启动 &#xff08;2&#xff09;查看自己数据库的用户名和密码是否和老师的一样&#xff0c;不一样的话需要在application-dev.yml文件中把老师的用户名密码修改成自己的 老师的用户名…

单⽬相机成像过程_看这一篇就够了

单⽬相机成像过程:看这一篇就够了 附赠宝贵的全套自动驾驶学习资料&#xff1a; 资料链接 附赠宝贵的全套自动驾驶学习资料&#xff1a; 资料链接

开发反应式API

开发反应式API 开发反应式API1 使用SpringWebFlux1.1 Spring WebFlux 简介1.2 编写反应式控制器 2 定义函数式请求处理器3 测试反应式控制器3.1 测试 GET 请求3.2 测试 POST 请求3.3 使用实时服务器进行测试 4 反应式消费RESTAPI4.1 获取资源4.2 发送资源4.3 删除资源4.4 处理错…

107. 如何使用Docker以及Docker Compose部署Go Web应用

文章目录 一、为什么需要Docker&#xff1f;二、Docker部署示例1. 准备代码2. 创建Docker镜像3. 编写Dockerfile4. Dockerfile解析5. 构建镜像6. 通过镜像创建容器运行 三、分阶段构建示例四、附带其他文件的部署示例五、关联其他容器六、Docker Compose模式七、总结 本文将介绍…

49、东北大学、阿尔伯塔大学:MVS-GCN多视角脑区、具有先验脑结构学习的图模型[GCN六元理论识别所有EEG!]

本文由东北大学医学图像智能计算教育部重点实验室&#xff0c;加拿大阿尔伯塔大学于2022年1.19日发表于<Computers in Biology and Medicine> JCR\IF: Q1\7.7 Abstract&#xff1a; 目的:近年来&#xff0c;脑功能网络(FBN)已被用于神经系统疾病的分类&#xff0c;如自…

力扣题目训练(21)

2024年2月14日力扣题目训练 2024年2月14日力扣题目训练605. 种花问题617. 合并二叉树628. 三个数的最大乘积289. 生命游戏299. 猜数字游戏149. 直线上最多的点数 2024年2月14日力扣题目训练 2024年2月14日第二十一天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;…

【智能算法】人工水母搜索算法(JS)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2020年&#xff0c;Chou 等人受到水母运动行为启发&#xff0c;提出了人工水母搜索算法(Artificial Jellyfish Search Optimizer, JS)。 2.算法原理 2.1算法思想 JS模拟了水母的搜索行为&#xf…

关于OPC-UA客户端调用服务端方法CallMethod节点的问题

在OpcUaClient中可以通过CallMethodByNodeId调用方法节点 //// 摘要:// call a server method//// 参数:// tagParent:// 方法的父节点tag//// tag:// 方法的节点tag//// args:// 传递的参数//// 返回结果:// 输出的结果值public object[] CallMetho…

栈与队列|232.用栈实现队列

力扣题目链接 class MyQueue { public:stack<int> stIn;stack<int> stOut;/** Initialize your data structure here. */MyQueue() {}/** Push element x to the back of queue. */void push(int x) {stIn.push(x);}/** Removes the element from in front of que…

数据结构--线性表

1.线性表的定义&#xff1a; 存在唯一的一个被称为“第一个”的数据元素&#xff1b; 存在唯一的一个被称为“最后一个”的数据元素&#xff1b; 除第一个之外&#xff0c;集合中的每一个数据元素都只有一个前驱&#xff1b; 除最后一个之外&#xff0c;集合中的每一个数据…

大话设计模式——7.抽象工厂模式(Abstract Factory Pattern)

1.介绍 抽象工厂模式是工厂模式的进一步优化&#xff0c;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。属于创建型模式。 UML图&#xff1a; 2.示例 车辆制造工厂&#xff0c;不仅可以制造轿车也可以用来生产自行车。 1&#xff09;Abs…