平衡二叉树---》插入、删除

        平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树。

定义:平衡二叉树或为空树,或为如下性质的二叉排序树:

 (1)左右子树深度之差的绝对值不超过1;

 (2)左右子树仍然为平衡二叉树.

        平衡二叉树可以避免排序二叉树深度上的极度恶化,使树的高度维持在O(logn)来提高检索效率。

平衡二叉树算法思想

      若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树 。

  要维持二叉树的平衡需要使用左旋和右旋操作。

   右旋(如图1):以A为中心顺时针旋转。实际就是把B移到A的位置,把B的右子树变为A的左子树,在把A作为B的右子树。在平衡二叉树中以平衡因子为2的节点进行右旋。

                         《平衡二叉树---》插入、删除》                                     《平衡二叉树---》插入、删除》

                                          右旋前                                                            右旋后

右旋代码:

//右旋 void RotateRight(tNode** root) { tNode* p=*root;//图中相当于A tNode* rc=p->pleft;//相当于B tNode* rrc=rc->pright;//相当于D p->pleft=rrc; rc->pright=p; *root=rc; }

左旋(如图):以A为中心逆时针旋转,实际就是:把B移到A的位置,把B的左子树作为A的右子树,再把A变成B的左子树。在平衡树种以平衡因子为-2的节点为中心做左旋操作

                                          《平衡二叉树---》插入、删除》                                                     《平衡二叉树---》插入、删除》

                                                             左旋前                                                      左旋后

左旋代码:

	//左旋
	void RotateLeft(tNode** root)
	{
		tNode * p=*root;//相当于A
		tNode* lc=p->pright;//相当于B
		tNode* llc=lc->pleft;//相当于C
		p->pright=llc;
		lc->pleft=p;
		*root=lc;
	}

插入新的节点:

1、LL的情况:

       由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。

                        《平衡二叉树---》插入、删除》

2、LR情况:

       由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。

                          
《平衡二叉树---》插入、删除》

左平衡代码:

