概念
平衡二叉樹(Balanced binary tree)是由蘇聯數學家Adelson-Velskii and Landis於1962年首先提出的,所以又稱爲AVL樹。
定義:平衡二叉樹或爲空樹,或滿足如下性質的二叉樹:
(1)本身首先是一棵二叉搜索樹
(2)左右子樹深度之差的絕對值不超過1;
(3)左右子樹仍然爲平衡二叉樹.
平衡因子BF=左子樹深度-右子樹深度.
平衡二叉樹每個結點的平衡因子只能是1,0,-1。若其絕對值超過1,則該二叉排序樹就是不平衡的。
通過下面幅圖可以看出,平衡二叉樹可以保證最差查找效率爲O(logN),而二叉查找樹的最差查找效率爲O(N)。
插入節點的調整方法
若向平衡二叉樹中插入一個節點後破壞了平衡二叉樹的平衡性,首先從根節點到該新插入節點的路徑之逆向根節點方向找到第一個失去平衡的節點,然後以該失衡節點和它相鄰的剛查找過的兩個節點構成調整子樹,使之成爲新的平衡子樹。當失去平衡的最小子樹被調整爲平衡子樹後,原來其他所有不平衡子樹無需調整,整個二叉排序樹就又成了一個平衡二叉樹。
失去平衡的最小子樹是指以離插入節點最近,且平衡因子絕對值大於1的節點作爲根的子樹。
(1)LL型平衡旋轉法
由於在A的左孩子B的左子樹上插入結點F,使A的平衡因子由1增至2而失去平衡。故需進行一次順時針旋轉操作。 即將A的左孩子B向右上旋轉代替A作爲根結點,A向右下旋轉成爲B的右子樹的根結點。而原來B的右子樹則變成A的左子樹。
(2)RR型平衡旋轉法
由於在A的右孩子C 的右子樹上插入結點F,使A的平衡因子由-1減至-2而失去平衡。故需進行一次逆時針旋轉操作。即將A的右孩子C向左上旋轉代替A作爲根結點,A向左下旋轉成爲C的左子樹的根結點。而原來C的左子樹則變成A的右子樹。
(3)LR型平衡旋轉法
由於在A的左孩子B的右子數上插入結點F,使A的平衡因子由1增至2而失去平衡。故需進行兩次旋轉操作(先逆時針,後順時針)。即先將A結點的左孩子B的右子樹的根結點D向左上旋轉提升到B結點的位置,然後再把該D結點向右上旋轉提升到A結點的位置。即先使之成爲LL型,再按LL型處理。
如圖中所示,即先將圓圈部分先調整爲平衡樹,然後將其以根結點接到A的左子樹上,此時成爲LL型,再按LL型處理成平衡型。
(4)RL型平衡旋轉法
由於在A的右孩子C的左子樹上插入結點F,使A的平衡因子由-1減至-2而失去平衡。故需進行兩次旋轉操作(先順時針,後逆時針),即先將A結點的右孩子C的左子樹的根結點D向右上旋轉提升到C結點的位置,然後再把該D結點向左上旋轉提升到A結點的位置。即先使之成爲RR型,再按RR型處理。
如圖中所示,即先將圓圈部分先調整爲平衡樹,然後將其以根結點接到A的左子樹上,此時成爲RR型,再按RR型處理成平衡型。
平衡化靠的是旋轉。參與旋轉的是3個節點(其中一個可能是外部節點NULL),旋轉就是把這3個節點轉個位置。注意的是,左旋的時候p->right一定不爲空,右旋的時候p->left一定不爲空,這是顯而易見的。
如果從空樹開始建立,並時刻保持平衡,那麼不平衡只會發生在插入刪除操作上,而不平衡的標誌就是出現bf == 2或者 bf == -2的節點。
刪除節點的調整方法
在平衡二叉樹上刪除節點x(假定有且僅有一個節點等於x)的過程如下:
1、採用二叉排序樹的刪除方法找到節點x並刪除之;
2、沿着根到被刪除節點的路線之逆逐層向上查找,必要時修改x祖先節點的平衡因子,因爲刪除後會使得某些子樹的高度降低;
3、查找途中,一旦發現x的某個祖先*p失衡,就要進行調整。不妨設x節點在*p的左子樹中,在*p節點失衡後,要做何種調整,取決於*p節點的右孩子,*p1,若*p1的平衡因子是1,則做RL調整;若*p1的平衡因子是-1,則做RR調整;若*p1的平衡因子是0,則做RL或者RR調整均可。如果x在*p右子樹中,調整過程類似。
4、如果調整後,子樹的高度降低了,這個過程還要繼續,直到根節點爲止。即,刪除一個節點可能會引起多次調整,而不是插入時的至多一次調整。
節點查找
和二叉排序樹完全相同。
C語言實現一
由於沒有刪除某一個節點的功能,故在數據結構的定義中沒有提供父節點指針。
#include <stdio.h>
#include <malloc.h>
typedef enum
{
EH = 0,
LH = 1,
RH = -1
}bh_t;
typedef enum
{
FALSE = 0,
TRUE = 1
}bool_t;
typedef int ElemType;
typedef struct BSTNode
{
ElemType key;
int bf;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
void InOrderTraverse(BSTree root)
{
if(NULL != root)
{
InOrderTraverse(root->lchild);
printf("%d\t",root->key);
InOrderTraverse(root->rchild);
}
}
void PreOrderTraverse(BSTree root)
{
if(NULL != root)
{
printf("%d\t",root->key);
PreOrderTraverse(root->lchild);
PreOrderTraverse(root->rchild);
}
}
void R_Rotate(BSTree *p)
{
BSTree lc=(*p)->lchild;
(*p)->lchild=lc->rchild;
lc->rchild=*p;
*p=lc;
}
void L_Rotate(BSTree *p)
{
BSTree rc=(*p)->rchild;
(*p)->rchild=rc->lchild;
rc->lchild=*p;
*p=rc;
}
void LeftBalance(BSTree *T)
{
BSTree lc=(*T)->lchild;
BSTree rd = lc->rchild;
switch(lc->bf)
{
case LH:
(*T)->bf=lc->bf=EH;
R_Rotate(T);
break;
case RH:
switch(rd->bf)
{
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));
R_Rotate(T);
break;
}
}
void RightBalance(BSTree *T)
{
BSTree rc=(*T)->rchild;
BSTree ld=rc->lchild;
switch(rc->bf)
{
case RH:
(*T)->bf=rc->bf=EH;
L_Rotate(T);
break;
case LH:
switch(ld->bf)
{
case RH:
(*T)->bf=LH;
rc->bf=EH;
break;
case EH:
(*T)->bf=rc->bf=EH;
break;
case LH:
(*T)->bf=EH;
rc->bf=RH;
break;
}
ld->bf=EH;
R_Rotate(&((*T)->rchild));
L_Rotate(T);
break;
}
}
bool_t InsertAVL(BSTree *t,ElemType e,bool_t *taller)
{
if(NULL == t)
return FALSE;
if(NULL == *t)
{
*t=(BSTree)malloc(sizeof(BSTNode));
if(NULL == *t)
return FALSE;
(*t)->key=e;
(*t)->lchild=(*t)->rchild=NULL;
(*t)->bf=EH;
*taller=TRUE;
}
else
{
if(e==(*t)->key)
{
*taller=FALSE;
return FALSE;
}
if(e<(*t)->key)
{
if(FALSE == InsertAVL(&((*t)->lchild),e,taller))
return FALSE;
if(*taller)
{
switch((*t)->bf)
{
case LH:
LeftBalance(t);
*taller=FALSE;
break;
case EH:
(*t)->bf=LH;
*taller=TRUE;
break;
case RH:
(*t)->bf=EH;
*taller=FALSE;
break;
}
}
}
else
{
if(FALSE == InsertAVL(&((*t)->rchild),e,taller))
return FALSE;
if(*taller)
{
switch((*t)->bf)
{
case RH:
RightBalance(t);
*taller=FALSE;
break;
case EH:
(*t)->bf=RH;
*taller=TRUE;
break;
case LH:
(*t)->bf=EH;
*taller=FALSE;
break;
}
}
}
}
return TRUE;
}
BSTree searchAVL(BSTree t,ElemType key)
{
BSTree p=t;
while(p)
{
if(p->key==key)
return p;
else if(p->key<key)
p=p->rchild;
else
p=p->lchild;
}
return p;
}
static void destroy(BSTree *t)
{
if(NULL != *t)
{
destroy(&((*t)->lchild));
destroy(&((*t)->rchild));
free(*t);
*t = NULL;
}
return;
}
void destroyAVL(BSTree root)
{
if(NULL != root)
{
destroy(&root);
}
return;
}
int main(int argc,char *argv[])
{
BSTree root=NULL,r;
bool_t taller=FALSE;
int array[]={13,24,37,90,53};
int i = 0;
for(i=0;i<5;i++)
InsertAVL(&root,array[i],&taller);
printf("inorder traverse\n");
InOrderTraverse(root);
printf("\npreorder traverse\n");
PreOrderTraverse(root);
printf("\nsearch key\n");
r=searchAVL(root,37);
if(r)
{
printf("%d\n",r->key);
}
else
{
printf("not find!\n");
}
destroyAVL(root);
root = NULL;
}