初识C++ · AVL树(1)

news2024/9/24 19:17:34

目录

前言:

1 AVL树的创建

2 部分成员函数

2.1 查找

2.2 中序遍历

2.3 插入

2.4 左旋转

2.5右旋转


前言:

上文,上上文提到了map set,二叉搜索树,其实都是为了近两文做铺垫的,虽然map的底层是红黑树,但是也为AVL树的学习打下了基础,那么什么是AVL树呢?由谁发明的呢?
两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis发明的,名字就是发明家的首字母咯,发明是用来干什么的呢?发明出来是为了解决当树的结构趋近于单链表时候,效率接近O(N)的问题,只要能有效降低高度,那么就能提高速度,所以AVL树,诞生了。

创建AVL树有很多种方式,我们本次介绍的是使用的平衡因子的概念,也就是每个节点都有个平衡因子,绝对值不能超过1,也就是取值范围是0 1 -1,如果超过2,也就代表树失衡了,需要进行旋转调整。

这里旋转右子树高度减左子树高度的值作为平衡因子的大小,当然,AVL树的满足条件就是,左右子树都是AVL树,并且每个节点的平衡因子都小于2。


1 AVL树的创建

AVL树的创建和二叉平衡搜索树是一样的,无非是每个节点的区别而已,前言,上文提及,节点涉及到的内容有,平衡因子,左右指针,key或者是key-value,这里我们使用key-value模型实现。

但是!当我们旋转的时候,涉及到的节点势必有某个节点的父节点,所以还有一个成员变量,指向父节点的指针,所以AVL树实际上是三叉链的结构:

template<class T, class V>
struct AVLTreeNode
{
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;
	pair<T, V> _kv;

	int _bf;//平衡因子

	AVLTreeNode(const pair<T,V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
};

这就是节点的创建。

那么AVL树的大体如下:

template<class T>
class AVLTree
{
public:
	typedef AVLTreeNode<T, V> Node;



private:
	Node* _root = nullptr;
};

2 部分成员函数

这里要实现的成员函数有左旋,右旋,以及查找,插入(实现一半),以及中序遍历。

但是部分函数其实变化不打,改的都是细枝末节的东西,这里直接给代码即可:

2.1 查找

Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

2.2 中序遍历

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);

	}

2.3 插入

插入和二叉搜索树都是一样的,但是呢,插入完成之后,平衡因子怎么改变呢?

我们现在不妨来分析一下,平衡因子在插入后会有哪些改变?

当我们在8的左边插入数据后,8的平衡因子变为了0,此时,父节点的平衡因子就不用更新了,因为作为7的右子树来说,8这个子树的高度没有变。

当我们在6的任意部分插入数据,6的平衡因子变为了1或者是-1,此时,需要往上更新数据,7的平衡因子被更新为了0,那么不用往上了,所以此时我们就可以发现一个规律,基于原有的AVL树的基础上,插入数据之后平衡因子变为0的,都不用继续遍历了,因为平衡了,但是要注意是 基于原来的树就是AVL树。

当我们在9的右边插入数据,此时9的平衡因子变为了1,往上更新,8的平衡因子变为了2,那么就需要旋转了,此时是完全的右子树高,所以需要左旋转。

当我们在9的右边插入数据,此时9的平衡因子变为了1,往上更新,8的平衡因子变为了2,那么就需要旋转了,此时是完全的右子树高,所以需要左旋转。

当我们在0的左边边插入数据,此时0的平衡因子变为了-1,往上更新,1的平衡因子变为了-2,那么就需要旋转了,此时是完全的左子树高,所以需要右旋转。

所以插入这里要干的事是更新平衡因子,一直往上更新,直到出现0或者是更新到根节点位置,更新到根节点也就说明了父节点为空,就可以结束了,为0也可以直接结束:

bool Insert(const pair<T,V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* root = _root;
	Node* parent = nullptr;
	//判断部分
	while (root)
	{
		if (kv.first > root->_kv.first)
		{
			parent = root;
			root = root->_right;
		}
		else if (val < root->_kv.first)
		{
			parent = root;
			root = root->_left;
		}
		else
		{
			return false;
		}
	}
	Node* newnode = new Node(kv);
	//连接部分 开始判断大小关系
	if (parent->_key > kv.first)
	{
		parent->_left = newnode;
	}
	else
	{
		parent->_right = newnode;
	}

	//更新平衡因子
	newnode->_parent = parent;
	while (parent)
	{
		if (parent->_left == newnode)
		{
			parent->_bf--;
		}
		else
		{
			parent->_bf++;
		}
		if (parent->_bf == 0)
		{
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			newnode = parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			//右单旋
			if (parent->_bf == -2 || newnode->_bf == -1)
			{
				RotateR(parent);
			}
			//左单旋
			else if (parent->_bf == 2 || newnode->_bf == 1)
			{
				RotateL(parent);
			}
			else if (parent->_bf == 2 || parent->_bf == -1)
			{

			}
			else
			{

			}
		}
		else
		{
			//理论而言不可能出现这种情况
			assert(false);
		}
	}

	return true;
}

为了保险期间,出现了超过2的情况就要报错了。

现在的问题就是,面对完全的右子树高或者是完全的左子树高我们应该怎么操作?

2.4 左旋转

此时,这棵AVL树就不平衡了,那么我们可以从一个有趣的角度来看,7是当家的,8的二当家,6是三当家,当家的不管事,二当家一不小心做大做强了,那么当家的就得易主了是吧?所以7要下位了,此时8的左子树就应该指向7,那么7的右子树就应该指向8的左子树,空也没有关系。那么旋转完成了吗?没有,现在不够形象,换个结构,我们这样看:

旋转的核心就是这两条线的指向,二号指向7,一号指向7.5,这个过程大家脑部一下,就十分形象了。

但是不要忘了,AVL树实际上是三叉链结构,所以还有父节点需要更新,二号的节点如果不为空,父节点应该指向的是7,7如果不是根节点,那么我们还要用7的父节点作为连接的下一步,判断相对位置,让7的父节点连8,如果是根节点,那么根节点易位就可以了。此时连接的差不多了,但是涉及到的8的父节点还要连接,可能是空,也可能是7的父节点,这里代码给上,结合文字的说明,代码写的也是有区域性的,结合相对来说好理解许多:

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	subR->_left = parent;
	parent->_right = subRL;
	parent->_parent = subR;

      //可能为空,比如接近单链表
	if (subRL)
	{
		subRL->_parent = parent;
	}
    
     //旋转的时候判断是不是根节点
	if (parent == _root)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
        //根据位置进行连接
		Node* pparent = parent->_parent;
		if (parent == _root->_left)
		{
			_root->_left = subR;
		}
		else
		{
			_root->_left = subRL;
		}
        //三叉链记得更新完
		subR->_parent = _pparent;
	}
    //更新平衡因子
	parent->_bf = subR->_bf = 0;
}

2.5右旋转

右旋转的是同理的,就直接给代码了:

void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	subL->_right = parent;
	parent->_left = subLR;
	parent->_parent = subL;
	
	if (subLR)
		subLR->_parent = parent;

	if (parent == _root)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		Node* pparent = parent->_parent;
		if (parent == _root->_left)
		{
			_root->_left = subL;
		}
		else
		{
			_root->_right = subL;
		}
		subL->_parent = pparent;
	}
	//更新平衡因子
	parent->_bf = subL->_bf = 0;
}

以上两种情况是完全的左右子树高,所以只需要一个左旋或者是右旋,下篇是,复合旋转!

至于为什么平衡因子旋转之后一定为0,请见下文~


感谢阅读!

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

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

相关文章

Android statsd 埋点简析

源码基于&#xff1a;Android U 0. 前言 最近在研究 Android 自带的系统数据指标采集功能&#xff0c;框架依旧很严谨、完美&#xff0c;这里做个分享。 1. Android S 之后变化 stats 的代码从 framework 或 system/core 中转移到了 packages/modules/StatsD 目录中。 2. 框架…

结构拼图的艺术——组合模式(Python实现)

大家好&#xff0c;今天我们继续来讲结构型设计模式&#xff0c;上一期我们介绍了桥接模式&#xff0c;帮助大家理解了如何通过分离抽象部分和实现部分来实现代码的解耦。 今天&#xff0c;我们将介绍另一个非常实用的设计模式——组合模式&#xff0c;这个模式特别适合用于处…

Vue3 + Vite 打包引入图片错误

1. 具体报错 报错信息 报错代码 2. 解决方法 改为import引入&#xff0c;注意src最好引用为符引入&#xff0c;不然docker部署的时候可能也会显示不了 <template><img :src"loginBg" alt""> </template><script langts setup> …

ili9341数据手册中的常用命令

一.设置液晶显示窗口 根据液晶屏的要求&#xff0c;在发送显示数据前&#xff0c;需要先设置显示窗口确定后面发送的像素数据的显示区域。下面的0x2A和0x2B分别对应的是y轴与x轴的命令。 /********** ILI934 命令 ********************************/ #define CMD_SetCoor…

keil调试SH79F7416

仿真器JET51A, 调试设置 选择器件 再次点击调试就一切正常啦

使用moco 完成挡板测试

这里写自定义目录标题 背景使用 moco 工具完成mock挡板功能1. 下载jar包2. 简单启动2.1 准备一个简单的json文件2.2 启动 高级运用同一接口的不同返回字段部分匹配 SONPath参数结构匹配 SON Struct JSON分模块 背景 mock测试&#xff08;挡板测试&#xff09;就是在测试过程中…

在jmeter中使用javascript脚本

工作上遇到一个压力测试的需求&#xff0c;需要测试几个考试相关的接口。其中有一个获取试题详情的接口&#xff0c;和一个提交答题信息的接口。后一个接口以上一接口的返回内容为参数&#xff0c;添加上用户的答案即可。jmeter提供了非常多的方式可以实现该需求&#xff0c;这…

保障企业数据主权:安全可控的爬虫工具与管理平台

摘要 在数据驱动的时代&#xff0c;企业对数据的需求日益增长&#xff0c;但如何在保障数据主权的前提下高效采集数据&#xff1f;本文深入探讨了选择安全可控爬虫工具与管理平台的重要性&#xff0c;分析了关键特性&#xff0c;并提出实用建议&#xff0c;助力企业维护数据安…

AWT200-HPLC-M载波通讯模块/智能网关

安科瑞AWT200-HPLC-M载波通讯模块适用于对数据实时性要求不高的系统&#xff0c;数据刷新速度大于1分钟&#xff0c;比如Acrel-5000能耗管理系统 电力线载波通讯模块AWT200-HPLC-M具备载波接收和网关通讯功能&#xff0c;支持三相载波数据采集&#xff0c;协议转换和数据上传平…

【Plotly-驯化】一文教你通过plotly画出动态可视化多变量分析:create_scatterplotmatrix

【Plotly-驯化】一文教你通过plotly画出动态可视化多变量分析&#xff1a;create_scatterplotmatrix 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &am…

windows USB 设备驱动开发- WinUSB 简介

WinUSB 是 Windows 随附的 USB 设备的通用驱动程序。WinUSB 包括&#xff1a; 内核模式驱动程序 (Winusb.sys)&#xff1b;公开 winusb.h 中所述的 WinUSB 函数的用户模式动态链接库 (Winusb.dll)。 借助这些函数&#xff0c;你可以使用用户模式软件管理 USB 设备&#xff1b;…