void AVLTree::LeftBalance(tNode** root) { tNode* p=*root; tNode* c=p->pleft; tNode* rc=c->pright; switch(c->bf) { //LL情况 case 1: { p->bf=c->bf=0; RotateRight(root); break; } //LR情况 case -1: { switch(rc->bf) { case 1: { c->bf=0; p->bf=-1; break; } case 0: { c->bf=0; p->bf=0; break; } case -1: { c->bf=1; p->bf=0; break; } } rc->bf=0; RotateLeft(&c); RotateRight(root); break; } } }

左平衡操作在处理LR情况时又根据rc节点也就是图中D的平衡因子分三种情况来确定平衡后图中B和A的平衡因子。其他的平衡因子不变。

怎么来确定A和B左平衡操作后的平衡因子?

假设在LR情况下,新的节点插入后C的平衡因子是1,那么设C的左子树的高度为h则右子树为h-1,B的右子树为h+1,B的平衡因子是-1,则它左子树高度为h,A左子树高度为h+2,它的平衡因子为2,则它的右子树高度为h。在平衡化后,A的左子树以C为根,右子树为D,所以A的平衡因子为0.而B的右子树以A为根,则它的平衡因子为-1.

       在C的平衡因子为0,-1的情况下也可以用上面方法推出,平衡后A和B的平衡因子。

                                                      《平衡二叉树---》插入、删除》

3 RR情况:

       由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。

                                      
《平衡二叉树---》插入、删除》

4、RL情况:

        由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树。

                                《平衡二叉树---》插入、删除》

右平衡代码:

void AVLTree::RightBalance(tNode** root) { tNode* p=*root; tNode* c=p->pright; tNode* lc=c->pleft; switch(c->bf) { //RR情况 case -1: { p->bf=-1; c->bf=0; RotateLeft(root); break; } //RL情况 case 1: { switch(lc->bf) { case 1: { p->bf=0; c->bf=-1; break; } case 0: { p->bf=0; c->bf=0; break; } case -1: { p->bf=1; c->bf=0; break; } lc->bf=0; RotateRight(&c); RotateLeft(root); break; } } } }

右平衡在RL的情况下平衡因子的变化也分三种情况。因子推断与上面相同。

左旋,右旋,左平衡,右平衡都齐全了。那么插入新节点操作也只是把他们穿针引线的连起来而已。

          插入新的节点代码 :

插入新节点以递归的方式。在插入新节点成功后回溯更新和维护树的平衡性。

int AVLTree::Insert(tNode** root,int key,int& taller) { if(*root==NULL) { tNode* newNode=new tNode(key); taller=1; *root=newNode; return 1; } else if((*root)->mvalue==key) { return 0; } else if(key<(*root)->mvalue) { if(Insert(&((*root)->pleft),key,taller)) { return 1; } if(taller) { switch((*root)->bf) { case 1: LeftBalance(root); taller=0; break; case 0: (*root)->bf=1; taller=1; break; case -1: (*root)->bf=0; taller=0; break; } } return 0; } else if(key>(*root)->mvalue) { if(Insert(&((*root)->pright),key,taller)) { return 1; } if(taller) { switch((*root)->bf) { case -1: RightBalance(root); taller=0; break; case 0: (*root)->bf=-1; taller=1; break; case 1: (*root)->bf=0; taller=0; break; } } return 0; } }

        
删除节点操作:

      删除节点操作比插入稍微复杂。插入只需要一次平衡操作即可恢复树的平衡性。而删除节点在删除后,可能需要多次操作才能恢复树的平衡。

      要先找到最底部不平衡的子树,将其恢复平衡。在根据情况判断需不需要继续沿着子树向上恢复平衡。

     为了找到最底部不平衡的子树,将删除操作迁移到最底部的子树上进行。

     删除节点的代码为:

//返回1表示要删除的点在最底部子树上,删除成功。返回0表示将删除节点向下迁移。
int AVLTree::deleteNode(tNode* p,tNode* pp,int& key,bool& shorter)
{
      //p表示当前节点,pp表示当前节点的父节点。key表示要删除元素的键值,
       //shorter 表示是否需要平衡化操作。
       if(p->pleft&&p->pright)
	{
		tNode* s=p->pleft;
		tNode* ps=p;
		while(s->pright!=NULL)
		{
			ps=s;
			s=s->pright;
		}
		p->mvalue=s->mvalue;//覆盖原来要删除的点的值。
		key=s->mvalue;//将关键字的值更改。
		return 0;//没删除成功只是替换了要删除的元素
	}
	tNode* c=NULL;
	if(p->pleft)
		c=p->pleft;
	else
		c=p->pright;
	if(p==pRoot)//pRoot表示树的根
		pRoot=c;
	else
	{
	   if(p==pp->pleft)
	   {
		   pp->pleft=c;
		  
	   }
	   else
	   {
		   pp->pright=c;
	   }
	}
	shorter=true;
	delete p;
	return 1;
}

删除节点的代码

int AVLTree::Delete(tNode* root,tNode* parent,int key,bool& shorter)
{
	if(root==NULL)
	{
		shorter=false;
		return 0;
	}
	else if(root->mvalue==key)
	{
		if(deleteNode(root,parent,key,shorter))
		   return 1;
	}
	else if(root->mvalue>=key)
	{
		if(Delete(root->pleft,root,key,shorter))
		{
			if(shorter)
			{

				switch(root->bf)
				{
				case 1:
					{
						root->bf=0;
						shorter=true;
						break;
					}
				case 0:
					{
						root->bf=-1;
						shorter=false;
						break;
					}
				case -1:
                                      {
                                                
                                                if(root->pright->bf==0)
						{//为L0情况经过平衡化后子树
                                                 //的高度和删除前一样 
                                                 shorter=false;
						}
						else
                                                   //L1,L-1,平衡化后与删除前
                                                   //比子树高度减小1,其父树
                                                    //还需要平衡化操作
                                                     shorter=true;

 RightBalance(&root);break;}}}return 1;}return 0;}else if(root->mvalue<key){if(Delete(root->pright,root,key,shorter)){if(shorter){switch(root->bf){case 1:{

                                                if(root->pleft->bf==0)
							shorter=false;
						else
							shorter=true;

 LeftBalance(&root);break;}case 0:{root->bf=1;shorter=false;break;}case -1:{root->bf=0;shorter=true;break;}}}return 1;}return 0;}}

平衡树类定义代码:

struct tNode
{
	int bf;//平衡因子
	int mvalue;
	tNode* pleft,
		*pright,
		*parent;
	tNode():bf(0),mvalue(0),pleft(0),pright(0),parent(0)
	{

	}
	tNode(int ivalue):bf(0),mvalue(ivalue),pleft(0),pright(0),parent(0)
	{

	}
};
class AVLTree
{
public:
	AVLTree(void):pRoot(0){}
	~AVLTree(void);
	//插入值为key的节点
	int AVL_Insert(int key)
	{
		int taller;
		Insert(&pRoot,key,taller);
	}
	//删除值为key的节点
	int AVL_Delete(int key)
	{
		bool shorter;
		Delete(pRoot,0,key,shorter);
	}
private:
	//左旋
	void RotateLeft(tNode** root)
	{
		tNode * p=*root;
		tNode* lc=p->pright;
		tNode* llc=lc->pleft;
		p->pright=llc;
		lc->pleft=p;
		*root=lc;
	}
	//右旋
	void RotateRight(tNode** root)
	{
		tNode* p=*root;
		tNode* rc=p->pleft;
		tNode* rrc=rc->pright;
		p->pleft=rrc;
		rc->pright=p;
		*root=rc;
	}
	//左平衡
	void LeftBalance(tNode** root);
	//右平衡
	void RightBalance(tNode** root);
	//插入新的节点
	 int Insert(tNode** root,int key,int& taller);
	 //删除某个节点
	 int Delete(tNode* root,tNode* parent,int key,bool& shorter);
	 //删除节点的实际操作
	 int deleteNode(tNode* p,tNode* pp,int& key,bool& shorter);
private:
	tNode* pRoot;
};     

程序有Bug,大概的实现方法是这样的。整了几天,还是觉得和方法和代码结合。容易被接受。总结出来也方便以后查看。

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/linmars24/article/details/7781901
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