模拟实现AVL树插入操作

平衡二叉树(AVL)的插入和删除详解(上) AVL树维基百科:http://zh.wikipedia.org/wiki/AVL树 在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M 阅读详情

avl树

本文展示的是通过平衡因子控制高度的avl树。
平衡因子(balance factor简称bf)等于右子树高度-左子树高度,我们要控制树中的所有平衡因子都小于等于1。

结构

树中节点的成员如下

template<class K, class V>// k为键值
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)
	{}
};

插入

插入操作与搜索树类似,如下图

在这里插入图片描述

我们想把7插入,那么我们就会找到6的右节点,将7插入即可。不同点是,在avl树中,我们需要控制bf值,我们发现将7插入后,6的bf将成为1,5的bf值将变成2,此时5的bf值大于1,不符合avl树的规则,因此我们需要一定的操作来使avl树重新平衡,这个操作就叫旋转,我们接下来将详细解释旋转的做法。

旋转

左旋转

在刚刚的例子中我们发现根节点的bf值为2,且他的右节点的bf值为1,此时我们需要使用左旋转。这是一张抽象图,代表需要左旋转的情况。
在这里插入图片描述
具体操作是把subR的左子树给parent作为右子树,再把parent作为subR的左子树,如下图
在这里插入图片描述

左单旋代码
void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pParent = parent->_parent;
		parent->_right = subRL;
		if (subRL != nullptr)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;
		parent->_parent = subR;
		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			subR->_parent = pParent;
			if (pParent->_left == parent)
			{
				pParent->_left = subR;
			}
			else
			{
				pParent->_right = subR;
			}
		}
		parent->_bf = subR->_bf = 0;
	}

右旋转

当父节点的bf值为-2,且他的左节点的bf值为-1时即需要使用右旋转,如下图是需要右旋转的抽象图
在这里插入图片描述
右旋转的操作与左旋转向反,先把subL的右子树作为parent的左子树,再把parent作为subL的右子树即可,如下图
在这里插入图片描述

右旋转代码
void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pParent = parent->_parent;
		parent->_left = subLR;
		if (subLR != nullptr)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		parent->_parent = subL;
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			subL->_parent = pParent;
			if (pParent->_left == parent)
			{
				pParent->_left = subL;
			}
			else
			{
				pParent->_right = subL;
			}
		}
		subL->_bf = parent->_bf = 0;
	}

右左双旋

在一些情况下以此单旋无法解决问题,比如下面这三种情况在这里插入图片描述
这三种情况有一个共同点,父节点的bf值都是2,右节点的bf值都是-1,在这种情况下都需要使用右左双旋,即先对subR进行一次右旋,再对parent进行一次左旋,唯一需要考虑的是bf值的控制,这点在后面会详细介绍。
先从最简单的情况三开始,
在这里插入图片描述

我们发现情况三在右左双旋之后parent,subR,subRL的bf值都变成了0
情况一:
在这里插入图片描述
情况二:
在这里插入图片描述

右左双旋代码
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(subR);
		RotateL(parent);
		if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
	}

左右双旋

在父节点的bf值为-2,父节点的左节点的bf值为1时就需要使用左右双旋,如下图所示的三种情况
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

左右双旋代码
	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;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
	}

insert代码

bool Insert(const pair<K, V>& kv)
	{
		Node* parent = nullptr, * cur = _root;
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		cur->_parent = parent;
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		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 && parent->_left->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && parent->_right->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == 2 && parent->_right->_bf == -1)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && parent->_left->_bf == 1)
				{
					RotateLR(parent);
				}
			}
		}
	}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pParent = parent->_parent;
		parent->_left = subLR;
		if (subLR != nullptr)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		parent->_parent = subL;
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			subL->_parent = pParent;
			if (pParent->_left == parent)
			{
				pParent->_left = subL;
			}
			else
			{
				pParent->_right = subL;
			}
		}
		subL->_bf = parent->_bf = 0;
	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pParent = parent->_parent;
		parent->_right = subRL;
		if (subRL != nullptr)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;
		parent->_parent = subR;
		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			subR->_parent = pParent;
			if (pParent->_left == parent)
			{
				pParent->_left = subR;
			}
			else
			{
				pParent->_right = subR;
			}
		}
		parent->_bf = subR->_bf = 0;
	}
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(subR);
		RotateL(parent);
		if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_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;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
	}
