【C++模拟实现】手撕AVL树

【C++模拟实现】手撕AVL树

目录

  • 【C++模拟实现】手撕AVL树
      • AVL树的介绍(百度百科)
      • AVL树insert函数的实现代码
      • 验证是否为AVL树
      • AVL树模拟实现的要点
        • 易忘点
        • AVL树的旋转思路

作者:爱写代码的刚子

时间:2023.9.10

前言:本篇博客将会介绍AVL树的模拟实现(模拟AVL树的插入),以及如何去验证是否为AVL树

AVL树的介绍(百度百科)

AVL树本质上还是一棵二叉搜索树,它的特点是:

  1. 本身首先是一棵二叉搜索树。

  2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。

AVL树insert函数的实现代码

template<class K,class V>
class AVLTreeNode
{
public:AVLTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;//需要设立父节点指针pair<K,V> _kv;int _bf;
};template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K,V> Node;
public:AVLTree():_root(nullptr){}bool insert(const pair<K,V>& kv){if(_root==nullptr){_root=new Node(kv);return true;}else{Node* cur=_root;Node* parent=nullptr;//设计parent指针是必要的while(cur){if(cur->_kv.first>kv.first){parent=cur;cur=cur->_left;}else if(cur->_kv.first<kv.first){parent=cur;cur=cur->_right;}else{return false;}}cur=new Node(kv);//判断新加入的节点是父节点的左子树还是右子树if(parent->_kv.first>kv.first){parent->_left=cur;}else{parent->_right=cur;}cur->_parent=parent;while(parent){//及时调整父节点的平衡因子if(parent->_left==cur){--parent->_bf;}else{++parent->_bf;}if(parent->_bf==0)//当父节点的平衡因子为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){_RotateR(parent);//右单旋}else if(parent->_bf==2&&cur->_bf==1){_RotateL(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;}else{assert(false);}}}return true;} void _RotateR(Node* parent)//右单旋的实现{Node*cur=parent->_left;Node*curRight=cur->_right;Node*ppnode=parent->_parent;cur->_right=parent;parent->_left=curRight;if(curRight)//curRight可能是nullptr{curRight->_parent=parent;}parent->_parent=cur;//处理ppnodeif(parent==_root)//parent为头节点时需要单独处理{_root=cur;cur->_parent=nullptr;}else{if(ppnode->_left==parent){ppnode->_left=cur;}else{ppnode->_right=cur;}cur->_parent=ppnode;}parent->_bf=cur->_bf=0;}void _RotateL(Node* parent){Node* cur=parent->_right;Node* curLeft=cur->_left;Node* ppnode=parent->_parent;cur->_left=parent;parent->_right=curLeft;if(curLeft){curLeft->_parent=cur;}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;}parent->_bf=cur->_bf=0;}void _RotateLR(Node* parent){Node* cur=parent->_left;Node* curRight=cur->_right;int bf=curRight->_bf;_RotateL(cur);_RotateR(parent);//最好再处理一下平衡因子,减少耦合度if(bf==0)//单链情况下{parent->_bf=0;cur->_bf=0;curRight->_bf=0;}else if(bf==-1){parent->_bf=1;curRight->_bf=0;cur->_bf=0;}else if(bf==1){parent->_bf=-1;curRight->_bf=0;cur->_bf=0;}else{assert(false);}}void _RotateRL(Node* parent){Node* cur=parent->_right;Node* curLeft=cur->_left;int bf=curLeft->_bf;_RotateR(cur);_RotateL(parent);if(bf==0){parent->_bf=0;curLeft->_bf=0;cur->_bf=0;}else if(bf==1){parent->_bf=0;curLeft->_bf=-1;cur->_bf=0;}else if(bf==-1){parent->_bf=0;curLeft->_bf=1;cur->_bf=0;}else{assert(false);}}private:Node* _root;
};

验证是否为AVL树

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;}bool _Isbalance(){return _Isbalance(_root);}bool _Isbalance(Node* root){if(root==nullptr){return true;}int right=_Height(root->_right);int left=_Height(root->_left);if(root->_bf!=right-left){cout<<"平衡因子异常"<<root->_bf<<" "<<right<<" "<<left<<endl;return false;}return abs(right-left)<2&&_Isbalance(root->_left)&&_Isbalance(root->_right);}
  • 根据AVL树的特性引入两个成员函数_Height函数用于计算二叉树的高度

  • 以下为验证结果:
    在这里插入图片描述

