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

news2024/9/22 15:45:28

引言:

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.coloradmin.cn/o/1237773.html

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

相关文章

揭秘成都瀚网科技有限公司:抖音带货是否靠谱?

随着抖音等短视频平台的兴起&#xff0c;越来越多的商家和网红开始利用这些平台进行带货销售。这其中&#xff0c;成都瀚网科技有限公司&#xff08;以下简称瀚网科技&#xff09;也宣称能够在抖音等平台上实现带货销售&#xff0c;那么这家公司是否靠谱呢&#xff1f;本文将为…

外贸自建站的指南?新手如何玩转海洋建站?

外贸自建站工具有哪些&#xff1f;外贸新手怎么搭建独立网站&#xff1f; 拥有自己的外贸网站是提高企业国际竞争力和扩大市场份额的有效途径。然而&#xff0c;许多企业在外贸自建站的过程中感到困惑。海洋建站将为您提供一份详细的外贸自建站指南&#xff0c;助您轻松打造一…

数据库实验二 数据库表的数据插入、修改、删除操作

数据库实验二 数据库表的数据插入、修改、删除操作 一、实验目的二、设计性实验三、观察与思考 一、实验目的 1&#xff0e;掌握MySQL数据库表的数据插入、修改、删除操作SQL语法格式 2&#xff0e;掌握数据表的数据的录入、增加和删除的方法 二、设计性实验 某超市的食品管…

Oracle(2-5)Usage and Configuration of the Oracle Shared Server

文章目录 一、基础知识1、 Server Configurations服务器配置2、Dedicated server process专用服务器进程3、Oracle Shared ServerOracle共享服务器4、Benefits of Shared Server 共享服务器的优点5、Processing a Request 处理请求6、Configuring Shared Server 配置共享服务器…

计算公式-dB转换,噪声,IP3,OP1dB,耗散

1. dB和log转换公式 dB在缺省情况下总是定义功率单位&#xff0c;以 10lg 为计。 d B 10 l g ( B ) dB 10lg(B) dB10lg(B) P o w e r G a i n ( d B ) 10 l g ( P o u t P i n ) Power Gain(dB) 10lg(\frac{P_{out}}{P_{in}}) PowerGain(dB)10lg(Pin​Pout​​) 2. 级联情…

爬取春秋航空航班信息

一、使用fiddler爬取小程序春秋航空航班信息 使用Fiddler爬取春秋航空微信小程序&#xff08;手机上由于网络问题&#xff0c;无法进入&#xff0c;使用电脑版&#xff09; 搜索航班信息 搜索记录 使用Fiddler查找url(没有得到有效url) 继续查找&#xff0c;发现航班信息列…

uniapp 富文本以及移动端富文本的展示问题

富文本展示有几种方式: 1.<view v-html"content"></view> 2. uniapp自带组件 rich-text rich-text | uni-app官网 <rich-text :nodes"content"></rich-text> 3.uView组件 u-parse Parse 富文本解析器 | uView 2.0 - 全面兼…

Docker安装Rabbitmq3.12并且prometheus进行监听【亲测可用】

一、安装Rabbitmq 下载镜像&#xff1a; docker pull rabbitmq:3.12-management 安装镜像&#xff1a; docker run -id --restartalways --namerabbitmq -v /usr/local/rabbitmq:/var/lib/rabbitmq -p 15692:15692 -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USERgu…

普冉PY32系列(十一) 基于PY32F002A的6+1通道遥控小车II - 控制篇

目录 普冉PY32系列(一) PY32F0系列32位Cortex M0 MCU简介普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单普冉PY32系列(四) PY32F002A/003/030的时钟设置普冉PY32系列(五) 使用JLink RTT代替串口输出日志普冉PY32…

电脑磁盘怎么设置密码?磁盘加密软件哪个好?

电脑磁盘经常被用于存放各种重要数据&#xff0c;而为了避免数据泄露&#xff0c;我们需要为磁盘设置密码&#xff0c;以防止未授权人员使用磁盘。那么&#xff0c;电脑磁盘怎么设置密码呢&#xff1f;下面我们就一起来了解一下。 如何设置磁盘密码&#xff1f; 想要为磁盘设置…

腾讯云服务器99元一年?假的,阿里云是99元

腾讯云服务器99元一年是真的吗&#xff1f;假的&#xff0c;不用99元&#xff0c;只要88元即可购买一台2核2G3M带宽的轻量应用服务器&#xff0c;99元太多了&#xff0c;88元就够了&#xff0c;腾讯云百科活动 txybk.com/go/txy 活动打开如下图&#xff1a; 腾讯云服务器价格 腾…

Redis主从复制,哨兵和Cluster集群

主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff08;和同步&#xff09;&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a;故障恢复无法自动化…

Java核心知识点整理大全8-笔记

Java核心知识点整理大全7-笔记-CSDN博客文章浏览阅读1.2k次&#xff0c;点赞27次&#xff0c;收藏26次。但是如果锁的竞争激烈&#xff0c;或者持有锁的线程需要长时间占用锁执行同步块&#xff0c;这时候就不适合 使用自旋锁了&#xff0c;因为自旋锁在获取锁前一直都是占用 c…

什么是凸函数

假设函数是定义在某个向量空间的凸子集上的实值函数&#xff0c;并且&#xff0c;如果对于中的任何两个向量和&#xff0c;都满足&#xff1a; 则称为上的凸函数

Threejs_11 补间动画的实现

啥是补间动画呢&#xff1f;其实就是我们在threejs中移动一个物体的时候&#xff0c;不让他是瞬时移动&#xff0c;让他跟css动画的transition一样&#xff0c;有个过度效果&#xff0c;就是补间动画。补间动画如何设置呢&#xff1f; 补间动画实现 1.引入补间动画库 在我们…

BW4HANA 从头到脚 概念详解 ---- 持续更新中

1. 理解BW4HANA是干嘛的 好歹干了这么久的活了&#xff0c;从当初的啥也不懂到现在感觉啥都知道点&#xff0c;虽然知道的有限&#xff0c;但是也不是小白。渐渐的也知道了SAP开发的一些逻辑。本来咱是想当个BW的大牛的。但是现在感觉这条船要沉了是怎么回事。个人才稍微摸到点…

Windows常用cmd网络命令详解

中午好&#xff0c;我的网工朋友。 上回给你们梳理了一些有趣的cmd命令&#xff0c;很多朋友希望再拓展一下&#xff0c;这不就来了&#xff1f; 今天从windows切入&#xff0c;给你分享一些常用cmd网络命令&#xff0c;如果能熟悉上手&#xff0c;很多功能都可以快速实现&am…

今日定音,博通以610亿美元成功收购VMware | 百能云芯

博通&#xff08;Broadcom&#xff09;日前宣布&#xff0c;已获得中国监管机构的批准&#xff0c;将于今日完成对云计算公司VMware的收购交易。这意味着&#xff0c;610亿美元的收购案正式收关。 据悉&#xff0c;中国市场监管总局在11月21日晚发布了有关附加限制性条件批准博…

基于STM32的电子时钟(论文+源码)

1. 系统设计 电子时钟是一种广泛使用的工具&#xff0c;其可以帮助人们准确掌握时间&#xff0c;本课题基于STM32的电子时钟系统的设计&#xff0c;在功能上设计如下&#xff1a; 具有电子时钟的基本功能&#xff0c;显示年月日&#xff0c;时分秒等基本信息&#xff1b;可以…

代码文档浏览器 Dash mac中文版软件特色

Dash mac是一个基于 Python 的 web 应用程序框架&#xff0c;它可以帮助开发者快速构建数据可视化应用。Dash 的工作原理是将 Python 代码转换成 HTML、CSS 和 JavaScript&#xff0c;从而在浏览器中呈现交互式的数据可视化界面。Dash 提供了一系列组件&#xff0c;包括图表、表…