【C++】详解AVL树——平衡二叉搜索树
个人主页:东洛的克莱斯韦克-CSDN博客
祝福语:愿你拥抱自由的风
目录
二叉搜索树
AVL树概述
平衡因子
旋转情况分类
左单旋
右单旋
左右双旋
右左双旋
AVL树节点设计
AVL树设计
详解单旋
左单旋
右单旋
详解双旋
左右双旋
平衡因子情况如下
右左双旋
平衡因子情况如下
二叉搜索树
【C++】详解二叉搜索树-CSDN博客
AVL树概述
平衡树:左子树的高度等于右子树的高度
不平衡树:左子树的高度不等于等于右子树的高度
二叉搜索树很难是一颗平衡树。
对二叉树进行插入和删除的操作,或插入大量的数据不够随机,都会是使二叉搜索树不够平衡。
极端情况下,二叉树会退化成类似链表的结构,那么二叉搜索树查询数据的效率荡然无存。
在二叉树的基础上加入平衡的概念就是平衡二叉搜索树,也叫AVL树。
AVL树也不是一颗绝对的平衡树,AVL树的平衡是相对的,它允许左子树和右子树的高度为 1 ,但不能超过 1 。
平衡是相对的很好理解,因为一个父亲节点最多只能有两个孩子节点,而数据又是一个一个插入的,所以一定会出现左子树和右子树高度差为 1 的情况。
B树可达到绝对平衡,因为B树是多叉结构——一个父亲节点有多个孩子节点
如果左子树和右子树的高度差为 2 ,就视为打破平衡。
如果打破平衡,就需要通过旋转这一操作让左右子树的高度差小于等于 1 。
AVL树是保持一种相对平衡的状态,而不是绝对平衡。那么AVL树搜索数据的效率只能是接近。
AVL树只是保证了搜索效率的下限,而不是提高了上限
平衡因子
平衡因子这一概念并不是AVL树所必备的——从代码实现的角度来说,如果不加入平衡因子的概念理解起来会比较抽象。
平衡因子:让每个节点存一个整型,该整形值的大小等于右子树的高度减左子树的高度
平衡因子等于 0 :左右子树平衡
平衡因子等于 1 :左右子树相对平衡,右树偏高
平衡因子等于 -1 :左右子树相对平衡,左树树偏高
平衡因子等于 2 或 -2 :左右子树不平衡
平衡因子的更新:
插入父亲节点的右边平衡因子加加,插入父亲节点的右边平衡因子减减,
父亲节点更新后的平衡因子等于 1 或 -1 ,需要不断往上(溯源)更新,直到父亲节点的平衡因子为 0 或 更新至整棵树的根节点就停止更新。
如果父亲节点的平衡因子为 2 或 -2 时,需要对这棵子树旋转,旋转后更新平衡因子
示例
旋转情况分类
旋转分为:
左单旋 右单旋 左右双旋 右左双旋
左单旋
:新节点插入较高右子树的右侧
具象图:
抽象图:
那么左单旋是怎么旋的呢?核心步骤为:
设父亲节点为:fathernode 孩子节点为:cur
让cur的左孩子成为fathernode的右孩子,
再让fathernode成为cur的左孩子。
如下示意图
右单旋
:新节点插入较高左子树的左侧
具象图:
抽象图:
那么右单旋是怎么旋的呢?核心步骤为:
设父亲节点为:fathernode 孩子节点为:cur
让cur的右孩子成为fathernode的左孩子,
再让fathernode成为cur的右孩子
如下示意图:
左右双旋
:新节点插入在较高左子树的右侧——先左单旋再右单旋
左右双旋的核心步骤为:
设父亲节点为:fathernode
父亲的左孩子节点为:fathernodeL
父亲的左孩子节点的右孩子节点的为fathernodeLR
先让fathernodeL左单旋,再让fathernodeLR进行右单旋
这里小编直接上抽象图:
右左双旋
:新节点插入再较高右子树的左侧——先右单旋再左单旋
设父亲节点为:fathernode
父亲的 右孩子节点为:fathernodeR
父亲的右孩子节点的左孩子节点的为fathernodeRL
先对fathernodeR进行右单旋,再对fathernode进行左单旋。
示意图:
AVL树节点设计
【C++】详解C++的模板-CSDN博客
AVL树的节点需要三个指针,分别指向左孩子节点,右孩子节点,父亲节点。指向父亲节点的指针是为了能溯源更新平衡因子。
需要一个整型存储平衡因子,平衡因子在构造函数的初始化列表中初始化为 0,因为新节点左右孩子都为空。
template <class K>
class AVLTreeNode
{
public:AVLTreeNode(const K& key) //构造函数:_key(key), _left(nullptr), _right(nullptr), _FatherNode(nullptr), _bf(0){}K _key; //键值 AVLTreeNode<K>* _left;//左孩子AVLTreeNode<K>* _right;//右孩子AVLTreeNode<K>* _FatherNode;//父亲 int _bf;//平衡因子};
AVL树设计
template <class K>
class AVLTree
{typedef AVLTreeNode<K> node; node* _root;public:AVLTree() //构造函数,初始化为空树:_root(nullptr){}bool Insert(const K& key)//插入元素{
//if (nullptr == _root) //是否是空树{_root = new node(key); return true;}
//node* cur = _root;node* fathernode = nullptr;while (cur) //查找插入的位置,如果树中已经有要插入的值,则插入失败,{if (cur->_key < key){fathernode = cur;cur = cur->_right;}else if (cur->_key > key){fathernode = cur;cur = cur->_left;}else{return false;}}cur = new node(key); //新插入节点 if (fathernode->_key < cur->_key) //判断新节点应该是左孩子还是右孩子{fathernode->_right = cur;cur->_FatherNode = fathernode;}else{fathernode->_left = cur;cur->_FatherNode = fathernode;}//while (fathernode)//更新平衡因子{if (cur == fathernode->_left){fathernode->_bf--;}else if (cur == fathernode->_right){fathernode->_bf++;}//if (fathernode->_bf == 0){// 更新结束break;}else if (fathernode->_bf == 1 || fathernode->_bf == -1){// 继续往上更新cur = fathernode;fathernode = fathernode->_FatherNode;}else if (fathernode->_bf == 2 || fathernode->_bf == -2){// 子树不平衡了,需要旋转if (fathernode->_bf == 2 && cur->_bf == 1){RevolveLeft(fathernode);//左单旋}else if (fathernode->_bf == -2 && cur->_bf == -1){RevolveRight(fathernode);//右单旋}else if (fathernode->_bf == 2 && cur->_bf == -1){RevolveRightLeft(fathernode); //右左双旋 }else if (fathernode->_bf == -2 && cur->_bf == 1){RevolveLeftRight(fathernode);//左右双旋}else{assert(false); //平衡因子出问题了}break;}}return true;}}
下面通过代码的细节来深入理解旋转
详解单旋
左单旋
完整代码如下
void RevolveLeft(node *& fathernode)//左单旋
{node* cur = fathernode->_right; //父亲节点的右孩子fathernode->_right = cur->_left; //更改指向关系if (cur->_left != nullptr) //特殊情况cur->_left->_FatherNode = fathernode;//更改指向关系cur->_FatherNode = fathernode->_FatherNode;//更改指向关系if (fathernode->_FatherNode != nullptr) //为空是特殊情况,{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更改指向关系}else{fathernode->_FatherNode->_left = cur;//更改指向关系}}cur->_left = fathernode;//更改指向关系fathernode->_FatherNode = cur;//更改指向关系fathernode->_bf = 0; //更新平衡因子cur->_bf = 0;}
处理指向关系时,一定不要忘了更改父亲的指向关系
经过左单旋之后,父亲节点和右孩子节点的平衡因子都为 0 ,可参考上文图示。
特殊情况如下,如果不处理特殊情况,程序很容易崩溃
右单旋
void RevolveRight(node *& fathernode) //右单旋
{node* cur = fathernode->_left; //父亲节点的左节点fathernode->_left = cur->_right;//更新指向关系if (cur->_right != nullptr) //特殊情况cur->_right->_FatherNode = fathernode;//更新指向关系cur->_FatherNode = fathernode->_FatherNode;//更新指向关系if (fathernode->_FatherNode != nullptr)//特殊情况{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更新指向关系}else{fathernode->_FatherNode->_left = cur;//更新指向关系}}cur->_right = fathernode;//更新指向关系fathernode->_FatherNode = cur;//更新指向关系fathernode->_bf = 0;//更新平衡因子cur->_bf = 0;
}
详解双旋
左右双旋
左右双旋只需复用左单旋和右单旋即可,但平衡因子的更新却比较麻烦。
完整代码如下
void RevolveLeftRight(node *& fathernode)//左右双旋 {node* fathernodeL = fathernode->_left; //父亲节点的左孩子节点node* fathernodeLR = fathernodeL->_right;//父亲节点的左孩子节点的右孩子节点int bf = fathernodeLR->_bf; //保存平衡因子,实际是为了判断是插入了fathernodeLR左边还是右边还是fathernodeLR本身插入RevolveLeft(fathernodeL);RevolveRight(fathernode);//更新平衡因子if (bf == 0){fathernode->_bf = 0;fathernodeL->_bf = 0;fathernodeLR->_bf = 0;}else if (bf == -1){fathernode->_bf = 1;fathernodeL->_bf = 0;fathernodeLR->_bf = 0;}else if (bf == 1){fathernodeL->_bf = -1;fathernode = 0;fathernodeLR = 0;}else{assert(false);}}
平衡因子情况如下
右左双旋
完整代码如下
void RevolveRightLeft(node *& fathernode) //右左双旋 {node* fathernodeR = fathernode->_right; node* fathernodeRL = fathernodeR->_left;int bf = fathernodeRL->_bf;RevolveRight(fathernodeR);RevolveLeft(fathernode);if (bf == 0){fathernode->_bf = 0;fathernodeR->_bf = 0;fathernodeRL->_bf = 0;}else if (bf == 1){fathernode->_bf = -1;fathernodeR->_bf = 0;fathernodeRL->_bf = 0;}else if (bf == -1){fathernodeR->_bf = 1;fathernode->_bf = 0;fathernodeRL->_bf = 0;}else{assert(false); }}
平衡因子情况如下
相关文章:

【C++】详解AVL树——平衡二叉搜索树
个人主页:东洛的克莱斯韦克-CSDN博客 祝福语:愿你拥抱自由的风 目录 二叉搜索树 AVL树概述 平衡因子 旋转情况分类 左单旋 右单旋 左右双旋 右左双旋 AVL树节点设计 AVL树设计 详解单旋 左单旋 右单旋 详解双旋 左右双旋 平衡因子情况如…...

《计算机网络微课堂》2-2 物理层下面的传输媒体
请大家注意,传输媒体不属于计算机网络体系结构的任何一层,如果非要将它添加到体系结构中,那只能将其放在物理层之下。 传输媒体可分为两类:一类是导引型传输媒体,另一类是非导引型传输媒体。 在导引型传输媒体…...

【算法设计与分析】基于Go语言实现动态规划法解决TSP问题
本文针对于最近正在学习的Go语言,以及算法课实验所需内容进行Coding,一举两得! 一、前言 由于这个实验不要求向之前的实验一样做到那种连线的可视化,故可以用图形界面不那么好实现的语言进行编写,考虑到Go语言的…...

Golang单元测试
文章目录 传统测试方法基本介绍主要缺点 单元测试基本介绍测试函数基准测试示例函数 传统测试方法 基本介绍 基本介绍 代码测试是软件开发中的一项重要实践,用于验证代码的正确性、可靠性和预期行为。通过代码测试,开发者可以发现和修复潜在的错误、确保…...

mac下安装airflow
背景:因为用的是Mac的M芯片的电脑,安装很多东西都经常报错,最近在研究怎么把大数据集群上的crontab下的任务都配置到一个可视化工具中,发现airflow好像是个不错的选择,然后就研究怎么先安装使用起来,后面再…...

二进制中1的个数c++
题目描述 计算鸭给定一个十进制非负整数 NN,求其对应 22 进制数中 11 的个数。 输入 输入包含一行,包含一个非负整数 NN。(N < 10^9) 输出 输出一行,包含一个整数,表示 NN 的 22 进制表示中 11 的个数。 样例输入 100 …...

【面试干货】数据库乐观锁,悲观锁的区别,怎么实现
【面试干货】数据库乐观锁,悲观锁的区别,怎么实现 1、乐观锁,悲观锁的区别2、总结 💖The Begin💖点点关注,收藏不迷路💖 1、乐观锁,悲观锁的区别 悲观锁(Pessimistic Lo…...

移动端仪表盘,支持更多组件
05/22 主要更新模块概览 定位函数 快捷筛选 轨迹图表 时间组件 01 表单管理 1.1 【表单组件】- 表单关联新增支持自定义按钮样式 说明: 表单关联-关联数据按钮,原仅支持默认按钮样式,现增加关联数据按钮自定义功能,满…...

科技产业园3D探秘:未来科技之城的奇幻之旅
在数字时代的浪潮中,科技产业园区成为了推动城市经济发展、科技创新的重要引擎。 当我们打开科技产业园的3D可视化模型,仿佛穿越时空,来到了一个充满奇幻色彩的科技世界。在这里,高楼大厦鳞次栉比,绿色植被点缀其间&am…...

【Python搞定车载自动化测试】——Python基于Pytest框架实现UDS诊断自动化(含Python源码)
系列文章目录 【Python搞定车载自动化测试】系列文章目录汇总 文章目录 系列文章目录💯💯💯 前言💯💯💯一、环境搭建1.软件环境2.硬件环境 二、目录结构三、源码展示1.诊断基础函数方法2.诊断业务函数方法…...

探索SPI单线传输模式中时钟线与数据传输的简化
探索SPI单线传输模式:时钟线与数据传输的简化之道 在当今的嵌入式系统和微控制器通信中,串行外设接口(SPI)因其高速、全双工和同步的特点而广受欢迎。然而,随着设备尺寸和复杂性的不断减少,对SPI通信的简化…...

使用FFmpeg推流实现在B站24小时点歌直播
使用FFmpeg推流实现在B站24小时点歌直播 本文首发于个人博客 安装FFmpeg centos7 https://www.myfreax.com/how-to-install-ffmpeg-on-centos-7/ https://linuxize.com/post/how-to-install-ffmpeg-on-centos-7/ 使用FFmpeg在B站直播 https://zhuanlan.zhihu.com/p/2395…...

汽车防抱死制动系统ABS的单片机程序Proteus仿真设计
次设计对汽车防抱死系统进行简单的设计,针对车速、轮速两个信号进行分析,并根据最佳滑移率计算。采用对比实时滑移率对比分析,ECU控制制动器进行制动力调节使滑移率在制动过程处于最佳范围,保证系统具有良好制动性能。 汽车的制动液压调节器主要包含以下几个部件:调压电磁…...

IOS开发者证书快捷申请
App Uploader 在进行iOS应用开发中,可以借助appuploader辅助工具进行证书制作、上传和安装测试等操作。首先,您需要访问官方网站获取最新版本的appuploader。最新版本已经优化了与Apple账号的登录流程,无需支付688元,并提供了Windows版和Mac版供用户选择。下载完成后,解压…...

python 火焰检测
在日常生活,总是离不开火,有时候我们需要预防火灾发生,但是我们又不可能一直盯着,这时候我们就需要一款程序帮我们盯着,一旦发生火灾从而告知我们,今天就带大家编写这么一款应用。 安装需要的库 pip install opencv-python 代码实现 import cv2 # Library for…...

栈——顺序存储
#include<stdio.h> #define MaxSize 10 //栈的所有操作时间复杂度都是O(1) //定义 typedef struct{int data[MaxSize];int top; //栈顶指针,永远指向栈顶元素 }SqStack;//初始化,使栈顶指针指向-1 void InitStack(SqStack &S){S.top-1; }…...

军队仓库管理系统|DW-S301系统特点
部队仓库管理系统DW-S301系统通过数据采集、互联网和物联网技术,实现数字化智能管控,以提高军用物资的仓储准确率和流转率,缩短周转时间,降低库存成本,也有助于消除生产过程中的不确定性。 系统功能:通过部…...

MySQL和MongoDB数据库的区别
MySQL和MongoDB数据库的区别 随着大数据和云计算技术的兴起,数据库的选择成为开发者和架构师必须面对的重要决策。MySQL和MongoDB作为关系型数据库和非关系型数据库的代表,在各自领域都有着广泛的应用。本文将从多方面详细比较MySQL和MongoDB࿰…...

类脑计算和量子计算、人工智能的关系
According to www.iAsk.ai Ask Ai Search Engine: 类脑计算、量子计算和人工智能是三个不同但相关的领域。它们在不同层面上探索和利用了不同的计算模型和技术,但都旨在推动计算能力的发展和创新。 类脑计算是一种受到人脑神经系统启发的计算模型。它试图通过模拟…...

Qt5 互动地图,实现无人机地面站效果
一、概述 本文主要通过Qt5opmapcontrol实现一个简单的无人机地面站效果。opmapcontrol是一个比较古老的QT开源地面站库,可选择谷歌地图,必应地图, 雅虎地图,GIS等。可直接使用源码,也可以编译生成库进行调用。实现效果…...

【文末附gpt升级方案】TikTok Symphony AI套件:智能视频制作的新篇章
TikTok Symphony AI套件:智能视频制作的新篇章 摘要 随着短视频平台的兴起,视频内容的创作与制作已成为品牌方吸引用户、传递信息的重要手段。TikTok作为全球领先的短视频平台,近日宣布推出Symphony AI套件,旨在通过人工智能技术…...

面试回答——有高并发、高性能、高可用系统架构设计实践以及性能调优经验
🌈hello,你好鸭,我是Ethan,一名不断学习的码农,很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 🏃人生之义,在于追求,不在成败,勤通…...

rocketmq初识
package com.ldj.rocketmq.producer;import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message;import java.nio.charset.StandardCharsets;/*** User: ldj* Date: 2024/3/26* Time: 2:26* Description: 单向消息生产…...

php 使用phpoffice导出导出excel
荆轲刺秦王 在PHP中,可以使用 PhpSpreadsheet 库来创建和导出Excel文件。PhpSpreadsheet 是一个纯PHP 编写的组件库,它使用现代 PHP 写法,代码质量和性能比 PHPExcel 高不少,完全可以替代PHPExcel(PHPExcel已不再维护…...

安装docker版elasticsearch和kibana
本文将介绍用docker的方式安装elasticsearch和kibana,并用浏览器访问elasticsearch。这里的elasticsearch主要给测试环境使用,因此不会设置https和密码。kibana是elasticsearch的前端,可以用来访问elasticsearch,展示数据图表、搜…...

大语言模型的工程技巧(四)——梯度检查点
相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型:从线性回归到通用人工智能》,欢迎有兴趣的读者多多支持。 本文将讨论如何利用梯度检查点算法来减少模型在训练时候(更准确地说是运行反向传播算法时)的内存开支。…...

批量复制文件智能删除已复制,轻松管理文件新体验!让您的文件整理更高效无忧
在信息爆炸的时代,文件管理无疑成为我们日常生活和工作中不可或缺的一部分。面对堆积如山的文件,我们时常陷入无尽的复制、粘贴、删除循环中,不仅耗时耗力,还容易出错。但今天,我要向您推荐一款颠覆传统的文件管理工具…...

从零训练yolov8
1.收集数据 2.数据标注 pip install labelimg3.划分数据集 0.2的验证机0.8的训练集 import os from shutil import copyfile from sys import exit import randomsource r"D:\Data\imgs\screenc" \\ target_train r"D:\Data\imgs\datasets\mydata\images\t…...

民国漫画杂志《时代漫画》第14期.PDF
时代漫画14.PDF: https://url03.ctfile.com/f/1779803-1247458399-6732ac?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了,截止1937年6月战争来临被迫停刊共发行了39期。 ps:资源来源网络!...

maven-依赖管理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Maven BOM二、使用三、SpringBoot的依赖管理 前言 提示:这里可以添加本文要记录的大概内容: 依赖管理能带来啥: 避免…...