手撕AVL树之平衡因子完成四种旋转

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、搜索二叉树缺点

如果是有序的或者接近有序,那么查找的效率就退化的很厉害,效率无法保证,那么就要开始尝试控制平衡

二、AVL概述

AVL数是以人名来命名的(G.M.Adelson-Velsk和E.<.Landis),发现一种解决搜索二叉树缺点的办法。当向二叉树中插入新节点时,如果能保证每个结点的左右子树高度之差绝对值不超过1,就能减少平均搜索长度

AVL树规则:
一棵AVL树 或者是空树,或者是具有以下性质的任意一棵树
①它的左右子树都是AVL树,也就是任意一颗子树左右高度差的绝对值都不超过1
②左右子树高度差绝对值不超过1,AVL这里用平衡因子来实现,平衡因子只能为-1,0,1、(实现AVL树也可以不用平衡因子)

在这里插入图片描述
AVL树也叫做高度平衡搜索二叉树

三、AVL树

①AVLTree的结构

struct AVLTree
{
	AVLTreeNode<K,V>*left;
	AVLTreeNode<K,V>*right;
	AVLTreeNode<K,V>*parent;
	pair<K,V> _kv;
	int _bf;
	//平衡因子 balance factor
};

并不是说AVL树一定有平衡因子,AVL树只规定了左右高度差不超过1,这里只是选择平衡因子这种方式来实现。平衡因子 = 右子树高度-左子树高度(新插入的结点平衡因子都是0)

②更新平衡因子的规则

平衡因子等于右树-左数高度
1.新增在右,parent->_bf++,新增在左,parent->_bf–;(1就代表右边高,-1就代表左边高)

2.更新后,parent->_bf==1or-1,说明parent插入前的平衡因子是0,说明parent的平衡因子之前就是0,说明之前左右子树高度相等,现在进行了插入,要往上进行平衡因子的调整

3.更新后,parent->_bf==0,这就说明这一次的插入只是平衡了上一次的高度差,parent所在的子树高度不变,所以不用继续往上更新

4.更新后,parent->_bf==2or-2,说明现在以及打破平衡因子临界值,parent所在的子树需要做旋转处理

5.更新后,parent->_bf>2或者<-2这是不可能的,因为到2或者-2的时候就已经旋转调整了,不可能出现这种情况,如果有,说明之前就错了。需要检查之前的过程

旋转的价值和意义:
1.树或者子树平衡
2.降高度

③AVL旋转

AVL树包括红黑树最复杂的地方就是在于旋转,所以重点掌握旋转和插入就可以了
在这里插入图片描述

新结点插入较高左子树的左侧:右单旋

在这里插入图片描述
因为现在要让40作为跟,所以把30的右挂在60的左,因为30的右一定小于60.然后再让30成为根即可

也就是说某个根的左子树高度>右子树高度 超过1,那么我进行右旋,按照上面的这种方式,右单旋需要保证左边树高并且最高的是最左边那个树(下面会说原因)

//旋转的并不是插入结点的parent,而是平衡因子不对劲的哪个结点
void RotateR(Node*parent)
{
	Node*subL = parent->_left;
	Node*subLR = subL->_right;
	Node*ppNode = parent->_parent;
	//旋转
	parent->_left = subLR;
	if(subLR)
		subLR->_parent = parent;
	subL->right = parent;
	parent->_parent = subL;
	//处理特殊
	if(_root==parent)
	{
		_root=subL;
		subL->_parent = nullptr;
	}
	else
	{
		if(ppNode->_left == parent)
		{
			ppNode->_left  = subL;
		}
		else
		{
			ppNode->_right = subL;
		}
		subL->_parent = ppNode;
	}
	paret->_bf = subL->_bf =0;//旋转完后是平衡的,看图
	
}

右单旋后,parent和subL的平衡因子是会为0的,这个是从上图看出来的,上图包含了右单旋的全部情况

新结点插入较高右子树的右侧:左单旋

在这里插入图片描述
和右单旋同理,需要右树高并且右树的右树更高,直接写代码

void RotateL(Node*parent)
{
	Node*subR = parent->_right;
	Node*subRL = subR->_left;
	parent->_right = subRL;
	if(subRL) 
		subRL->_parent = parent;
	Node*ppNode = parent->parent;
	subR->_left = parent;
	parent->_parent = subR;
	if(_root == parent)
	{
		_root = subR;
		_subR->_parent = nullptr;
	}
	else
	{	
		if(ppNode->_left == parent)
		{
			ppNode ->_left = subR;
		}
		else
		{
			ppNode->_right = subR;
		}
		subR->_parent = ppNode;
	}
	subR->_bf = parent -> bf = 0;
	//旋转后平衡,可验证
}

新结点插入较高左子树的右侧:左右双旋

在这里插入图片描述
这种情况如果只进行右单旋,会导致右子树又比左子树高,可以自己验证,所以我们需要把最高的首先进行旋转

void RotateLR(Node*parent)
{
	Node*subL = parent ->_left;
	Node*subLR = subL->_right;
	int bf = subLR->_bf;//
	RotateL(subL);
	RotateR(parent);//复用搞定
	//还需要我们去调整平衡因子,虽然RotateL或者RotateR代码中有调节平衡因子的
	//但是别忘了有一次左旋是我们强制的,所以调节的平衡因子是无效的,我们需要自己手动调
	//看图填空的感觉,因为包含了所有情况(上面的图还有其他的,比如高的在右边)
	subLR->_bf==0//看图得出,一定是0
	if(bf==1)//说明是subLR右边插入
	{
		parent->bf=0;
		subL->_bf =-1;
	}
	else if(bf==-1)
	{
		parent->_bf=1;
		subL->_bf = 0;
	}
	else if(bf==0)
	{
		parent->_bf =0;
		subL->_bf =0;
	}
	else
	{
		assert(false);//其他情况直接断言错误
	}
}

新节点插入较高右子树的左侧:右左双旋

在这里插入图片描述

void RotateLR(Node*parent)
{
	Node*subR = parent ->_right;
	Node*subRL = subR->_left;
	int bf = subRL->_bf;//
	RotateL(subR);
	RotateR(parent);//复用搞定
	
	subLR->_bf==0//看图得出,一定是0
	if(bf==-1)//说明是subLR右边插入
	{
		parent->bf=0;
		subL->_bf =1;
	}
	else if(bf==1)
	{
		parent->_bf=-1;
		subL->_bf = 0;
	}
	else if(bf==0)
	{
		parent->_bf =0;
		subL->_bf =0;
	}
	else
	{
		assert(false);//其他情况直接断言错误
	}
}

④判断是否为AVL树

AVL树又叫做高度平衡搜索二叉树,所以需要检查是否每棵树都高度平衡,最好再进行中序遍历看看是否 去重并排序

public:
bool IsBalance()
{
	return  _IsBalance(_root);
}
private:
bool _IsBalance(Node*root)
{
	if(root==nullptr) return true;
	int leftHT = Height(root->left);//求高度
	int rightHT = Height(root->right);
	return abs(rightHT-leftHT)<2
	&&_IsBalance(root->_left)
	&&_IsBalance(root->_right);
}

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值