AVL树插入接口涉及到的四个旋转

本文详细介绍了AVL树的插入操作及四种旋转方法,包括左单旋、右单旋、左右双旋和右左双旋的具体实现,并通过代码示例展示了如何保持树的平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AVL树插入接口:涉及到4个旋转。

从接口出发:

主要分为以下几个步骤:

1.找到插入的位置

2.将需要插入的点插入进树

3.更新平衡因子(如果不平衡,则进入第4步) 

4.旋转调节平衡因子  (重点)

bool AVLTree<T>::Insert(const T& data)
{
	if (_pRoot == nullptr) 若AVL树为空树,则插入结点直接作为根结点
	{
		_pRoot = new Node(data);
		return true;
	}
	1、按照二叉搜索树的插入方法,找到待插入位置
	Node* cur = _pRoot;
	Node* parent = nullptr;
	while (cur)
	{
		if ( data < cur->_data) 待插入结点的key值小于当前结点的key值
		{
			//往该结点的左子树走
			parent = cur;
			cur = cur->_pLeft;
		}
		else if (data > cur->_data) 待插入结点的key值大于当前结点的key值
		{
			往该结点的右子树走
			parent = cur;
			cur = cur->_pRight;
		}
		else 待插入结点的key值等于当前结点的key值
		{
			插入失败(不允许key值冗余)
			return false;
		}
	}

	2、将待插入结点插入到树中
	cur = new Node(data); 根据所给值构造一个新结点
	if (data < parent->_data) 新结点的key值小于parent的key值
	{
		插入到parent的左边
		parent->_pLeft = cur;
		cur->_pParent = parent;
	}
	else 新结点的key值大于parent的key值
	{
		插入到parent的右边
		parent->_pRight = cur;
		cur->_pParent = parent;
	}

	3、更新平衡因子,如果出现不平衡,则需要进行旋转
	while (cur != _pRoot) 最坏一路更新到根结点
	{
		if (cur == parent->_pLeft) parent的左子树增高
		{
			parent->_bf--; parent的平衡因子--
		}
		else if (cur == parent->_pRight) parent的右子树增高
		{
			parent->_bf++; parent的平衡因子++
		}

		判断是否更新结束或需要进行旋转
		if (parent->_bf == 0) 更新结束(新增结点把parent左右子树矮的那一边增高了,此时左右高度一致)
		{
			break; parent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
		}
		else if (parent->_bf == -1 || parent->_bf == 1) 需要继续往上更新平衡因子
		{
			parent树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
			cur = parent;
			parent = parent->_pParent;
		}
		else if (parent->_bf == -2 || parent->_bf == 2) 需要进行旋转(此时parent树已经不平衡了)
		{
			if (parent->_bf == -2)
			{
				if (cur->_bf == -1)
				{
					RotateR(parent); 右单旋
				}
				else    cur->_bf == 1
				{
					RotateLR(parent); 左右双旋
				}
			}
			else        parent->_bf == 2
			{
				if (cur->_bf == -1)
				{
					RotateRL(parent); 右左双旋
				}
				else cur->_bf == 1
				{
					RotateL(parent); //左单旋
				}
			}
			break; 旋转后就一定平衡了,无需继续往上更新平衡因子(旋转后树高度变为插入之前了)
		}
		else
		{
			assert(false); 在插入前树的平衡因子就有问题
		}
	}
	return true; 插入成功
}

旋转第一招:左单旋

1.了解什么时候需要左单旋!

 

 此时需要我们进行左单旋

 左单旋后的结果:

此时还需要再更新平衡因子。

可以看出,此时SubR与parent的平衡因子均为0。

代码实现:

template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data = T())
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _bf(0)
	{}

	AVLTreeNode<T>* _pLeft;
	AVLTreeNode<T>* _pRight;
	AVLTreeNode<T>* _pParent;
	T _data;
	int _bf;   // 节点的平衡因子
};

template<class T>
void AVLTree<T>::RotateL(Node* pParent)
{
	Node* SubR = pParent->_pRight;
	Node* SubRL = SubR->_pLeft;
	Node* parentParent = pParent->_pParent;

	//建立SubRL与pParent的关系
	pParent->_pRight = SubRL;
	if (SubRL)
	{
		SubRL->_pParent = pParent;
	}
	//建立parent与SubR的关系
	SubR->_pLeft = pParent;
	pParent->_pParent = SubR;
	
	//建立SubR与parentParent的关系 
    //parentParent如果为空,证明parent是根节点。
	if (parentParent ==nullptr)
	{
		_pRoot = SubR;
		_pRoot->_pParent = nullptr;
	}
	else
	{
		if (pParent == parentParent->_pLeft)
		{
			parentParent->_pLeft = SubR;
		}
		else
		{
			parentParent->_pRight = SubR;
		}
		SubR->_pParent = parentParent;
	}
	SubR->_bf = 0;
	pParent->_bf = 0;
}

