数据结构:AVL树

news2024/10/23 20:16:37

目录

1、AVL树的概念

2、二叉搜索树的功能与实现

1、AVL树节点定义

2、AVL树的插入

3、AVL树的旋转操作

1、左旋

2、右旋

3、左右旋

 4、右左旋

 3、AVL树完整代码实现


1、AVL树的概念

        在前面的文章中,我们学过了二叉搜索树,二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是 AVL
左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1)
平衡因子:右子树高度减去左子树高度
如果一棵二叉搜索树是高度平衡的,它就是 AVL 树。如果它有 n 个结点,其高度可保持在
O(log2 n) ,搜索时间复杂度 O(log2 n)
以下是一个简单AVL树的示例:节点周围的是平衡因子。

2、二叉搜索树的功能与实现

1、AVL树节点定义

template<class K,class V>
	struct AVLTreeNode
	{
		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;
		int _bf; //平衡因子
		pair<K, V> _kv;
		AVLTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _bf(0)
			,_kv(kv)
		{

		}
	};
每个节点的平衡因子设置为0,每个节点包含左右指针,以及父节点指针,每个节点的数据用pair来实现,第一个元素为Key来比较。

2、AVL树的插入

AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看作二叉搜索树

插入过程与二叉搜索树一致,只不过要注意更新节点的平衡因子。

因为平衡因子是右子树高度减去左子树高度,所以如果在左子树添加节点,bf(平衡因子)--,在右子树添加,bf++。

parent节点的平衡因子更新有三种情况:

1、parent的平衡因子为0,说明满足AVL性质,插入成功。

2、parent的平衡因子为1或-1,说明该树的高度增加,需要向上更新。

3、parent的平衡因子为-2或2,说明该节点违反了平衡树性质,需要对其进行旋转处理。

旋转处理下面会介绍,我们先来看插入的代码实现:

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;
				}
				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 = cur->_parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == 2 || parent->_bf == -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
						{
							RotateRL(parent);
						}
						break;
					}
					else
					{
						assert(false);
					}
				}
			return true;
		}

寻找插入位置与搜索二叉树一致,然后更新平衡因子 ,对于不同的违反AVL树性质需要不同的旋转操作。

3、AVL树的旋转操作

我们先来看左旋对应的情况:

1、左旋

a,b,c具有相同的高度,旋转后注意更新parent和cur的平衡因子为0;
void RotateL(Node* parent)
		{
			Node* sub = parent->_right;
			Node* subl = sub->_left;
			parent->_right = subl;
			if (subl)
			{
				subl->_parent = parent;
			}
			sub->_left = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = sub;
			if (parent == _root)
			{
				_root = sub;
				sub->_parent = nullptr;
			}
			else
			{
				if (parent == ppnode->_left)
				{
					ppnode->_left = sub;
				}
				else
				{
					ppnode->_right = sub;
				}
				sub->_parent = ppnode;
			}
			parent->_bf = 0;
			sub->_bf = 0;
		}

2、右旋

原理与左旋相似,不过是向右旋转而已 (a,b,c具有相同的高度)

void RotateR(Node* parent)
		{
			Node* sub = parent->_left;
			Node* subr = sub->_right;
			parent->_left = subr;
			if (subr)
			{
				subr->_parent = parent;
			}
			sub->_right = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = sub;
			if (parent == _root)
			{
				_root = sub;
				sub->_parent = nullptr;
			}
			else
			{
				if (parent == ppnode->_left)
				{
					ppnode->_left = sub;
				}
				else
				{
					ppnode->_right = sub;
				}
				sub->_parent = parent;
			}
			parent->_bf = 0;
			sub->_bf = 0;
		}

3、左右旋

先以subl为根左旋,再以parent为根进行右旋。(a,b,c具有相同的高度)

