AVL树模拟实现

目录

前言

什么叫平衡呢?

平衡因子

 代码实现

基础结构

函数部分

构造部分

Insert函数

旋转情况(敲重点!!!~\(≧▽≦)/~)

1、右右情况 ——— 左单旋

左旋总步骤

拆解

为什么叫左旋呢?

代码 

2、左左情况

右旋步骤

平衡因子变化:

代码

 3、左右情况

情况1:subLR->_bf = 0

情况2:subLR->_bf = 1

情况3:subLR->_bf = -1

为什么叫左右双旋呢?

代码

4、右左情况

代码

Inorder中序遍历

isBalance检查是否平衡

测试代码

结语


前言

AVL树,是一种“平衡”的二叉搜索树,关于搜索树的介绍和模拟,我已经在该篇文章(二叉搜索树的模拟实现-CSDN博客)介绍过,想复习或者了解二叉搜索树的读者可以去看看哦

♪(´▽`)

什么叫平衡呢?

AVL树在二叉搜索树的基础上,进行了平衡调整,也就是每插入一个数,就会检查是否有两棵子树的高度差超过1,若超过,就将“旋转”调整至平衡,这是为了解决二叉树在数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下的问题

而AVL树的最重要的部分,也就是调整平衡啦ヾ(≧▽≦*)o平衡因子是可以用来检测是否平衡的哦,我的模拟实现也是用这种方法哦~( ̄▽ ̄)~***

平衡因子

平衡因子 = 右子树高度 - 左子树高度

当平衡因子的绝对值大于1时,就出现了“不平衡”现象,就要分情况来进行旋转调整啦~

知道了上面这些,相信你对AVL树有了基本了解啦,现在让我们开始吧( ‵▽′)ψ

 代码实现

基础结构

AVL树与普通树的节点的不同

① 它的每个节点除了有左右孩子的指针,还有父母的指针

② 存的数据是键值对,也就是key-value结构,我在二叉搜索树的模拟实现-CSDN博客中介绍过

 key结构:
        通常用来用于:

                查找某值是否存在
        现实使用场景:
                门禁系统

key、value结构
        通常用于:
                1、确定一个值是否存在
                2、通过key查找value
        现实使用场景:
                字典

以下就是该部分的代码啦

template<typename K, typename V>// 类模板
struct AVLTreeNode// 类外也要使用,所以定义为struct
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;/**/pair<K, V>_kv;/*键值对*/int _bf;// 平衡因子(右子树高度 - 左子树高度)// 构造函数AVLTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0){}
};

函数部分

构造部分

template<typename K, typename V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;// typedef后,方便使用
private:Node* _root = nullptr;// 初始化
};

Insert函数

	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 (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}elsereturn 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++;}// 平衡因子 = 0,说明并未破坏平衡,不用再向上调整平衡因子if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){// 继续向上检查是否平衡cur = parent;parent = cur->_parent;}else{// 旋转}}return true;}

旋转情况(敲重点!!!~\(≧▽≦)/~)

1、右右情况

2、左左情况

3、左右情况

4、右左情况

1、右右情况 ——— 左单旋

右右情况

① 对于不平衡节点(该处是1)来说,插入元素在其右子树

② 对于不平衡节点的右孩子(这里是2)来说,插入节点也在其右子树

这种情况我们需要左旋

左旋总步骤

拆解

① 将不平衡节点(root)的右孩子(subR)的左孩子(subRL) 给 不平衡节点 的右孩子

② 不平衡节点(root)做 subR的左孩子

最后各点的平衡因子如下

 

为什么叫左旋呢?

是不是很像向左旋转了一下呢 q(≧▽≦q)

代码 
	// 左单旋void RotateL(Node* root){Node* subR = root->_right;Node* subRL = subR->_left;/*可能为空*/// 更新root的右孩子root->_right = subRL;// 更新父亲// 注意subRL可能为空的情况if (subRL){subRL->_parent = root;}subR->_parent = root->_parent;root->_parent = subR;// 更新subR的左孩子subR->_left = root;        // 注意root为整棵树的根时的情况if (root == _root)_root = subR;// 将 原本指向root的父节点 改为 指向subRif (subR->_parent){if (root == subR->_parent->_left){subR->_parent->_left = subR;}elsesubR->_parent->_right = subR;}// 最后平衡因子都变为0subR->_bf = root->_bf = 0;}
2、左左情况

和右右情况相反

1、插入元素在不平衡节点(root)的左子树(subL)

2、在不平衡节点(root)的左子树(subL)的左子树

右旋步骤

右旋

1、subL的右孩子subLR做root的左孩子

2、root做subL的右孩子

 为什么叫右旋?

是不是向右旋转了呢(´▽`ʃƪ)

