在计算机科学中,AVL树是最先发明的自平衡二叉查找树。

在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。

查找、插入和删除在平均和最坏情况下都是O(log n)。

增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

平衡因子pf  等于左子树深度减右子树深度

 

性质:

它或者是颗空树,或者是具有下列性质的二叉树:

  • 它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
  • 若将二叉树节点的平衡因子BF定义为该节点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有节点的平衡因子只可能为-1,0,1.
  • 只要二叉树上有一个节点的平衡因子的绝对值大于1,那么这颗平衡二叉树就失去了平衡。

平衡算法总结

看完了上面的例子,我们总结一下二叉排序树的不平衡情况以及如何将其转化为平衡情况。
一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是离插入结点最近,且平衡因子绝对值不超过1的祖先结点),则失去平衡后进行调整的规律可以归纳为一下4种情况:
1、单向右旋平衡处理:由于在a的左子树根结点的左子树上插入结点,a的平衡因子由1增加到2,致使以a为根结点的子树失去平衡,则需要进行一次右向顺时针旋转操作。简称LL型旋转

 
左左型

2、单向左旋平衡处理:由于在a的右子树根结点的右子树上插入结点,a的平衡因子由-1增加到-2,致使以a为根结点的子树失去平衡,则需要进行一次左向逆时针旋转操作。简称RR型旋转
 
右右型

3、双向旋转(先左后右)平衡处理:由于在a的左子树的根结点的右子树上插入结点,a的平衡因子由1增加到2,致使a为根结点的子树失去平衡,则需要进行两次旋转(先左旋后右旋)操作。简称LR型旋转
 
左右型

4、双向旋转(先右后左)平衡处理:由于在a的右子树的根结点的左子树上插入结点,a的平衡因子由1增加到2,致使a为根结点的子树失去平衡,则需要进行两次旋转(先右旋后左旋)操作。简称RL型旋转 (上图F)
 
右左型

 

总结图

 

c语言代码分析:

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>

#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)>(b))
#define LH +1 //左高
#define EH 0 //等高
#define RH -1 //右高

typedef struct BiTNode{
int data;
int bf; //节点平衡因子
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;


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

//左旋操作
void L_Rotate(BiTree *p)
{
BiTNode *R;
R=(*p)->rchild;
(*p)->rchild=R->lchild;
R->lchild=(*p);
*p=R;
}

/*
对以指针t所指结点为根的二叉树作左平衡旋转处理
包含LL旋转和LR旋转两种情况
平衡因子的改变其实很简单,自己画图就出来了
*/
void LeftBalance(BiTree *T)
{
BiTNode *lc = NULL;
BiTNode *rd = NULL;
lc = (*T)->lchild;
switch(lc->bf)
{
case LH: //LL旋转
(*T)->bf = EH;
lc->bf = EH;
R_Rotate(T);
break;

case EH: //deleteAVL需要,insertAVL用不着
(*T)->bf = LH;
lc->bf = RH;
R_Rotate(T);
break;

case RH: //LR旋转
rd = lc->rchild;
switch(rd->bf)
{
case LH:
(*T)->bf = RH;
lc->bf = EH;
break;
case EH:
(*T)->bf = EH;
lc->bf = EH;
break;
case RH:
(*T)->bf = EH;
lc->bf = LH;
break;
}
rd->bf = EH;
L_Rotate(&(*T)->lchild);//不能写L_Rotate(lc);采用的是引用参数
R_Rotate(T);
break;
}
}

/*
对以指针t所指结点为根的二叉树作右平衡旋转处理
包含RR旋转和RL旋转两种情况
*/
void RightBalance(BiTree *T)
{
BiTNode *rc = NULL;
BiTNode *ld = NULL;

rc = (*T)->rchild;
switch(rc->bf)
{
case LH: //RL旋转
ld = rc->lchild;
switch(ld->bf)
{
case LH:
(*T)->bf = EH;
rc->bf = RH;
break;
case EH:
(*T)->bf = EH;
rc->bf = EH;
break;
case RH:
(*T)->bf = LH;
rc->bf = EH;
break;
}
ld->bf = EH;
R_Rotate(&(*T)->rchild);//不能写R_Rotate(rc);采用的是引用参数
L_Rotate(T);
break;

case EH: //deleteAVL需要,insertAVL用不着
(*T)->bf = RH;
rc->bf = LH;
L_Rotate(T);
break;

case RH: //RR旋转
(*T)->bf = EH;
rc->bf = EH;
L_Rotate(T);
break;
}
}

/*
若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个
数据元素为e的新结点,并返回true,否则返回false。若因插入而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否
*/
bool InsertAVL(BiTree *T,int e,bool *taller)
{
if(!(*T)){//插入新结点,树“长高”,置taller为true
printf("e=%d\n",e);
(*T) = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = EH;
*taller = true;
}else{
if(EQ(e,(*T)->data)) //树中已存在和有相同关键字的结点
{
*taller = false;
printf("已存在相同关键字的结点\n");
return false;
}//则不再插入
if(LT(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);
*taller = false;
break;
}
}
}
}
return true;
}

