1. 程式人生 > >查詢-平衡二叉樹

查詢-平衡二叉樹

當用戶進行二叉排序樹的建立時,對於同一組資料,不同的插入次序的序列生成的不同形態的二叉排序樹。

而不同形態平均查詢長度一般是不同的,最壞形態的平均查詢長度是(n+1)/2,這顯然不是我們想要的情況,所以我們需要對二叉排序樹進行改進。 

那麼怎樣提高二叉排序樹的查詢效率呢?我們需要讓二叉樹的形狀均衡。

平衡二叉樹(AVL樹):所有結點的左右子樹深度之差的絕對值<=1 .

平衡因子:該結點左子樹與右子樹高度差。

對於一顆有n個結點的AVL樹,其高度保持在O(logN)數量級,ASL也保持在O(logN)量級。

是二叉平衡樹,同時是二叉排序樹。是二叉排序樹,可能不是平衡二叉樹。

首先是輔助巨集的定義:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define UNDERFLOW -2
#define NULL 0
#define LH 1 //左高
#define EH 0  //等高
#define RH -1 //右高
typedef int Status;
typedef int KeyType;
typedef char* InfoType;

平衡二叉樹的儲存結構定義:

//平衡二叉排序樹的儲存結構定義
typedef struct{
   KeyType key;
   InfoType otherinfo; //附加資訊
}ElemType;
typedef struct BiTNode{ 
   ElemType data;
   struct BiTNode *lchild,*rchild;
   int bf; //平衡因子
}BiTNode,*BiTree;
typedef BiTNode BSTNode;
typedef BiTree BSTree; 

平衡二叉樹的構造:

二叉排序樹進行構造,每插入一個結點,檢查該結點是否不平衡。若導致不平衡 則找最小不平衡樹,通過旋轉使該最小的不平衡樹平衡。

對以*p為根的二叉排序樹作左旋處理,處理之後p指向新的樹根結點,即旋轉處理之前的右子樹的根結點 。

void L_Rotate(BSTree &p)
{
   //對以*p為根的二叉排序樹作左旋處理,處理之後p指向新的樹根結點,即旋轉
   //處理之前的右子樹的根結點	
   BSTNode *rc=p->rchild;  //rc指向的*p的右子樹根結點 
   p->rchild=rc->lchild;//rc的左子樹掛接為*p的右子樹
   rc->lchild=p; 
   p=rc;//p指向新的根結點
}

對以*p為根的二叉排序樹作右旋處理,處理之後p指向新的樹根結點,即旋轉處理之前的左子樹的根結點.

void R_Rotate(BSTree &p)
{
	//對以*p為根的二叉排序樹作右旋處理,處理之後p指向新的樹根結點,即旋轉
	//處理之前的左子樹的根結點
   BSTNode *lc=p->lchild; //lc指向的*p的左子樹根結點 
   p->lchild=lc->rchild;//lc的右子樹掛接為*p的左子樹
   lc->rchild=p; 
   p=lc;//p指向新的根結點
}

若在平衡二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個數據元素 為e的新結點,並返回OK 否則返回ERROR 若因插入而使二叉排序樹失去平衡,則作平衡旋轉處理,布林變數taller反映T長高與否。

