C++ AVL树

1.概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种解决上述问题的方法:

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1),如下图:

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在logn搜索时间复杂度O(logN)

2.AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。

平衡因子(_bf)就是该节点右子树高度减去左子树高度

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

1. 按照二叉搜索树的方式插入新节点

2. 调整节点的平衡因子

调整节点的平衡因子

一个合格的AVL树在插入之前,就应该满足AVL树的条件,插入节点的父亲节点的平衡因子只有三种可能(-1,0,1)

插入分以下几种情况:

第一种和第四种插入后子树的高度没有变化,所以上面的节点的平衡因子都不会变,平衡因子的更新可以结束

第二种和第三种插入后子树的高度发生变化,所以上面的节点的平衡因子要变,要继续更新平衡因子

继续更新后如果有一个节点的平衡因子不再是(-1,0,1)也就是变成2或者-2,就要进行处理

平衡因子变成2或者-2的节点的子树,对于这个子树,一定是高的那个子树插入一个节点,导致在原先就高的那一边的子树,更加高了

我们可以分成以下几种情况继续分析

2 1树(左旋)

平衡因子不合格的本质是树的高度差太大,所以需要降高度。

对8左旋:

-2 -1树(右旋)

对8右旋:

-2 1树(左右旋)

先对8左旋再对10右旋:

这里加在b,也可以加在c,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。

2 -1 树(右左旋)

先对10右旋再对8左旋:

和上面一样,这里加在c,也可以加在b,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。

3.AVL树的删除

删除我熬了一个通宵搞明白的,不容易啊

3.1.删除节点

先考虑一下,删除有哪些情况:

1.删除的节点左右都为空,删除的就是叶子节点

2.删除的节点左右一边为空,只有以下两种情况,因为这是AVL树,所有节点的平衡因子只能是(-1,0,1)

现在已知一边为空,所以平衡因子=另一边子树的高度,所以只能有一个节点

3.删除的节点的左右都都不为空,这种情况,和搜索二叉树的找月嫂思想是一样的

这三种情况的解决方式有异曲同工之处

情况1:删除叶子节点,如果他是父亲的左孩子,(bf)平衡因子++,反之--

情况2:可以把被删除的节点存储的值和他的唯一的孩子交换,然后删除他的孩子,问题转换成情况1

情况3:找到左子树的最大值节点,与其交换一下存储的内容,问题转换成删除那个左子树最大的节点,这个节点,又符合左为空的情况,如果右也为空就是情况1,反之情况2

删除节点最后都会转为删除叶子节点

3.2.平衡因子更新

节点删除了,被删除节点的父亲节点的平衡因子也更新了,但是是否需要继续更新,当前位置是否平衡都需要考虑

平衡因子的正常范围是[-1,1]

被删除节点的父亲节点平衡因子可以分为以下三种情况:

1.bf == 0 

说明原先该节点的bf == -1 或者 1 变成 0 后  说明原先高的那一边被删除了,导致高的子树高度降低,变得和另一边一样高,整颗树树的高度发生了变化,需要继续更新

2.bf == 1 或者 -1

说明原先该节点的bf == 0 0 变成 1 后 说明原先平衡,删除了一边,导致一边子树的高度变低了,但整颗树的高度没有变化,不需要更新

3. bf == 2 或者 bf == -2

这颗子树不平衡了,需要处理

3.3.旋转

不平衡分成两种大情况:

1.由于删除节点,导致当前子树不平衡

2.由于删除节点后导致平衡因子更新,而导致树不平衡

在更新平衡因子过程中,cur的bf一定是变成0,才会进一步,导致parent变化

在这个过程中,parent的bf变成2或者-2,是判断是否需要旋转的条件

而具体该怎么旋转,就由叔叔决定,因为cur的bf一定是0。和上面插入的旋转道理是一样的,也是这四种旋转。

这里右六种情况:

因为parent的bf可以是-2或者2,uncle的bf可以是-1,0,1