private:
	Node* _root;
};

插入操作和搜索树一样,从根节点开始,若插入值大于根节点就向右

C++版【AVL树的模拟实现】 希望大家可以自行写出遍历操作、求树的高度、求树的节点个数等成员函数,需要注意的是,利用递归来完成的,都需要写个子函数,然后封装一下子函数.大家感兴趣,可以自行查阅删除操作后续会实现红黑树,红黑树那里会模拟实现一下map和set。 阅读详情

相关推荐

从HAL库SPI状态机机制剖析:为什么你的DMA发送会卡在BUSY状态?

本文深入剖析了STM32 HAL库中SPI状态机机制导致DMA发送卡在BUSY状态的常见问题。通过分析状态机设计原理,揭示了混合使用阻塞方式和DMA方式时状态切换冲突的根源,并提供了三种实战解决方案:正确配置SPI中断、使用HAL_SPI_Abort()函数以及手动管理状态机,帮助开发者实现稳定流畅的SPI DMA通信。

bush的博客 268

AVL树(一)之 图文解析 和 C语言的实现

文章目录平衡二叉树(AVL树)一、概要二、AVL树的介绍1、树的高度2. 旋转2.1 LL的旋转2.2 RR的旋转2.3 LR的旋转2.4 RL的旋转三、新建流程四、实现代码1、定义2、 节点的创建3、树的高度4、大小比较5、旋转5.1 LL的旋转5.2 RR的旋转5.3 LR的旋转5.4 RL的旋转6、插入7、删除8、完整代码9、测试数据编译环境后记 平衡二叉树(AVL树) 一、概要 平衡二叉...

qq_45768871的博客 991

2022北京高考数学压轴题21题:Python暴力枚举验证k≥7的5种反例

本文探讨了2022北京高考数学压轴题21题的编程解法,通过Python暴力枚举验证k≥7的5种反例。文章详细介绍了m-连续可表数列的定义、问题转化与约束条件,并提供了暴力枚举算法设计和优化技巧,帮助读者理解如何用编程思维解决数学问题。

weixin_33552698的博客 233

【数据结构与算法】第四篇:AVL树

这个概念很简单,就是节点左子树高度减去节点右子树的高度,如果差值的绝对值小于等于1,就是平衡的,反之就是失衡的。),能够很好的解决链表O(n)的复杂度,但是二叉搜索树仍然有很大的提升空间,比如说如果我们为二叉搜索树添加元素,但是我们添加的元素都是有规律的从小到大或者从大到小就会使其退化为链表。AvL树就是一种二叉搜索树的改良的树,它继承与二叉搜索树,而且节点类型新增了高度属性,最重要的是能实现自平衡功能,保证了代码的运行效率。具体代码细节可以移步代码仓库----->◼ 让p成为这棵子树的根节点。

qq_61571098的博客 613

博科的SAN交换机基本操作

2、添加用户testuser赋予user权限可以访问1-128VF默认VF为128账号描述"Test user"密码为password123。1、创建zone别名(100是指当前设备Domain ID,1和2是指对应的index号)2,删除新接入设备的ZONE或保持ZONE配置一致。3、将zone添加到当前生效的CFG中。2、将zone添加到当前生效的CFG中。3、将zone添加到当前生效的CFG中。3,将添加的级联端口持续关闭。4、将当前zone生效。4、将当前zone生效。1、查看当前所有用户。

chenjunokok的博客 1838

自动化测试软件设计:C# + VISA 库实现多接口(GPIB/USB)测试仪器控制

