目录
LL型:(可以这样理解:RR型表示失衡节点的右子树过高,因此要通过左旋来降低右子树的高度)
RR型:(可以这样理解:RR型表示失衡节点的右子树过高,因此要通过左旋来降低右子树的高度)
AVL树:
是平衡二叉查找树的一种,平衡二叉查找树(Self-balancing binary search tree)通常是指一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且任意节点的左右两个子树都是一棵平衡二叉树。
特点:
其本身是一颗二叉查找树
每个结点的左右子树的高度之差的绝对值不超过1。
作用
与二叉查找树类似,但是解决了二叉查找树的极端情况。AVL树会是结点在两侧分布较均匀,而不会偏向某一侧。例如:
预备知识:
平衡因子bf(Balance Factor):
结点左子树的深度减去右子树的深度的值。如图所示:
头节点10的左子树深度为3,右子树的深度也为3,因此头节点的bf值是0
结点5的左子树的深度为1,右子树的深度为2,因此该节点的bf值是-1。
结点11的左子树深度为0,右子树的深度为2,因此该节点的bf值是-2,后面的结点同理。
最小不平衡子树
距离插入结点最近的,且以平衡因子的绝对值大于1的结点为根的子树就是最小不平衡子树。
当插入结点13后,根节点的右子树各个节点的bf值发生了变化:
不难看出,距离插入节点最近且以bf值的绝对值大于1的结点为根的树就是以结点11为根的子树:
因此,这颗就是最小不平衡子树。
那么随着插入的结点的增多,要怎么才能使得结点的两个左右子树的深度之差的绝对值不大于1呢。
每当我们插入了一个新结点的时候都要检查这个节点是否会破坏树的平衡性,若是,就要找出最小不平衡子树,在保证二叉排序树特性的前提下通过对其进行旋转操作来使其平衡(注意:旋转操作针对的都是最小不平衡树)。
旋转
由于本人看到很多书,博客描述旋转都是什么逆时针旋转啊,顺时针旋转啊,为了避免混淆,我直接从代码执行的步骤来描述旋转这个概念。
有四中基本类型(LL,RR,LR,RL四种类型):
LL型:最小不平衡树的左子树的左子树还有非空结点
RR型:最小不平衡树的右子树的右子树还有非空结点
LR型:最小不平衡树的左子树的右子树还有非空结点
RL型:最小不平衡树的右子树的左子树还有非空结点
LL型:(可以这样理解:RR型表示失衡节点的右子树过高,因此要通过左旋来降低右子树的高度)
第一步:把失衡结点的 左子树的右子树 变成 失衡节点的左孩子。(这一步操作是为了保证旋转后的树还能符合二叉树中每个结点最多只能有两个子树的性质)
第二步:把失衡节点作为失衡节点的 右孩子。(此时步骤过后,失衡节点的左孩子就变成了最小不平衡树的根节点了)
有点绕,就结合下面图跟代码会便于理解。
先上代码:
void R_Rotate( BiTree *T )
{
BiTree L = ( *T )->lchild ;
( *T )->lchild = L->rchild ;
L->rchild = ( *T ) ;
( *T ) = L ;
}
最后让失衡结点的左孩子作为根节点:
RR型:(可以这样理解:RR型表示失衡节点的右子树过高,因此要通过左旋来降低右子树的高度)
第一步:把失衡结点的 右子树的左子树 变成 失衡节点的右孩子。
第二步:把失衡节点作为失衡节点的 左孩子。
思路和LL型完全一样,这里就不再赘述,直接上代码
void L_Rotate( BiTree *T )
{
BiTree R = ( *T )->rchild ;
( *T )->rchild = R->lchild ;
R->lchild = ( *T ) ;
( *T ) = R ;
}
至于后面的LR,RL类型就比较麻烦一点了,首先我们对一个LR型的最小不平衡树做多次旋转,看看有什么效果:
由于旋转后变成了RR型,在对其进行做左旋:
最后我们发现:不管一个LR型做多少次左旋和右旋,他会进入一个死循环,最终结果不是LL型,就是RR型,RL型也是如此。
究其原因:
我们发现:失衡节点的bf值的符号与其左孩子的bf的值的符号相反,我们必须先把左孩子的bf值的符号变得与失衡节点的bf值的符号相同(通过对失衡节点的左子树进行旋转)。
LR型:
第一步:(左孩子的右子树太高了)对失衡节点的左子树进行左旋
第二步:对整颗最小不平衡树进行右旋
对失衡节点的左子树进行做平衡示意图:
代码如下:
void LeftBalance( BiTree *T )
{
BiTree L , Lr ;
L = ( *T )->lchild ;
switch( L->bf )
{
case LH://左孩子的bf值是LH,说明左孩子和根节点的bf值的符号相同,做单旋处理
( *T )->bf = L->bf = EH ;
R_Rotate( T ) ;
break ;
case RH://新结点插入在T的左孩子的右子树上,要作双旋处理
Lr = L->rchild ;
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( &L ) ;//对T的左子树作左旋平衡处理
R_Rotate( T ) ;// 对T作右旋平衡处理
}
}
其中外部的switch中的case RH就是对LR型的处理。
这里要解释下嵌套的switch中对不同情况的结点赋值的情况。由于旋转之后我们很难再找到原本最小不平衡子树中的根节点,以及他的左孩子,所以我们就要在旋转之前就修改这些节点的值。
这个是case LH的情况:
其他类型的也都大同小异,这里就不一一列举。
RL型:
第一步:(右孩子的左子树太高了)对失衡节点的右子树进行右旋
第二步:对整颗最小不平衡树进行左旋
直接附上代码:
void RightBalance( BiTree *T )
{
BiTree R , Rl ;
R = ( *T )->rchild ;
switch( R->bf )
{
case RH: //新结点插入在T的右孩子的右子树上,要作单左旋处理
( *T )->bf=R->bf=EH ;
L_Rotate( T ) ;
break ;
case LH: //新结点插入在T的右孩子的左子树上,要作双旋处理
Rl = R->lchild ;
switch( Rl->bf )
{ //修改T及其右孩子的平衡因子
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( &R ) ; //对T的右子树作右旋平衡处理
L_Rotate( T ) ; // 对T作左旋平衡处理
}
}
可惜博主不才,不会AVL删除操作。
求树的深度(对代码测试有用):
int DeepTree( BiTree T )
{
if( T == NULL )
return 0 ;
int ldeep , rdeep , deep = 0 ;
ldeep = DeepTree( T->lchild ) ;
rdeep = DeepTree( T->rchild ) ;
deep = ( ldeep > rdeep ? ldeep : rdeep ) ;
return ( deep + 1 ) ;
}
中序遍历:
void InorderTraverse( BiTree T )
{
if( T == NULL )
return ;
else
{
InorderTraverse( T->lchild ) ;
printf( "%d " , T->data ) ;
InorderTraverse( T->rchild ) ;
}
}
最后附上源代码:
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define LH 1
#define EH 0
#define RH -1
typedef int Status ;
typedef struct BiTNode
{
int data ;
int bf ;
struct BiTNode *rchild , *lchild ;
}BiTNode , *BiTree ;
void R_Rotate( BiTree *T ) ;
void L_Rotate( BiTree *T ) ;
void LeftBalance( BiTree *T ) ;
void RightBalance( BiTree *T ) ;
Status InsertAVL( BiTree *T , int e , Status *taller) ;
void InorderTraverse( BiTree T ) ;
int DeepTree( BiTree T ) ;
int main( void )
{
int i , n , val ;
Status taller ;
BiTree T = NULL ;
printf( "请输入结点的个数:" ) ;
scanf( "%d" , &n ) ;
for( i = 0 ; i < n ; i++ )
{
printf( "请输入第%d个结点的信息:" , i+1 ) ;
scanf( "%d" , &val ) ;
InsertAVL( &T , val , &taller ) ;
printf( "现在AVL的深度是:%d层\n" , DeepTree( T ) ) ;
}
printf( "对AVL进行中序遍历:" ) ;
InorderTraverse( T ) ; printf( "\n" ) ;
return 0 ;
}
void R_Rotate( BiTree *T )
{
BiTree L = ( *T )->lchild ;
( *T )->lchild = L->rchild ;
L->rchild = ( *T ) ;
( *T ) = L ;
}
void L_Rotate( BiTree *T )
{
BiTree R = ( *T )->rchild ;
( *T )->rchild = R->lchild ;
R->lchild = ( *T ) ;
( *T ) = R ;
}
void LeftBalance( BiTree *T )
{
BiTree L , Lr ;
L = ( *T )->lchild ;
switch( L->bf )
{
case LH://左孩子的bf值是LH,说明左孩子和根节点的bf值的符号相同,做单旋处理
( *T )->bf = L->bf = EH ;
R_Rotate( T ) ;
break ;
case RH://新结点插入在T的左孩子的右子树上,要作双旋处理
Lr = L->rchild ;
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( &L ) ;//对T的左子树作左旋平衡处理
R_Rotate( T ) ;// 对T作右旋平衡处理
}
}
void RightBalance( BiTree *T )
{
BiTree R , Rl ;
R = ( *T )->rchild ;
switch( R->bf )
{
case RH: //新结点插入在T的右孩子的右子树上,要作单左旋处理
( *T )->bf=R->bf=EH ;
L_Rotate( T ) ;
break ;
case LH: //新结点插入在T的右孩子的左子树上,要作双旋处理
Rl = R->lchild ;
switch( Rl->bf )
{ //修改T及其右孩子的平衡因子
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( &R ) ; //对T的右子树作右旋平衡处理
L_Rotate( T ) ; // 对T作左旋平衡处理
}
}
Status InsertAVL( BiTree *T , int e , Status *taller )
{
if( ( *T ) == NULL )
{
( *T ) = ( BiTree )malloc( sizeof( BiTNode ) ) ;
( *T )->data = e ;
( *T )->lchild = ( *T )->rchild = NULL ;
( *T )->bf = EH ;
*taller = TRUE ;
}
else
{
if( e == ( *T )->data )
{
*taller = FALSE ; return FALSE ;
}
if( e < ( *T )->data )
{
if( !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( !InsertAVL( &( *T )->rchild , e , taller ) )
return FALSE ;
if( *taller )
{
switch( ( *T )->bf )
{
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 ;
}
void InorderTraverse( BiTree T )
{
if( T == NULL )
return ;
else
{
InorderTraverse( T->lchild ) ;
printf( "%d " , T->data ) ;
InorderTraverse( T->rchild ) ;
}
}
int DeepTree( BiTree T )
{
if( T == NULL )
return 0 ;
int ldeep , rdeep , deep = 0 ;
ldeep = DeepTree( T->lchild ) ;
rdeep = DeepTree( T->rchild ) ;
deep = ( ldeep > rdeep ? ldeep : rdeep ) ;
return ( deep + 1 ) ;
}