parent->bfuncle->bf旋转方式
21左旋
20左旋
2-1右左双旋
-21右旋
-20右旋
-2-1左右双旋

在旋转之后还需要更新cur和parent,因为还有继续更新平衡因子的需要

4.源代码

#pragma once
#include<assert.h>template<class K, class V>
struct AVLtreeNode
{AVLtreeNode* _left;AVLtreeNode* _right;AVLtreeNode* _parent;pair<K, V> _kv;int _bf;AVLtreeNode(const K& key, const V& val):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(key, val),_bf(0){}
};template<class K, class V>
class AVLtree
{
public:typedef AVLtreeNode<K, V> Node;typedef pair<K, V> value_type;AVLtree() :_root(nullptr){}bool insert(const value_type& val){if (_root == nullptr){_root = new Node(val.first, val.second);return true;}// 找插入位置Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (cur->_kv.first > val.first)cur = cur->_left;else if (cur->_kv.first < val.first)cur = cur->_right;elsereturn false;}// 插入if (parent->_kv.first > val.first){parent->_left = new Node(val.first, val.second);cur = parent->_left;cur->_parent = parent;}else{parent->_right = new Node(val.first, val.second);cur = parent->_right;cur->_parent = parent;}// 更新_bf平衡因子while (parent){if (cur == parent->_left)parent->_bf--;elseparent->_bf++;if (parent->_bf == -1 || parent->_bf == 1){//继续更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 0){//无需继续更新break;}else if (parent->_bf == -2 || parent->_bf == 2){//不平衡,要旋转if (parent->_bf > 0 && cur->_bf > 0)RotateL(parent);else if (parent->_bf < 0 && cur->_bf < 0)RotateR(parent);else if (parent->_bf > 0 && cur->_bf < 0)RotateRL(parent);elseRotateLR(parent);break;}else{assert(false);}}return true;}bool erase(const K& key){if (_root == nullptr){return false;}//找keyNode* cur = _root;while(cur){if (cur->_kv.first > key){cur = cur->_left;}else if (cur->_kv.first < key){cur = cur->_right;}else//找到了{if (cur->_left == nullptr && cur->_right == nullptr)//删除的节点是叶子节点{cur = DeleteNode(cur);}else if (cur->_left == nullptr){Node* del = cur->_right;cur->_kv = del->_kv;cur = DeleteNode(del);}else if (cur->_right == nullptr){Node* del = cur->_left;cur->_kv = del->_kv;cur = DeleteNode(del);}else{//找左子树的最大节点Node* leftmax = cur->_left;while (leftmax->_right)leftmax = leftmax->_right;cur->_kv = leftmax->_kv;//是否需要托孤if(leftmax->_left){Node* del = leftmax->_left;leftmax->_kv = del->_kv;cur = DeleteNode(del);}else{Node* del = leftmax;cur = DeleteNode(del);}}if (cur == nullptr)//根节点被删除return true;if (cur->_bf == 2 || cur->_bf == -2)//删除那个节点导致不平衡{Node* parent = cur;cur = nullptr;Rotate_delete_check(parent, cur);}//更新平衡因子Node* parent = cur->_parent;while (parent && cur->_bf == 0)//cur的平衡因子是0才需要更新{if (parent->_left == cur)parent->_bf++;elseparent->_bf--;if (parent->_bf == -1 || parent->_bf == 1){//无需继续更新break;}else if (parent->_bf == 0){//继续更新cur = parent;parent = parent->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){cout << "//" << endl;Rotate_delete_check(parent, cur);}}return true;}}return false;}bool check_balance(){return _check_balance(_root);}void InOrder(){_InOrder(_root);cout << endl;}private:Node* DeleteNode(Node* cur)//删除叶子节点,并且返回其父亲{Node* del = cur;Node* parent = cur->_parent;if (parent == nullptr)//cur是根节点{_root = nullptr;delete del;return nullptr;}if (parent->_left == del){parent->_left = nullptr;parent->_bf++;}else{parent->_right = nullptr;parent->_bf--;}delete del;return parent;}void Rotate_delete_check(Node*& parent, Node*& cur){Node* uncle;if (parent->_left == cur)uncle = parent->_right;elseuncle = parent->_left;if (parent->_bf == 2 && uncle->_bf == 1){RotateL(parent);cur = uncle;}else if (parent->_bf == -2 && uncle->_bf == -1){RotateR(parent);cur = uncle;}else if (parent->_bf == 2 && uncle->_bf == -1){cur = uncle->_left;RotateRL(parent);}else if (parent->_bf == -2 && uncle->_bf == 1){cur = uncle->_right;RotateLR(parent);}else if (parent->_bf == 2 && uncle->_bf == 0){RotateL(parent);parent->_bf = 1;uncle->_bf = -1;cur = uncle;}else if (parent->_bf == -2 && uncle->_bf == 0){RotateR(parent);parent->_bf = -1;uncle->_bf = 1;cur = uncle;}else assert(false);parent = cur->_parent;}void balence_check(Node* parent, Node* cur){if (parent == nullptr)return;if (parent->_bf == 0){//无需继续更新}else if (parent->_bf == -2 || parent->_bf == 2){//不平衡,要旋转if (parent->_bf > 0 && cur->_bf > 0)RotateL(parent);else if (parent->_bf < 0 && cur->_bf < 0)RotateR(parent);else if (parent->_bf > 0 && cur->_bf < 0)RotateRL(parent);elseRotateLR(parent);}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}bool _check_balance(Node* root){if (root == nullptr)return true;int lefthight = TreeHight(root->_left);int righthight = TreeHight(root->_right);if (abs(lefthight - righthight) >= 2 || righthight - lefthight != root->_bf){cout << root->_kv.first << ":" << root->_bf << endl;return false;}return _check_balance(root->_left) && _check_balance(root->_right);}int TreeHight(Node* root){if (root == nullptr)return 0;int lefthight = TreeHight(root->_left);int righthight = TreeHight(root->_right);return lefthight > righthight ? lefthight + 1: righthight + 1;}void RotateL(Node* parent)//左单旋{Node* Rightson = parent->_right;//旋转节点之间的链接parent->_right = Rightson->_left;if (parent->_right)parent->_right->_parent = parent;Rightson->_left = parent;Node* pparent = parent->_parent;parent->_parent = Rightson;//更新祖先的链接if (pparent == nullptr){Rightson->_parent = nullptr;_root = Rightson;}else{if (pparent->_kv.first > Rightson->_kv.first)pparent->_left = Rightson;elsepparent->_right = Rightson;Rightson->_parent = pparent;}parent->_bf = Rightson->_bf = 0;}void RotateR(Node* parent)//右单旋{Node* Leftson = parent->_left;//旋转节点之间的链接parent->_left = Leftson->_right;if (parent->_left)parent->_left->_parent = parent;Leftson->_right = parent;Node* pparent = parent->_parent;parent->_parent = Leftson;//更新祖先的链接if (pparent == nullptr){Leftson->_parent = nullptr;_root = Leftson;}else{if (pparent->_kv.first > Leftson->_kv.first)pparent->_left = Leftson;elsepparent->_right = Leftson;Leftson->_parent = pparent;}parent->_bf = Leftson->_bf = 0;}void RotateLR(Node* parent){Node* Leftson = parent->_left;Node* LRightsson = Leftson->_right;int bf = LRightsson->_bf;RotateL(Leftson);RotateR(parent);//平衡因子更新if (bf == 1){Leftson->_bf = -1;}else if (bf == -1){parent->_bf = 1;}else if (bf == 0) {}else{assert(false);}}void RotateRL(Node* parent){Node* Rightson = parent->_right;Node* RLeftsson = Rightson->_left;int bf = RLeftsson->_bf;RotateR(Rightson);RotateL(parent);//平衡因子更新if (bf == 1){parent->_bf = -1;}else if (bf == -1){Rightson->_bf = 1;}else if(bf == 0){}else{assert(false);}}private:Node* _root;
};

写完了。

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

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

相关文章

基于51 单片机的交通灯系统 源码+仿真+ppt

主要内容&#xff1a; 1&#xff09;南北方向的绿灯、东西方向的红灯同时亮40秒。 2&#xff09;南北方向的绿灯灭、黄灯亮5秒&#xff0c;同时东西方向的红灯继续亮。 3&#xff09;南北方向的黄灯灭、左转绿灯亮&#xff0c;持续20秒&#xff0c;同时东西方向的红灯继续…

HTTP2:基础概念

http2 相较于http2最大的改变在于用户和网站之间可以复用一条连接实现多流交互。其推出并没有改变http1.1 的基本语义。http2的目的是响应复用&#xff0c;头部压缩来提高极致的性能。 http2 的版本标识 h2&#xff1a;基于TLS之上构建的HTTP/2&#xff0c;作为ALPN的标识符&…

并发编程 java锁机制

1、什么是锁&#xff0c;为什么需要锁&#xff1f; 并发环境下&#xff0c;会存在多个线程对同一个资源进行争抢的情况&#xff0c;假设线程A对资源正在进行修改&#xff0c;此时线程B又对同一资源进行了修改&#xff0c;就会导致数据不一致的问题。为了解决这个问题&#xff…

【flink状态管理(2)各状态初始化入口】状态初始化流程详解与源码剖析

文章目录 1. 状态初始化总流程梳理2.创建StreamOperatorStateContext3. StateInitializationContext的接口设计。4. 状态初始化举例&#xff1a;UDF状态初始化 在TaskManager中启动Task线程后&#xff0c;会调用StreamTask.invoke()方法触发当前Task中算子的执行&#xff0c;在…

常用的前端模块化标准总结

1、模块化标准出现以前使用的模块化方案&#xff1a; 1&#xff09;文件划分&#xff1a; 将不同的模块定义在不同的文件中&#xff0c;然后使用时通过script标签引入这些文件 缺点&#xff1a; 模块变量相当于是定义在全局的&#xff0c;容易造成变量名冲突&#xff08;即不…

flink反压及解决思路和实操

1. 反压原因 反压其实就是 task 处理不过来&#xff0c;算子的 sub-task 需要处理的数据量 > 能够处理的数据量&#xff0c;比如&#xff1a; 当前某个 sub-task 只能处理 1w qps 的数据&#xff0c;但实际上到来 2w qps 的数据&#xff0c;但是实际只能处理 1w 条&#…

Qt信号和槽机制(什么是信号和槽,connect函数的形式,按钮的常用信号,QWidget的常用槽,自定义槽函数案例 点击按钮,输出文本)

一.什么是信号和槽 信号槽式Qt中的一个很重要的机制。信号槽实际上是观察者模式,当发生了感兴趣的事件&#xff0c;某一个操作就会被自动触发。当某个事件发生之后&#xff0c;比如按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号。这种发出类似广播。如果有对象对…

ArcGIS学习(五)坐标系-2

3.不同基准面坐标系之间的转换 在上一关中,我们学习了ArcGIS中的投影(投影栅格)工具,并以"WGS1984地理坐标系与WGS1984的UTM投影坐标系的转换”为例进行讲解。 "WGS1984地理坐标系与WGS1984的UTM投影坐标系的转换”代表的是同一个基准面下的两个坐标的转换。 …

人工智能 | 深度学习的进展

深度学习的进展 深度学习是人工智能领域的一个重要分支&#xff0c;它利用神经网络模拟人类大脑的学习过程&#xff0c;通过大量数据训练模型&#xff0c;使其能够自动提取特征、识别模式、进行分类和预测等任务。近年来&#xff0c;深度学习在多个领域取得了显著的进展&#…

cesium mapboxgl+threebox glb 朝向问题

一、3Dbuilder打开glb 二、cesium在pitch和heading都为0的情况下&#xff0c;不设置模型的朝向 三、mapboxglthreebox在pitch和bearing都为0的情况下&#xff0c;不设置模型的朝向 四、对于地图默认视角&#xff0c;cesium设置pitch-90、heading0的时候和mapboxglthreebox设置p…

光学PCIe 6.0技术引领AI时代超大规模集群

随着云计算、大数据和人工智能技术的快速发展&#xff0c;超大规模数据中心正经历一场前所未有的变革。传统的集中式架构逐渐转变为解聚式&#xff08;disaggregated&#xff09;架构&#xff0c;这种架构将计算、存储和网络资源从单一的物理服务器中分离出来&#xff0c;形成独…

前端vite+vue3——自动化配置路由布局

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐ 自动化配置路由&#x1f496;引入vite版本自定义目录映射&#x1f496;自动化读取文件下的路由&#x1f496;main入口加载路由&#x1f496;入口app.vue配置&#x1f496;layout基础布局配置&#x1f496;效果 ⭐总结⭐结束 ⭐前言…

text-generation-webui搭建大模型运行环境与踩坑记录

text-generation-webui搭建大模型运行环境 text-generation-webui环境初始化准备模型启动项目Bug说明降低版本启动项目 text-generation-webui text-generation-webui是一个基于Gradio的LLM Web UI开源项目&#xff0c;可以利用其快速搭建部署各种大模型环境。 环境初始化 下载…

【漏洞复现】EPON上行A8-C政企网关未授权下载漏洞

Nx01 产品简介 EPON上行A8-C政企网关是一款终端产品&#xff0c;提供企业网络解决方案。 Nx02 漏洞描述 EPON上行A8-C政企网关配置文件未授权下载漏洞&#xff0c;攻击者在未授权状态下下载配置文件&#xff0c;获取配置文件内敏感信息。 Nx03 产品主页 fofa-query: "Z…

Retinexformer论文精读笔记

Retinexformer论文精读笔记 论文为2023年ICCV的Retinexformer: One-stage Retinex-based Transformer for Low-light Image Enhancement。论文链接&#xff1a;browse.arxiv.org/pdf/2303.06705.pdf&#xff0c;代码链接&#xff1a;caiyuanhao1998/Retinexformer: “Retinexfo…

Mac 下载安装Java、maven并配置环境变量

下载Java8 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 根据操作系统选择版本 没有oracle账号需要注册、激活登录 mac直接选择.dmg文件进行下载&#xff0c;下载后安装。 默认安装路径&#xff1a;/Library/Java/JavaVirtualMachines/jdk-1…

【C#】.net core 6.0 创建默认Web应用,以及默认结构讲解,适合初学者

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

SpringCloud-创建多模块项目

在微服务架构中&#xff0c;项目的组织结构对于代码的维护和团队的协作至关重要。Spring Cloud作为一个强大的微服务框架&#xff0c;提供了丰富的功能和组件&#xff0c;同时也支持多模块项目的创建&#xff0c;使得代码结构更加清晰、易于管理。本文将介绍如何使用 Spring Cl…

HTML5+CSS3+移动web——HTML 基础

目录 一、标签语法 HTML的基本框架 1. 标题标签 2. 段落标签 3. 换行和水平线 4. 文本格式化标签 5. 图像标签 6. 路径 相对路径 绝对路径 7. 超链接标签 8. 音频 9. 视频 10. 注释 二、标签结构 一、标签语法 HTML 超文本标记语言——HyperText Markup Langua…

Kubernetes基础(十四)-k8s网络通信

1 k8s网络类型 2 Pod网络 2.1 同一pod内不同容器通信 Pod是Kubernetes中最小的可部署单元&#xff0c;它是一个或多个紧密关联的容器的组合&#xff0c;这些容器共享同一个网络命名空间和存储卷&#xff0c;因此Pod中的所有容器都共享相同的网络命名空间和IP地址——PodIP&a…