中英文网站建设/关键词排名点击
前言
因为二叉搜索树在插入的时候最坏的情况可能会变成一条单一链表,从而使查找或者插入的时候消耗大量的时间。所以为了解决这一情况诞生了平衡二叉搜索树,其作用是为了减少二叉搜索树的整体高度,从而使查找插入删除的效率提高。
一、平衡二叉搜索树插入的实现思路
平衡二叉搜索树是二叉搜索树的升级版,通过插入和删除时旋转子树的方法,提高了查找的效率。
那么对于插入的升级将会是本节的重点内容。主要思路如下:
a. 插入结点之后仍然是AVL树,;
b. 插入结点之后不再满足AVL树条件,则进行调整,根据导致不平衡的原因,分为:
(a) LL型――单旋转调整
(b) LR型――双旋转调整
(c)RL型――双旋转调整
(d) RR型――单旋转调整
那么如何确定子树需要旋转呢?我们会在树的节点中增加高度差计数,通常情况下为“右子树高度-左子树高度”。如果高度差超过2则需要进行调整。
1、插入节点后仍然是AVL树
如果插入节点之后向上修改节点记录高度的数据仍然能够报纸平衡,那么AVL树将不需要进行调整:
图1-1 节点平衡
节点平衡的情况很多,主要在于两种情况,插入的时候二叉树的高度没有改变。或者改变之后仍然在高度差的范围之内。
那么在代码中我们不难看出能够分为三种情况:
(1)没有父节点继续修改记录值。
(2)差入的时候子树高度没有改变,就不需要继续向上修改父节点的记录值。
(3)向上遍历的时候发现子树的高度改变了,就需要判断子树在父节点的哪一边,从而对父节点的记录值进行加减。并继续向上修改父节点记录值。
2、插入之后不是AVL树
这种情况我们通常就需要旋转子树节点以达到旋转之后使子树仍然保持是AVL树。
需要旋转的情况大致分为4种,左单旋、右单旋、左右双旋、右左双旋。
那么接下来分别讲讲需要旋转的4种情况。
2.1、左单旋
左单旋的情况出现在父节点右节点右子树超高的情况:
图1-2 需要左单旋的例图
其中右图为n=0的特殊情况。
左旋转就将图中的2节点改为1的父节点,b改为1的右子树,这样2的左子树是1。这样总体高度相较于插入之前没有改变都是n+2。就不需要继续向上查找父节点了。
图1-3 左单旋
2.2、右单旋
右单旋就相当于是左单旋镜面对称之后的情况,是父节点的左节点的左子树超高需要旋转。
图1-4 右单旋
和左单旋类似,将1移动为0的右子树,0成为1的父节点,把0的右子树b给1当成左子树。
2.3、左右双旋
左右双旋发生在父节点的左子树超高,但是左节点的右子树更高一点。
图1-5 需要左右双旋举例图
其中”A“为基本情况,当”n“等于0的时候会比较特殊,这个时候”-1“右子树的记录值为0。“B”、“C”是“n”不等于0的时候的两种状况,表示为0的节点记录值分别为1或者-1.
图1-6 左右双旋
需要注意的是根据“0”节点位置的记录值的不同,旋转完成后的子节点记录值会不同。例如当“b2”更高的时候,节点“-1”的右子树会矮一截。相反如果“b1”比“b2”更高,则节点“1”的左子树会矮一截。如果从左向右看方框形态的子树,会发现他们的顺序并没有改变,只是链接的节点改变了。
2.4、右左双旋
和左右双旋的情况正好相反。
图1-7 右左双旋
和左右双旋一样,根据节点“2”的记录值不同,旋转完成后子节点的记录值也会不同。如果记录值为0,那么“1”和“3”节点的记录值都为0,如果为1则节点“1”的记录值为-1,如果为-1则节点“3”的记录值为1。
二、平衡二叉搜索树实现
#include <iostream>
#include <string>
#include <assert.h>
#include <utility>
#include <algorithm>
#include <cmath>using namespace std;template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data = T()): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _pLeft;AVLTreeNode<T>* _pRight;AVLTreeNode<T>* _pParent;T _data;int _bf; // 节点的平衡因子
};// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:AVLTree(): _pRoot(nullptr){}// 在AVL树中插入值为data的节点bool Insert(const T& data){// 根节点为空直接插入并修改if(nullptr == _pRoot){_pRoot = new Node(data);return true;}Node* parent = nullptr;Node* cur = _pRoot;// 找到对应的插入节点while(cur){parent = cur;if(data < cur->_data){cur = cur->_pLeft;}else if(data > cur->_data){cur = cur->_pRight;}else{return false;}}// 插入节点cur = new Node(data);if(data < parent->_data){parent->_pLeft = cur;cur->_pParent = parent;}else if(data > parent->_data){parent->_pRight = cur;cur->_pParent = parent;}else{assert(false);return false;}// AVL树向上检查,修改平衡因子while(parent){// 修改平衡因子if(parent->_pLeft == cur){parent->_bf--;}else if(parent->_pRight == cur){parent->_bf++;}else{// 节点存储的有问题assert(false);}// 考虑是否旋转子树// 1. 为0不需要继续向上修改if(0 == parent->_bf){break;}// 2. 等于1或者-1需要继续向上修改平衡因子else if(1 == parent->_bf || -1 == parent->_bf){cur = parent;parent = parent->_pParent;}// 3. 因子等于2或者-2就说明需要旋转else if(-2 == parent->_bf || 2 == parent->_bf){// 继续分为4种情况// 3.1. 左子树的左子树超高if(-2 == parent->_bf && -1 == cur->_bf){RotateR(parent);}// 3.2. 右子树的右子树超高else if(2 == parent->_bf && 1 == cur->_bf){RotateL(parent);}// 3.3. 左子树的右子树超高else if(-2 == parent->_bf && 1 == cur->_bf){RotateLR(parent);}// 3.4. 右子树的左子树超高else if(2 == parent->_bf && -1 == cur->_bf){RotateRL(parent);}break;}}return true;}// AVL树的验证bool IsAVLTree(){return _IsAVLTree(_pRoot).second;}private:// 根据AVL树的概念验证pRoot是否为有效的AVL树pair<int, bool> _IsAVLTree(Node* pRoot){if(pRoot == nullptr){return make_pair(0, true);}pair<int, bool> lson = _IsAVLTree(pRoot->_pLeft);cout << pRoot->_data << " ";pair<int, bool> rson = _IsAVLTree(pRoot->_pRight);int hight = max(rson.first, lson.first) + 1;bool Is = rson.second && lson.second && abs(rson.first - lson.first) <= 1;return make_pair(hight, Is);}// size_t _Height(Node* pRoot)// {// if(nullptr == pRoot)// {// return 0;// }// else{// return max<size_t>(_Height(pRoot->_pLeft), _Height(pRoot->_pRight)) + 1;// }// }// 右单旋void RotateR(Node* pParent){Node* SubL = pParent->_pLeft;Node* SubLR = SubL->_pRight;Node* parent = pParent->_pParent;pParent->_pLeft = SubLR;pParent->_pParent = SubL;if(SubLR)SubLR->_pParent = pParent;SubL->_pRight = pParent;SubL->_pParent = parent;if(pParent == _pRoot){_pRoot = SubL;}else{if(parent->_pLeft == pParent){parent->_pLeft = SubL;}else if(parent->_pRight == pParent){parent->_pRight = SubL;}else{assert(false);}}pParent->_bf = SubL->_bf = 0;}// 左单旋void RotateL(Node* pParent){Node* SubR = pParent->_pRight;Node* SubRL = SubR->_pLeft;Node* parent = pParent->_pParent;pParent->_pRight= SubRL;pParent->_pParent = SubR;if(SubRL)SubRL->_pParent = pParent;SubR->_pLeft = pParent;SubR->_pParent = parent;if(pParent == _pRoot){_pRoot = SubR;}else{if(parent->_pLeft == pParent){parent->_pLeft = SubR;}else if(parent->_pRight == pParent){parent->_pRight = SubR;}else{assert(false);}}pParent->_bf = SubR->_bf = 0;}// 右左双旋void RotateRL(Node* pParent){Node* SubR = pParent->_pRight;Node* SubRL = SubR->_pLeft;int bf = SubRL->_bf;RotateR(SubR);RotateL(pParent);if(0 == bf){SubR->_bf = SubRL->_bf = pParent->_bf = 0;}else if(-1 == bf){SubRL->_bf = pParent->_bf = 0;SubR->_bf = 1;}else if(1 == bf){SubRL->_bf = SubR->_bf = 0;pParent->_bf = -1;}else{assert(false);}}// 左右双旋void RotateLR(Node* pParent){Node* SubL = pParent->_pLeft;Node* SubLR = SubL->_pRight;int bf = SubLR->_bf;RotateL(SubL);RotateR(pParent);if(0 == bf){SubL->_bf = SubLR->_bf = pParent->_bf = 0;}else if(1 == bf){SubLR->_bf = pParent->_bf = 0;SubL->_bf = -1;}else if(-1 == bf){SubLR->_bf = SubL->_bf = 0;pParent->_bf = 11;}else{assert(false);}}private:Node* _pRoot;
};
需要注意的是旋转之后改变了节点的顺序,那么需要重新连接父节点或者改变根节点。
三、测试
#include "AVLtree.hpp"int main()
{AVLTree<int> avltree;for(int i = 0; i < 100; ++i){if(i == 2){int j = 0;}avltree.Insert(i);}if(avltree.IsAVLTree()){cout << "avltree是二叉搜索树" << endl;}else{cout << "avltree不是二叉搜索树" << endl;}return 0;
}
作者结语
平衡二叉搜索树的难度在于旋转的时候很容易转晕,序结合图形仔细编写代码。容易出错的点还在于不要把“=”写成“==”,不然带代码出错了找半天不知道在哪错了。
以此类推平衡二叉搜索树的删除大致也需要这么几种状况。父节点记录值等于1或者-1不需要旋转,父节点的节点为0,继续向上修改记录值,父节点的记录值为2或者-2,就需要旋转了。
如果有时间,或者有小伙伴有需要的话,下一节就将平衡二叉搜索树的删除实现出来。
相关文章:

平衡二叉搜索树插入的实现
前言 因为二叉搜索树在插入的时候最坏的情况可能会变成一条单一链表,从而使查找或者插入的时候消耗大量的时间。所以为了解决这一情况诞生了平衡二叉搜索树,其作用是为了减少二叉搜索树的整体高度,从而使查找插入删除的效率提高。 一、平衡二…...

ROS理论与实践学习笔记——2 ROS通信机制之通信机制实践
5.1 话题发布 需求描述:编码实现乌龟运动控制,让小乌龟做圆周运动。 实现分析: ①乌龟运动控制实现,关键节点有两个,一个是乌龟运动显示节点 turtlesim_node,另一个是控制节点,二者是订阅发布模…...

CDGA|数据治理:策略与价值的深度融合
在当今这个数据驱动的时代,企业数据治理的重要性日益凸显。数据不仅是企业的核心资产,更是驱动业务决策、优化运营流程、创新产品服务的关键力量。然而,要让数据治理真正发挥价值,企业需要采取一系列策略来确保数据的准确性、完整…...

49. 建模软件绘制3D场景(Blender)
这文章主要给大家科普一些三维模型创建、美术和程序员协作的相关问题。 三维建模软件作用 对于简单的立方体、球体等模型,你可以通过three.js的几何体相关API快速实现,不过复杂的模型,比如一辆轿车、一栋房子、一个仓库,一般需要…...