void RotateLR(Node* parent)
		{
			Node* subl = parent->_left;
			Node* sublr = subl->_right;
			int bf = sublr->_bf;
			RotateL(subl);
			RotateR(parent);
			if (bf == -1)
			{
				sublr->_bf = 0;
				parent->_bf = 1;
				subl->_bf = 0;
			}
			else if (bf == 1)
			{
				sublr->_bf = 0;
				parent->_bf = 0;
				subl->_bf = -1;
			}
			else if (bf == 0)
			{
				subl->_bf = 0;
				parent->_bf = 0;
				sublr->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。

 4、右左旋

原理与左右旋相似,只是换了个方向。(a,b,c具有相同的高度)

void RotateRL(Node* parent)
		{
			Node* subr = parent->_right;
			Node* subrl = subr->_left;
			int bf = subrl->_bf;
			RotateR(subr);
			RotateL(parent);
			if (bf == -1)
			{
				subrl->_bf = 0;
				parent->_bf = 0;
				subr->_bf = 1;
			}
			else if (bf == 1)
			{
				subrl->_bf = 0;
				parent->_bf = -1;
				subr->_bf = 0;
			}
			else if(bf==0)
			{
				subrl->_bf = 0;
				parent->_bf = 0;
				subr->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。 

 3、AVL树完整代码实现

内部包含查找以及判断是否是AVL树的函数,以及中序遍历。

#pragma once
namespace AVLTree_test
{
	template<class K,class V>
	struct AVLTreeNode
	{
		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;
		int _bf; //平衡因子
		pair<K, V> _kv;
		AVLTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _bf(0)
			,_kv(kv)
		{

		}
	};

	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;
				}
				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 = cur->_parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == 2 || parent->_bf == -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
						{
							RotateRL(parent);
						}
						break;
					}
					else
					{
						assert(false);
					}
				}
			return true;
		}

		void RotateL(Node* parent)
		{
			Node* sub = parent->_right;
			Node* subl = sub->_left;
			parent->_right = subl;
			if (subl)
			{
				subl->_parent = parent;
			}
			sub->_left = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = sub;
			if (parent == _root)
			{
				_root = sub;
				sub->_parent = nullptr;
			}
			else
			{
				if (parent == ppnode->_left)
				{
					ppnode->_left = sub;
				}
				else
				{
					ppnode->_right = sub;
				}
				sub->_parent = ppnode;
			}
			parent->_bf = 0;
			sub->_bf = 0;
		}
		void RotateR(Node* parent)
		{
			Node* sub = parent->_left;
			Node* subr = sub->_right;
			parent->_left = subr;
			if (subr)
			{
				subr->_parent = parent;
			}
			sub->_right = parent;
			Node* ppnode = parent->_parent;
			parent->_parent = sub;
			if (parent == _root)
			{
				_root = sub;
				sub->_parent = nullptr;
			}
			else
			{
				if (parent == ppnode->_left)
				{
					ppnode->_left = sub;
				}
				else
				{
					ppnode->_right = sub;
				}
				sub->_parent = parent;
			}
			parent->_bf = 0;
			sub->_bf = 0;
		}

		void RotateLR(Node* parent)
		{
			Node* subl = parent->_left;
			Node* sublr = subl->_right;
			int bf = sublr->_bf;
			RotateL(subl);
			RotateR(parent);
			if (bf == -1)
			{
				sublr->_bf = 0;
				parent->_bf = 1;
				subl->_bf = 0;
			}
			else if (bf == 1)
			{
				sublr->_bf = 0;
				parent->_bf = 0;
				subl->_bf = -1;
			}
			else if (bf == 0)
			{
				subl->_bf = 0;
				parent->_bf = 0;
				sublr->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}
		void RotateRL(Node* parent)
		{
			Node* subr = parent->_right;
			Node* subrl = subr->_left;
			int bf = subrl->_bf;
			RotateR(subr);
			RotateL(parent);
			if (bf == -1)
			{
				subrl->_bf = 0;
				parent->_bf = 0;
				subr->_bf = 1;
			}
			else if (bf == 1)
			{
				subrl->_bf = 0;
				parent->_bf = -1;
				subr->_bf = 0;
			}
			else if(bf==0)
			{
				subrl->_bf = 0;
				parent->_bf = 0;
				subr->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_kv.first << " " << root->_bf << endl;
			_InOrder(root->_right);
		}
		void InOrder()
		{
			_InOrder(_root);
		}

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

			return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
		}
		bool _IsBalance(Node* root)
		{
			if (root == nullptr)
				return true;
			int leftHeight = Height(root->_left);
			int rightHeight = Height(root->_right);
			if (abs(rightHeight - leftHeight) >= 2)
			{
				cout << root->_kv.first << "不平衡" << endl;
				return false;
			}
			if (rightHeight - leftHeight != root->_bf)
			{
				cout << root->_kv.first << "平衡因子异常" << endl;
				return false;
			}
			return _IsBalance(root->_left) && _IsBalance(root->_right);
		}
		bool IsBalance()
		{
			return _IsBalance(_root);
		}
		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 NULL;
		}
	private:
		Node* _root = nullptr;
	};
	void TestAVLTree1()
	{
		int a[] = { 4, 2, 6, 1,0 ,67,56,33,212,90};
		AVLTree<int, int> t;
		for (auto e : a)
		{
			if (e == 14)
			{
				int x = 0;
			}

			t.Insert(make_pair(e,e));
		}

		t.InOrder();
		cout << t.IsBalance();
	}
}

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

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

