数据结构:AVL树的旋转(高度平衡树)

1、AVL树简介

 AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

  • 它的子树都是AVL树
  • 左右子树高度插(平衡因子)的绝对值不超过1

AVL树还是一个搜索二叉树,只不过因为普通的搜索二叉树有可能变成单只树,这样就使查找效率非常的低。我们就给普通的搜索二叉树增加了一个平衡因子来使AVL左右子树的高度差的绝对值不超过1

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;              
	pair<K, V> _kv;                          // 存储的键对
	int _bf;                                 // balance factor

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

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv);
	bool IsBalance();
	void InOrder();
	void Height();

private:
	void RotateL(Node* parent);              //左旋转
	void RotateR(Node* parent);              //右旋转
	void RotateRL(Node* parent);             //右左旋转
	void RotateLR(Node* parent);             //左右旋转
	bool _IsBalance(Node* root);
	void _InOrder(Node* root);
	int _Height(Node* root);


	Node* _root = nullptr;
};

2、AVL树的插入

  • AVL树的插入前面和搜索二叉树的插入一模一样,只不过我们要注意平衡因子
  • 因为AVL树保持平衡是要平衡因子的绝对值不超过1,而插入会使平衡因子改变,所以我们也要要相应的旋转,使平衡因子的绝对值不超过一,我们又有几种情况
  1. 假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
  2. parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR,当subR的平衡因子为1时,执行左单旋。当subR的平衡因子为-1时,执行右左双旋
  3. parent的平衡因子为-2,说明parent的左子树高,设parent的左子树的根为subL,当subL的平衡因子为-1是,执行右单旋。当subL的平衡因子为1时,执行左右双旋
  4. 旋转完成后,原parent为根的子树个高度降低,已经平衡,不需要再向上更新
template<class K, class V>
bool AVLTree<K, V>::Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(kv);
	if (parent->_kv.first > kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;

	// 更新平衡因子
	while (parent)
	{
		if (cur == parent->_right)
		{
			parent->_bf++;
		}
		else
		{
			parent->_bf--;
		}

		if (parent->_bf == 1 || parent->_bf == -1)
		{
			// 继续更新
			parent = parent->_parent;
			cur = cur->_parent;
		}
		else if (parent->_bf == 0)
		{
			break;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
			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)
			{
				RotateLR(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(parent);
			}
			else
			{
				assert(false);
			}

			break;
		}
		else
		{
			assert(false);
		}
	}

	return true;
}

2.1、左单旋

  1. subRL变成parent的右子树 
  2. parent变成subR的左子树
  3. subR变成新根
  4. 更新平衡因子
template<class K, class V>
void AVLTree<K, V>::RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

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

	Node* parentParent = parent->_parent;

	parent->_parent = subR;
	if (subRL)
		subRL->_parent = parent;

	if (_root == parent)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subR;
		}
		else
		{
			parentParent->_right = subR;
		}

		subR->_parent = parentParent;
	}

	parent->_bf = subR->_bf = 0;
}

2.2、右单旋

60为parent,30为subL,b为subLR 

  1. subLR变成parent的左子树 
  2. parent变成subL的右子树
  3. subL变成新根
  4. 更新平衡因子
template<class K, class V>
void AVLTree<K, V>::RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

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

	Node* parentParent = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (_root == parent)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;
		}
		else
		{
			parentParent->_right = subL;
		}

		subL->_parent = parentParent;
	}

	subL->_bf = parent->_bf = 0;
}

2.3、左右双旋 

先对30进行左单旋,先对90右单旋

template<class K, class V>
void AVLTree<K, V>::RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;

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

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

2.3、右左双旋 

先对90进行右单旋,先对30左单旋 

template<class K, class V>
void AVLTree<K, V>::RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

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

	if (bf == 0)
	{
		// subRL自己就是新增
		parent->_bf = subR->_bf = subRL->_bf = 0;
	}
	else if (bf == -1)
	{
		// subRL的左子树新增
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 1;
	}
	else if (bf == 1)
	{
		// subRL的右子树新增
		parent->_bf = -1;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

 3、AVL树的性能

AVL树是带有平衡条件的二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,左右子树树高不超过1,和红黑树相比,它是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的,由此我们可以知道AVL树适合用于插入删除次数比较少,但查找多的情况。

如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合

4、实现

#include<assert.h>

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;              
	pair<K, V> _kv;                          // 存储的键对
	int _bf;                                 // balance factor

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

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv);
	bool IsBalance();
	void InOrder();
	void Height();

