【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块

二叉搜索树:【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客

目录

一、AVL树的概念

二、AVL树的原理与实现

AVL树的节点

AVL树的插入

AVL树的旋转

AVL树的打印

AVL树的检查

三、实现AVL树的完整代码

四、总结


前言:

在前面我们学习二叉搜索树的时候,虽然大部分情况下二叉搜索树的效率都是很高的,但是如果是一组相对有序的数字,我们用二叉搜索树来排序就会显得比较麻烦了,因此,AVL树就出现了,下面就让我们一起来学习以下AVL树的相关知识

一、AVL树的概念

AVL树实际上就是特殊的二叉搜索树,是对二叉搜索树的改进,我们在用树形结构来查找或操作数据的时候,一般都是要从树根一层一层往下找,所以树高越低,效率越高,但是二叉搜索树在有些场景下比较麻烦,比如一个相对有序的数组{2,1,3,4,5,6}

这样一个数组如果通过二叉搜索树处理就会显得层数太多,效率很低

所以人们也就有了这样一种思考:我们可不可以通过某种操作让左右子树的高度差不超过1,这样就能极大的提高效率,这就是AVL树的概念

AVL树中任何节点的左右子树的高度差(平衡因子)绝对值不超过1。这意味着树始终保持平衡,避免了二叉搜索树在节点插入或删除后可能出现的退化现象。

二、AVL树的原理与实现

了解了AVL树的基本内容之后,接下来我们就来一步一步学习以下AVL树的原理到底是什么以及如何实现一个AVL树:

AVL树的节点

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;    //平衡因子(通过这个可以直到左右子树存在情况)

	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)     //平衡因子起始值是0,当左子树插入一个节点时-1,右子树插入一个节点时+1
	{}
};

AVL树的节点操作与二叉搜索树还是比较相似的,都有左子树右子树和父亲节点的叉式结构,比较不同的是加入了一个平衡因子

AVL树的插入

实现AVL树的重点就是解决AVL树的插入问题,而解决插入问题最关键的就是要做到如何让左右子树高度的绝对值适合不大于1,我们是通过合理的旋转来实现的,而且需要旋转的情况也是分为四种的:

RR型:左旋

LL型:右旋

RL型:先右旋,再左旋

LR型:先左旋,再右旋

下面我们来看这样几个例子:

1、RR型(左旋)

2、LL型(右旋)

3、LR型(先左旋,再右旋)

4、RL型(先右旋,再左旋)

操作与3正好相反,不过多赘述

下面就让我们看一下插入的代码:

	bool 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->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//管控平衡
		while (parent)    //当为根时,停止
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) //旋转
			{
				if (parent->_bf == 2&&cur->_bf==1)
				{
					//左单旋
					RotateL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					//右单旋
					RotateR(parent);
					break;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//RL型
					RotateRL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					//LR型
					RotateLR(parent);
					break;
				}
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

AVL树的旋转

	void RotateL(Node* parent)   //左单旋(RR型)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		parent->_right = subRL;
		subR->_left = parent;
		
		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->_parent = subR;
		parent->_bf = 0;
		subR->_bf = 0;
	}
	void RotateR(Node* parent)   //右单旋(LL型)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		parent->_left = subLR;
		subL->_right = parent;

		if (subLR)
		{
			subLR->_parent = parent;
		}
		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
		parent->_parent = subL;
		subL->_bf = parent->_bf = 0;
	}
	void RotateRL(Node* parent)      //先右单旋,再左单旋(RL型)
	{
		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 = 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);
		}
	}
	void RotateLR(Node* parent)      //先左单旋,再右单旋(LR型)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

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

AVL树的打印

AVL树的打印与二叉搜索树一致,都是中序打印,我们就直接来看代码了

	//中序打印
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << " ";
		_Inorder(root->_right);
	}

AVL树的检查

