您的位置:首页 > 其它

平衡二叉树研究(AVL树)

2015-08-20 21:42 417 查看
最近在研究数据结构,使用教材是程杰的《大话数据结构》,这本树写的非常好,看到了平衡二叉树,对该章研究了下,敲了下代码,并通过加断点调试,学习了不少,记录下来,供大家参考。

(1)平衡二叉树(AVL树概念)

一种二叉排序树,其中每一个结点的左子树和右子树的高度差的绝对值小于等于1。

平衡因子BF:左子树深度减去右子树深度的值(有正有负)

最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。

(2)平衡二叉树实现原理

基本思想:当插入一个新的结点,出现不平衡情况时,就对最小不平衡子树进行调整。

调整平衡时主要是两种情况:

(a)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号同号,直接进行左旋或者右旋即可。

(b)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号异号,先对最小不平衡子树根节点的左右子树根节点进行旋转,使两者平衡因子符号由异号变为同号,然后对最小不平衡子树根节点进行旋转(其实就是进行两次旋转,双旋处理)。

(3)平衡二叉树实现

读代码的一点感悟:

(a)插入结点函数InsertAVL是一个递归插入操作,当新增一个结点成功后,然后退出当前的插入操作,递归回退到上一次插入操作,并对新插入结点的上一次结点的平衡因子值进行修改。

(b)直接阅读代码有些困难,最好就是在编译器中加断点进行调试,这样对于理解插入结点时,如何调整平衡的原理很有用。(我是这样的情况,纸上代码看了很久都没看懂,上机调试了一把,立马恍然大悟)

(c)结点删除时,由插入结点是递归,可以推测出删除时也用递归,可见递归操作很强大啊!二叉排序树删除结点三种情况:(a1)叶子结点(a2)仅有左或者右子树(a3)左右子树都存在。删除结点时,判断树的高度是否变矮。(a1)(a2)情况,树高度变矮了,(a3)情况,找到直接前驱,方法是"先向左,然后向右到尽头",将直接前驱的值付给根节点,然后在根节点左子树中递归删除直接前驱结点。

二叉链表结点结构体定义:

typedef struct BiTNode          //结点结构体
{
int data;
int bf;                      //结点平衡因子
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
右旋操作:

/*********************************************
**功能:对以p为根的二叉排序树做右旋处理
**p:二叉排序树
*********************************************/
void R_Rotate(BiTree *p)
{
BiTree L;
L = (*p)->lchild;               //L指向根节点的左子树
(*p)->lchild = L->rchild;       //L的右子树挂在根节点的左子树上
L->rchild = (*p);
(*p) = L;                        //p指向新的根节点
}
左旋操作:

/*********************************************
**功能:对以p为根的二叉排序树做左旋处理
**p:二叉排序树
*********************************************/
void L_Rotate(BiTree *p)
{
BiTree R;
R = (*p)->rchild;
(*p)->rchild = R->lchild;
R->lchild = (*p);
(*p) = R;
}
左平衡旋转处理:

#define LH 1            //左高
#define EH 0            //等高
#define RH -1           //右高

/*********************************************
**功能:对指针T所指结点为根的结点进行左平衡旋转处理
**T:二叉排序树
*********************************************/
void LeftBalance(BiTree *T)
{
BiTree L,Lr;
L = (*T)->lchild;               //L指向左子树根节点
switch(L->bf)
{
case LH:                        //新节点插入在左孩子的左子树上,要作单右旋处理
(*T)->bf = L->bf = EH;
R_Rotate(T);
break;
case  RH:
Lr = L->rchild;
switch(Lr->bf)
{
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所指结点为根的结点进行右平衡旋转处理
**T:二叉排序树
*********************************************/
void RightBalance(BiTree *T)
{
BiTree R,Rl;
R = (*T)->rchild;
switch(R->bf)
{
case RH:                 //此时,根节点和右子树结点平衡因子相同,直接对根节点左旋
(*T)->bf = R->bf = EH;
L_Rotate(T);
break;
case LH:
Rl = R->lchild;       //指向右子树的根节点的左子树,异号,需要进行双旋处理
switch(Rl->bf)
{
case RH:
(*T)->bf = LH;
R->bf = EH;
break;
case EH:
(*T)->bf = R->bf=EH;
break;
case LH:(*T)->bf = EH;
R->bf = RH;
break;
}
Rl->bf = EH;
R_Rotate(&(*T)->rchild);           //对T的右子树根节点做右旋操作
L_Rotate(T);                       //对根节点T做左旋操作
}
}
插入结点主函数:

/*********************************************
**功能:平衡二叉树中插入一个结点e,若e不存在,插入并返回true,否则返回false
**T:平衡二叉树
**e:带插入结点
**taller:树是否增高
*********************************************/
int InsertAVL(BiTree *T,int e,int *taller)
{
if(!*T)
{
*T =(BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->lchild = NULL;
(*T)->rchild = NULL;
(*T)->bf = EH;
*taller = 1;                      //表示树长高了
}
else
{
if (e == (*T)->data)             //该节点已经存在  不用插入了
{
*taller = 0;
return 0;
}
if (e < (*T)->data)
{
if (!InsertAVL(&(*T)->lchild,e,taller))         //左子树中继续搜索  但是没有插入
{
return 0;
}
if (*taller)                                    //插入新的结点  并且树长高了
{
switch((*T)->bf)
{
case LH:                                     //原来左子树比右子树高,做左平衡处理
LeftBalance(T);
*taller = 0;
break;
case EH:                                     //原来左、右子树等高,现在左子树增高使树增高
(*T)->bf = LH;
*taller = 1;
break;
case RH:                                    //原来右子树较高,现在左右子树一样高
(*T)->bf = EH;
*taller = 0;
break;
}
}
}
else                                            //右子树中进行搜索
{
if (!InsertAVL(&(*T)->rchild,e,taller))       //右子树中未插入
{
return 0;
}
if (*taller)
{
switch((*T)->bf)                         //检查T的平衡度
{
case LH:
(*T)->bf = EH;
*taller = 0;
break;
case EH:
(*T)->bf = RH;
*taller = 1;
break;
case RH:                               //本来右子树比左子树高,还在右子树插入结点,需要做右平衡处理
RightBalance(T);
*taller = 0;
break;
}
}
}
}
return 1;
}
打印二叉排序树:

/*********************************************
**功能:递归打印二叉树,中序遍历
**T:二叉排序树
*********************************************/
void PrintBTree(BiTree T)
{
if (!T)                    //递归出口
return;
PrintBTree(T->lchild);
printf("%d\t",T->data);
PrintBTree(T->rchild);
}


删除结点函数:

/*********************************************
**功能:平衡二叉树中进行删除,又该节点,删除并返回true,若没有该节点,返回false
**T:平衡二叉树
**e:删除的结点键值
**shorter:树的高度是否降低
*********************************************/
int DeleteAVL(BiTree *T,int e,int* shorter)
{
//提示:插入结点使用的递归插入,可以联想到删除也是用的递归删除
if (!*T)                 //不存在该节点
{
return 0;
}
else if ((*T)->data == e)      //找到该节点
{
BiTree q;
if (!(*T)->lchild)          //如果左子树为空,只需要重接右子树
{
q = (*T);
(*T) = (q->rchild);
free(q);
*shorter = 1;
}
else if (!(*T)->rchild)    //右子树为空,只需要重接左子树
{
q = (*T);
(*T) = q->lchild;
free(q);
*shorter = 1;
}
else                       //左右子树都不为空
{
q = (*T)->lchild;       //先向左,然后向右到尽头
while(q->rchild)
{
q = q->rchild;
}
(*T)->data = q->data;
DeleteAVL(&(*T)->lchild,q->data,shorter);             //左子树中递归删除前驱结点
}
}
else if (e < (*T)->data)       //左子树中递归查找
{
if (!DeleteAVL(&(*T)->lchild,e,shorter))       //没找到
{
return 0;
}
if (*shorter)                                //删除结点,而且高度降低
{
switch((*T)->bf)
{
case LH:
(*T)->bf = EH;
*shorter = 1;
break;
case EH:
(*T)->bf = RH;
*shorter = 0;
break;
case RH:                              //右边本来就高  左边删除后,需要进行右平衡处理
RightBalance(T);
if ((*T)->rchild->bf == EH)        //画图看一下,很easy
{
*shorter = 0;
}
else
{
*shorter = 1;
}
break;
}
}
}
else                          //右子树中递归查找
{
if (!DeleteAVL(&(*T)->rchild,e,shorter))       //没找到
{
return 0;
}
if (*shorter)                                //删除结点,而且高度降低
{
switch((*T)->bf)
{
case LH:
LeftBalance(T);
if((*T)->lchild->bf == EH)             //画图,不多说
{
*shorter = 0;
}
else
{
*shorter = 1;
}
break;
case EH:
(*T)->bf = LH;
*shorter = 0;
break;
case RH:
(*T)->bf = EH;
*shorter = 1;
break;
}
}
}
return 0;
}


测试代码主函数:
int _tmain(int argc, _TCHAR* argv[])
{
int a[] = {3,2,1,4,5,6,7,10,9,8};
BiTree T = NULL;
int taller;
for (int i=0;i<10;++i)
{
InsertAVL(&T,a[i],&taller);
}
PrintBTree(T);
int shorter;
DeleteAVL(&T,10,&shorter);
PrintBTree(T);
printf("\n");

return 0;


打印结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: