AVL树的底层实现

文章目录

  • 什么是AVL树?
  • 平衡因子
  • Node节点
  • 插入
    • 新节点插入较高左子树的左侧
    • 新节点插入较高左子树的右侧
    • 新节点插入较高右子树的左侧
    • 新节点插入较高右子树的右侧
  • 验证是否为平衡树
  • 二叉树的高度
  • AVL的性能

什么是AVL树?

AVL树又称平衡二叉搜索树,相比与二叉搜索树多了平衡的概念,为什么要有平衡二叉搜索树呢?因为二叉搜索树可能会出现退化现象,导致左右子树高度相差较大,甚至退化成单链表的形式,因此我们提出了平衡二叉搜索树,可以有效地解决高度相差过大的情况,平衡二叉搜索树要求任意节点的左右子树高度相差不大于1.

平衡因子

为了解决高度差问题,我们在每一个节点中添加了平衡因子变量,这个变量用于记录该节点的左右子树的高度差。
若规定平衡因子等于右子树高度减去左子树高度,那么如果新增节点的位于当前节点的左侧那么该节点平衡因子-1,如果位于该节点的右侧,则该节点平衡因子+1,并且一直向上修改平衡因子,直到某个节点平衡因子为0,或者到根节点,或者需要进行调整

Node节点

template <class K, class V>
struct AVLTreeNode
{pair<K, V> _kv;//pair是库中的类,包含first和second两个成员变量AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;//blance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};

我们规定平衡因子等于右子树高度减去左子树高度

插入

平衡二叉搜索树也是二叉搜索树,因此它的插入和二叉搜索树类似,都是找到一个合适的节点进行插入,不同的是这棵树要保证每个节点的平衡因子不大于1,因此我们在插入后可能需要对该树的节点进行调整。
需要调整的情况有以下四种:分别是

新节点插入较高左子树的左侧

情况1:
在这里插入图片描述

情况2
在这里插入图片描述

上面两种情况的处理方式是一样的,因为这两种情况都属于较高子树的左边,以图为例,5和10所在位置分别是不平衡节点9的左右子树,而新插入的节点都是位于左子树的根节点5的左侧

思路:如果新插入的节点位于不平衡节点的左子树的左侧,要想让不平衡节点变平衡,就要让高的一侧子树高度-1,低的一侧子树高度+1,这样就可以使得原本高度差为2的左右子树的高度差变为0,同时还要保证二叉树的性质不被破坏,所以还要对6进行处理。
右单旋:不平衡节点以及它的右子树进行,绕不平衡节点的左孩子旋转90度。
右单旋是指不平衡节点以及它的右子树进行旋转

//新节点插入较高左子树的左侧----左左: 右单旋
void RotateR(Node* parent)
{++_rotateCount;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;/if (curright){//parent->_right = curright;curright->_parent = parent;}cur->_right = parent;Node* ppnode = parent->_parent;parent->_parent = cur;//if (parent = _root)if(ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (parent == ppnode->_left){//cur = ppnode->_left;ppnode->_left = cur;}else{//cur = ppnode->_right;ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;
}

最后不要忘记把新插入节点和那个不满足平衡因子的左孩子的平衡因子都置为0

新节点插入较高左子树的右侧

在这里插入图片描述
新插入节点位于7的左右两侧都属于同一种情况,这里就不做演示了。
先把不满足平衡因子的节点的左孩子进行左旋转,在对不满足平衡因子的节点进行右旋转。

//新节点插入较高左子树的右侧---左右:先左单旋再右单旋
void RotateLR(Node* parent)//先左旋再右旋
{Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){curright->_bf = 0;cur->_bf = -1;parent->_bf = 0;}else if (bf == -1){curright->_bf = 0;cur->_bf = 0;parent->_bf = 1;}else{assert(false);}
}

调整完之后不要忘记对旋转节点的平衡因子进行调整

新节点插入较高右子树的左侧

插入到较高右子树左侧与插入到较高左子树右侧对称,因此调整方式恰好相反,因此是先右旋再左旋

//新节点插入较高右子树的左侧---右左:先右单旋再左单旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;RotateR(parent->_right);
RotateL(parent);if (bf == 0)
{parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;
}
else if (bf == 1)
{curleft->_bf = 0;cur->_bf = 0;parent->_bf = -1;}
else if (bf == -1)
{curleft->_bf = 0;cur->_bf = 1;parent->_bf = 0;
}
else
{assert(false);
}
}

新节点插入较高右子树的右侧

左单旋是指不平衡节点以及它的左子树进行旋转
同理,与插入到较高左子树的左侧对称,因此是左单旋