检查是否为AVL树,一方面我们可以通过打印的结果先来判断一下它是不是二叉搜索树,然后我们可以通过比较左右子树的高度差来判断它是否为AVL树(根据前面可知AVL树左右子树高度差最大为1)

	//检查是否为AVL树
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	int _High(Node* root)
	{
		if (root == nullptr)
			return 0;

		int LeftHigh = _High(root->_left);
		int RightHigh = _High(root->_right);
		return LeftHigh > RightHigh ? LeftHigh + 1 : RightHigh + 1;
	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int LeftHigh = _High(root->_left);
		int RightHigh = _High(root->_right);
		if (RightHigh - LeftHigh != root->_bf)
		{
			cout << _root->_kv.first << "当前节点平衡因子有问题" << endl;
			return false;
		}
		return abs(LeftHigh- RightHigh) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

三、实现AVL树的完整代码

AVLTree.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;    //平衡因子(通过这个可以直到左右子树存在情况)

	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)     //平衡因子起始值是0,当左子树插入一个节点时-1,右子树插入一个节点时+1
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool 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->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//管控平衡
		while (parent)    //当为根时,停止
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) //旋转
			{
				if (parent->_bf == 2&&cur->_bf==1)
				{
					//左单旋
					RotateL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					//右单旋
					RotateR(parent);
					break;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//RL型
					RotateRL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					//LR型
					RotateLR(parent);
					break;
				}
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

	void RotateL(Node* parent)   //左单旋(RR型)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		parent->_right = subRL;
		subR->_left = parent;
		
		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->_parent = subR;
		parent->_bf = 0;
		subR->_bf = 0;
	}
	void RotateR(Node* parent)   //右单旋(LL型)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		parent->_left = subLR;
		subL->_right = parent;

		if (subLR)
		{
			subLR->_parent = parent;
		}
		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
		parent->_parent = subL;
		subL->_bf = parent->_bf = 0;
	}
	void RotateRL(Node* parent)      //先右单旋,再左单旋(RL型)
	{
		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 = 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);
		}
	}
	void RotateLR(Node* parent)      //先左单旋,再右单旋(LR型)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

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

	//中序打印
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << " ";
		_Inorder(root->_right);
	}

	//检查是否为AVL树
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	int _High(Node* root)
	{
		if (root == nullptr)
			return 0;

		int LeftHigh = _High(root->_left);
		int RightHigh = _High(root->_right);
		return LeftHigh > RightHigh ? LeftHigh + 1 : RightHigh + 1;
	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int LeftHigh = _High(root->_left);
		int RightHigh = _High(root->_right);
		if (RightHigh - LeftHigh != root->_bf)
		{
			cout << _root->_kv.first << "当前节点平衡因子有问题" << endl;
			return false;
		}
		return abs(LeftHigh- RightHigh) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}
private:
	Node* _root = nullptr;
};

test.c

//AVL树
#include"AVLTree.h"
int main()
{
	int a[] = { 16,3,7,11,9,26,18,14,15 };   
	AVLTree<int, int> t;
	for (auto e : a)
	{
		t.Insert(make_pair(e, e));
	}
	t.Inorder();
	cout << "输出1代表是AVL树,输出0代表不是:" << t.IsBalance() << endl;

	return 0;
}

运行结果:

四、总结

AVL树的理解和实现总体来说还是比较难的,思路一定要搞清楚,代码实现上尽力而为

感谢各位大佬观看,创作不易,还请各位大佬点一个小小的赞!!!

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

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

相关文章

2024世界人工智能大会(WAIC)学习总结

1 前言 在2024年的世界人工智能大会&#xff08;WAIC&#xff09;上&#xff0c;我们见证了从农业社会到工业社会再到数字化社会的深刻转变。这一进程不仅体现在技术的单点爆发&#xff0c;更引发了整个产业链的全面突破&#xff0c;未来将是技术以指数级速度发展的崭新时代。…

21天学通C++:第十一、十二章节

第十一章&#xff1a;多态 多态基础 多态&#xff08;Polymorphism&#xff09;是面向对象语言的一种特征&#xff0c;让您能够以类似的方式处理不同类似的对象。 有一句话总结的很好&#xff1a;多态其实就是父类型引用指向子类型对象。 为什么需要多态 #include <ios…

Profibus协议转Profinet协议网关模块连接智能电表通讯案例

一、背景 在工业自动化领域&#xff0c;Profibus协议和Profinet协议是两种常见的工业通讯协议&#xff0c;而连接智能电表需要用到这两种协议之间的网关模块。本文将通过一个实际案例&#xff0c;详细介绍如何使用Profibus转Profinet模块&#xff08;XD-PNPBM20&#xff09;实…

docker 安装 onlyoffice

