二叉查找树之--AVL树

AVL树是带有平衡条件的二叉查找树。左右子树的高度差不超过1,其实就是-1,0,1。
对AVL树的插入会破坏其平衡。我们把必须调整的节点叫做a。a不平衡会由下面4个插入情况导致:
1.对的左儿子的左子树进行插入。
2.对的左儿子的右子树进行插入。
3.对的右儿子的左子树进行插入。
4.对的右儿子的右子树进行插入。

如下图所示。


        上图中,1.4对称,2.3也对称,1.4这一种没有折线的不平衡像是在树的外部插入,而2.3像是在树的内部插入,因为可以从折线中形象的看出来。1.4这样的情况可以通过一次单旋转完成,2.3通过单旋不行,不管是向左还是向右都不行,需要先把不平衡的转成直线形的,然后在来一次左旋或者右旋就可以了。

        单旋转的树可以抽象成一下图(以单右旋为例),下图中,k2右旋,然后调整相应节点,就平衡了。


        代码如下:

Position CAvlTree::singleRotateToRight(Position k2)// k2将右旋
{
    if ( NULL == k2 )
    {
		return NULL;
    }

	Position k1 = NULL;

        k1 = k2->lchild;              
	k2->lchild = k1->rchild;
	k1->rchild = k2;

	if ( CAvlNode *pCAvlNode = (CAvlNode*)(k1) )
	{
               pCAvlNode->iHeight = height(pCAvlNode);
	}
	if ( CAvlNode *pCAvlNode1 = (CAvlNode*)(k2) )
	{
		pCAvlNode1->iHeight = height(pCAvlNode1);
	}

	return k1;
}

Position CAvlTree::singleRotateToLeft(Position k2)// k2将左旋
{
	if ( NULL == k2 )
	{
		return NULL;
	}

	Position k1 = NULL;

	k1 = k2->rchild;
	k2->rchild = k1->lchild;
	k1->lchild = k2;

	if ( CAvlNode *pCAvlNode = (CAvlNode*)(k1) )
	{
		pCAvlNode->iHeight = height(pCAvlNode);
	}
	if ( CAvlNode *pCAvlNode1 = (CAvlNode*)(k2) )
	{
		pCAvlNode1->iHeight = height(pCAvlNode1);
	}

	return k1;
}

        对于2.3情况,单旋不能消除不平衡,只能两次旋转,第一次讲不平和节点旋转成为直线,然后再进行一次单旋,可以抽象成如下(左-右旋转为例),下图中,先将不平衡节点k3的做孩子k1进行左旋,然后再对k3进行右旋。如下图:

        代码如下:
Position CAvlTree::doblueRotateToRight( Position k3 )
{
        k3->lchild = singleRotateToLeft(k3->lchild);  //对k3的左子树左旋
    
	return singleRotateToRight(k3);                 //对k3右旋
}

Position CAvlTree::doblueRotateToLeft( Position k3 )
{
	k3->rchild = singleRotateToRight(k3->rchild);   /对k3的右子树右旋
	return singleRotateToLeft(k3);                  //对k3左旋
}
        下面是数据结构
public:
    CTreeNode(int iElement)
	{
		idata      = iElement;
		lchild     = NULL;
		rchild     = NULL;
		uiTimes    = 1;
	}

	int           idata;
	CTreeNode   *lchild;
	CTreeNode   *rchild;
	unsigned int  uiTimes;

}TreeNode,*Position,*Root,*BinarySearchTree;

class CAvlNode : public CTreeNode{
public:
	CAvlNode(int iElement)
	:CTreeNode(iElement)
	{
         iHeight = 0;
	}
    
	int iHeight;
};
       下面是工具函数,第一个是计算一个节点的高度,第二个是计算一个节点的左右子树的高度差。
int CAvlTree::height(TreeNode* pNode)
{
	if ( NULL == pNode )//空树的高度为-1
	{
		return -1;
	}else
	{
		return std::max( height( pNode->lchild ),height( pNode->rchild) ) + 1;   //选取左右子树的最高的再加1就是这个节点的高度;
	}
}

int CAvlTree::altitudeDifference( TreeNode* pNode )
{
    if ( NULL == pNode )//空树处理
    {
		return -1;
    }

	return std::abs( height(pNode->lchild) - height(pNode->rchild) );
}
        有了以上的函数,就可进行插入操作了,采用的是递归算法:
Root CAvlTree::insertNode( int iElement,Root pRoot )
{
    if ( NULL == pRoot )
    {
		pRoot = new CAvlNode(iElement);

    }else if ( iElement < pRoot->idata )
	{
		pRoot->lchild = insertNode(iElement,pRoot->lchild);


		if ( altitudeDifference(pRoot) == 2 )
		{
			if ( iElement < pRoot->lchild->idata )     //这里为何不用pRoot->rchild?因为pRoot->rchild有可能是空,下面以此类推。
			{
				pRoot = singleRotateToRight(pRoot);   //直线型
			}else
			{
				pRoot = doblueRotateToRight(pRoot);   //半棱形
			}
		}
	}else if ( iElement > pRoot->idata )
	{
		pRoot->rchild = insertNode(iElement, pRoot->rchild);

		if ( altitudeDifference(pRoot) == 2 )
		{
			if ( iElement > pRoot->rchild->idata )
			{
				
				pRoot = singleRotateToLeft(pRoot);
			}else
			{
				pRoot = doblueRotateToLeft(pRoot);
			}
		}
	}else
	{
		pRoot->uiTimes++;
	}

	CAvlNode* node = static_cast<CAvlNode*>(pRoot);
        node->iHeight = height(pRoot);
	return pRoot;
}
        把原理弄清楚了,写代码就很容易了,所以在学习的时候,我一般都是先从原理入手,然后画图,最后写代码验证,跟做设计一个流程。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值