如何使用 DomCrawler 进行复杂的网页数据抓取?
在互联网时代,数据是宝贵的资源。无论是市场分析、客户洞察还是内容聚合,从网页中抓取数据都是一项关键技能。Symfony 的 DomCrawler 是一个强大的工具,可以帮助开发者从复杂的网页中提取所需的数据。本文将详细介绍如何使用 DomCrawler 进行…...

维修服务品牌小程序渠道客获
维修服务可覆盖家电电器、家具、手机电脑等多个细分类目,本地同城也有着不少目标用户且该行业客户有着一定粘性,比如服务完成递上一张名片/电话留存则有着较高复购率。 线上各行业便捷化程度提升,服务进店/上门都需要快捷完成,包…...

【全新课程】正点原子《基于GD32 ARM32单片机项目实战入门》培训课程上线!
正点原子《基于GD32 ARM32单片机项目实战入门》全新培训课程上线啦!正点原子工程师手把手教你学!彻底解决ARM32单片机项目入门难的问题! 一、课程介绍 本课程专为ARM32单片机的入门学习者设计,涵盖了环境搭建、编程软件使用、模…...

Kafka系列之:安装使用kafka_exporter详细步骤
Kafka系列之:安装使用kafka_exporter详细步骤 一、kafka_exporter二、下载kafka_exporter三、理解Topic Metrics指标四、理解Consumer Groups Metrics指标五、启动kafka_exporter六、查看页面七、systemctl托管服务一、kafka_exporter kafka_exporter源码kafka_exporter下载页…...

