【C++进阶05】AVL树的介绍及模拟实现

在这里插入图片描述

一、AVL树的概念

二叉搜索树的缺点
二叉搜索树虽可以缩短查找效率
但如果数据有序或接近有序
二叉搜索树将退化为单支树
查找元素相当于在顺序表中搜索元素,效率低下

AVL树便是解决此问题

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

AVL树或空树
或是具有以下性质的二叉搜索树

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)
    的绝对值不超过1(-1/0/1)

AVL树不一定有平衡因子
平衡因子只是其中一种实现方式
在这里插入图片描述
如果一棵二叉搜索树是高度平衡的
它就是AVL树,如果它有n个结点
其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n)
搜索时间复杂度O( l o g 2 n log_2 n log2n)

二、AVL树实现的基本框架

2.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;// balance factor 平衡因子int _bf; // 构造函数AVLTreeNode(const pair<K, V>& kv), left(nullptr), right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};

2.2 AVL树的基本结构

template <class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
private:Node* _root = nullptr; // 根节点
};

2.3 AVL树的插入

AVL树插入步骤:

  1. 按二叉搜索树的方式插入新节点
  2. 更新平衡因子
  3. 若平衡因子失衡,需要旋转处理

平衡因子失衡后的旋转处理

  1. 更新完, 平衡因子没问题(|bf| <= 1)
    平衡因子结构未受影响, 不需要处理

  2. 更新完,平衡因子有问题(|bf| > 1)
    平衡结构受影响,需要处理(旋转)

原因:

插入新增节点
会影响祖先的平衡因子(全部或部分)
当前节点平衡因子等于
右树节点个数减左树节点个数

  1. cur == parent->right 则parent->bf++
  2. cur == parent->left 则parent->bf–

parent所在子树高度发生变化
则需要继续往上更新爷爷节点
否则就不更新

parent->bf == 1 || parent->bf == -1 
// 则说明parent所在子树变了, 继续更新

插入节点更新平衡因子后分为三种情况

  1. 插入前parent->bf == 0
    说明插入前左右两边高度相等
    插入后有一边高1
    说明parent一边高,一边低,高度变了

在这里插入图片描述
2.

parent->bf == 2 || parent->bf == -2

在这里插入图片描述

则说明parent所在子树不平衡
需要处理这颗子树(旋转处理)

  1. parent->bf == 0
    parent所在子树高度不变
    不用继续往上更新,这一次插入结束
    说明插入前parent->bf == 1 or -1
    插入前一边高,一边低
    插入节点填上矮的那边,高度不变

在这里插入图片描述

三、AVL树的旋转

旋转的原则:
保持它是搜索树

旋转的目的:

  1. 让这棵子树平衡
  2. 降低这棵子树的高度

左旋过程

  1. 30的左子树25变成20的右子树
  2. 20变成30的左子树
    30变成整棵树的根

实际旋转中的节点值可能不是这些值
但也是按这些点位去旋转的
在这里插入图片描述
根据节点插入位置的不同
AVL树的旋转分为四种:
1. 新节点插入较高左子树的左侧—左左:
右单旋
图中h为子树的高度
在这里插入图片描述

2. 新节点插入较高右子树的右侧—右右:
左单旋

在这里插入图片描述
3. 新节点插入较高左子树的右侧—左右:
先左单旋再右单旋

在这里插入图片描述

将双旋变成单旋后再旋转,即:
先对30进行左单旋
然后再对90进行右单旋

图中只展示了b插入引发双旋的场景
本质有三种引发双旋的场景:

  1. 在b插入,b的高度变化+1
  2. 在c插入,c的高度变化+1
  3. 60本身就是新增节点

旋转完成后再考虑平衡因子的更新
不同场景的插入,60的平衡因子也不同
分别为-1,1,0
且每种场景的插入旋转完后90和30的
平衡因子都不一样
代码的实现通过记录60这个点位的平衡因子
旋转完后
根据不同场景的插入更新90和30的平衡因子

4. 新节点插入较高右子树的左侧—右左:
先右单旋再左单旋

在这里插入图片描述
参考先左单旋再右单旋

四、插入代码的实现

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->_left = cur;}else{parent->_right = cur;}// new的节点的parent还指向空cur->_parent = parent;// 1. 更新平衡因子while (parent){if (cur == parent->_right){parent->_bf++;}else{parent->_bf--;}if (parent->_bf == 1 || parent->_bf == -1){// 继续更新parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 0){break;}else if (parent->_bf == 2 || parent->_bf == -2){// 需要旋转处理 --- 1. 让这棵子树平衡 2. 降低这棵子树的高度if (parent->_bf == 2 && cur->_bf == 1) // parent->right是cur{RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1) // parent->{RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{assert(false);}break; // 处理完,break,否则会一直循环}else{// 如果插入之前就有问题assert(false);}}return true;
}

五、AVL树旋转代码实现

void RotateL(Node* parent) // 左单旋
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) // subRL可能为空subRL->_parent = parent;// 旋转的不一定是整棵树Node* pparent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (pparent == nullptr){_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;
}void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* pparent = parent->_parent;subL->_right = parent;parent->_parent = subL;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 = subL->_bf = 0;
}void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);subLR->_bf = 0; // subLR的左一定等于0if (bf == 1){parent->_bf = 0;subL->_bf = -1;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subL->_bf = 0;}else{assert(false);}
}void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);subRL->_bf = 0;if (bf == 1){parent->_bf = -1;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;}else{assert(false);}
}

六、全部代码实现

AVL树模拟实现全部代码:gitee链接

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

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

相关文章

Java诊断利器Arthas

https://arthas.aliyun.com/doc/https://arthas.aliyun.com/doc/ 原理 利用java.lang.instrument(容器类) 做动态 Instrumentation(执行容器) 是 JDK5 的新特性。使用 Instrumentation&#xff0c;开发者可以构建一个独立于应用程序的代理程序&#xff08;Agent&#xff09;&…

汽车IVI中控开发入门及进阶(十二):手机投屏

前言: 汽车座舱有车载中控大屏、仪表/HUD多屏的显示能力,有麦克风/喇叭等车载环境更好的音频输入输出能力,有方控按键、旋钮等方便的反向控制输入能力,还有高精度的车辆数据等。但汽车座舱中控主机硬件计算能力升级迭代周期相对较长,汽车的应用和服务不够丰富。现在很多汽…

.NetCore部署微服务(二)

目录 前言 概念 一 Consul注册服务中心 1.1 consul下载 1.2 consul运行 二 服务注册 2.1 安装Consul包 2.2 修改配置文件 2.3 注入Consul服务 2.3 修改Controller&#xff0c;增加HealthCheck方法 三 运行服务 3.1 docker运行服务 前言 上一篇讲到微服务要灵活伸缩…

「超级细菌」魔咒或将打破,MIT 利用深度学习发现新型抗生素

作者&#xff1a;加零 编辑&#xff1a;李宝珠、三羊 MIT 利用图神经网络 Chemprop 识别潜在抗生素&#xff0c;特异性杀死鲍曼不动杆菌。 自然界中充满了各种各样的微生物&#xff0c;例如结核杆菌&#xff08;导致肺结核&#xff09;、霍乱弧菌&#xff08;导致霍乱&#…

数据结构实验4:链表的基本操作

目录 一、实验目的 二、实验原理 1. 节点 2. 指针 3.链表的类型 3.1 单向链表 3.2 双向链表 3.3 单向循环链表 3.4 双向循环链表 4. 单链表的插入 4.1 头插法 4.2 尾插法 4.3 在指定位置插入元素 5. 单链表的删除 5.1 删除指定数值的节点 5.2 删除指定位置的节点 …

Pytorch从零开始实战16

Pytorch从零开始实战——ResNeXt-50算法的思考 本系列来源于365天深度学习训练营 原作者K同学 对于上次ResNeXt-50算法&#xff0c;我们同样有基于TensorFlow的实现。具体代码如下。 引入头文件 import numpy as np from tensorflow.keras.preprocessing.image import Ima…

TensorRt(5)动态尺寸输入的分割模型测试

文章目录 1、固定输入尺寸逻辑2、动态输入尺寸2.1、模型导出2.2、推理测试2.3、显存分配问题2.4、完整代码 这里主要说明使用TensorRT进行加载编译优化后的模型engine进行推理测试&#xff0c;与前面进行目标识别、目标分类的模型的网络输入是固定大小不同&#xff0c;导致输入…

