平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
整篇文章基于自己对代码的理解,如果有错误希望大家不吝赐教,同时希望看到的人给点意见!刚开始写博客,希望自己能坚持下来。谢谢大家!
给出几个概念:
bf :即平衡因子,bf=左子树的高度减去右子树的高度。
讲解平衡二叉树,首先应该认识一下平衡二叉树的旋转问题,这是平衡二叉树的关键。
旋转的基本要求
1保持中序有序性。
2保证旋转后二叉树平衡。
1 右旋转
首先给出示意图,直观的理解右旋转,何谓以右,右就是A以B为支点向右旋转“120”度。
那么为什么进行右旋转呢?很简单,递归插入一个结点后,回溯的过程中,有一个结点的A->bf由1变成2且A的左子树B的B->bf==1,这时候A的左子树高度比右子树高度高二,如何使以A结点的二叉树平衡呢?很显然,如果我们想办法把A往下‘压’,把B‘提’上来,我们就可以是以A结点二叉树再次平衡,移动的结果就像图a中第二个图所示,至于指针是如何移动的,看代码即可。
在此先把二叉树中插入函数的声明给出来,
int InsertAVL(BSTree &T,ElemType e,int &taller)
解释一下,在以T为根节点的二叉树中插入e, taller值记录了插入e结点后的以T为根节点的二叉树高度是否增加(理解taller的含义对理解整个递归函数的意义极其重要),若增加,taller=1,否则,taller=0。所以,此时进行二叉树右旋转的条件就是回溯到A结点时,A结点的bf=1,且taller=1,写这个纯属为了增加对函数的理解,如果在这看不懂,请看函数代码。
2 左旋转
右旋转讲过之后,左旋转就不必赘述了吧~直接上图。
条件还是说一下,从B结点回溯到A结点后,bf等于-1,且taller=1。
3 先左后右旋转
书上讲所有的先做后右当作一种情况,在这我把他分成两种情况,以便增加函数的理解。
一种是插入节点在c的子树中情况,这样在插入某个结点后,c->bf等于1或者-1.
曾经想过很多次为什么要做这样的旋转,试了很多办法证明,发现证不出来什么。
但有一个东西看着很明显,把A中序遍历写出来,发现是B(L) B C(L) C C(R) A A(R),C正好在正中间。
另一种是插入节点就是c结点,此时c->bf=0。
然后再讲解什么是谁先左,谁后右的问题。由图示可知应该把c提到最上面,左旋转是B绕c左旋转,然后A绕C右旋转。
4 先右后左旋转
理解同上。
#include<stdio.h>
#include<malloc.h>
#define LH +1
#define EH 0
#define RH -1
#define TRUE 1
#define FALSE 0
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))
#define LEN 16 //LEN表示二叉平衡树中插入元素的个数
typedef struct //结构体,方便起见,只保存了关键字
{
int key;
}ElemType;
typedef struct BSTNode
{
ElemType data;
int bf; //平衡因子
BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
void R_Rotate(BSTree &p) //对以p为根的二叉树进行右旋转
{
BSTree lc = p->lchild;
p->lchild = lc->rchild;
lc->rchild = p;
p = lc;
}
void L_Rotate(BSTree &p) //对以p为根的二叉树进行左旋转
{
BSTree rc = p->rchild;
p->rchild = rc->lchild;
rc->lchild = p;
p = rc;
}
void LeftBalance(BSTree &T)
//对以T为根的二叉树进行左平衡处理,
//条件是当函数回溯到某个结点时,这个节点的bf=1,且taller=1。
{
BSTree lc = T->lchild;
switch(lc->bf){
case LH: //对应只需要右旋转的情况
T->bf = lc->bf = EH;
R_Rotate(T);
break;
case RH: //对应需要先左后右的情况
BSTree rd = lc->rchild;
switch(rd->bf){
case LH: //对应先左后右情况1中的插在C的左子树
lc->bf = EH;
T->bf = RH;
break;
case EH: //对应先左后右的情况2
lc->bf = EH;
T->bf = EH;
break;
case RH: //对应先左后右情况1中的插在C的右子树
lc->bf = LH;
T->bf = EH;
break;
}//switch
rd->bf = EH;
L_Rotate(T->lchild);//对应图中对B进行左旋转
R_Rotate(T); //对应图中对A进行右旋转
break;
}
}
void RightBalance(BSTree &T)
{
BSTree rc = T->rchild;
switch(rc->bf){
case RH:
T->bf = rc->bf = EH;
L_Rotate(T);
break;
case LH:
BSTree ld = rc->lchild;
switch(ld->bf){
case LH:
T->bf = EH;
rc->bf = RH;
break;
case EH:
T->bf = rc->bf = EH;
break;
case RH:
T->bf = LH;
rc->bf = EH;
break;
}//swithc(ld->bf)
ld->bf = EH;
R_Rotate(T->rchild);
L_Rotate(T);
break;
}//switch(rc->bf)
}
int InsertAVL(BSTree &T,ElemType e, int &taller)
{
if(!T){
T = (BSTree)malloc(sizeof(BSTNode));
T->data = e;
T->lchild = NULL;
T->rchild = NULL;
taller = TRUE;
T->bf = EH;
}
else{
if(EQ(T->data.key,e.key)){ //如果存在与插入值相等的元素,插入失败。
taller = FALSE;
return 0;
}
if(LT(e.key,T->data.key)){
if(!InsertAVL(T->lchild, e, taller) )//如果插入元素比T->data小 插在T的左子树中,
return 0;
if(taller) //插入后,对该节点进行是否需要进行平衡处理的检查
switch(T->bf){
case LH: //在T的左子树中插入该节点后,taller=1,且T->bf=1,需要进行平衡处理,处理后taller=0
LeftBalance( T );
taller = FALSE;
break;
case EH: //在T的左子树中插入该节点后,taller=1,且T->bf=0,不需要进行平衡处理,仅将T->bf改为LH即可,加入后taller=1
T->bf = LH;
taller = TRUE;
break;
case RH: //在T的左子树中插入该节点后,taller=1,且T->bf=-1,不需要进行平衡处理,此时左右子树平衡,taller=0
T->bf=EH;
taller = FALSE;
break;
}//switch(bf)
}//if
else{
if(!InsertAVL(T->rchild, e, taller))
return 0;
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;
}//switch
}//else
}
}
void PreOrderPrint(BSTree T)
{
if(T)
{
printf("%d ", T->data.key);
PreOrderPrint(T->lchild);
PreOrderPrint(T->rchild);
}
}
void InOrderPrint(BSTree T)
{
if(T)
{
InOrderPrint(T->lchild);
printf("%d ", T->data.key);
InOrderPrint(T->rchild);
}
}
int main()
{
int a[LEN] = {100,75,50,150,25,120,200,10,30,60,80,110,130,180,5,15};
BSTree T = NULL;
ElemType e[LEN];
int i;
for(i=0; i < LEN; i++)
e[i].key = a[i];
int taller = 0;
int shorter = 0;
for(i=0; i < LEN; i++)
InsertAVL(T, e[i], taller);
InOrderPrint(T);
printf("\n");
PreOrderPrint(T);
printf("\n");
return 1;
}