Paddlets时间序列集成模型回测实战:MLPRegressor、NHiTSModel与RNNBlockRegressor
好的,我们继续深入理解代码的每个部分。以下是每个主要模块的详细解释: 1. 导入模块和库 import json import os import glob import pandas as pd from tqdm import tqdm from paddlets.datasets import TSDataset from paddlets.transform import StandardScaler from pa…...

【anki】显示 “连接超时,请更换网络后重试” 怎么办
文章目录 前言一、问题描述二、解决方案 前言 在 anki同步 时遇到的问题 一、问题描述 二、解决方案 从电信换为了移动热点,电脑手机都同步成功了...

第一批学习大模型的程序员,已经碾压同事了,薪资差距都甩出一条街了...
前言 随着人工智能技术的突飞猛进,AI大模型已成为引领未来的核心技术。从ChatGPT的横空出世到GPT-4o的震撼发布,AI技术正以前所未有的速度改变着我们的生活和工作方式。 在这场AI革命中,企业对AIGC人才的需求正以指数级增长。据《AIGC就业趋…...

Unity NetCode 客户端连接不上服务器,局域网模式 Failed to connect to server.
报错代码: Failed to connect to server. 报错截图: 解决办法: 服务端:绑定127.0.0.1和端口 客户端:写好对应服务端ip和端口 如何查看服务端所在局域网IP,192.xxx.xxx.xx,就不用教了吧。 注意这个钩,得点下,默认不勾选。 意…...

C++远端开发环境安装(centos7)
使用VMWare安装centos7 启用网卡设备 修改文件/etc/sysconfig/network-scripts/ifcfg-ens33中的ONBOOTyes 重启网络服务 systemctl restart network 配置yum仓库 直接将如下内容覆盖原有的/etc/yum.repos.d/CentOS-Base.repo文件 清理yum缓存 yum clean all 刷新yum yu…...

LaTeX 编辑器-TeXstudio
TeXstudio 是一款开源跨平台 LaTeX 编辑软件,界面与 Texmaker 类似。TeXstudio 为用户提供互动式拼写检查、代码折叠、语法高亮、代码提示和自动完成等特性,功能丰富,界面美观,但软件本身不提供底层功能,需要使用者自行…...

[深度学习]循环神经网络
1 自然语言处理概述 语料:一个样本,句子/文章语料库:由语料组成词表:分词之后的词语去重保存成为词表 2 词嵌入层 import jieba import torch.nn as nn import torch # 文本数据 text北京东奥的进度条已经过半,不少外…...

景联文科技精准数据标注:优化智能标注平台,打造智能未来
景联文科技是一家致力于为人工智能提供全面数据标注解决方案的专业公司。 拥有一支由经验丰富的数据标注师和垂直领域专家组成的团队,确保数据标注的质量和专业性。 自建平台功能一站式服务平台,提供从数据上传、标注、审核到导出的一站式服务࿰…...

商场促销——策略模式
文章目录 商场促销——策略模式商场收银软件增加打折简单工厂实现策略模式策略模式实现策略与简单工厂结合策略模式解析 商场促销——策略模式 商场收银软件 时间:2月27日22点 地点:大鸟房间 人物:小菜、大鸟 “小菜,给你…...

万字长文,AIGC算法工程师的面试秘籍,推荐收藏!
目录先行 AI绘画基础: 什么是DreamBooth技术?正则化技术在AI绘画模型中的作用? 深度学习基础: 深度学习中有哪些常用的注意力机制?如何寻找到最优超参数? 机器学习基础: 判别式模型和生成…...