1.文档地址 Installing ONLYOFFICE Docs for Docker on a local server - ONLYOFFICE 2.安装onlyoffice docker run -i -t -d -p 9000:8000 --restartalways -e JWT_ENABLEDfalse onlyoffice/documentserver 如果发现镜像无法下载,可以尝试更换镜像源 {"registry-mir…

wifi信号处理的CRC8、CRC32

&#x1f9d1;&#x1f3fb;个人简介&#xff1a;具有3年工作经验&#xff0c;擅长通信算法的MATLAB仿真和FPGA实现。代码事宜&#xff0c;私信博主&#xff0c;程序定制、设计指导。 &#x1f680;wifi信号处理的CRC8、CRC32 目录 &#x1f680;1.CRC概述 &#x1f680;1.C…

【链表】算法题(一) ---- 力扣 / 牛客

一、移除链表元素 移除链表中值为val的元素&#xff0c;并返回新的头节点 思路&#xff1a; 题目上这样说&#xff0c;我们就可以创建一个新的链表&#xff0c;将值不为val的节点&#xff0c;尾插到新的链表当中&#xff0c;最后返回新链表的头节点。 typedef struct ListNo…

[React 进阶系列] useSyncExternalStore hook

[React 进阶系列] useSyncExternalStore hook 前情提要&#xff0c;包括 yup 的实现在这里&#xff1a;yup 基础使用以及 jest 测试 简单的提一下&#xff0c;需要实现的功能是&#xff1a; yup schema 需要访问外部的 storage外部的 storage 是可变的React 内部也需要访问同…

linux adb命令

⏩ 大家好哇&#xff01;我是小光&#xff0c;正在努力寻找自己的职业方向。 ⏩ 在调试设备时&#xff0c;经常会用到adb命令&#xff0c;本文对linux adb命令做一个知识分享。 ⏩ 感谢你的阅读&#xff0c;不对的地方欢迎指正。 1.adb命令 即 Android Debug Bridge 是一种允许…

解决第三方模块ts声明文件缺失的问题

最近小卷在用vite脚手架学习vue组件开发&#xff0c;使用的语言框架是typescript。在搭建vitepress在线文档服务时&#xff0c;用到了vitepress-demo-preview模块来展示vue组件示例和源代码。 发现import相关依赖时&#xff0c;会有这样的编译错误&#xff1a; 也就是没找到第…

Transformer模型:Postion Embedding实现

前言 这是对上一篇WordEmbedding的续篇PositionEmbedding。 视频链接&#xff1a;19、Transformer模型Encoder原理精讲及其PyTorch逐行实现_哔哩哔哩_bilibili 上一篇链接&#xff1a;Transformer模型&#xff1a;WordEmbedding实现-CSDN博客 正文 先回顾一下原论文中对Posit…

张量分解(5)——Tucker分解

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…

【C++】构造函数详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

【开源 Mac 工具推荐之 1】gibMacOS:方便快捷的 macOS 完整包下载 Shell 工具

简介 gibMacOS 是由 GitHub 开发者 corpnewt 编写的一款 Shell 工具。它采用 Python 编程语言&#xff0c;可以让用户打开后在纯文本页面中轻松选择并下载来源于 Apple 官方的 macOS 完整安装包。 Repo 地址&#xff1a;https://github.com/corpnewt/gibMacOS &#xff08;其…

阿里通义音频生成大模型 FunAudioLLM 开源

简介 近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术的进步极大地改变了人类与机器的互动方式&#xff0c;特别是在语音处理领域。阿里巴巴通义实验室最近开源了一个名为FunAudioLLM的语音大模型项目&#xff0c;旨在促进人类与大型语言模型&#xff08;LLMs&…

HTML+CSS博客文章列表

源代码在图片后面 点赞❤️收藏⭐️关注&#x1f495; 图示 感谢各位大佬支持 &#x1f618;&#x1f618;&#x1f618; 源代码 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <title>博…

解决ESLint和Prettier冲突的问题

在配置了ESLint的项目中使用Prettier进行格式化可能会出现冲突&#xff0c;不如Prettier配置了使用双引号&#xff0c;ESLint配置了单引号&#xff0c;当然可以一个一个改成一样的配置&#xff0c;但是比较麻烦。我发现可以直接使用ESLint的规则进行格式化。在VSCode配置过程如…

springmvc1

以前的servlet程序&#xff1a; springmvc 不同的处理器&#xff1a;不同的方法或者处理类 所有的请求都会经过dispathcherservlet的doservice方法&#xff1a; mvc原理&#xff1a; 前端控制器&#xff1a;jsp或者什么东西

AutoMQ 中的元数据管理

本文所述 AutoMQ 的元数据管理机制均基于 AutoMQ Release 1.1.0 版本 [1]。 01 前言 AutoMQ 作为新一代基于云原生理念重新设计的 Apache Kafka 发行版&#xff0c;其底层存储从传统的本地磁盘替换成了以对象存储为主的共享存储服务。对象存储为 AutoMQ 带来可观成本优势的…

【C++】初识C++(下)

前言 本篇博客继续总结一下C入门的一些小知识 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;C 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 ​ 目录 1.引用 1.1引用的概念 1.2const引用 1.3指针和引用的…

外包干了1个月,技术明显退步。。。

有一种打工人的羡慕&#xff0c;叫做“大厂”。 真是年少不知大厂香&#xff0c;错把青春插稻秧。 但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂…