AVL树的底层实现

文章目录

  • 什么是AVL树?
  • 平衡因子
  • Node节点
  • 插入
    • 新节点插入较高左子树的左侧
    • 新节点插入较高左子树的右侧
    • 新节点插入较高右子树的左侧
    • 新节点插入较高右子树的右侧
  • 验证是否为平衡树
  • 二叉树的高度
  • AVL的性能

什么是AVL树?

AVL树又称平衡二叉搜索树,相比与二叉搜索树多了平衡的概念,为什么要有平衡二叉搜索树呢?因为二叉搜索树可能会出现退化现象,导致左右子树高度相差较大,甚至退化成单链表的形式,因此我们提出了平衡二叉搜索树,可以有效地解决高度相差过大的情况,平衡二叉搜索树要求任意节点的左右子树高度相差不大于1.

平衡因子

为了解决高度差问题,我们在每一个节点中添加了平衡因子变量,这个变量用于记录该节点的左右子树的高度差。
若规定平衡因子等于右子树高度减去左子树高度,那么如果新增节点的位于当前节点的左侧那么该节点平衡因子-1,如果位于该节点的右侧,则该节点平衡因子+1,并且一直向上修改平衡因子,直到某个节点平衡因子为0,或者到根节点,或者需要进行调整

Node节点

template <class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;//pair是库中的类,包含first和second两个成员变量
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;//blance factor
	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};

我们规定平衡因子等于右子树高度减去左子树高度

插入

平衡二叉搜索树也是二叉搜索树,因此它的插入和二叉搜索树类似,都是找到一个合适的节点进行插入,不同的是这棵树要保证每个节点的平衡因子不大于1,因此我们在插入后可能需要对该树的节点进行调整。
需要调整的情况有以下四种:分别是

新节点插入较高左子树的左侧

情况1:
在这里插入图片描述

情况2
在这里插入图片描述

上面两种情况的处理方式是一样的,因为这两种情况都属于较高子树的左边,以图为例,5和10所在位置分别是不平衡节点9的左右子树,而新插入的节点都是位于左子树的根节点5的左侧

思路:如果新插入的节点位于不平衡节点的左子树的左侧,要想让不平衡节点变平衡,就要让高的一侧子树高度-1,低的一侧子树高度+1,这样就可以使得原本高度差为2的左右子树的高度差变为0,同时还要保证二叉树的性质不被破坏,所以还要对6进行处理。
右单旋:不平衡节点以及它的右子树进行,绕不平衡节点的左孩子旋转90度。
右单旋是指不平衡节点以及它的右子树进行旋转

//新节点插入较高左子树的左侧----左左: 右单旋
void RotateR(Node* parent)
{
	++_rotateCount;
	Node* cur = parent->_left;
	Node* curright = cur->_right;
	parent->_left = curright;/
	if (curright)
	{
		//parent->_right = curright;
		curright->_parent = parent;
	}
	cur->_right = parent;
	Node* ppnode = parent->_parent;
	parent->_parent = cur;
	//if (parent = _root)
	if(ppnode == nullptr)
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else
	{
		if (parent == ppnode->_left)
		{
			//cur = ppnode->_left;
			ppnode->_left = cur;
		}
		else
		{
			//cur = ppnode->_right;
			ppnode->_right = cur;
		}
		cur->_parent = ppnode;
	}
	cur->_bf = parent->_bf = 0;
}

最后不要忘记把新插入节点和那个不满足平衡因子的左孩子的平衡因子都置为0

新节点插入较高左子树的右侧

在这里插入图片描述
新插入节点位于7的左右两侧都属于同一种情况,这里就不做演示了。
先把不满足平衡因子的节点的左孩子进行左旋转,在对不满足平衡因子的节点进行右旋转。

//新节点插入较高左子树的右侧---左右:先左单旋再右单旋
void RotateLR(Node* parent)//先左旋再右旋
{
	Node* cur = parent->_left;
	Node* curright = cur->_right;
	int bf = curright->_bf;
	RotateL(parent->_left);
	RotateR(parent);
	if (bf == 0)
	{
		parent->_bf = 0;
		cur->_bf = 0;
		curright->_bf = 0;
	}
	else if (bf == 1)
	{
		curright->_bf = 0;
		cur->_bf = -1;
		parent->_bf = 0;
	}
	else if (bf == -1)
	{
		curright->_bf = 0;
		cur->_bf = 0;
		parent->_bf = 1;
	}
	else
	{
		assert(false);
	}
}

调整完之后不要忘记对旋转节点的平衡因子进行调整

新节点插入较高右子树的左侧

插入到较高右子树左侧与插入到较高左子树右侧对称,因此调整方式恰好相反,因此是先右旋再左旋

//新节点插入较高右子树的左侧---右左:先右单旋再左单旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;

RotateR(parent->_right);
RotateL(parent);

if (bf == 0)
{
	parent->_bf = 0;
	cur->_bf = 0;
	curleft->_bf = 0;
}
else if (bf == 1)
{
	curleft->_bf = 0;
	cur->_bf = 0;
	parent->_bf = -1;
		
}
else if (bf == -1)
{
	curleft->_bf = 0;
	cur->_bf = 1;
	parent->_bf = 0;
}
else
{
	assert(false);
}
}

新节点插入较高右子树的右侧

左单旋是指不平衡节点以及它的左子树进行旋转
同理,与插入到较高左子树的左侧对称,因此是左单旋

//新节点插入较高右子树的右侧----右右:左单旋
void RotateL(Node* parent)
{
	++_rotateCount;
	Node* cur = parent->_right;
	Node* curleft = cur->_left;

	parent->_right = curleft;/
	if (curleft)
	{
		//parent->_right = curleft;
		curleft->_parent = parent;
	}
	cur->_left = parent;
	Node* ppnode = parent->_parent;
	parent->_parent = cur;
	if (parent == _root)
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else
	{
		if (ppnode->_left == parent)
		{
			ppnode->_left = cur;
		}
		else
		{
			ppnode->_right = cur;
		}
		cur->_parent = ppnode;
	}
	cur->_bf = parent->_bf = 0;
}

验证是否为平衡树

bool IsBlance()
{
	return IsBlance(_root);
}

bool IsBlance(Node* root)
{
	if (root == nullptr)
	{
		return true;
	}
	int leftHeight = Height(root->_left);
	int rightHeight = Height(root->_right);
	if (rightHeight - leftHeight != root->_bf)
	{
		cout << "平衡因子异常:" << root->_kv.first << " " << root->_bf << endl;
		return false;
	}
	return abs(rightHeight - leftHeight) < 2
		&& IsBlance(root->_right)
		&& IsBlance(root->_left);
}

二叉树的高度

int Height()
{
	return Height(_root);
}