本文介绍了如何利用C#和VISA库开发支持多接口的自动化测试软件。VISA库作为标准化API,能够统一控制GPIB、USB等不同接口的测试设备。文章详细阐述了软件设计目标,包括多接口支持、统一控制、自动化脚本执行等功能。重点讲解了C#中VISA库的安装配置,并提供了设备控制类的核心代码实现,涵盖设备连接、命令发送、响应读取等关键操作。通过具体示例展示了GPIB和USB设备的连接方式,并扩展了自动化测试脚本功能。该方案具有高扩展性和维护性,适用于电子设备测试、生产线检测等多种场景,为自动化测试系统开发提供了实

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1712

平衡二叉树(AVL)的4种插入调整过程

平衡二叉树(AVL)的4种插入调整过程 平衡二叉树定义: 在插入中为了保证二叉排序树的性能,规定在插入和删除二叉树结点时,要保证任意结点的左、右子树的高度差的绝对数值不超过1,这样的二叉树被称为平衡二叉树(Balanced Binary Tree),简称平衡树(AVL)。 平衡二叉树的插入: 我们知道,进行插入和删除操作时势必会对树的整体的造成的一定的影响,树的结构发生一定的变化,如果这...

今天好像忘写了 4725

Windows安装ros2(Jazzy版本+win10/win11)-4.构建ros2结点

这个位置也是ros2搜索package以及搜索node的路径, 如果把构建好的可执行文件乱动位置的话就找不到节点了,当然如果复制一个可执行文件以及可执行文件-script.py文件, ros2也就会认为你有一个新的节点了,运行这个拷贝的可执行文件程序都是ok的。由于上一个文章给powershell7也增加了启动执行ros2的环境初始化文件local_setup.ps1, 所以我们也可以直接在vscode中运行节点, 此种方式没有经由ros2托管, 缺少其他ros2的附加能力。

weixin_48398592的博客 7113

AVL树(插入)

目录AVL树的概念AVL树节点的定义AVL树的插入AVL树的旋转左单旋右单旋右左双旋左右双旋完整代码 AVL树的插入 AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步: 1. 按照二叉搜索树的方式插入新节点 2. 调整节点的平衡因子 代码如下: 以上代码中_root为AVL树底层维护的指针,指向AVL树的根节点,对于插入节点我们并不陌生,在平衡因子为1或者-1时,需要向上调整平衡因子也非常好理解。而当平衡因子为

Enthusiastic_boy的博客 1083

【Zephyr存储专题】06_存储块设备驱动开发(Nand Flash)

本文介绍了基于Zephyr系统的NAND Flash驱动实现方案,重点阐述了Flash驱动模型的核心架构和关键API设计。

zero的博客 1803

[NoC]Outstanding和Credit的概念详解

摘要:本文详细解析了计算机体系结构中outstanding和credit两个核心概念。outstanding指已发出但未完成的请求数量,通过跟踪未决事务实现并发和乱序执行,常见于AXI总线等场景;credit则是基于信用令牌的流控机制,通过控制发送速率防止接收端缓冲溢出,多用于NoC网络。二者在目的、机制上存在差异:outstanding侧重提升并发度,credit关注流控保护。文章通过AXI协议实例和SystemC代码示例展示了二者的应用与结合方式,并探讨了在SoC设计中参数优化的方法,包括性能/资源权衡

元直的博客 2284

C++_AVL树的学习

二叉搜索树虽可以缩短查找的效率,但。所以就出现了即可降低树的高度,从而减少平均搜索长度一棵AVL或者是,或者是具有以下性质的AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那AVL树的插入过程就可以分为:, 插入分以下两种情况:1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可。

qq_63915254的博客 966

HSM 网络安全 信息安全

HSM HSM信息安全功能 汽车电子网络安全中HSM 的应用场景 选择适合汽车电子网络安全的 HSM 符合汽车安全标准和法规要求的HSM HSM 在汽车电子网络安全中的可靠性和安全性 评估汽车电子网络的安全性 汽车 HSM(Hardware Security Module)模块开发 汽车 HSM(Hardware Security Module)模块测试

天赐好车的博客 1238

从失衡到平衡:手撕 AVL 树的插入旋转操作

本文使用C++实现了AVL树的插入功能,并配图详细讲解了四种不同旋转的具体过程

Dai_renwen的博客 1516

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

【C++map和set容器:AVL树、红黑树详解并封装实现map和set】

