AVL树的旋转
目录
1.平衡因子
2.旋转
a.节点定义
b.插入
插入
平衡因子更新
旋转
左单旋
右单旋
右左双旋
左右双旋
3.AVL树的验证
1.平衡因子
我们知道搜索二叉树有缺陷,就是不平衡,比如下面的树

什么是搜索树的平衡?就是每个节点的左右子树的高度差不超过1,称平衡的搜索树为AVL树, 那我们怎么控制搜索树的平衡呢?
给出了平衡因子,每个节点的平衡因子=节点右子树的高度-节点左子树的高度(或者反过来,我们用前面那种)平衡因子=[-1,1],当超出这个范围,搜索树就不平衡了
树的平衡因子可以这样表示:

2.旋转
a.节点定义
template<class K,class V>
class AVLNode
{
public:typedef AVLNode<K,V> Node;AVLNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}Node* _left;Node* _right;Node* _parent;pair<K, V> _kv;//库里面提供的结构体,表示key和valueint _bf;//平衡因子};

b.插入
插入
左边小插左边,右边大插右边
template<class K,class V>
class AVLTree
{
public:typedef AVLNode<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 (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_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;//更新平衡因子//...............return true;}protected:
//........
private:Node* _root=nullptr;
};
平衡因子更新
前面都好理解插入一个节点,那插入节点后平衡因子怎么更新呢?

//更新平衡因子while (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 1 || parent->_bf == -1){//继续更新parent = parent->_parent;cur =cur->_parent;}else if (parent->_bf == 0){//已经平衡break;}else if (parent->_bf == 2 || parent->_bf == -2){//进行旋转//...........}else{assert(false);//有可能不会出现上面的情况,出现大问题了,立马断死}break;//直接跳出了}
旋转
有四种情况需要旋转

else if (parent->_bf == 2 || parent->_bf == -2){// 进行旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度if(parent->_bf==2&&cur->_bf==1){ }else if (parent->_bf == 2 && cur->_bf == -1){}else if (parent->_bf == -2 && cur->_bf == 1){}else if (parent->_bf == -2 && cur->_bf == -1){}else{assert(false);//有可能不会出现上面的情况,出现大问题了,立马断死}}
左单旋

代码怎么写呢?
是不是感觉这样就链接上了,其实不对的,每个节点的父亲也要更新的
你以为又结束了吗?

protected:void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)//有可能subRL是空subRL->_parent = parent;//记录父亲的父亲节点Node* pparent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (pparent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}subR->_parent = pparent;}//更新平衡因子parent->_bf = subR->_bf = 0; }
总结:更新节点指向是一定要更新他的父亲节点的指向
右单旋
和左单旋是类似的,读者可以模仿上面来分析,自己把它写出来

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subR->_right;parent->_left = subLR;if (subLR) //有可能subLR是空subLR->_parent = parent;//记录父亲的父亲节点Node* pparent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (pparent == nullptr){_root = subL;_root->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}subL->_parent = pparent;}//更新平衡因子parent->_bf = subL->_bf = 0;}
右左双旋

代码怎么写呢?
我们可以对单旋进行复用

这样就可以了吗?不是,单旋会把平衡因子都置为0,所以还要更新平衡因子

void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//记录平衡因子int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);//更新平衡因子if (bf == 1){subRL->_bf = 0;subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){subRL->_bf = 0;subR->_bf = 1;parent->_bf = 0;}else if (bf == 0){subRL->_bf = 0;subR->_bf = 0;parent->_bf = 0;}else{assert(false);}}
左右双旋
类似的,读者自行分析

void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 1){parent->_bf = 0;subLR->_bf = 0;subL->_bf = -1;}else if (bf == -1){parent->_bf = 1;subLR->_bf = 0;subL->_bf = 0;}else if (bf== 0){parent->_bf = 0;subLR->_bf = 0;subL->_bf = 0;}else{assert(false);}}
else if (parent->_bf == 2 || parent->_bf == -2){// 进行旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度if(parent->_bf==2&&cur->_bf==1){ RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else{assert(false);//有可能不会出现上面的情况,出现大问题了,立马断死}break;//直接跳出}
3.AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
1. 验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
2. 验证其为平衡树
每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子),节点的平衡因子是否计算正确
第一点很简单啦!!!!
void Inorder(){_Inorder(_root);cout << endl;}
void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);cout << root->_kv.first << ' ';_Inorder(root->_right);}
怎么验证平衡树呢?
bool Isbalance(){return _Isbalance(_root);}bool _Isbalance(Node* root){if (root == nullptr)return true;int leftH = _Height(root->_left);int rightH = _Height(root->_right);//用于快速判断哪个节点错误if (rightH - leftH != root->_bf){cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}//不只检查本节点左右子树的平衡,其他节点的子树也要检查return abs(leftH - rightH) < 2&& _Isbalance(root->_left)&& _Isbalance(root->_right);}int Height(){return _Height(_root);}int _Height(Node* root){if (root == nullptr){return 0;}int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH+ 1;}
你们可以用这两个用例
void testAVLtree1()
{/*int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,14 };*/int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };AVLTree<int, int> av;for (auto e : a){if (e == 14){int a = 0;}av.Insert(make_pair(e, e));}av.Inorder();cout << av.Isbalance()<<endl;
}
也要用随机数验证
void testAVLtree2()
{srand(time(0));const size_t N = 500000;AVLTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand() + i;t.Insert(make_pair(x, x));//cout << t.IsBalance() << endl;}//t.Inorder();cout << t.Isbalance() << endl;cout << t.Height() << endl;
}
这两个都过了说明你的树就没问题了
相关文章:
AVL树的旋转
目录 1.平衡因子 2.旋转 a.节点定义 b.插入 插入 平衡因子更新 旋转 左单旋 右单旋 右左双旋 左右双旋 3.AVL树的验证 1.平衡因子 我们知道搜索二叉树有缺陷,就是不平衡,比如下面的树 什么是搜索树的平衡?就是每个节点的左右子树的…...
C++(动态规划之拆分整数)
其实我交上去都有点似懂非懂 题目:(343. 整数拆分 - 力扣(LeetCode)) 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 …...
unix C之环境变量
什么是环境变量 每个进程都有自己的一张环境变量表,表中的每个条目都是形如 keyvalue 的键值对形式的环境变量。 进程可以通过环境变量访问计算机资源。 在终端下输入env命令,可以查看环境变量列表。 通过echo $name 可以查看某个环境变量的值。 环…...
Flutter实战记录-协作开发遇到的问题
一.前言 Android项目使用了混合架构,部分模块使用Flutter进行开发。在电脑A上开发的项目提交到git仓库,电脑B拉取后进行操作,遇到两个问题,特此做一下记录; 二.问题A Settings file ‘D:\xxx\settings.gradle’ line…...
Linux 安装JDK和Idea
安装JDK 下载安装包 下载地址: Java Downloads | Oracle (1) 使用xshell 上传JDK到虚拟机 (2) 移动JDK 包到/opt/environment cd ~ cd /opt sudo mkdir environment # 在 /opt下创建一个environment文件夹 ls# 复制JDK包dao /opt/environment下 cd 下载 ls jd…...
c#绘制渐变色的Led
项目场景: c#绘制渐变色的button using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using static System.Windows.Forms.AxHost;namespace WindowsFormsApp2 {public class Gradie…...
LifeCycle之ProcessLifeCycleOwner
问题:想要知道应用程序当前处在前台、后台、或从后台回到前台,想要知道应用的状态, LifeCycle提供了ProcessLifeCycleOwner的类,方便我们知道整个应用程序的生命周期情况 ProcessLifeCycleOwner 使用方法 1.首先添加依赖 imple…...
C++ | Leetcode C++题解之第79题单词搜索
题目: 题解: class Solution { public:bool exist(vector<vector<char>>& board, string word) {rows board.size();cols board[0].size();for(int i 0; i < rows; i) {for(int j 0; j < cols; j) {if (dfs(board, word, i, …...
如何通过PHP语言实现远程控制空调
如何通过PHP语言实现远程控制空调呢? 本文描述了使用PHP语言调用HTTP接口,实现控制空调,通过不同规格的通断器,来控制不同功率的空调的电源。 可选用产品:可根据实际场景需求,选择对应的规格 序号设备名称…...
【AI+换脸换装】从OpenAI 探索色情露骨内容领域浅聊AI换脸换装
5月9日消息,据外电报道,OpenAI 周三发布了文档草案,阐述了它希望 ChatGPT 及其其他人工智能技术如何运作。冗长的Model Spec 文件的一部分透露,该公司正在探索进军色情和其他露骨内容领域。 看完这个,心里有点惊讶&am…...
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
Flutter笔记 Widgets Easier组件库(13)使用底部弹窗 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this …...
RobbitMQ基本消息队列的消息发送过程
RabbitMQ: One broker to queue them all | RabbitMQ RabbitMQ官网 SpringAmqp的官方地址:Spring AMQP 代码示例:对着代码看应该能看明白 publisher:消息发送者的代码示例 package cn.itcast.mq.helloworld;import com.rabbitmq.client.Channel; import com.rabb…...
MongoDB聚合运算符:$topN
MongoDB聚合运算符:$topN 文章目录 MongoDB聚合运算符:$topN语法用法关于null和缺失值的处理BSON数据类型排序 举例查找三个得分最高的查找全部游戏中三个最高的得分基于分组key来计算参数n $topN聚合运算符返回分组中指定顺序的最前面 n个元素…...
什么是顶级域名、二级域名、三级域名?
什么是顶级域名、二级域名、三级域名? 一般域名都由两部分组成,中间用“.”隔开,一个域名是几级域名,简单的可以通过数“.”的方式来判断。 如baidu.com,它是由baidu和后缀“.com”组成,我们可以认定它是顶…...
[Android]四大组件简介
在 Android 开发中,“四大组件”(Four Major Components)是指构成 Android 应用程序的四种核心组件,它们通过各自的方式与系统交互,实现应用的多样功能。这些组件是:Activity、Service、Broadcast Receiver…...
一次完整的GC流程
Java堆中内存区分 Java的堆由新生代(Young Generation)和老年代(Old Generation)组成。新生代存放新分配的对象,老年代存放长期存在的对象。 新生代(Young)由年轻区(Eden&a…...
GAME101-Lecture06学习
前言 上节课主要讲的是三角形的光栅化。重要的思想是要利用像素的中心对三角形可见性的函数进行采样。 这节课主要就是反走样。 课程链接:Lecture 06 Rasterization 2 (Antialiasing and Z-Buffering)_哔哩哔哩_bilibili 反走样引入 通过采样,得到…...
202203青少年软件编程(Python)等级考试试卷(二级)
第 1 题 【单选题】 关于Python中的列表,下列描述错误的是?( ) A :列表是Python中内置可变序列,是若干元素的有序集合; B :列表中的每一个数据称为“元素”; C :在Python中,一个列表中的数据类型可以各不相同; D :可以使用s[1]来获取列表s的第一个元素。 正确答案…...
带有-i选项的sed命令在Linux上执行成功,但在MacOS上失败了
问题: 我已经成功地使用以下 sed 命令在Linux中搜索/替换文本: sed -i s/old_string/new_string/g /path/to/file然而,当我在Mac OS X上尝试时,我得到: command i expects \ followed by text我以为我的Mac运行的是…...
[Linux_IMX6ULL驱动开发]-GPIO子系统和Pinctrl子系统
目录 Pinctrl子系统的概念 GPIO子系统的概念 定义自己的GPIO节点 GPIO子系统的函数 引脚号的确定 基于GPIO子系统的驱动程序 驱动程序 设备树修改 之前我们进行驱动开发的时候,对于硬件的操作是依赖于ioremap对寄存器的物理地址进行映射,以此来达…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.
报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符,最后运行:npm run lint --fix...
EC2安装WebRTC sdk-c环境、构建、编译
1、登录新的ec2实例,证书可以跟之前的实例用一个: ssh -v -i ~/Documents/cert/qa.pem ec2-user70.xxx.165.xxx 2、按照sdk-c demo中readme的描述开始安装环境: https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c 2…...