相关文章

智能控制:物联网智能插座对接文档

介绍 一开始买的某米的插座&#xff0c;但是好像接口不开放&#xff0c;所以找到了这个插座&#xff0c;然后自己开发了下&#xff0c;用接口控制插座开关。wifi的连接方式&#xff0c;通电后一般几秒后就会连接上wifi&#xff0c;这个时候通过接口发送命令给他。 产品图片 通…

cesium-天际线

主要是两个着色器 let postProccessStage new Cesium.PostProcessStage({//unform着色器对象 textureScalefragmentShader:// 声明一个纹理采样器 colorTexture 用于读取纹理颜色uniform sampler2D colorTexture; // 声明一个纹理采样器 depthTexture 用于读取深度纹理unifor…

windows安装程序无法将windows配置为此计算机

目录 问题描述 问题原因 解决办法 方法一 方法二 方法三&#xff1a; 问题描述 重装系统时显示windows安装程序无法将windows配置在此计算机硬件上. 问题原因 安装介质已损坏 如果可引导的安装介质&#xff08;如DVD或USB驱动器&#xff09;损坏或损坏&#xff0c;安装过…

XXE-XML实体注入漏洞

目录 1.xml基础 1.1什么是xml 1.2xml文档结构 1.3 什么是DTD 1.4 什么是实体 1.5 什么是外部实体 2.xxe漏洞 2.1xxe漏洞基本介绍 2.2xxe漏洞的危害 经典漏洞案例分析 3.xxe漏洞挖掘和利用 3.1. 识别潜在的XML入口 3.2. 检查XML处理逻辑 3.3. 构造试探Payload 常…

CVE-2024-25600 WordPress Bricks Builder RCE-漏洞分析研究

本次代码审计项目为PHP语言&#xff0c;我将继续以漏洞挖掘者的视角来分析漏洞的产生&#xff0c;调用与利用..... 前方高能&#xff0c;小伙伴们要真正仔细看咯..... 漏洞简介 CVE-2024-25600 是一个严重的&#xff08;CVSS 评分 9.8&#xff09;远程代码执行 (RCE) 漏洞&am…

软件设计师13--进程调度

软件设计师13--进程调度 考点1&#xff1a;PV操作的概念进程的同步与互斥PV操作例题&#xff1a; 考点2&#xff1a;信号量与PV操作进程管理 - PV操作与互斥模型进程管理 - PV操作与同步模型进程管理 - 互斥与同步模型结合例题&#xff1a; 考点3&#xff1a;前趋图与PV操作进程…

爬虫(四)

1.图片验证码 import requestsres requests.get(https://www.gushiwen.cn/RandCode.ashx)with open("code.png", "wb") as f:f.write(res.content)2.打码平台 网址&#xff1a;http://www.ttshitu.com/&#xff0c;找到开发文档点击Python,没有钱了要用我…

如何在Linux中安装ARM交叉环境编译链

安装ARM交叉环境编译链过程如下&#xff1a; 首先创建一个文件夹如下&#xff1a; mkdir -p Linux_ALPHA/toolcahin然后将arm交叉编译工具链安装包拖到Linux中如下&#xff1a; 先输入mv 拖入的安装包即可 mv /var/run/vmblock-fuse/blockdir/pXeysK/gcc-4.6.4.tar.xz .直接…

-bash: unzip: 未找到命令的解决方案

遇到 -bash: unzip: 未找到命令 这样的错误信息&#xff0c;表示你的系统中没有安装 unzip 工具。unzip 是一个常用的解压工具&#xff0c;用于解压缩 .zip 文件。你可以通过系统的包管理器安装它。 根据你使用的 Linux 发行版&#xff0c;安装 unzip 的命令会有所不同。下面是…

动态内存管理-c语言

目录 1.为什么要有动态内存分配 2.malloc函数和free函数 malloc 函数原型 栗子 free 函数原型 栗子 3.calloc和***realloc*** 3.1calloc函数 原型如下&#xff1a; 栗子 3.2***recalloc*** 第一种情况 第二种情况 第三种情况 recalloc模拟实现calloc函数 4.六…

基于springboot+vue的球队训练信息管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

“揭秘网络握手与挥别:TCP三次握手和四次挥手全解析“

前言 在计算机网络中&#xff0c;TCP&#xff08;传输控制协议&#xff09;是一种重要的通信协议&#xff0c;用于在网络中的两台计算机之间建立可靠的连接并交换数据。TCP协议通过“三次握手”和“四次挥手”的过程来建立和终止连接&#xff0c;确保数据的准确传输。 一、三…

2024年腾讯云优惠政策_腾讯云服务器特价购买活动入口

腾讯云优惠活动2024新春采购节活动上线&#xff0c;云服务器价格已经出来了&#xff0c;云服务器61元一年起&#xff0c;配置和价格基本上和上个月没什么变化&#xff0c;但是新增了8888元代金券和会员续费优惠&#xff0c;腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

数据结构(八)——初识单链表

&#x1f600;前言 单链表是数据结构中最基本的一种链表结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含数据和指向下一个节点的指针。单链表具有灵活性和动态性&#xff0c;可以根据需要插入、删除和查找元素&#xff0c;适用于各种场景和问题的解决。 在本篇文章…

网络编程 · 代码笔记1

目录 前言1、编程环境2、编译命令3、标题名前缀解释4、注意事项 0011客户端读取服务端字符_服务端0012客户端读取服务端字符_客户端0021回声测试_服务端0022回声测试_客户端0030启动端口复用解决端口绑定失败问题0041服务器不间断进行侦听通信_服务端0042服务器不间断进行侦听通…

liunx操作系统 环境变量

环境变量 main函数参数 命令行参数环境变量 环境变量的查看环境变量的获取 main函数参数 命令行参数 main函数是有参数的&#xff0c;只是我们一般不适用 这是main函数从bash中读取进程数据使用的一个基本入口。 下面进行简单演示。 o 好oo都是我们输入的命令行参数。其实&a…

如何查看前端的vue项目是vue2还是vue3项目

1. 检查package.json文件 在项目的根目录下&#xff0c;打开package.json文件&#xff0c;查找dependencies或devDependencies部分中的vue条目。版本号将告诉你是Vue 2还是Vue 3。例如&#xff1a; Vue 2.x: "vue": "^2.x.x"Vue 3.x: "vue": &…

【Linux基础(二)】进程管理

学习分享 1、程序和进程1.1、程序1.2、进程和进程ID 2、Linux下的进程结构3、init进程4、获取进程标识5、fork系统调用5.1、fork函数实例分析 6、进程的特性7、在Linux下进程指令7.1、终止进程指令7.2、查看进程指令&#xff1a;7.3、以树状图列出进程 8、多进程运行异常情况8.…

判断连续数据同意特征的方法:插旗法

bool isMonotonic(int* nums, int numsSize) {int flag 2;for (int i 1; i < numsSize; i) {if (nums[i-1] > nums[i]) {if (flag 0)return false;flag 1;}else if (nums[i-1] < nums[i]) {if (flag 1)return false;flag 0;}}return true; }此代码较为简单&…

Vue中如何处理组件间的耦合问题?

在Vue中处理组件间的耦合问题是前端开发中常见的挑战之一。耦合问题指的是组件之间的依赖关系过于紧密&#xff0c;一旦某个组件发生改动&#xff0c;则可能导致其它组件也需要作出相应调整。为了解决这个问题&#xff0c;我们可以采取以下几种方法&#xff1a; 使用事件总线&…