C++:AVL树(平衡二叉树)

引言:

AVL树是一种特殊的二叉搜索树,二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树具有如下性质:

  • 左右子树都是AVL树
  • 左右子树高度差绝对值不超过1

 一、AVL树结点的定义

 

template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft;  // 该节点的左孩子
AVLTreeNode<T>* _pRight;  // 该节点的右孩子
AVLTreeNode<T>* _pParent; // 该节点的双亲
T _data;
int _bf;          // 该节点的平衡因子
};

二、AVL树的插入

 与普通的二叉搜索树不同,AVL树在数据插入后需要维护整棵树的平衡,并且更新结点的平衡因子

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

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
bool Insert(const T& data){//如果是空树,直接插入并结束if (_pRoot == nullptr){_pRoot = new Node(data);return true;}//这一块代码的目的是找到插入位置的父亲结点Node* parent = nullptr;Node* cur = _pRoot;while (cur){if (data < cur->_data){parent = cur;cur = cur->_pLeft;}else if (data > cur->_data){parent = cur;cur = cur->_pRight;}else{return false;}}if (cur == parent->_pLeft){cur = new Node(data);parent->_pLeft = cur;}else{cur = new Node(data);parent->_pRight = cur;}//从parent结点开始向上更新祖先的平衡因子while (parent){if (parent->_bf == 0){break;//插入之后,该结点平衡因子为0说明以该结点为根的子树平衡了//且插入前后高度不变,所以这个结点的祖先的平衡因子不会受到影响,结束}else if (parent->_bf == 1 || parent->_bf == -1){//平衡因子从0变为1或者-1,祖先可能变成2或-2,需要继续向上更新cur = parent;parent = parent->_pParent;}else{//结点的平衡因子变成了2或者-2,需要进行旋转了//这里开始需要对AVL树进行旋转,之后再讲if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}break;}}return true;}

三、AVL树的旋转

1.右单旋

 

 

这是一幅抽象图,左子树较高且在左子树的左侧插入 ,`h>=0,此时需要进行右单旋

 我们可以看到这样旋转之后该子树依然是一个二叉搜索树,巧妙的是,根节点的平衡因子被更新  成0了,这说明:在旋转之后不用再对祖先更改平衡因子了

 

void RotateR(Node* pParent){Node*subL = pParent->_pLeft;Node* subLR = subL->_pRight;pParent->_pLeft = subRL;if (subLR){subLR->_pParent = pParent;}Node*parentParent = pParent->_pParent;subL->_pRight = pParent;if (pRoot == pParent){pRoot = subL;subL->_pParent = nullptr;}else{if (parentParent->_pLeft == pParent){parentParent->_pLeft = subL;}else{parentParent->_pRight = subL;}subL->_pParent = parentParent;}pParent->_bf = subL->_bf = 0;}

2.左单旋

 左单旋和右单旋类似,只是方向相反,右子树较高且在右子树的右侧插入

void RotateL(Node* pParent){Node*subR = pParent->_pRight;Node* subRL = subR->_pLeft;pParent->_pRight = subRL;if (subRL){subRL->_pParent = pParent;}Node* parentParent = pParent->_pParent;pParent->_pParent = subR;subR->_pLeft = pParent;if (parent == pRoot){pRoot = subR;subR->_pParent = nullptr;}else{if (parentParent->_pLeft = pParent){parentParent->_pLeft = subR;}else{parentParent->_pRight = subR;}}pParent->_bf = subR->_bf = 0;}

 

3. 左右双旋

 对于单侧插入,只需要旋转一次。但是对于如下的情况,单次旋转无法解决问题

和单旋不同,我们需要对子树再往下画一层,如图所示

这棵树的左子树较高,并且在左子树的右子树插入,导致单旋无法一次解决

旋转过程 

 step1:

step2: 

 

新插入结点无论插入在subLR的左还是右, 最终subLR平衡因子为0

 

// 左右双旋void RotateLR(Node* pParent){Node* subL = pParent->_pLeft;Node* subLR = pParent->_pRight;int bf = subLR->_bf;RotateL(subL);RotateR(pParent);//subLR就是新插入结点if (bf == 0){pParent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == 1){pParent->_bf = -1;subL->_bf = -1;subLR->_bf = 0;}else if(bf==-1){pParent->_bf = 1;subL->_bf = 1;subLR = 0;}}

4.右左双旋

 与左右双旋类似

 

//右左双旋
void RotateRL(Node* pParent){Node* subR = pParent->_pRight;Node* subRL = subR->_pLeft;int bf = subRL->_bf;RotateR(subR);RotateL(pParent);//subRL就是新插入结点if (bf == 0){pParent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == 1){pParent->_bf = 0;subR->_bf = -1;subRL->_bf = 0;}else if(bf==-1){pParent->_bf = 1;subR->_bf = 1;subRL->_bf = 0;}}

 

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

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

相关文章

论文《Unsupervised Dialog Structure Learning》笔记:详解DD-VRNN

D-VRNN模型和DD-VRNN模型 总体架构 离散-可变循环变分自编码器&#xff08;D-VRNN&#xff09;和直接-离散-可变循环变分自编码器&#xff08;DD-VRNN&#xff09;概述。D-VRNN和DD-VRNN使用不同的先验分布来建模 z t z_t zt​之间的转换&#xff0c;如红色实线所示。 x t x_t…

RabbitMQ安装说明

注意: 本次安装以 CentOS 7为例 1、 准备软件 erlang 18.3 1.el7.centos.x86_64.rpm socat 1.7.3.2 5.el7.lux.x86_64.rpm rabbitmq server 3.6.5 1.noarch.rpm 2、安装Erlang rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm 3.、安装RabbitMQ 安装 rpm -ivh socat-1.7.3.2-…

坚鹏:湖北银行数字化转型背景下银行运营管理创新培训圆满结束

湖北银行正式成立于2011年2月27日&#xff0c;总部设在武汉。现有员工5000余人。营业网点从成立之初的93家增长至241家&#xff0c;实现全省17个市州、59个县域营业网点全覆盖。截至2022年末&#xff0c;全行资产总额4026亿元&#xff0c;存款总额2956亿元&#xff0c;贷款总额…

ESP32 碰上内存分配问题

1、背景 看图片 _calloc_r ->_malloc_r ->heap_caps_malloc_default->heap_caps_malloc->multi_heap_malloc->multi_heap_malloc_impl->get_next_block /* Return the next sequential block in the heap.*/ static inline heap_block_t *get_next_block(co…

网络和Linux网络_4(应用层)序列化和反序列化(网络计算器)

目录 1. 重新理解协议 2. 网络版本计算器 2.1 前期封装 Log.hpp sock.hpp TcpServer.hpp 第一次测试(链接) 2.2 计算器实现 第二次测试(序列化和反序列化) 第三次测试(客户端字节流) CalServer.cc CalClient.cc 3. 守护进程 3.1 守护进程和前后台进程 3.1 变成…

搞清楚Java值传递还是引用传递

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

TypeError: expected np.ndarray (got Tensor)解决办法

文章目录 一、错误展示二、错误分析三、解决办法四、其余解决办法总结 一、错误展示 二、错误分析 这个错误表示正在尝试将一个PyTorch的Tensor对象作为numpy的ndarray对象来使用。我们需要使用numpy的ndarray而不是PyTorch的Tensor。 三、解决办法 在我的程序中去掉这一行代…

产品需求分析师的基本职责(合集)

产品需求分析师的基本职责1 职责 1、主要对用友司库云产品进行调研及产品规划; 2、根据司库云业务需求进行详细需求的用户故事、原型设计、需求分析、详细需求文档编写等; 3、进行产品的需求管理、需求验证、产品演示等需求工作; 4、配合开发、UE人员完成对产品的开发任务;…

PowerQuery领域的经典之作“猴子书“中文版来啦!

与数据打交道&#xff0c;还在纠结于Excel、SQL、VBA、Python&#xff1f;数据处理领域经典之作PowerQuery"猴子书"让你用更聪明的方法处理数据。学完这本书&#xff0c;你就掌握了Power Query的一切&#xff0c;想要学Power Query&#xff0c;只需要这一本就够啦&am…

PC分页操作

page-size 每页显示条目个数 current-page 当前页数 total 数据总数 current-change【currentPage 改变时会触发】 <el-paginationbackgroundlayout"prev, pager, next"align"right"style"padding: 10px":page-size"pageParams.pagesize…

合格的全栈测试工程师,需要掌握哪些测试工具?

前言 俗话说&#xff0c;工欲善其事&#xff0c;必先利其器&#xff0c;所以一个好的软件测试工程师必须善于使用各种软件测试工具。软件测试工具是通过一些工具能够使软件的一些简单问题直观的展示在测试人员的面前&#xff0c;这样能使测试人员更好的找出软件错误的所在&…

【github】初学者使用指南

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…