private:
	void RotateL(Node* parent);              //左旋转
	void RotateR(Node* parent);              //右旋转
	void RotateRL(Node* parent);             //右左旋转
	void RotateLR(Node* parent);             //左右旋转
	bool _IsBalance(Node* root);
	void _InOrder(Node* root);
	int _Height(Node* root);


	Node* _root = nullptr;
};

template<class K, class V>
bool AVLTree<K, V>::Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(kv);
	if (parent->_kv.first > kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;

	// 更新平衡因子
	while (parent)
	{
		if (cur == parent->_right)
		{
			parent->_bf++;
		}
		else
		{
			parent->_bf--;
		}

		if (parent->_bf == 1 || parent->_bf == -1)
		{
			// 继续更新
			parent = parent->_parent;
			cur = cur->_parent;
		}
		else if (parent->_bf == 0)
		{
			break;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
			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)
			{
				RotateLR(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(parent);
			}
			else
			{
				assert(false);
			}

			break;
		}
		else
		{
			assert(false);
		}
	}

	return true;
}

template<class K, class V>
void AVLTree<K, V>::RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

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

	Node* parentParent = parent->_parent;

	parent->_parent = subR;
	if (subRL)
		subRL->_parent = parent;

	if (_root == parent)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subR;
		}
		else
		{
			parentParent->_right = subR;
		}

		subR->_parent = parentParent;
	}

	parent->_bf = subR->_bf = 0;
}

template<class K, class V>
void AVLTree<K, V>::RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

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

	Node* parentParent = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (_root == parent)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;
		}
		else
		{
			parentParent->_right = subL;
		}

		subL->_parent = parentParent;
	}

	subL->_bf = parent->_bf = 0;
}

template<class K, class V>
void AVLTree<K, V>::RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

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

	if (bf == 0)
	{
		// subRL自己就是新增
		parent->_bf = subR->_bf = subRL->_bf = 0;
	}
	else if (bf == -1)
	{
		// subRL的左子树新增
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 1;
	}
	else if (bf == 1)
	{
		// subRL的右子树新增
		parent->_bf = -1;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

template<class K, class V>
void AVLTree<K, V>::RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;

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

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

template<class K, class V>
void AVLTree<K, V>::InOrder()
{
	_InOrder(_root);
	cout << endl;
}

template<class K, class V>
void AVLTree<K, V>::_InOrder(Node* root)
{
	if (root == nullptr)
		return;

	_InOrder(root->_left);
	cout << root->_kv.first << " ";
	_InOrder(root->_right);
}

template<class K, class V>
bool AVLTree<K, V>::IsBalance()
{
	return _IsBalance(_root);
}

template<class K, class V>
int AVLTree<K, V>::_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;
}

template<class K, class V>
void AVLTree<K, V>::Height()
{
	cout << _Height(_root) << endl;
}

template<class K, class V>
bool AVLTree<K,V>::_IsBalance(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 << "平衡因子异常" << endl;
		return false;
	}

	return abs(rightHeight - leftHeight) < 2
		&& _IsBalance(root->_left)
		&& _IsBalance(root->_right);
}

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

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

相关文章

CSS3渐变颜色

CSS3 渐变可以让你在两个或多个指定的颜色之间显示平稳的过渡。 CSS3渐变有两种类型&#xff1a;线性渐变&#xff08;Linear Gradients&#xff09;和径向渐变&#xff08;Radial Gradients&#xff09;。 线性渐变&#xff08;Linear Gradients&#xff09;&#xff1a; 线性…

MSVCP140_CODECVT_IDS.dll丢失怎么办?推荐三个解决方法帮你解决

MSVCP140_CODECVT_IDS.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它包含了一些运行时库文件。当您在运行某些程序时&#xff0c;可能会遇到“msvcp140_codecvt_ids.dll丢失”的错误提示。为了解决这个问题&#xff0c;您可以尝试以下三种方法&#x…

CH11_重构API

将查询函数和修改函数分离&#xff08;Separate Query from Modifier&#xff09; function getTotalOutstandingAndSendBill() {const result customer.invoices.reduce((total, each) > each.amount total, 0);sendBill();return result; }function totalOutstanding() …

Adobe ME下载、Media Encoder下载

Media Encoder 2021 是一款可以帮助Adobepremiere pro和Adobe After Effects的用户使用集成视频编码器进行创作的视频和音频编码软件。Media Encoder 2021 mac新版本中针对上一个版本进行了多方面的改进与优化&#xff0c;提升了软件的性能与支持文件格式提升&#xff0c;有需要…

CSS3图片反射box-reflect属性

CSS3中有一个box-reflect属性&#xff0c;也就是图片反射&#xff0c;主要的效果是将一张图片反射到对应面并且可以设置想要的一些效果&#xff08;如&#xff1a;渐变&#xff09;等 语法 box-reflect&#xff1a;包括三个值 direction 定义方向 above 反射到对象的上面below…