平衡因子变化:

root : -2 ----->  0

subL:-1 ------> 0

subRL :不变

代码
	// 右单旋// 与左单旋代码类似void RotateR(Node* root){Node* subL = root->_left;Node* subLR = subL->_right;/*可能为空*/root->_left = subLR;if (subLR){subLR->_parent = root;}subL->_right = root;subL->_parent = root->_parent;root->_parent = subL;if (root == _root)_root = subL;if (subL->_parent){if (root == subL->_parent->_left){subL->_parent->_left = subL;}elsesubL->_parent->_right = subL;}subL->_bf = root->_bf = 0;}
 3、左右情况
情况1:subLR->_bf = 0

平衡因子变化:

root: -2 -> 0

subL: 1 -> 0

subLR: 0不变

情况2:subLR->_bf = 1

 

平衡因子变化:

root: -2 -> 0

subL: 1不变

subLR: 1 -> 0

情况3:subLR->_bf = -1

为什么叫左右双旋呢?

无论是上面三种哪种情况,都是单旋(subL),再单旋(root)

代码
// 左右双旋void RotateLR(Node* root){/*其实就是将根节点的左子树左旋,根节点再右旋*/Node* subL = root->_left;Node* subLR = subL->_right;// 记录subLR的平衡因子,方便后续分情况来更改subLR,subL,root的平衡因子int bf = subLR->_bf;// 左子树先左旋,根再右旋RotateL(root->_left);RotateR(root);if (bf == 0){// subLR就是新增root->_bf = subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){// 在subLR的右边新增subL->_bf = -1;root->_bf = subLR->_bf = 0;}else if (bf == -1){// 在subRL的左边新增root->_bf = 1;subLR->_bf = subL->_bf = 0;}}
4、右左情况

与左右情况极其类似

① 先对右子树右单旋

② 再对根节点左单旋

大家可以自己画画图哦,看一下自己理解的怎么样哦(●ˇ∀ˇ●)

代码
// 右左双旋
void RotateRL(Node* root)
{/*其实就是将根节点的右子树右旋,根节点再左旋*/Node* subR = root->_right;Node* subRL = subR->_left;int bf = subRL->_bf;// 子树先右旋,根再左旋RotateR(root->_right);RotateL(root);if (bf == 0){// subRL就是新增root->_bf = subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){// 在subRL的右边新增root->_bf = -1;subR->_bf = subRL->_bf = 0;}else if (bf == -1){// 在subRL的左边新增subR->_bf = 1;subRL->_bf = root->_bf = 0;}
}

Inorder中序遍历

与二叉搜索树的地方一样哦

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

isBalance检查是否平衡

检查平衡要有三个要求:

1、根节点的平衡因子 = 右子树高度 - 左子树高度

2、两子树高度差的绝对值 < 2