//新节点插入较高右子树的右侧----右右:左单旋
void RotateL(Node* parent)
{++_rotateCount;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;/if (curleft){//parent->_right = curleft;curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;
}

验证是否为平衡树

bool IsBlance()
{return IsBlance(_root);
}bool IsBlance(Node* root)
{if (root == nullptr){return true;}int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常:" << root->_kv.first << " " << root->_bf << endl;return false;}return abs(rightHeight - leftHeight) < 2&& IsBlance(root->_right)&& IsBlance(root->_left);
}

二叉树的高度

int Height()
{return Height(_root);
}int Height(Node* root)
{if (root == nullptr){return 0;}int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

AVL的性能

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

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

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

相关文章

YOLOv3 学习记录

文章目录 简介整体介绍整体架构图 网络架构的改进Backbone 的改进FPNAnchor 机制 坐标表示与样本匹配目标边界框的预测正负样本匹配 损失函数 简介 关注目标在哪里 目标是什么 目标检测的发展路径&#xff1a; proposal 两阶段 --> anchor-base/ anchor-free --> nms f…

SSM框架

SSM SSM框架说明SpringBootMyBatis整合MyBatis数据库中表的设计Pojo对象设计Dao接口设计Dao单元方法进行测试 XML管理整合MyBatis框架映射配置文件的位置XML配置SQL标签常用的SQL标签 动态SQL语句动态删除数据动态修改数据 SSM框架说明 Spring 指 Spring Framework&#xff0c…

【入门篇】1.1 redis 基础数据类型详解和示例

文章目录 1. 简介2. Redis基础数据类型2.1 String类型场景示例常用命令示例 2.2 List类型场景示例 2.3 Set类型场景示例 2.4 Hash类型场景示例 2.5 Sorted Set类型 3. 使用Redis存储数据的注意事项1. 内存管理2. 数据持久化3. 高并发下的性能考量 4. 参考资料 1. 简介 Redis概…

web环境实现一键式安装启动

部署的痛点 一般在客户环境安装web环境&#xff0c;少说需要花费1-2小时。一般需要安装jdk、nginx、mysql、redis等 等你接触到了inno setup &#xff0c;你有可能会节约更少的时间去部署。也有可能是一个不懂技术的人&#xff0c;都可以进行操作的。废话不多说&#xff0c;接…

Unity Text文本首行缩进两个字符的方法

Text文本首行缩进两个字符的方法比较简单。通过代码把"\u3000\u3000"加到文本字符串前面即可。 参考如下代码&#xff1a; TMPtext1.text "\u3000\u3000" "这是一段有首行缩进的文本内容。\n这是第二行"; 运行效果如下图所示&#xff1a; 虽…

人工智能引领环境保护的新浪潮:技术应用及其影响

在全球范围内&#xff0c;环境保护已经成为一个迫切的话题。随着人工智能技术的发展&#xff0c;它开始在环境保护领域扮演越来越重要的角色。AI不仅能够帮助更有效地监测环境变化&#xff0c;还能提出解决方案来应对环境问题。 污染监测与控制&#xff1a; AI系统可以分析来自…

基于STC12C5A60S2系列1T 8051单片机的模数芯片ADC0832实现模数转换应用

基于STC12C5A60S2系列1T 8051单片的模数芯片ADC0832实现模数转换应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍模数芯片ADC0832介绍通过模数芯片ADC0832把电压模…

飞书开发学习笔记(八)-开发飞书小程序Demo

飞书开发学习笔记(八)-开发飞书小程序Demo 一.小程序开发概述 1.1 小程序开发概述 飞书开发文档中查看&#xff1a;小程序开发概述 飞书小程序是指可以运行在飞书客户端中的小程序&#xff0c;小程序的一套代码可以适配 Android、iOS、PC 多平台&#xff0c;且用户体验与飞书…

linux 查看命令使用说明

查看命令的使用说明的命令有三种&#xff0c;但并不是每个命令都可以使用这三种命令去查看某个命令的使用说明&#xff0c;如果一种不行就使用另外一种试一试。 1.whatis 命令 概括命令的作用 2.命令 --help 命令的使用格式和选项的作用 3.man 命令 命令的作用和选项的详细…

034、test

之——全纪录 目录 之——全纪录 杂谈 正文 1.下载处理数据 2.数据集概览 3.构建自定义dataset 4.初始化网络 5.训练 杂谈 综合方法试一下。 leaves 1.下载处理数据 从官网下载数据集&#xff1a;Classify Leaves | Kaggle 解压后有一个图片集&#xff0c;一个提交示…

C语言指针详解(1)(能看懂字就能明白系列)文章超长,慢慢品尝

目录 1、内存和地址 2、指针简介 与指针相关的运算符&#xff1a; 取地址操作符&#xff08;&&#xff09; 解引用操作符&#xff08;间接操作符&#xff09;&#xff08;*&#xff09; ​编辑 指针变量的声明 指针变量类型的意义 指针的基本操作 1、指针与整数相加…

GMS CTS测试命令汇总

目录 跑CTS之前的准备 样机环境要求 跑各模块版本要求 CTS 简介 复测上轮的失败项 多台设备测试 单跑指定模块和测试用例 GTS VTS STS GSI 获取fingerprint 跑CTS之前的准备 样机环境要求 1、打开stay wake&#xff08;保持屏幕常亮&#xff09;、OEM unlocking、…