AVL树模拟实现的要点

易忘点

一定要时刻注意_parent指针的修改!尤其旋转函数中需要判断旋转后的二叉树的根节点是否还有父亲节点,如果有,需要在旋转前先保存,之后再链接上。

AVL树的旋转思路

  1. 新增在左,parent平衡因子减减
  2. 新增在右,parent平衡因子加加
  3. 更新后parent平衡因子 == 0,说明parent所在的子树的高度不变,不会再影响祖先,不用再继续沿着到eot的路径往上更新
  4. 更新后parent平衡因子 == 1 0r -1,说明parent所在的子树的高度变化,会再影响祖先,需要继续沿着到root的路径往上更新更新后
  5. 更新后parent平衡因子 == 2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让他平衡
  6. 更到根节点,插入结束

由于AVL树画图较为麻烦,作者先不画了,可以看看其他大佬的博客,一些需要注意的地方已经写在代码注释里了,AVL树的删除之后有机会可以模拟实现一下。
AVL树的调试较为麻烦,模拟实现可以提高自己的调试能力。

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

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

相关文章

Nodejs 第十五章(child_process)

child_process 子进程 子进程是Nodejs核心API&#xff0c;如果你会shell命令&#xff0c;他会有非常大的帮助&#xff0c;或者你喜欢编写前端工程化工具之类的&#xff0c;他也有很大的用处&#xff0c;以及处理CPU密集型应用。 创建子进程 Nodejs创建子进程共有7个API Sync…

跟踪源码技巧+阅读源码工具Sourcetrail

基于Eclipse IDE 1、Quick Type Hierarchy 快速查看类继承体系 &#xff08; 快捷键&#xff1a;Ctrl T&#xff09; 查看类很多人可能都知道&#xff0c;可源码阅读的时候更多用来查看方法体系更重要&#xff0c;可以方便快速的定位到方法的实现类。如&#xff1a; 此时如…

Kafka入门与安装

为什么要用消息中间件&#xff1f; 异步处理 场景说明&#xff1a;用户注册后&#xff0c;需要发注册邮件和注册短信。传统的做法有两种1.串行的方式&#xff1b;2.并行方式。 串行方式&#xff1a;将注册信息写入数据库成功后&#xff0c;发送注册邮件&#xff0c;再发送注…

无涯教程-JavaScript - COUPDAYSNC函数

描述 COUPDAYSNC函数返回从结算日期到下一个息票日期的天数。 语法 COUPDAYSNC (settlement, maturity, frequency, [basis])争论 Argument描述Required/OptionalSettlement 证券的结算日期。 证券结算日期是指在发行日期之后将证券交易给买方的日期。 RequiredMaturity 证…

elk安装篇之 Kibana安装

Kibana是一个开源的分析与可视化平台&#xff0c;设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。是es的可视化客户端之一。 一&#xff1a;下载 https://www.elastic.co/cn/kibana 我的es是elasticsearch-7.10.2版本&#x…

javascript中如何将多个数组的一个元素相加求和

三个数组第一个元素相加并输出 在JavaScript中&#xff0c;通过以下方式将这三个数组的每个数组的第一个元素相加并输出&#xff1a; let array1 [1,2,3]; let array2 [3,4,5]; let array3 [5,6,7];let sum array1[0] array2[0] array3[0]; console.log(sum);这段代码首…

Hilbert-Schmidt Operator

Hilbert-Schmidt Operator See https://mathworld.wolfram.com/Hilbert-SchmidtOperator.html See https://people.kth.se/~laptev/FA08/l8.pdf

OpenCV(二十九):图像腐蚀

1.图像腐蚀原理 腐蚀操作的原理是将一个结构元素&#xff08;也称为核或模板&#xff09;在图像上滑动&#xff0c;并将其与图像中对应位置的像素进行比较。如果结构元素的所有像素与图像中对应位置的像素都匹配&#xff0c;那么该位置的像素值保持不变。如果结构元素的任何一个…