3、所有子树都满足以上要求

	bool isBalance(){/*这种对外接口内部调用另一函数的原因,我在开头提到的那篇博客讲过这样子写就可以不需要外部调用时还要知道AVL树的根了,直接调用就好*/// 检验该树是否平衡return _isBalance(_root);}bool _isBalance(Node* root){if (root == nullptr)return true;// 获取左右子树高度int leftHeight = getHeight(root->_left);int rightHeight = getHeight(root->_right);// 看根节点的平衡因子是否合理if (root->_bf != rightHeight - leftHeight){cout << "_bf错误" << endl;return false;}// 看高度差的绝对值是否小于2return abs(rightHeight - leftHeight) < 2// 继续检查子树是否合理&& _isBalance(root->_left)&& _isBalance(root->_right);}int getHeight(Node* root){if (root == nullptr)return 0;int leftHeight = getHeight(root->_left);int rightHeight = getHeight(root->_right);// 根节点高度 = 左右子树较高者的高度 + 1return max(leftHeight, rightHeight) + 1;}

测试代码

以下测试代码可以帮你测试是否所有情况你的代码都能正确解决

void test1()
{// int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };// 右左双旋int a[] = {10, 3, 7 };// 左右双旋AVLTree<int, int> t;for (auto e : a){t.Insert({e, e});}t.InOrder();if (!t.isBalance())cout << "该树不平衡" << endl;
}void test2()
{// 随机数据测试const int N = 30;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand());// cout << v.back() << endl;}AVLTree<int, int> t;for (auto e : v){if (e == 14604){int x = 0;}t.Insert(make_pair(e, e));if (!t.isBalance())cout << "该树不平衡" << endl;}
}

结语

好啦,恭喜你今天又进步一点点哦~~如果支持博主的话,可以给博主点赞、收藏、关注(´▽`ʃƪ)哦,后续我将更新《红黑树》部分,待我学成归来(*≧︶≦))( ̄▽ ̄* )ゞ

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

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

相关文章

考研概率论如何复习最高效?能拿满分

概率论跟哪写老师的课程&#xff1f; 推荐三个老师&#xff1a; 喻老&#xff1a;基础讲的很好 喻老的线性代数课在今年已经非常有名&#xff0c;但其实他讲授的概率论课程同样十分出色。喻老的课程特点在于讲解非常细致&#xff0c;特别适合基础较为薄弱的学生。此外&#…

如何评估一个APP是否适合进行ASO优化呢

ASO&#xff08;App Store Optimization&#xff09;优化是提升APP在各类应用商店排行榜和搜索结果排名的过程。那么怎么评估一个APP是否适合进行ASO优化呢&#xff0c;可以从以下几个方面进行考量&#xff1a; 一、市场竞争情况 1.行业竞争激烈程度 首先分析APP所在行业的竞…

python媒体下载工具 you-get

you-get 是一个基于 Python 3 的强大的命令行工具&#xff0c;使用方式简单&#xff0c;使用 you-get 可以很轻松的下载到网络上的各种媒体文件&#xff08;视频、图片及音乐等&#xff09;。 相关功能和配置选项&#xff0c;可以查阅以下以获取详细信息&#xff1a; GitHub 官…

Unity | AmplifyShaderEditor插件基础(第一集:简单了解ASE和初识)

前言 我本来老老实实的写着我的Shader&#xff0c;群里的小伙伴强烈建议我开始讲ASE&#xff0c;我只能说&#xff0c;我是一个听话的Up。 一、什么是ASE 全称AmplifyShaderEditor&#xff0c;是一个unity插件&#xff0c;存在于unity商城中&#xff0c;售价看他们心情。&am…

Spring中WebSocket的使用

文章目录 前言什么是 WebSocketWebSocket 协议和 HTTP 协议的区别WebSocket 原理解析WebSocket 报文格式 Spring 中 WebSocket 的使用前后端发送的数据的数据类型是对象该如何做使用websocket协议如何获取到HTTP协议中的HttpSession WebSocket使用的完整代码 前言 我们在使用 …

Pixel Adventure Unity2D开发完整指南

本文参考&#xff1a;2-2. Get and Setup Assets_哔哩哔哩_bilibili 1、下载资源 在Asset Store中下载Pix Adventure1 2的资源&#xff1a; 在import的时候&#xff0c;不用到Scene import进来&#xff0c;如下图所示&#xff0c;Scenes目录反勾选一下。 两个资源都下载完成后…

Unity 使用 NewtonSoft Json插件报错

JsonReaderException: Unexpected character encountered while parsing value: . Path , line 0, position 0. 通过断点发现&#xff0c;头有一串ZWNBSP&#xff0c;这个是BOM格式的JSON。在文件下看不到。 解决方法&#xff1a;改编码格式&#xff0c;Remove BOM.

(回溯) LeetCode 51. N 皇后

原题链接 一. 题目描述 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后…

腾讯云AI代码助手:智能AI代码助手 ,新一代的高效代码开发辅助工具

前言 近些年是一个科技大爆发的时代&#xff0c;自从大模型发布以来越来越多的科技产品出现。例如去年的智能编码助手自出现以来&#xff0c;各大老牌大厂腾讯&#xff0c;百度 阿里也都紧随其后&#xff0c;智能编码助手的出现可以说大大的节省了我们写一些冗余代码的时间成本…

十七、访问者模式

文章目录 1 基本介绍2 案例2.1 Element 接口2.2 Vehicle 抽象类2.3 Car 类2.4 Jeep 类2.5 VehicleCollection 类2.6 Action 抽象类2.7 Repair 类2.8 Drive 类2.9 Client 类2.10 Client 类的运行结果2.11 总结 3 各角色之间的关系3.1 角色3.1.1 Element ( 元素 )3.1.2 ConcreteE…

靓图!多点创新!CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解+卷积长短期+注意力多元时间序列预测

靓图&#xff01;多点创新&#xff01;CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解卷积长短期注意力多元时间序列预测 目录 靓图&#xff01;多点创新&#xff01;CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解卷积长短期注意力多元时间序列预测效果一览基本介绍程序设计…

LVS 调度器 nat和DR模式

lvs-nat 修改请求报文的目标IP,多目标IP的DNAT 配置网络 LVS主机 注意网卡的顺序 &#xff08;nat和主机模式&#xff09; [rootlvs ~]# cat /etc/NetworkManager/system-connections/ens160.nmconnection [connection] idens160 typeethernet interface-nameens160 ​ [ip…

Linux使用学习笔记3 系统运维监控基础

系统运维监控类命令 查询每个进程的线程数 for pid in $(ps -ef | grep -v grep|grep "systemd" |awk {print $2});do echo ${pid} > /tmp/a.txt;cat /proc/${pid}/status|grep Threads > /tmp/b.txt;paste /tmp/a.txt /tmp/b.txt;done|sort -k3 -rn for pid…

数据结构与算法-16高级数据结构_图论(图论基础)

图论基础 1 什么是图 1.1 基础定义 图&#xff08;Graph&#xff09;是一个用于描述一组对象之间关系的数学结构。这些对象被称为顶点&#xff08;Vertex&#xff09;&#xff0c;也称为节点&#xff08;Node&#xff09;或点&#xff08;Point&#xff09;&#xff0c;而对…

2024国赛Word论文模板【一键生成式操作】

一、比赛介绍 该竞赛创办于1992年&#xff0c;每年一届&#xff0c;是首批列入“高校学科竞赛排行榜”的19项竞赛之一。2023年&#xff0c;来自全国及美国、澳大利亚、马来西亚的1685所院校/校区、59611队(本科54158队、专科5453队)、近18万人报名参赛。 而今年的国赛马上就要…

【CTF | WEB】001、攻防世界WEB题目之backup

文章目录 backup题目描述:解题思路&#xff1a;解题过程&#xff1a; backup 题目描述: X老师忘记删除备份文件&#xff0c;他派小宁同学去把备份文件找出来,一起来帮小宁同学吧&#xff01; 进入题目后显示&#xff1a; 解题思路&#xff1a; 在进行网站安全检查时&#xf…

网络协议四 物理层,数据链路层

从这一节开始学习 五层模型。学习方法是从最底层物理层开始学习 七层模型 五层模型 各个层用的协议&#xff0c;以及加上协议后的称谓 各个层的作用 应用层&#xff1a;可以认为是原始数据&#xff0c;该数据称为 报文&#xff0c;用户数据。 运输层&#xff1a;也叫传输层&am…

全网超详细攻略——LVS原理详解及部署

目录 一、LVS原理 1.LVS简介 2.LVS结构 3.IP负载均衡技术 4.LVS相关术语 二、LVS负载均衡四种工作模式 1.LVS-DR模式 2.LVS-NAT模式 3.LVS-TUN模式&#xff08;了解&#xff09; 4.FULL-NAT模式&#xff08;了解&#xff09; 三、LVS负载均衡十种调度算法 四、LVS部…

米思奇安装——Mac版本

米思奇安装——Mac版本 1.下载 访问米思奇官网https://mixly.org/bnu-maker/mixl2.0rc 打开官网后在首页点击导航栏的软件平台&#xff0c;选择Mixly离线版 点击Mixly2.0RC4发布下载。 进入百度网盘分享的文件&#xff0c;选择Mac一键更新版本&#xff0c;等待下载完成。 …

尚品汇-ES(三十一)

目录&#xff1a; &#xff08;1&#xff09;封装搜索相关实体对象 &#xff08;2&#xff09;搜索接口封装 &#xff08;3&#xff09;在service-list-client模块添加远程接口 &#xff08;1&#xff09;封装搜索相关实体对象 搜索参数实体&#xff1a;SearchParam 搜索参…