【C++】AVL树的插入操作实现以及验证是否正确(带平衡因子)

文章目录

  • 前言
  • 一、AVL树结点的定义
  • 二、AVL树的插入(Insert)
    • 插入完整代码:
    • 1.左单旋(RotateL)
    • 2.右单旋(RotateR)
    • 3.先右单旋再左单旋(RotateRL)
      • 1.保存的bf为0
      • 2.保存的bf为1
      • 3.保存的bf为-1
    • 4.先左单旋再右单旋(RotateLR)
  • 三、AVL树的验证


前言

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

如果一棵二叉搜索树是高度平衡的,它就是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树结点的定义

template<class K,class V>
//我们这里用键值的数据类型来举例
struct AVLTreeNode {pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;//(父结点)int _bf;//(平衡因子大小)AVLTreeNode(const pair<K, V>& kv)//构造函数:_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};

二、AVL树的插入(Insert)

AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

在这里插入图片描述

插入完整代码:

typedef AVLTreeNode<K, V> Node;
bool Insert(const pair<K, V>& kv) {if (_root == nullptr) {//若为空树就先建立结点_root = new Node(kv);return  true;}Node* cur = _root;Node* parent = nullptr;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;}else {parent->_left = cur;}cur->_parent = parent;while (parent) {//更新平衡因子if (parent->_left == cur) {parent->_bf--;}else {parent->_bf ++ ;}// 更新后检测双亲的平衡因子if (parent->_bf == 0) {break;}else if (parent->_bf == 1 || parent->_bf == -1) {// 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,说明以双亲//为根的二叉树的高度增加了一层,因此需要继续向上调整cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) {// 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以parent// 为根的树进行旋转处理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);//双旋}break;}else {assert(false);}}return true;}

1.左单旋(RotateL)

在这里插入图片描述
在这里插入图片描述

 void RotateL(Node* parent) {Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;//  核心操作if (curleft) {//因为curleft可能为空curleft->_parent = parent;}Node* ppnode = parent->_parent;//提前记录下父结点的父亲cur->_left = parent;//     核心操作parent->_parent = cur;if (ppnode == nullptr) {//判断原先parent是否为根节点//因为根节点的父亲为空_root = cur;cur->_parent = nullptr;}else {//说明parent不为根节点//parent可能为ppnode的左或者右子树,改变ppnode的指向if (ppnode->_left == parent) {ppnode->_left = cur;}else {ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;// 根据调整后的结构更新部分节点的平衡因子} 

2.右单旋(RotateR)

在这里插入图片描述

 void RotateR(Node* parent) {Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright) {//因为curleft可能为空curright->_parent = parent;}cur->_right = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr) {//判断原先parent是否为根节点//因为根节点的父亲为空_root = cur;cur->_parent = nullptr;}else {//说明parent不为根节点//parent可能为ppnode的左或者右子树,改变ppnode的指向if (ppnode->_left == parent) {ppnode->_left = cur;}else {ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;// 根据调整后的结构更新部分节点的平衡因子} 

3.先右单旋再左单旋(RotateRL)

以cur为旋转点进行右旋,以parent为旋转点进行左旋,之后进行平衡因子的修改

 void RotateRL(Node* parent) {Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;// 旋转之前,保存curleft的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节//点的平衡因子RotateR(cur);RotateL(parent);if (bf == 0) {cur->_bf = 0;curleft->_bf = 0;parent->_bf = 0;}else if (bf == 1) {cur->_bf = 0;curleft->_bf = 0;parent->_bf = -1;}else if (bf == -1){cur->_bf = 1;curleft->_bf = 0;parent->_bf = 0;}else{assert(false);}} 

1.保存的bf为0

在这里插入图片描述

2.保存的bf为1

在这里插入图片描述

3.保存的bf为-1

在这里插入图片描述

4.先左单旋再右单旋(RotateLR)

以cur为旋转点进行左旋,以parent为旋转点进行右旋,之后进行平衡因子的修改

 void RotateLR(Node* parent) {Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;// 旋转之前,保存curright的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节//点的平衡因子RotateL(cur);RotateR(parent);if (bf == 0) {cur->_bf = 0;parent->_bf = 0;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else {assert(false);}} 

在这里插入图片描述

三、AVL树的验证

验证其为平衡树关键点:
1.每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
2.节点的平衡因子是否计算正确

int  Height(Node* root) {if (root == nullptr) {return 0;}int left =  Height(root->_left);int right =  Height(root->_right);return left > right ? left + 1 : right + 1;}bool IsBalance() {return IsBalance(_root);}bool IsBalance(Node* root) {if (root == nullptr) {return true;}int left = Height(root->_left);int right = Height(root->_right);if (right - left != root->_bf) {//节点的平衡因子是否计算正确cout << "平衡因子异常" << root->_kv.first << "=>" << root->_bf << endl;return false;}//每个节点子树高度差的绝对值不超过1//之后在验证左右子树return abs(right-left)<2&&IsBalance(root->_left) && IsBalance(root->_right);}

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

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

相关文章

Excel中将文本格式的数值转换为数字

在使用excel时&#xff0c;有时需要对数字列进行各种计算&#xff0c;比如求平均值&#xff0c;我们都知道应该使用AVERAGE()函数&#xff0c;但是很多时候结果却“不尽如人意”。 1 问题&#xff1a; 使用AVERAGE函数&#xff1a; 结果&#xff1a; 可以看到单元格左上角有个…

docker 方式安装mysql 主从方式keepalived实现高可用

一、环境介绍 二、MySQL安装 在两台服务器上都安装mysql 1、拉取镜像 docker pull mysql:8.0.272、创建挂载目录 mkdir -p /data/mysql/3、运行容器 主节点 docker run \--restartalways \--name master_mysql -p 3306:3306 \-e MYSQL_ROOT_PASSWORD123456 -d \-v /data/m…

基于视觉重定位的室内AR导航APP的大创项目思路(3)手机相机内参数据获取和相机标定

文章目录 相机内参为什么要获取相机的内参数据&#xff1a;获取相机内存数据的方法棋盘格标定自动相机标定 前情提要&#xff1a; 是第一次做项目的小白&#xff0c;文章内的资料介绍如有错误&#xff0c;请多包含&#xff01; 相机内参 相机内参是本身的物理数据&#xff0c…

消息队列MQ

一、消息队列 网络端的Http请求默认采用的是同步请求方式&#xff0c;客户端与服务器端是基于请求和响应模式进行通信的。也就意味着&#xff0c;客户端发起请求。必须要等待服务器端完成处理结果给客户端才能继续进行下一步操作&#xff0c;如果服务器发送网络延迟、宕机、卡顿…

使用C语言EasyX 创建动态爱心背景

简介 在计算机图形学的世界中&#xff0c;有很多方法可以使程序的界面更加吸引人。在本篇博客中&#xff0c;我将向大家介绍如何使用 EasyX 图形库在 C 中创建一个动态的爱心背景。这不仅是一个简单的动画效果&#xff0c;它还包括背景的星星、旋转的心形以及一个美观的背景渐…

Java基于SpringBoot+Vue的 4S店车辆管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1 简介2 技术栈3 功能总览4 系统设计4.1 系统设计主要功能4.2 数据库设计4.2.1 数据库设计规范4.2…

freeRTOS系列教程之freeRTOS入门与工程实践【文档+视频教程+进阶视频教程】

《freeRTOS入门与工程实践》 前言课程内容授课方式 学前知识普及学前准备硬件准备资料准备 视频教程文档教程更好的阅读体验 前言 课程内容 嵌入式软件工程师的学习路线一般是&#xff1a;单片机->RTOS->Linux。当你掌握单片机开发后&#xff0c;如果要进一步提升编程水…

VUE 的eslint 代码规范检查

报错&#xff1a; You may use special comments to disable some warnings. Use // eslint-disable-next-line to ignore the next line. Use /* eslint-disable */ to ignore all warnings in a file. 解决&#xff1a; 注释&#xff1a;...(config.dev.useEslint ? [creat…

go初识iris框架(七) - 实战资源导入和项目框架搭建

实战项目框架搭建 如下是项目框架搭建后的说明&#xff1a; config:&#xff1a;项目配置文件及读取配置文件的相关功能controller:控制器目目录,项目各个模块的控制器及业务逻辑处理的所在目录datasource:实现mysql连接和操作、封装操作mysql数据库的目录。model:数据实体目…

趣解设计原则之《只是买台咖啡机,竟然要学习咖啡器的运行原理?》

〇、小故事 小王最近工作特别的忙&#xff0c;每天要早出晚归&#xff0c;睡眠质量很差&#xff0c;为了可以精力充沛的投入到每天的工作中&#xff0c;她都在上班路上买一杯公司楼下的星巴克咖啡来提提神。咖啡味道很好&#xff0c;但是每天买咖啡的人真的是太多了&#xff0…

Linux编辑器-vim使用

文章目录 前言一、vim编辑器1、vim的基本概念2、vim的基本操作2.1 命令模式切换至插入模式2.2 插入模式切换至命令模式2.3 命令模式切换至底行模式 3、vim命令模式命令集3.1 移动光标3.2 删除文字3.3 复制与粘贴3.4 替换3.5 撤销上一次操作3.6 更改3.7 跳至指定的行 4、vim末行…

关于运行franka_ros包中的franka_gazebo报错VMware: vmw_ioctl_command error 无效的参数.

参考的博文&#xff0c;感谢&#xff0c;解决Vmware下虚拟机下打开gazebo报错 &#xff0c;VMware: vmw_ioctl_command error 无效的参数. 首先第一个VMware: vmw_ioctl_command error 无效的参数的问题。这应该是虚拟机的bug&#xff0c;毕竟使用虚拟机和真实的物理机上是有差…

C语言实现三子棋游戏(详解)

目录 引言&#xff1a; 1.游戏规则&#xff1a; 2.实现步骤&#xff1a; 2.1实现菜单&#xff1a; 2.2创建棋盘并初始化&#xff1a; 2.3绘制棋盘&#xff1a; 2.4玩家落子&#xff1a; 2.5电脑落子&#xff1a; 2.6判断胜负&#xff1a; 3.源码&#xff1a; 结语&…

GB28181学习(三)——心跳保活

心跳保活 要求&#xff1a; 1. 当原设备发现工作异常时&#xff0c;应立即向本SIP监控域的SIP服务器发送状态信息&#xff1b; 2. 无异常时&#xff0c;定时向本SIP监控域的SIP服务器发送状态信息&#xff1b; 3. 状态信息报送采用**MESSGAE**方法&#xff1b; 4. SIP设备宜在…

简简单单教你如何用C语言实现获取当前所有可用网口!

一、获取本机所有可用网卡名 原理&#xff1a; 在 Linux 系统中&#xff0c;/proc 目录是一个位于内存中的伪文件系统。 /proc目录是内核提供给我们的查询中心&#xff0c;通过查询该目录下的文件内容&#xff0c;可以获取到有关系统硬件及当前运行进程的信息&#xff0c;如…

leetcode 54. 螺旋矩阵

1. 题解 如果一条边从头遍历到底&#xff0c;则下一条边遍历的起点随之变化选择不遍历到底&#xff0c;可以减小横向、竖向遍历之间的影响一轮迭代结束时&#xff0c;4条边的两端同时收窄 1一轮迭代所做的事情变得很清晰&#xff1a;遍历一个“圈”&#xff0c;遍历的范围收缩为…

日常开发小汇总(3)js类型判断

1.typeof 能判断出字符串、数字、方法和undefined&#xff0c;array、null、object判断不出 let num 1;let str "x";let fn function user(){}let arr [1,2]let obj {name:"zhangs"}let und;let nul null;console.log(typeof num) //numberconsole.l…

JAVAEE初阶相关内容第八弹--多线程(初阶)

本文目录 阻塞队列 阻塞队列是什么&#xff1f; 标准库中的阻塞队列 生产者消费者模型 阻塞队列的实现 普通队列实现&#xff1a; 入队列&#xff1a; 出队列&#xff1a; 完整代码&#xff1a; 加阻塞 加锁 加阻塞 阻塞队列 队列&#xff1a;先进先出&#xff0c;…

量化:基于支持向量机的择时策略

文章目录 参考机器学习简介策略简介SVM简介整体流程收集数据准备数据建立模型训练模型测试模型调节参数 参考 Python机器学习算法与量化交易 利用机器学习模型&#xff0c;构建量化择时策略 机器学习简介 机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。…

请重新安装msvcp140.dll的修复方法,多种msvcp140.dll的处理方案

当你在电脑上安装或运行某些程序时出现了“缺少msvcp140.dll”或“msvcp140.dll丢失”会要求你“重新安装msvcp140.dll”等的错误提示&#xff0c;这意味着你的计算机缺少了一个重要的动态链接库文件。msvcp140.dll是微软Visual C Redistributable for Visual Studio 2015库的一…