AVL平衡二叉树

本文介绍了AVL树的基本概念,包括其定义、平衡条件以及插入操作。详细解释了四种旋转操作:左旋、右旋、右左双旋和左右双旋,并提供了具体的实现代码。

我们知道,树的高度越小,查找速度越快。因此,希望二叉树的高度足够小。今天我们来看一种特殊类型的二叉搜索树——AVL平衡二叉树。在这种类型的树中,二叉树结构近乎平衡。

1. AVL树的定义

    AVL树是具有如下特征的二叉搜索树:

  (1)根的左子树和右子树的高度之差的绝对值不大于1;

  (2)根的左子树和右子树都是AVL树。

2.  AVL树的插入(Insert)

  要在一棵二叉平衡树中插入一个新的结点,首先要查找结点要插入的位置。如果要插入的结点在二叉平衡树中,则查找到某一空子树处结束;否则,将新结点插入在二叉平衡树中。

在插入新结点后,可能会破会二叉平衡树的结构,此时,我们需要根据情况对二叉树进行调整。

(1)在较高右子树的右侧插入新结点——左旋

void RotateL(PNode parent)
	{
		PNode pSubR = parent->_PRight;
		PNode pSubRL = pSubR->_PLeft;

		parent->_PRight = pSubRL;
		if (pSubRL)
			pSubRL->_pParent = parent;

		pSubR->_PLeft = parent;
		PNode Pparent = parent->_pParent;
		parent->_pParent = pSubR;
		
		pSubR->_pParent = Pparent;
		if (parent == _PRoot)
			_PRoot = pSubR;
		else
		{
			if (Pparent->_PLeft == parent)
				Pparent->_PLeft = pSubR;
			else
				Pparent->_PRight = pSubR;
			
		}
		
		parent->_bf = 0;
		 pSubR->_bf = 0;
	}

 

(2)在较高左子树的左侧插入新结点——右旋

 

右旋和左旋的两种考虑情况相似,我们可以自己画图分析分析。

void RotateR(PNode parent)
	{
		PNode pSubL = parent->_PLeft;
		PNode pSubLR = pSubL->_PRight;

		parent->_PLeft = pSubLR;
		if (pSubLR)
			pSubLR->_pParent = parent;

		pSubL->_PRight = parent;
		PNode Pparent = parent->_pParent;
		parent->_pParent = pSubL;
		
		if (parent == _PRoot)
			_PRoot = pSubL;
		else
		{
			if (Pparent->_PLeft == parent)
				Pparent->_PLeft = pSubL;
			else
				Pparent->_PRight = pSubL;
			
		}
		pSubL->_pParent = Pparent;
		   parent->_bf = 0;
			pSubL->_bf = 0;
		
		
	}

 

(3)在较高右子树的左侧插入新结点——右左双旋

 

右左双旋分为三种情况分析,各种情况下的平衡因子的更新不同。

(1)pSubRL->bf = -1,更新pSubR->bf = 1;

(2) pSubRL不存在或者pSubRL->bf = 0时,不用更新pParent 和 pSubR;

(3) pSubRL->bf = 1时,更新pParent->bf = -1;

void RotateRL(PNode pParent)
	{
		PNode pSubR = pParent->_PRight;
		PNode pSubRL = pSubR->_PLeft;
		int bf = pSubRL->_bf;

		RotateR(pParent->_PRight);
		RotateL(pParent);

		if (bf == -1)
			pSubR->_bf = 1;
		else if (bf == 1)
			pParent->_bf = -1;
		
		
	}

 

(4)在较高左子树的右侧插入新结点——左右双旋

 

左右双旋与右左双旋类似,也分为三种情况,在不同的情况下,对pParent 结点和 pSubL 结点的平衡因子的更新不同,类似于右左双旋,我们可以画图分析分析。

void RotateLR(PNode pParent)
	{
		PNode pSubL = pParent->_PLeft;
		PNode pSubLR = pSubL->_PRight;
		int bf = pSubLR->_bf;

		RotateL(pParent->_PLeft);
		RotateR(pParent);

		if (bf == -1)
			pParent->_bf = 1;
		else if (bf == 1)
			pSubL->_bf = -1;
		
	}

再分析完四种旋转的情况后,我们来看下插入的整体代码

	bool InsertNode(const K&key, const V& value)
	{
		if (_PRoot == NULL)
		{
			_PRoot = new Node(key, value);
			return true;
		}
		PNode pcur = _PRoot;
		PNode pParent = pcur->_pParent;
		while (pcur)
		{
			if (pcur->_key > key)
			{
				pParent = pcur;
				pcur = pcur->_PLeft;
			}
			else if (pcur->_key < key)
			{
				pParent = pcur;
				pcur = pcur->_PRight;
			}
			else
				return false;

		}

		  pcur = new Node(key, value);
			if (pParent->_key > key)
			{
				pParent->_PLeft = pcur;
				pcur->_pParent = pParent;

			}
			else
			{
				pParent->_PRight = pcur;
				pcur->_pParent = pParent;

			}
		
		
		while (pParent )
		{
			if (pParent->_PLeft == pcur)
				pParent->_bf--;
			else
				pParent->_bf++;

  			if ( pParent->_bf == 0)
			{
				break;
			}
			else if (pParent->_bf == -1 || pParent->_bf == 1)
			{
				pcur = pParent;
				pParent = pParent->_pParent;
			}
			else
			{
				//在较高右子树插入结点
				if (pParent->_bf == 2)
				{
					PNode subR = pParent->_PRight;
					if (subR->_bf == 1)//右子树的右侧,执行左单旋;
					{
						RotateL(pParent);
					}
					else if (subR->_bf == -1)//右子树的左侧,执行先右后左双旋
					{
						RotateRL(pParent);
					}

				}
				else if (pParent->_bf == -2)
				{
					PNode subL = pParent->_PLeft;
					if (subL->_bf == 1)//左子树的右侧,执行先左后右双旋
					{
						RotateLR(pParent);
					}
					else if (subL->_bf == -1)//左子树的左侧,执行右单旋
					{
						RotateR(pParent);
					}
				}
				break;
			}
		}
		return true;
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值