【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》

【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》 写在最前面私钥加密与伪随机性 第二部分流加密与CPA多重加密 CPA安全加密方案CPA安全实验、预言机访问&#xff08;oracle access&#xff09; 操作模式伪随机函数PR…

Java微服务系列之 ShardingSphere - ShardingSphere-JDBC

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列专栏目录 [Java项…

报错解决:No module named ‘pytorch_lightning‘ 安装pytorch_lightning

报错记录 执行如下代码&#xff1a; import pytorch_lightning报错&#xff1a; No module named ‘pytorch_lightning’ 解决方式 安装pytorch_lightning包即可。 一般情况下&#xff0c;缺失的包通过pip安装&#xff0c;即&#xff1a; pip install pytorch_lightning然…

1 快速前端开发

1 前端开发 目的&#xff1a;开发一个平台&#xff08;网站&#xff09;- 前端开发&#xff1a;HTML、CSS、JavaScript- Web框架&#xff1a;接收请求并处理- MySQL数据库&#xff1a;存储数据地方快速上手&#xff1a;基于Flask Web框架让你快速搭建一个网站出来。1.快速开发…

HarmonyOS应用开发学习笔记 应用上下文Context 获取文件夹路径

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…

14-股票K线图功能-个股日K线SQL分析__ev

需求&#xff1a;统计个股日K线数据&#xff0c;也就是把某只股票每天的最高价&#xff0c;开盘价&#xff0c;收盘价&#xff0c;最低价形成K线图。

山西电力市场日前价格预测【2024-01-11】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-11&#xff09;山西电力市场全天平均日前电价为231.43元/MWh。其中&#xff0c;最高日前电价为422.21元/MWh&#xff0c;预计出现在18:00。最低日前电价为0.00元/MWh&#xff0c;预计出…

现代软件测试中的自动化测试工具

自动化测试的重要性和优势 引言&#xff1a;随着软件开发的不断发展&#xff0c;自动化测试工具在现代软件测试中扮演着重要角色。提高效率&#xff1a;自动化测试可以加快测试流程&#xff0c;减少人工测试所需的时间和资源。提升准确性&#xff1a;自动化测试工具可以减少人…

PACS医学影像报告管理系统源码带CT三维后处理技术

PACS从各种医学影像检查设备中获取、存储、处理影像数据&#xff0c;传输到体检信息系统中&#xff0c;生成图文并茂的体检报告&#xff0c;满足体检中心高水准、高效率影像处理的需要。 自主知识产权&#xff1a;拥有完整知识产权&#xff0c;能够同其他模块无缝对接 国际标准…

Linux CentOS 7.6安装JDK详细保姆级教程

一、检查系统是否自带jdk java --version 如果有的话&#xff0c;找到对应的文件删除 第一步&#xff1a;先查看Linux自带的JDK有几个&#xff0c;用命令&#xff1a; rpm -qa | grep -i java第二步:删除JDK&#xff0c;执行命令&#xff1a; rpm -qa | grep -i java | xarg…

企业的 Android 移动设备管理 (MDM) 解决方案

移动设备管理可帮助您在不影响最终用户体验的情况下&#xff0c;通过无线方式管理和保护组织的移动设备群&#xff0c;现代 MDM 解决方案还可以控制 App、内容和安全性&#xff0c;因此员工可以毫无顾虑地在托管设备上工作。移动设备管理软件可有效管理个人设备上的公司空间。M…

优化CentOS 7.6的HTTP隧道代理网络性能

在CentOS 7.6上&#xff0c;通过HTTP隧道代理优化网络性能是一项复杂且细致的任务。首先&#xff0c;我们要了解HTTP隧道代理的工作原理&#xff1a;通过建立一个安全的隧道&#xff0c;HTTP隧道代理允许用户绕过某些网络限制&#xff0c;提高数据传输的速度和安全性。然而&…

工业交换机在智慧水务和水处理中的应用

智慧水务是一种基于互联网和物联网技术的水务管理模式。它利用现代信息技术&#xff0c;将传统的水务管理模式升级&#xff0c;实现智慧化的水务管理方式。智慧水务的实现离不开各种先进的技术手段。物联网技术是智慧水务的重要组成部分。通过在水务系统中部署工业交换机、传感…