[通用]计算机经典面试题基础篇Day3

[通用]计算机经典面试题基础篇Day3 1、请说明mysql的两种主要引擎 MySQL有多种存储引擎&#xff0c;但最常见的两种主要引擎是InnoDB和MyISAM。 2、说一下mysql这两种引擎的使用场景 MySQL的两种主要引擎&#xff0c;InnoDB和MyISAM&#xff0c;各自适用于不同的使用场景&…

利用python进行视频下载并界面播放快速下载素材

工具&#xff1a;python designer&#xff08;python自带&#xff09;:UI界面设计工具 VLC&#xff1a;视频播放工具 需要的库如下&#xff1a; import os,platform os.environ[PYTHON_VLC_MODULE_PATH] "./vlc-3.0.14" import vlc from 脚本 import Player from …

通过位运算打多个标记

通过位运算打多个标记 如何在一个字段上&#xff0c;记录多个标记&#xff1f; 如何在一个字段上&#xff0c;记录不同类型的多个标记&#xff1f; 如何用较少的字段&#xff0c;记录多个标记&#xff1f; 如何在不增加字段的要求下&#xff0c;记录新增的标记&#xff1f; 在实…

Oracle数据库连接之TNS-03505_无法解析服务名异常

在进行数据库开发的时候&#xff0c;通常需要连接Oralce数据库&#xff0c;在进行连接时&#xff0c;经常性的会提示TNS-03505: Failed to resolve name&#xff08;无法解析服务名&#xff09;&#xff0c;从而导致无法连接到数据库实例&#xff0c;那么通过本文对造成该问题的…

SpringMVC之文件上传下载以及jrebel的使用

目录 一、文件上传 1.1 导入依赖 1.2 配置文件上传解析器 1.3 配置服务器存放文件地址 1.3.1 点击编辑Configurations 1.3.2 将项目部署至tomcat服务器上 1.3.3 配置相对路径 1.4 导入PropertiesUtil工具类 1.5 编写resource.properties 1.6 添加sql 1.7 编写PageCo…

GIT高级使用技巧

GIT高级使用技巧 导出GIT日志到文件 按照 <哈希> - <作者名> <作者邮箱地址> - <作者日期> : <commit描述> 的格式导出日志 git log --prettyformat:"%H - %an <%ae> - %ad : %s" master > log.txt筛选日志并按照从旧到新…

2022年12月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:走迷宫 一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。 给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。 时间限制:1000 …

pandas笔记:显示中间的省略号

比如我们有这样一个数据&#xff08;Geolife中的数据&#xff09; 如何把中间的省略号完整地输出呢&#xff1f; pd.set_option(display.max_rows, None) data

机器人中的数值优化(十五)——PHR增广拉格朗日乘子法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

Stable Diffusion 免费升级 SDXL 1.0,哪些新特性值得关注?体验如何?5 分钟带你体验!

一、引言 7 月 26 日&#xff0c;Stability AI 发布了 SDXL 1.0&#xff0c;号称目前为止&#xff0c;最厉害的开放式图像生成大模型。 它到底有没有网上说的那么炸裂&#xff1f;真的已经实现了像 midjourney 一样 靠嘴出图 的功能吗&#xff1f;相对于之前的版本&#xff0c;…

专业的视觉特效处理包,FxFactory 8 Pro for Mac助您打造精彩视频

FxFactory 8 Pro for Mac是一款强大的视觉特效处理包&#xff0c;专门为Mac用户设计。它集成了超过200种高质量的视觉效果和过渡效果&#xff0c;可以轻松地应用于各种视频项目中。该软件提供了一个直观的界面&#xff0c;用户可以通过简单拖放操作将特效应用到视频片段上。它支…

lv4 嵌入式开发-3 标准IO的读写

目录 1 标准I/O – 读写流 2 标准I/O – 按字符输入 3 标准I/O – 按字符输出 4 标准I/O – 思考和练习 5 标准I/O – 按行输入 6 标准I/O – 按行输出 7 标准I/O – 思考和练习 1 标准I/O – 读写流 流支持不同的读写方式: 读写一个字符&#xff1a;fgetc()/fputc()一…