上一篇文章我们讲述了二叉排序树,然而当我们遇到特殊情况,如a[10] ={3,2,1,4,5,6,7,10,9,8}的情况时完全符合二叉树的定义,但是对这样高度达到8的二叉树老说是非常不利的,所以我们对此引入了一个新的概念——平衡二叉树~
一、什么是平衡二叉树
是一种高度平衡的二叉排序树,其中每一个结点的左子树和右子树的高度差至多等于1 。将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF当|BF|>1时,该二叉树是不平衡的。
二、平衡二叉树的实现原理
其构建的基本思想是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,找出最小不平衡子树。在保持二叉排序树的前提下,调整最小不平衡子树中各结点之间的连接关系,进行相应的旋转,使之成为新的平衡子树。
最小不平衡子树:
1、最小不平衡子树的调整有三种情况:
- 当BF > 1时,对整个树进行右旋(顺时针)旋转
- 当BF < 1时,对整个树进行左旋
- 当遇到BF的值相反的时,先进行一次使符号相同的旋转,再反向旋转一次。
2、实现算法:
结点结构:
typedef struct BiTNode
{
int bf;//用来存储平衡因子
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
右旋操作:
void R_Rotate(BiTree *p)
{
BiTree L;
L = (*P)->lchid;
(*P) -> lchid = L->rchild;
L->rchid = (*p);
*p = L;
}
左旋操作:
void L_Rotate(BiTree *p)
{
BiTree R;
R = (*P)->rchid;
(*P) -> rchid = R->lchild;
R->lchid = (*p);
*p = R;
}
左平衡旋转处理函数代码:
对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时,指针T指向新的根结点
函数被调用,传入一个需调整平衡性的子树T,该函数被调用时其实已经确认当前子树是不平衡状态,此时T的根结点应该是平衡因子BF大于1的数。
#define LH 1 //左高
#define EH 0 //等高
#define RH -1//右高
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;
switch(Lr->bf)
{
case LH:
(*T)->bf = RH;
L->bf = EH;
break;
case EH:
(*T)->bf = EH;
L->bf = LH;
break;
case RH:
(*T)->bf = EH;
L->bf = LH;
break;
}
Lr->bf = EH;
L_Rotate(&(*T)->lchid);//对T的左子树作左旋平衡处理
R_Rotate(T);//对T作右旋平衡处理
}
}
右平衡旋转处理同上做细微变化就行,在此不予阐述
主函数代码:
Status InsertAVL(BiTree * T,int e,Status *taller)
{
if(!*T)
{
//插入新结点,树“长高”,置taller为true
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->lchid = (*T)->rchid=null;
(*T)->bf = EH;
*taller = ture;
}
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 = fales;
break;
case EH://原本左右子树等高,现因左子树增高而树
(*T)->bf = LH;
*taller = ture;
break;
case RH://原本右子树比左子树高,现在左右子树等高
(*T)->bf = EH;
*taller = fales;
break;
}
}
}
else
{//应继续在T的右子树中进行搜索
if(!InsertAVL(&(*T)->lchild,e,taller))//未插入
return false;
if(taller)//已插入到T的右子树中且右子树“长高”
{
switch((*T)->bf)//检查T的平衡度
{
case LH://原本左子树比右子树高,现在左右子树等高
(*T)->bf = EH;
*taller = fales;
break;
case EH://原本左右子树等高,现因右子树增高而树
(*T)->bf = RH;
*taller = ture;
break;
case RH://原本右子树比左子树高,需要进行做平衡处理
RightBalance(T);
*taller = fales;
break;
}
}
}
}
return ture;
}
验证代码:
插入、删除、查找的时间复杂度都是O(logn)