软件测试产教融合高质量发展论坛举办,开源网安受邀解读国家标准

近年来&#xff0c;在国家政策引导下&#xff0c;横跨教育体系内外的产教融合改革正在进行&#xff0c;推动了教育与产业深度融合、学校与企业协同发展。在软件安全领域&#xff0c;开源网安也一直走在产教融合发展的最前线&#xff0c;与各大高校建立了深度合作&#xff0c;双…

Cuppa CMS v1.0 任意文件读取漏洞(CVE-2022-25578)

前言 春秋云镜靶场是一个专注于网络安全培训和实战演练的平台&#xff0c;旨在通过模拟真实的网络环境和攻击场景&#xff0c;提升用户的网络安全防护能力和实战技能。这个平台主要提供以下功能和特点&#xff1a; 实战演练&#xff1a; 提供各种网络安全攻防演练场景&#…

mysql字符类型字段设置默认值为当前时间

-- 2024-07-22 10:22:20 select (DATE_FORMAT(CURRENT_TIMESTAMP, %Y-%m-%d %H:%i:%s)); ALTER TABLE tablename MODIFY COLUNN CREATE_DATE varchar (23) DEFAULT(DATE_FORMAT(CURRENT_TIMESTAMP, %Y-%m-%d %H:%i:%s)) COMMENT "创建日期;

新校区,新视野——广州六中以太彩光打造智慧教育新高地

广州市第六中学总务处 林继青 广州市第六中学是拥有85年办学历史的著名市重点中学,也是广东省首批一级学校和国家级示范性高中。作为广州市首批智慧校园建设示范单位,广州第六中学在从化、花都新建校区的智慧校园建设中“破旧立新”,让先进的以太全光技术与学校新校区建设同频共…

CVE-2024-39700 (CVSS 9.9):JupyterLab 模板中存在严重漏洞

在广泛使用的 JupyterLab 扩展模板中发现了一个严重漏洞&#xff0c;编号为CVE-2024-39700 。此漏洞可能使攻击者能够在受影响的系统上远程执行代码&#xff0c;从而可能导致大范围入侵和数据泄露。 该漏洞源于在扩展创建过程中选择“测试”选项时自动生成“update-integratio…

基于 HTML+ECharts 实现智慧安防数据可视化大屏(含源码)

构建智慧安防数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着科技的不断进步&#xff0c;智慧安防系统已经成为保障公共安全的重要工具。通过数据可视化&#xff0c;安防管理人员可以实时监控关键区域的安全状况、人员流动以及设备状态&#xff0c;从而提高应急响…

TikTok批量养号方法

想要在TikTok平台上批量养号&#xff0c;确保账号的健康与活跃度非常重要&#xff0c;不然等于白干。下面&#xff0c;我们就来详细探讨一下TikTok养号的几个关键步骤。 首先&#xff0c;新注册的账号必须严格遵守一机一号一IP的原则。随着TikTok平台在识别IP技术方面的不断进步…

叶再豪老师-降龙精英课程

文章目录 1.思维认知1.1 稻盛和夫成功公式1.2 龙头主升模式1.3 龙头主升-两种路径1.4 股市新手的炒股思路1.5 龙头案例1.6 降龙心法 2.情绪周期2.1 情绪周期2.1 情绪演绎周期2.2 情绪的四个部分2.2.1 指数的情绪周期2.2.3 热点情绪周期2.2.4 热点情绪演绎周期2.2.5 大热点支线2…

CDGA|数据治理:安全如何贯穿数据供给、流通、使用全过程

随着信息技术的飞速发展&#xff0c;数据已经成为企业运营、社会管理和经济发展的核心要素。然而&#xff0c;数据在带来巨大价值的同时&#xff0c;也伴随着诸多安全风险。因此&#xff0c;数据治理的重要性日益凸显&#xff0c;它不仅仅是对数据的简单管理&#xff0c;更是确…