MySQL | 查询接口性能调优、编码方式不一致导致索引失效

背景 最近业务反馈&#xff0c;列表查询速度过慢&#xff0c;需要优化。 到正式环境系统去验证&#xff0c;发现没筛选任何条件的情况下&#xff0c;查询需要三十多秒&#xff0c;而筛选了条件之后需要13秒。急需优化。 先说结论&#xff1a;连表用的字段编码方式不一致导致索…

基于SSM的水果网上商城设计与实现

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

Netty入门指南之NIO 网络编程

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言基础扫…

Netty入门指南之NIO Buffer详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言ByteBu…

kubernetes prometheus监控

目录 一、部署prometheus 二、 部署nginx监控实例 三、部署prometheus-adapter 一、部署prometheus 清理镜像方便后面一次性上传 docker rmi docker images | grep -v REPOSITORY | awk {print $1":"$2} 删除 docker load -i kube-prometheus-stack-0.58.0.tar…

盘点U-Mail邮件系统安全设计

在当今社会&#xff0c;电子邮件已经成企业沟通和信息传递重要的手段之一&#xff0c;是企业办公中不可或缺的一部分。但是由于企业邮件服务器端口对外开放、企业邮件安全管理能力不足、邮件内容敏感性高等特点&#xff0c;电子邮件也成为了网络攻击者进行网络钓鱼、恶意软件传…

基于Python的pyAV读取H265(HEVC)编码的视频文件

1.问题出现 利用海康威视相机拍出来的视频是H265格式的&#xff0c;相比于常规的H264编码&#xff0c;压缩率更高&#xff0c;但因此如果直接用之前的方法读取&#xff0c;会出现无法读取的情况&#xff0c;如下。 可以看到&#xff0c;对于帧间没有改变的部分&#xf…

AD教程 (十一)封装的统一管理

AD教程 (十一)封装的统一管理 PCB封装添加 一个一个手动添加&#xff0c;效率太低&#xff0c;不建议使用 使用封装管理器快速添加&#xff0c;根据BOM表&#xff08;元器件清单&#xff09;&#xff0c;修改PCB封装 点击工具&#xff0c;选择封装管理器&#xff0c;进入封装…

全局前置路由守卫(beforeEach)

全局前置路由守卫&#xff08;beforeEach&#xff09; 功能&#xff1a;每一次切换任意路由组件之前都会被调用&#xff0c;相当于在进入另一个路由组件之前设置一个权限。 路由守卫的存在意义就是在不同的时间&#xff0c;不同的位置&#xff0c;去添加代码。如&#xff1a;J…

【开源】基于Vue和SpringBoot的生活废品回收系统

项目编号&#xff1a; S 003 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S003&#xff0c;文末获取源码。} 项目编号&#xff1a;S003&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&…

黑马程序员微服务SpringCloud实用篇02

SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我…

创新无处不在的便利体验——基于智能视频和语音技术的安防监控系统EasyCVR

随着科技的迅猛发展&#xff0c;基于智能视频和语音技术的EasyCVR智能安防监控系统正以惊人的速度改变我们的生活。EasyCVR通过结合先进的视频分析、人工智能和大数据技术&#xff0c;为用户提供了更加智能、便利的安全保护体验&#xff0c;大大提升了安全性和便利性。本文将介…

java数据结构--阻塞队列

目录 一.概念 二.生产者消费者问题 三.阻塞队列接口BlockingQueue 四.基于数组实现单锁的阻塞队列 1.加锁方式 2.代码实现 3.解释说明 (1).offer添加元素 &#xff08;2&#xff09;poll取出元素 4.timeout超时时间 5.测试 五.基于数组实现双锁的阻塞队列 1.问题 …

四.pyqt5 登录界面和功能

一.使用qt creator 设置登录界面 主界面为之前设计的界面 from123.py 文章地址&#xff1a;三.listview或tableviw显示 二.导出ui文件为py文件 # from123.py 为导出 py文件 form.ui 为 qt creator创造的 ui 文件 pyuic5 -o x:\xxx\Fromlogin20230809.py form.ui三.python 显…

中断处理程序的延迟可能导致中断标志位仍然被置位

当中断处理程序的执行时间超过了中断事件的频率时&#xff0c;可能出现中断标志位仍然被置位的情况。让我们来详细解释一下这种情况。 在一个典型的系统中&#xff0c;中断处理程序会在中断事件发生时被触发执行。中断处理程序负责处理中断事件&#xff0c;并可能执行一系列操…