平衡二叉树(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;
	}                                           
                                                     
左旋前 左旋后
左旋代码:
	//左旋
	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;
		}
	}
}怎么来确定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的平衡因子。
                                                      
由于在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;
			}
		}
	}
}左旋,右旋,左平衡,右平衡都齐全了。那么插入新节点操作也只是把他们穿针引线的连起来而已。
插入新的节点代码 :
插入新节点以递归的方式。在插入新节点成功后回溯更新和维护树的平衡性。
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;                                                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,大概的实现方法是这样的。整了几天,还是觉得和方法和代码结合。容易被接受。总结出来也方便以后查看。
 
                   
                   
                   
                   
                             本文详细介绍了平衡二叉树的基本概念、平衡条件及其在插入和删除节点时如何通过旋转操作来保持平衡。提供了完整的平衡二叉树类实现代码。
本文详细介绍了平衡二叉树的基本概念、平衡条件及其在插入和删除节点时如何通过旋转操作来保持平衡。提供了完整的平衡二叉树类实现代码。
           
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   963
					963
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            