AVL树(Insert)

文章详细介绍了AVL树的概念,作为高度平衡的二叉搜索树,它通过控制平衡因子和旋转操作保证树的平衡。插入操作可能导致五种旋转情况:右单旋、左单旋、左右双旋和右左双旋,以维持平衡。在删除节点后也需要更新平衡因子并可能进行旋转。AVL树的主要优点是查询效率高,但结构修改操作如插入和删除的性能较低。
摘要由CSDN通过智能技术生成

AVL树

代码

高度平衡二叉搜索树

  • 引入

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当

于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年

发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之

差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。AVL树用三个人名首字母命名的。

  • 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

    • 它的左右子树都是AVL树

    • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

      • 不一定需要平衡因子,使用平衡因子是一种控制实现方式

非常接近满二叉树,插入10亿个节点将近30层左右.

模拟实现

Insert重点

在SB树的基础之上如何保证接近平衡呢>-平衡因子.一个节点的平衡因子受到他的左右子树的影响。

  • 插入到节点的右边,该节点的平衡因子需要++,左边就–.
  • 新增节点只会影响他的祖先节点的平衡因子.
控制平衡:
  1. 更新平衡因子,新增节点到根节点的祖先路径

  2. 出现异常平衡因子就需要旋转平衡树

  3. 出现一下五种情况:

  • 更新以后,parent->bf == 0,更新结束。说明更新前parent->bf是1或者-1,现在变成0,说明填上了矮的那边,parent所处的子树高度不变也就不会对上一层进行影响。
  • 更新以后,parent->bf==1/-1,继续向上更新,更新前parent->bf=0,现在变成1、-1,我有一遍子树变高了
    parent所在的子树高度变了,需要继续向上更新.
  • 更新以后,parent->bf=2或者-2,parent已经不平衡了,需要旋转处理。根据平衡因子的正负来区别类型。

简单情况推演:

image-20230301201910640

旋转->平衡
右单旋
  • 右单旋引入

image-20230301211033321

右单旋(左腿长)

  1. 保持搜索树的规则

  2. 控制平衡

  • 右单旋抽象图

image-20230301210938276

  • 右单旋具象图

image-20230301211333066

  • 右单旋代码理解

image-20230301213111066

	void RotateR(Node* parent)
	{
		Node* subl = parent->_left;
		Node* sublr = subl->_right;
		//进行旋转维护三叉连
		parent->_left = sublr;
		if (sublr)
			sublr->_parent = parent;
		Node* pparent = parent->_parent;

		subl->_right = parent;
		parent->_parent = subl;

		if (parent == _root)
		{
			_root = subl;
			_root->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = subl;
			else
				pparent->_right = subl;
			subl->_parent = pparent;
		}
		subl->_parent = parent->_bf = 0;
	}
  • 连接时要注意是否是空节点,以及一定要保证三叉连。
左单旋

右边腿长

右单旋

	void RotateL(Node* parent)
	{
		Node* subr = parent->_right;
		Node* subrl = subr->_left;

		Node* pparent = parent->_parent;
		parent->_parent = subr;
		subr->_left = parent;
		parent->_right = subrl;
		if (subrl)
			subrl->_parent = parent;
		if (parent == _root)
		{
			_root = subr;
			subr->_parent == nullptr;
		}

		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subr;
			}
			else
			{
				pparent->_right = subr;
			}
			subr->_parent = pparent;
		}
		subr->_bf = parent->_bf = 0;
	}
左右双旋

image-20230302092416929

  • 如何验证AVL树是没有问题的?高度,每一颗子树都得检查一遍是不是AVL树的形状。IsBalance()

  • 平衡因子是否都更新正确?树构建完成之后再进行插入节点。

双旋平衡因子更新错误
右左双旋

双旋时平衡因子的更新是由两次单旋完成的,在这里出现的问题,在未是平衡树的时候就将平衡因子更新为0了。

image-20230302100708934

  • 如何识别是那种情况?

    可以通过60 的平衡因子识别三种情况,所以在两次旋转之前要将三个特殊节点保存

	void RotateRL(Node* parent)
	{
		Node* subr = parent->_right;
		Node* subrl = subr->_left;
		int bf = subrl->_bf;

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

		if (bf == 1)
		{
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subr->_bf = 1;
		}
		else if (bf == 0)
		{
			//
		}
		else
		{
			assert(false);
		}
	}
左右双旋

对三种情况特判来更新平衡因子即可.

image-20230302110113195

void RotateLR(Node* parent)
	{
		Node* subl = parent->_left;
		Node* sublr = subl->_right;
		int bf = sublr->_bf;

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

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

所以要在双旋的函数中,在单旋操作对于平衡因子更新之后,再进行对于平衡因子特殊情况的自我更新。对于值的更新不明白的自己画个图就明白了,-1 1
0都是确定的,因为只有出发这些情况才会满足。性能接近logN.

erase了解

首先按照搜索树的规则删除,然后更新平衡因子,如果有需要再进行旋转.

image-20230302111036957

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证

查询时高效的时间复杂度,即 logN.但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:

插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值