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.hqwc.cn/news/460315.html

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

相关文章

ueransim关于ue侧nas层相关代码解读

一.在文件UERANSIM\UERANSIM-3.2.6\src\ue\nas中enc.cpp中完成了NAS&#xff08;非接入层&#xff09;信令的加密和解密是通过NAS_ENC模块实现的。NAS_ENC模块负责将NAS信令消息进行加密&#xff0c;以确保其传输过程中的安全性。 具体来说&#xff0c;当UE发送NAS信令消息时&…

关于 Ant Design 的 Upload 组件使用 action 自动上传出现跨域问题的解决

问题描述 使用 Ant Design 的 Upload 组件时&#xff0c;可以通过 action 属性指定上传地址实现选择文件自动上传。但在我选择文件上传后浏览器控制台一直出现跨域错误。关键我已经在后端处理了跨域&#xff0c;还是一直会出现跨域错误。而且其它请求都可以正常处理跨域&#…

EF Core 模型优先——根据类对象创建数据表

需要的nuget包&#xff1a; Microsoft.EntityframeworkCore.SqlServer &#xff08;根据自己的数据库类型选择对应的nuget包&#xff09; Microsoft.EntityframeworkCore.Tools Microsoft.VisualStudio.Web.CodeGeneration.Design 说明&#xff1a; &#xff08;1&#xf…

Netty的序列化之MessagePack

目录 引入MessagePack依赖 实体类 服务端代码 客户端代码 执行结果 引入MessagePack依赖 <dependency><groupId>org.msgpack</groupId><artifactId>msgpack</artifactId><version>0.6.12</version></dependency> 实体类…

【开源】JAVA+Vue.js实现高校实验室管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…

notepad++成功安装后默认显示英文怎么设置中文界面?

前几天使用电脑华为管家清理电脑后&#xff0c;发现一直使用的notepad软件变回了英文界面&#xff0c;跟刚成功安装的时候一样&#xff0c;那么应该怎么设置为中文界面呢&#xff1f;具体操作如下&#xff1a; 1、打开notepad软件&#xff0c;点击菜单栏“Settings – Prefere…

如何开始深度学习,从实践开始

将“如何开始深度学习”这个问题喂给ChatGPT和文心一言&#xff0c;会给出很有专业水准的答案&#xff0c;比如&#xff1a; 要开始深度学习&#xff0c;你可以遵循以下步骤&#xff1a; 学习Python编程语言的基础知识&#xff0c;因为它在深度学习框架中经常被使用。 熟悉线性…

利用Intersection Observer实现图片懒加载性能优化

ntersection Observer是浏览器所提供的一个 Javascript API&#xff0c;用于异步的检测目标元素以及祖先或者是顶级的文档视窗的交叉状态 这句话的意思就是&#xff1a; 我们可以看的图片当中&#xff0c;绿色的 target element&#xff08;目标元素&#xff09;&#xff0c;…

百度员工:纠结!31岁,我年薪78w,老婆50w,两人在深圳同一家公司。武汉有房,现在有机会回去,但会降薪15%左右。...

上一篇&#xff1a;《繁花》里宝总成功的秘诀&#xff0c;句句真理&#xff01; 深圳的一名31岁的百度员工&#xff0c;拥有高薪和舒适的生活&#xff0c;与妻子过着看似完美的日子。当他们准备迎接家庭的新阶段时&#xff0c;面临一个艰难的抉择&#xff1a;是在深圳继续追求事…

性能篇:如何解决高并发下 I/O 瓶颈?

我们可以有效地解决高并发下I/O瓶颈的问题&#xff0c;提升系统的性能。当然&#xff0c;实际场景中的优化可能涉及到更多的细节和技术&#xff0c;但希望这篇文章能为大家提供一些思路和方法。​ 引言 大家好&#xff0c;我是小米&#xff01;今天我们来聊一个在高并发场景…

2021年通信工程师初级 实务 真题

文章目录 一、第1章 现代通信网概述&#xff0c;通信网的定义。第10章 通信业务&#xff0c;普遍服务原则10.2.4 通信行业的发展趋势&#xff08;六化&#xff09; 二、第2章 传输网SDH帧结构SDH线路保护倒换&#xff0c;“11 保护”和“1:1保护”波长值λc/f&#xff0c;中心频…

Docker-Learn(三)创建镜像Docker(换源)

根据之前的内容基础&#xff0c;本小点的内容主要涉及到的内容是比较重要的文本Dockerfile 1. 编辑Dockerfile 启动命令行终端&#xff08;在自己的工作空间当中&#xff09;,创建和编辑Dockerfile。 vim Dockerfile然后写入以下内容 # 使用一个基础镜像 FROM ubuntu:late…