平衡二叉树是一种二叉排序树,其中每一个结点的左子树和右子树的高度至多等于1,平衡二叉树又称为AVL树。平衡二叉树是一种高度平衡的二叉排序树,意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF,那么平衡二叉树上所有结点的平衡因子只可能是-1,0,1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,我们称为最小不平衡子树。
基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡二叉树。
这里我们介绍一个例子,我们现在有一个数组a[10]={3,2,1,4,5,6,7,10,9,8},图1是在没有学习平衡二叉树之前,根据二叉排序树的特性,建成的二叉树,这对查找是非常不利的。图2是利用平衡二叉树的原理建成的二叉树,现在我们来分析是如何一步步建成图2的平衡二叉树的。
首先改进二叉排序树的结点结构,增加一个bf,用来存储平衡因子。
//二叉树的二叉链表结点结构定义
typedef struct BiTNode//结点结构
{
int data;//结点数据
int bf;//结点的平衡因子
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
然后,对于右旋操作,我们的代码如下。
void R_Rotate(BiTree *P)
{
BiTree L;
L=(*P)->lchild;//L指向P的左子树根结点
(*P)->lchild=L->rchild;//L的右子树挂接为P的左子树
L->rchild=(*P);
*P=L;//P指向新的根结点
}
左旋操作代码如下。
//对以P为根的二叉排序树作左旋处理
//处理之后P指向新的树根结点,即选择处理之前的右子树的根结点0
void L_Rotate(BiTree *p)
{
BiTree R;
R=(*P)->rchild;//R指向P的右子树根结点
(*P)->rchild=R->lchild;//R的左子树挂接为P的右子树
R->lchild=(*P);
*P=R;//P指向新的根结点
}
现在我们来看左平衡旋转处理的函数代码。
#define LH +1//左高
#define EH 0//等高
#define RH -1//右高
//对以指针T所指结点为根的二叉树作左平衡旋转处理
//本算法结束时,指针T指向新的根结点
void LeftBalance(BiTree *T)
{
BiTree L,Lr;
L=(*T)->lchild;//L指向T的左子树根结点
switch(L->bf)
{
//检查T的左子树的平衡度,并作相应平衡处理
case LH://新结点插入在T的左孩子的左子树上,要作单右旋处理
(*T)->bf=L->bf=EH;
R_Rotate(T);
break;
case RH;//新结点插入在T的左孩子的右子树上,要作双旋处理
Lr->L->rchild;//Lr指向T的左孩子的右树根
switch(Lr->bf)//修改T及其左孩子的平衡因子
{
case LH:(*T)->bf=RH;
L->bf=EH;
break;
case EH:(*T)->bf=L->bf=EH;
break;
case RH:(*T)->bf=EH;
L->bf=LH;
break;
}
Lr->bf=EH;
L_Rotate(&(*T)->lchild);//对T的左子树作左平衡处理
R_Rotate(T);//对T作右旋平衡处理
}
}
下面我们来对代码进行解释说明:
//若在平衡的二叉排序树T中不存在和e有相同关键字段的结点,则插入一个
//数据元素为e的新结点并返回1,否则返回0。若因插入而使二叉排序树失去平衡,
//则作平衡旋转处理,布尔变量taller反映T长高与否。
Status insertAVL(BiTree *T,int e,Status *taller)
{
if(!*T)
{
//插入新结点,树“长高”,置taller为TRUE
*T=(BiTree)malloc(sizeof(BiTNode));
(*T)->data=e;
(*T)->lchild=(*T)->rchild=NULL;
(*T)->bf=EH;
*taller=TRUE;
}
else{
if(e==(*T)->data)
{
//树中已存在和e有相同关键字的结点则不再插入
*taller=FALSE;
return FALSE;
}
if(e<(*T)->data)
{
//应继续在T的左子树中进行搜索
if(!InsertAVL(&(*T)->lchild,e,taller))//未插入
{
return FALSE;
}
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 FALSE;
}
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);
(*T)->bf=EH;
*taller=FALSE;
break;
}
}
}
}
return TRUE;
}
对于这段代码来说,我们只需要在需要构建平衡二叉树的时候执行如下列代码及可在内存中生成一棵平衡二叉树。
int i;
int a[10]={3,2,1,4,5,6,7,10,9,8};
BiTree T=NULL;
Status taller;
for(i=0;i<10;i++)
{
InsertAVL(&T,a[i],&taller);
}
不容易,终于讲完了,本算法代码有点长,而且有点难理解,编程中容易在很多细节上出错,要想真正掌握它,需要多加练习。已泪奔