int Height(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int leftHeight = Height(root->_left);
	int rightHeight = Height(root->_right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

AVL的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log_2 (N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm+vue的程序设计课程可视化教学系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Jmeter进行压力测试不为人知的秘密

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是免…

Mysql中的进阶增删查改操作(二)

联合查询和合并查询 一.联合查询1.内连接2.外链接2.1左外连接2.2右外连接 3.自连接4.子查询5.合并查询 一.联合查询 步骤 1.进行笛卡尔积 2.列出连接条件 3.根据需求再列出其他条件 4.针对列进行精简(可以使用聚合函数) 我们先搭建一个多表查询的框架 这样一个多表查询就搭建出…

换硬币C语言(超详细分析!包会)

换硬币C语言&#xff08;详解&#xff09; 题目介绍分析题目代码题目讲解 题目介绍 分析 通过枚举的方式找出所有可能的找零方案&#xff0c;并统计满足条件的方案的个数。使用三层嵌套的循环遍历所有可能的组合&#xff0c;判断总金额是否等于给定的金额 x&#xff0c;并输出…

Smart Tomcat的使用

文章目录 Smart Tomcat的作用Smart Tomcat的安装Smart Tomcat的配置Smart Tomcat的启动 Smart Tomcat的作用 我们知道使用Servlet来完成一个项目一共需要七个步骤&#xff0c;即创建maven项目、添加依赖、创建目录结构、编写代码、打包程序、部署程序、验证程序。这样的确是完…

城市建设后如何进行对建筑的实时监测,预防危险?

建筑后健康监测是指对已建成的建筑物进行定期的结构健康监测&#xff0c;以确保其安全性和稳定性。这种监测可以包括对建筑物的振动、变形、裂缝、损伤等进行监测&#xff0c;以及对其结构完整性进行评估。此外&#xff0c;建筑物健康监测也可以促进建筑物的智能化和自动化管理…

Java-Review

题型分值总分分布简答 5 ∗ 8 ′ 5*8 5∗8′ 4 0 ′ 40 40′面向对象、异常处理、多线程、输入输出处理程序分析和补全 3 ∗ 1 0 ′ 3*10 3∗10′ 3 0 ′ 30 30′异常处理、Collection、图形化界面、输入输出处理编程 2 ∗ 1 5 ′ 2*15 2∗15′ 3 0 ′ 30 30′Collections、多线…

Docker Swarm: 容器编排的力量和优势深度解析

文章目录 Docker Swarm的核心概念1. 节点&#xff08;Node&#xff09;2. 服务&#xff08;Service&#xff09;3. 栈&#xff08;Stack&#xff09; 使用Docker Swarm1. 初始化Swarm2. 加入节点3. 创建服务4. 扩展和缩减服务5. 管理栈6. 管理服务更新 Docker Swarm的优势深度解…

JAVA深化篇_42—— 正则表达式

3 正则表达式 3.1正则表达式介绍 3.1.1 什么是正则表达式 正则表达式&#xff0c;又称规则表达式。&#xff08;英语&#xff1a;Regular Expression&#xff0c;在代码中常简写为 regex、regexp 或 RE&#xff09;&#xff0c;是计算机科学的一个概念。正则表达式通常被用来…

前端反卷计划-组件库-03-组件样式

Hi, 大家好&#xff01;我是程序员库里。 今天开始分享如何从0搭建UI组件库。这也是前端反卷计划中的一项。 在接下来的日子&#xff0c;我会持续分享前端反卷计划中的每个知识点。 以下是前端反卷计划的内容&#xff1a; 目前这些内容持续更新到了我的 学习文档 中。感兴趣…

2023年【陕西省安全员B证】考试题库及陕西省安全员B证找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 陕西省安全员B证考试题库是安全生产模拟考试一点通生成的&#xff0c;陕西省安全员B证证模拟考试题库是根据陕西省安全员B证最新版教材汇编出陕西省安全员B证仿真模拟考试。2023年【陕西省安全员B证】考试题库及陕西省…

5g路由器赋能园区无人配送车联网应用方案

随着人工智能、无人驾驶技术和自动化技术的不断进步&#xff0c;无人配送技术得到了极大的发展。园区内的物流配送任务通常是繁琐的&#xff0c;需要大量的人力资源和时间。无人配送技术能够提高配送效率并减少人力成本。无人配送车辆和机器人能够根据预定的路线和计划自动完成…

23111701[含文档+PPT+源码等]计算机毕业设计javaweb点餐系统全套餐饮就餐订餐餐厅

文章目录 **项目功能简介:****点餐系统分为前台和后台****前台功能介绍&#xff1a;****后台功能介绍&#xff1a;** **论文截图&#xff1a;****实现&#xff1a;****代码片段&#xff1a;** 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;77687156…

实践小记——C#格式化小数输出

文章导航 格式化小数位数示例格式化小数总结参考文章 面向Winform的实践过程中&#xff0c;遇到的一些使用到的小细节&#xff1b; 当然其他地方基本上也同理。 写作不易&#xff0c;希望友善多金的码友能够随手点一个赞&#xff0c;共同创建氛围更加良好的开发者社区&#xf…

QTcpSocket发送结构体的做法

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> QTcpSocket发送结构体其实很简单:使用QByteArray类对象进行封装发送&#xff0c;示例代码如下&#xff1a; /* 消息结构体 */ struct stMsg {int m_A…

VRRP专题

一&#xff0c;VRRP&#xff1a;虚拟路由冗余协议 将多个路由设备联合组成一台虚拟的路由设备&#xff0c;这台虚拟的路由设备做用户的网关&#xff0c;转发数据&#xff1b;这台虚拟的设备的网关由一个高优先级的设备承载&#xff0c;该设备被称为master路由器&#xff0c;其…

Flutter 应用启动从闪屏页短暂黑屏再到第一个页面

由于应用初始状态启动会有白屏现象&#xff0c;便使用 flutter_native_splash 2.3.5 插件生成了启动相关的配置&#xff0c;并且按照示例使用了 import package:flutter_native_splash/flutter_native_splash.dart;void main() {WidgetsBinding widgetsBinding WidgetsFlutte…

软件开发和测试

一&#xff0c;敏捷软件开发 二&#xff0c;软件测试

Vue3.0和2.0语法不同分析

前言&#xff1a;本篇文章只做VUE3.0和VUE2.0语法上的不同分析&#xff0c;不做性能和源码架构等的分析。 一、VUE3.0和VUE2.0代码结构不同 VUE3.0代码实例 <template><div><span>count is {{ count }}</span><span>plusOne is {{ plusOne }}…

HHDESK资源管理批量修改

HHDESK自带客户端支持批量修改。 右键资源&#xff0c;选择“批量修改”。 在弹出框中&#xff0c;选择需要修改的选项&#xff1b; 以及资源类型&#xff1b; 点击确定&#xff1b; 可在对话框下方的操作日志中&#xff0c;查看结果。