int _bf;,_bf(0),_kv(kv){}// 节点的颜色RED,BLACK// 红黑树节点的定义,_kv(kv),_col(RED){}思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?这里我们可以想象一下,如果我们将插入的节点的默认颜色设置为黑色,那么该条路径上的黑色节点就会增加一个,为了满足对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

qq_64446981的博客 1109

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【c++】红黑树的概念讲解与模拟实现、红黑树与AVL树的比对(2万字详解)

一、红黑树的概念讲解 二、红黑树的模拟实现 铺垫 insert IsBalance Height 三、验证我们的代码是否可以构建出红黑树 验证一 验证二 验证三 四、红黑树与AVL树的比对 比对一 比对结果分析 比对二 比对结果分析 总结红黑树和AVL树 五、源代码 RBTree.h test.cpp

重铸自身根基的博客 1208

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C++修炼:AVL树

AVL树是一种自平衡的二叉搜索树(BST),由G.M. Adelson-Velsky和E.M. Landis在1962年发明。它是计算机科学中最早发明的自平衡二叉搜索树结构之一。其实就是二叉搜索树的升级版。因为我们上期分析过了,二叉搜索树在特殊情况下高度会到达N:那么有什么办法让他平衡一下呢?如果说我们能够控制这棵树的高度,让这棵树趋近一颗完全二叉树,那么他的插入效率是非常高的,因为无论沿着哪条路径遍历都是一样的高度,不会出现这种特殊情况。

但凡的博客 984

AVL 树的模拟实现(入门必看,图文并茂)

本文内容包括AVL树概念的理解、AVL树的两个特性、AVL树的四种旋转方式以及引发这四种旋转方式的条件、四种旋转方式的机理、四种旋转方式的的平衡因子更新、进行插入引发旋转的三种情况,以及如何进行将搜索二叉树优化成AVL树、AVL树的验证方式的思路分析,并且提供了两组测试用例进行验证。

2302_80475369的博客 735

[C++随笔录] AVL树

AVL树的详细逻辑 和 模拟实现

qq_67549203的博客 241

【C++】图解AVL树

_kv(kv), _bf(0){}// 该节点的左孩子// 该节点的右孩子// 该节点的双亲int _bf;

luoshashou_123的博客 1156

【C++笔记】AVL树的模拟实现

/ 定义AVL树节点int _bf;// 平衡因子// 构造,_val(key),_bf(0){}// AVL树public:// ……

kk702392702的博客 437

红黑树的模拟实现(含删除结点)

红黑树的概念,红黑树的模拟实现:插入、删除、检查、迭代器封装

m0_73693552的博客 1123

[C++进阶]---AVL树模拟实现

/该节点的左孩子//该节点的右孩子//该节点的双亲int _bf;//该节点的平衡因子,_kv(kv),_bf(0){}①AVL树的删除(了解)因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不过与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。② AVL树的性能AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log2Nlog_2(N)

m0_74288306的博客 1254

【C++】AVL树

AVL树的简易模拟实现

博主在编程学习中的总结、笔记 982

AVL树 模拟实现(插入)

如果 parent 的平衡因子为 2 ,cur 的平衡因子为 1,那么使用的是左单旋,将 parent 和 cur 的平衡因子都跟新为 0.如果 parent 的平衡因子为 -2 ,cur 的平衡因子为 -1,那么使用的是右单,将 parent 和 cur 的平衡因子都跟新为 0.。

m0_73455775的博客 874

AVL树的介绍与模拟实现(insert+旋转调平精美图示超详解哦)

AVL树在满足二叉搜索树条件的基础上,给每个结点中增加了一个平衡因子(这个平衡因子的值是该结点左右子树的高度差)。AVL树要求平衡因子的绝对值小于等于1,即任意一个结点左右子树的高度差不超过1。通过降低树的高度,从而达到减少平均搜索时间的效果(在这里做例子的平衡二叉树是按照大小次序依次插入的结果)

weixin_73450183的博客 1412
上一篇: Linux进程间通信——信号量
下一篇: 红黑树原理
JayceSun449
博客等级 码龄5年 554粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayceSun449

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值