旋转第二招:右单旋

与左单旋类型类似,

右单旋前:

 

右单旋后:

 此时SubL的平衡因子为0,parent的平衡因子为0。

代码实现:

template<class T>
void AVLTree<T>::RotateR(Node* pParent)
{
	Node* SubL = pParent->_pLeft;
	Node* SubLR = SubL->_pRight;
	Node* parentParent = pParent->_pParent;

	//建立parent和SubL的关系
	SubL->_pLeft = pParent;
	pParent->_pParent = SubL;

	//建立parent和SubLR的关系
	if (SubLR)
	{
		pParent->_pRight = SubLR;
		SubLR->_pParent = pParent;
	}

	//建立SubL与parentParent之间的关系
	if (parentParent == nullptr)
	{
		_pRoot = SubL;
		_pRoot->_pParent = nullptr;
	}
	else
	{
		if (pParent == parentParent->_pLeft)
		{
			parentParent->_pLeft = SubL;
		}
		else
		{
			parentParent->_pRight = SubL;
		}
		SubL->_pParent = parentParent;
	}
	pParent->_bf = 0;
	SubL->_bf = 0;
}

旋转第三招:左右旋

如图这样的情况:插入值后,其高度改变为h,至两边高度差为2.

插入的值在a,b,c 均会影响平衡,本文仅讨论在b进行插入值的情况。

左旋前:

 对SubL进行左旋。

左旋后:

 

 此时:两边依旧不平衡。如果我们将图进行简化:

 再进行右旋: 对parent进行右旋

 最后,进行平衡因子的修改

平衡因子的情况分为三种:

1、当subLR原始平衡因子是-1时,左右双旋后parent、subL、subLR的平衡因子分别更新为1、0、0。

2、当subLR原始平衡因子是1时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、-1、0。

3、当subLR原始平衡因子是0时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、0、0。

三种情况读者有兴趣可以自己验证一下。

总结:

左右双旋:1.对SubL进行左旋  2.对parent 进行右旋

代码实现:

template<class T>
void AVLTree<T>::RotateLR(Node* pParent)
{
	Node* SubL = pParent->_pLeft;
	Node* SubLR = SubL->_pRight;
	//先对SubL进行左单旋
	RotateL(SubL);
	//再对pParent进行右单旋
	RotateR(pParent);

	//最后更新平衡因子。
	if (SubL->_bf == -1)
	{
		pParent->_bf = 1;
		SubL->_bf = 0;
		SubLR->_bf = 0;
	}
	else if (SubL->_bf == 1)
	{
		pParent->_bf = 0;
		SubL->_bf = -1;
		SubLR->_bf = 0;
	}
	else if (SubLR->_bf == 0)
	{
		pParent->_bf = 0;
		SubL->_bf = 0;
		SubLR->_bf = 0;
	}
	else
		assert(false);
}

 旋转第四招:右左旋

在b、c、d下插入均会影响平衡,本文仅讨论在b下插入值的情况。

 此时,对SubR进行右单旋:

再对parent进行左单旋:

 

 此时再进行平衡因子的修改:

右左双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:
1、当subRL原始平衡因子是1时,左右双旋后parent、subR、subRL的平衡因子分别更新为-1、0、0。

2、当subRL原始平衡因子是-1时,左右双旋后parent、subR、subRL的平衡因子分别更新为0、1、0。

3、当subRL原始平衡因子是0时,左右双旋后parent、subR、subRL的平衡因子分别更新为0、0、0。

代码实现:

template<class T>
void AVLTree<T>::RotateRL(Node* pParent)
{
	Node* SubR = pParent->_pRight;
	Node* SubRL = SubR->_pLeft;
	RotateR(SubR);
	RotateL(pParent);
	if (SubRL->_bf == 1)
	{
		pParent->_bf = -1;
		SubR->_bf = 0;
		SubRL->_bf = 0;
	}
	else if (SubRL->_bf == -1)
	{
		pParent->_bf = 0;
		SubR->_bf = 1;
		SubRL->_bf = 0;
	}
	else if (SubRL->_bf == 0)
	{
		pParent->_bf = 0;
		SubR->_bf = 0;
		SubRL->_bf = 0;
	}
	else
		assert(false);
}

总结:

右左双旋:1.对SubR进行右旋  2.对parent 进行左旋

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值