Status InsertAVL(BSTree &T,ElemType e,bool &taller)
{
	/*若在平衡二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個數據元素
	為e的新結點,並返回OK 否則返回ERROR 若因插入而使二叉排序樹失去平衡,則
	作平衡旋轉處理,布林變數taller反映T長高與否 */
   if(!T)
   {
	   //若插入新結點 樹長高 置taller為TRUE
      if(!(T=(BSTree)malloc(sizeof(BSTNode))))
		  exit(OVERFLOW);
	  T->bf=EH;
	  T->data=e;
	  T->lchild=T->rchild=NULL;
	  taller=TRUE;
   }
   else
   {
	  if(e.key==T->data.key) //樹中已存在和e有相同關鍵子的結點
	  {
	     taller=FALSE; //不再插入
         return ERROR;
	  }
	  else if(e.key<T->data.key) //應繼續在*T的左子樹中進行搜尋
	  {
	     if(!InsertAVL(T->lchild,e,taller)) //未插入
            return ERROR;
		 if(taller) //已插入到*T的左子樹中且左子樹長高
		 {
		    switch(T->bf) //檢查*T的平衡度
			{
	           case LH: //原本左子樹比右子樹高,需要作左平衡處理
                  LeftBalance(T);
                  taller=FALSE;
		          break;
	           case EH: //原本左右子樹等高,現因左子樹增高而使樹增高
		          T->bf=LH;
		          taller=TRUE;
		          break;
	           case RH: //原本右子樹比左子樹高,現左右子樹等高
		          T->bf=EH;
                  taller=FALSE;
		          break;
	        }
		 }
	  }
      else
	  { //應繼續在*T的右子樹中進行搜尋
         if(!InsertAVL(T->rchild,e,taller))//未插入
		    return ERROR;
		 if(taller)//已插入到*T的右子樹中且右子樹長高
		 {
		    switch(T->bf)//檢查*T的平衡度
			{
		   	    case LH://原本左子樹比右子樹高,現左右子樹等高
                   T->bf=EH;
		           taller=FALSE;
		           break;
	            case EH://原本左右子樹等高,現因右子樹增高而使樹增高
		           T->bf=RH;
		           taller=TRUE;
		           break;
	            case RH://原本右子樹比左子樹高,需要作右平衡處理
		           RightBalance(T);
		           taller=FALSE;
		           break;
			}
		 }
	  }
   }
   return OK;
}

對以指標T所指結點為根的二叉樹作左平衡旋轉處理,本演算法結束時,指標T指向,本演算法結束時,指標T指向新結點..

void LeftBalance(BSTree &T)
{
	//對以指標T所指結點為根的二叉樹作左平衡旋轉處理,本演算法結束時,指標T指向
	//新結點
   BSTNode *lc=T->lchild,*rd; //lc指向*T的左子樹根結點
   switch(lc->bf) //檢查*T 的左子樹的平衡度,並作相應平衡處理
   {
   case LH: //新結點插入在*T的左孩子的左子樹上,要作單右旋處理
	   T->bf=lc->bf=EH;
	   R_Rotate(T);
	   break;
   case RH: //新結點插入在*T的左孩子的右子樹上,要作雙旋處理
	   rd=lc->rchild;//rd指向*T的左孩子的右子樹根
       switch(rd->bf) //修改*T及其左孩子的平衡因子
	   {
	   case LH:
		   T->bf=RH;
           lc->bf=EH;
	       break;
	   case EH:
		   T->bf=lc->bf=EH;
		   break;
	   case RH:
	       T->bf=EH;
		   lc->bf=LH;
		   break;
	   }
	   rd->bf=EH;
       L_Rotate(T->lchild); //對*T的左子樹作左旋平衡處理 此處不能用lc代替!!
       R_Rotate(T); //對*T作右旋平衡處理
       break;
   }
}

對以指標T所指結點為根的二叉樹作右平衡旋轉處理,本演算法結束時,指標T指向新結點.

void RightBalance(BSTree &T)
{
    //對以指標T所指結點為根的二叉樹作右平衡旋轉處理,本演算法結束時,指標T指向
	//新結點
   BSTNode *rc=T->rchild,*ld;//rc指向*T的右子樹根結點
   switch(rc->bf) //檢查*T 的右子樹的平衡度,並作相應平衡處理
   {
   case RH://新結點插入在*T的右孩子的右子樹上,要作單左旋處理
	   T->bf=rc->bf=EH;
	   L_Rotate(T);
	   break;
   case LH://新結點插入在*T的右孩子的左子樹上,要作雙旋處理
       ld=rc->lchild;//ld指向*T的右孩子的左子樹根
       switch(ld->bf) //修改*T及其右孩子的平衡因子
	   {
	   case LH:
		   T->bf=EH;
           rc->bf=RH;
	       break;
	   case EH:
		   T->bf=rc->bf=EH;
		   break;
	   case RH:
	       T->bf=LH;
		   rc->bf=EH;
		   break;
	   }
	   ld->bf=EH;
       R_Rotate(T->rchild); //對*T的右子樹作右旋平衡處理 此處不能用rc代替!!
       L_Rotate(T);  //對*T作左旋平衡處理
       break;
   }
}