一些超好用的 GitHub 插件和技巧
聊聊我平时使用 GitHub 时学到的一些插件、技巧。 浏览器插件 在我的另一篇博客 浏览器插件推荐 里提到过跟 GitHub 相关的一些插件,这里重复下: Sourcegraph:在线打开项目,方便阅读,将 GitHub 变得和 IDE …...

记Flink SQL 将数据写入 MySQL时的一个优化策略
Flink SQL 将数据写入 MySQL 时,如果主分片数较少,可以通过调整 MySQL 的主分片数来提高读写性能 1. 检查当前的分片设置 在 MySQL 中,使用以下 SQL 查询来查看当前的分片情况: SHOW VARIABLES LIKE innodb_buffer_pool_size; …...

QT-自定义信号和槽对象树图形化开发计算器
1. 自定义信号和槽 核心逻辑: 需要有两个类,一个提供信号,另一个提供槽。 然后在窗口中将 信号和槽 链接起来。 示例目标: 创建一个 Teacher 类,提供信号。 创建一个 Student 类,提供槽。 实现步骤&…...

C# 字符串(String)的应用说明一
一.字符串(String)的应用说明: 在 C# 中,更常见的做法是使用 string 关键字来声明一个字符串变量,也可以使用字符数组来表示字符串。string 关键字是 System.String 类的别名。 二.创建 String 对象的方法说明&#x…...

Redis缓存淘汰算法详解
文章目录 Redis缓存淘汰算法1. Redis缓存淘汰策略分类2. 会进行淘汰的7种策略2.1 基于过期时间的淘汰策略2.2 基于所有数据范围的淘汰策略 3. LRU与LFU算法详解4. 配置与调整5. 实际应用场景 LRU算法以及实现样例LFU算法实现1. 数据结构选择2. 访问频率更新3. 缓存淘汰4. 缓存插…...

Sklearn 与 TensorFlow 机器学习实用指南
Sklearn 与 TensorFlow 机器学习实用指南 Scikit-learn(Sklearn) 1. 简介 2. 特点 3. 基本用法 TensorFlow 1. 简介 2. 特点 3. 基本用法 选择指南 总结 🎈边走、边悟🎈迟早会好 关于使用 Scikit-learn(Sk…...

RabbitMQ 界面管理说明
1.RabbitMQ界面访问端口和后端代码连接端口不一样 界面端口是15672 http://localhost:15672/ 后端端口是 5672 默认账户密码登录 guest 2.总览图 3.RabbitMq数据存储位置 4.队列 4.客户端消费者连接状态 5.队列运行状态 6.整体运行状态...

设备管理与点巡检系统
在现代企业管理中,设备的高效运作至关重要。为此,我们推出了设备管理与点巡检系统,通过自动化管理提升设备使用效率,保障生产安全。 系统特点 设备全生命周期管理 系统涵盖设备的各个阶段,从设备管理、点检、巡检、保…...

计算机网络的整体认识---网络协议,网络传输过程
计算机网络背景 网络发展 独立模式: 计算机之间相互独立; 网络互联: 多台计算机连接在一起, 完成数据共享; 局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起; 广域网WAN: 将远隔千里的计算机都连在一起;所谓 "局域网" 和 "广域网" 只是一个相…...

Battery management system (BMS)
电池管理系统(BMS)是一种专门用于监督电池组的技术,电池组由电池单元组成,在电气上按照行x列矩阵配置进行排列,以便在预期的负载场景下,在一段时间内提供目标范围的电压和电流。 文章目录 电池管理系统是如…...

和GPT讨论ZNS的问题(无修改)
主题:ZNS相关的疑问讨论,GPT逻辑回答,要是开高阶版本估计回答的更明智些。 ZNS的写和传统写的区别 ChatGPT 说: ChatGPT ZNS(Zoned Namespace)与传统写入方式的主要区别体现在以下几个方面: …...

6.8方框滤波
基本概念 方框滤波(Box Filter)是一种基本的图像处理技术,用于对图像进行平滑处理或模糊效果。它通过在图像上应用一个固定大小的方框核(通常是矩形),计算该区域内像素值的平均值来替换中心像素的值。这种…...