/*
若在平衡的二叉排序树T中存在和e有相同关键字的结点,则删除之
并返回true,否则返回false。若因删除而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量shorter反映T变矮与否
*/
bool deleteAVL(BiTree *T, int key, bool *shorter)
{
if((*T) == NULL) //不存在该元素
{
return false; //删除失败
}
else if(EQ(key, (*T)->data)) //找到元素结点
{
BiTNode *q = NULL;
if((*T)->lchild == NULL) //左子树为空
{
q = (*T);
(*T) = (*T)->rchild;
free(q);
q = NULL;
*shorter = true;
}
else if((*T)->rchild == NULL) //右子树为空
{
q = (*T);
(*T) = (*T)->lchild;
free(q);
q = NULL;
*shorter = true;
}
else //左右子树都存在,
{
q = (*T)->lchild;
while(q->rchild)
{
q = q->rchild;
}
(*T)->data = q->data;//这部分应该是把q的数据复制给(*T)
deleteAVL(&(*T)->lchild, q->data, shorter); //在左子树中递归删除前驱结点
}
}
else if(LT(key, (*T)->data)) //左子树中继续查找
{
if(!deleteAVL(&(*T)->lchild, key, shorter))
{
return false;
}
if(*shorter)
{
switch((*T)->bf)
{
case LH:
(*T)->bf = EH;
*shorter = true;
break;
case EH:
(*T)->bf = RH;
*shorter = false;
break;
case RH:
if((*T)->rchild->bf == EH)//注意这里,画图思考一下
*shorter = false;
else
*shorter = true;
RightBalance(T); //右平衡处理
break;
}
}
}
else //右子树中继续查找
{
if(!deleteAVL(&(*T)->rchild, key, shorter))
{
return false;
}
if(*shorter)
{
switch((*T)->bf)
{
case LH:
if((*T)->lchild->bf == EH)//注意这里,画图思考一下
*shorter = false;
else
*shorter = true;
LeftBalance(T); //左平衡处理
break;
case EH:
(*T)->bf = LH;
*shorter = false;
break;
case RH:
(*T)->bf = EH;
*shorter = true;
break;
}
}
}
return true;
}


/*
*Description: 销毁平衡二叉树
*/
void destroyAVL(BiTree *T)
{
if((*T))
{
destroyAVL(&(*T)->lchild);
destroyAVL(&(*T)->rchild);
free((*T));
(*T) = NULL;
}
}


//前序遍历
void preOrderTraverse(BiTree t)
{
if(t)
{
printf("t->data=%d\n",t->data);
preOrderTraverse(t->lchild);
preOrderTraverse(t->rchild);
}
}

//中序遍历
void inOrderTraverse(BiTree t)
{
if(t)
{
inOrderTraverse(t->lchild);
printf("t->data=%d\n",t->data);
inOrderTraverse(t->rchild);
}
}

//后续遍历
void postOrderTraverse(BiTree t)
{
if(t)
{
postOrderTraverse(t->lchild);
postOrderTraverse(t->rchild);
printf("t->data=%d\n",t->data);
}
}
/*
Description:
在根指针t所指平衡二叉树中递归地查找某关键字等于key的数据元素,
若查找成功,则返回指向该数据元素结点的指针,否则返回空指针。
根据需要,也可以返回一个bool值
*/
BiTNode* searchAVL(BiTree *t, int key)
{
if(((*t) == NULL)||EQ(key,(*t)->data))
return (*t);
else if LT(key,(*t)->data) /* 在左子树中继续查找 */
return searchAVL(&(*t)->lchild,key);
else
return searchAVL(&(*t)->rchild,key); /* 在右子树中继续查找 */
}

//按树状打印输出二叉树的元素,m表示结点所在层次,初次调用时m=0
void PrintBST(BiTree *T,int m)
{
int i;
if((*T)->rchild)
PrintBST(&(*T)->rchild,m+1);
for(i = 1; i<=m; i++)
printf(" ");//打印 i 个空格以表示出层次
printf("%d\n",(*T)->data);//打印 T 元素,换行
if((*T)->lchild)
PrintBST(&(*T)->lchild,m+1);
}


int main(int argc ,char *argv[])
{
BiTree T = NULL;
bool taller;
int i = 0;
int xx_t[] = {1 ,2, 3, 5 ,7, 8 ,10 ,13 ,15 ,17 ,20 ,23 ,30 ,31 ,50 ,60 ,0};
for(i = 0;i < sizeof(xx_t)/sizeof(int);i++){
InsertAVL(&T,xx_t[i],&taller);
}
inOrderTraverse(T);
printf("++++\n");
destroyAVL(&T);
printf("++++\n");
inOrderTraverse(T);
return 0;
}

 
posted on 2022-09-25 17:56  背影_墨白  阅读(610)  评论(0编辑  收藏  举报