二叉排序树可以完美地生成一个二叉树,但是会有一种极端的情况。就是会生成一棵斜树,这种情况下,树的深度很大,会和节点数一样大。那么查找一个元素的算法复杂度达到O(n)。这种效率比较低,如果生成一棵类似于一棵完全二叉树,则树的深度为[log n]+1。那么查找的复杂度就将为O(log n)了。
这里怎么生成这个平衡二叉树呢?这就需要在每次插入节点的时候先要对树形进行判断,看是否破坏了树的结构。
树形的判断依据又是什么呢?
我们首先需要对平衡二叉树进行一个定义,所谓的平衡二叉树就是属于二叉排序树的一种,每个节点的左子树与右子树的高度差的最大值为1.
为了形象地描述这一点,这里引入一个概念,平衡因子(Balance Factor),就是二叉树节点上左子树的深度减去右子树的深度,作为平衡二叉树,这个平衡因子的取值只有-1,0,1。接下来我们定义平衡二叉树的数据结构,写入avl.h中:
#ifndef _BITREE_AVL
#define _BITREE_AVL
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<time.h>
typedef struct BiTNode{
int data;
int bf;
struct BiTNode *lchild,*rchild;
}BiTNode,*Bitree;
#define max(a,b) a>b?a:b
#endif
函数定义,function.h。
#ifndef AVL_FUNC_
#define AVL_FUNC_
/*Average Binary Tree*/
#define LH +1
#define EH 0
#define RH -1
void R_Rotate(Bitree *P);
void L_Rotate(Bitree *P);
void LeftBalance(Bitree *P);
void RightBalance(Bitree* T);
int insertAVL(Bitree *T,int e,int *taller);
int DeleteAVL(Bitree *T, int key, int &shorter);
int search(Bitree *P,int e);
int getHeight(Bitree P);
#endif
插入节点后,发现二叉树不平衡了,但是怎么调整呢?不可能是考虑怎么调整整个二叉树。这里只需调整这局部不平衡树,消除局部不平衡因子,整个二叉树也就平衡了。找出这个局部不平衡树,就是最小不平衡子树。就是距离插入节点最近,平衡因子的绝对值大于1的节点为根的子树。
我们队最小不平衡子树的操作有两种,分别是左旋与右旋。
这里先考虑最简单的情况,新插入的节点均在左子树或者右子树一边。这种情况下,平衡因子的符号都一致,要么都正,要么都负,在左子树的就做右旋操作,在右子树的就做左旋操作。写入algorithm.c文件中,其左右旋的实现代码如下:
void R_Rotate(Bitree *P)
{
Bitree L;
L=(*P)->lchild;
(*P)->lchild=L->rchild;
L->rchild=(*P);
*P=L;
}
void L_Rotate(Bitree *P)
{
Bitree R;
R=(*P)->rchild;
(*P)->rchild=R->lchild;
R->lchild=(*P);
*P=R;
}
对于上面的代码可以这样理解,从插入节点到最小不平衡节点,这里称和插入节点相连的节点为旋转节点,最小不平衡节点为根。
左旋 带 旋转节点的左子树给最小不平衡节点的右子树,旋转节点成为新的根节点,最小不平衡节点成为左子树;
右旋 带 旋转节点的右子树给最小不平衡节点的左子树,旋转节点成为新的根节点,最小不平衡节点成为右子树。
考虑完最简单的情况,让我们考虑下复杂点儿的,世界上的事情啊。或许就是这个样子,就如前几天的高考的那个学霸说的那样“爱情像英语这么简单就好了~”。
咱们进入正题,这次的情况是这样的,插入的节点是最小不平衡节点的左子树的右子树,这种情况下,出现了平衡因子有正有负的情况。这种情况下就不能是简单的左旋或者右旋了。首先得调整平衡因子,使其符号一致。首先查看最小不平衡节点的左子树的右子树这种情况,先以最小不平衡节点的左子树为根,以插入节点为旋转节点,这样是在右侧,进行左旋,将所有节点转到左侧,然后平衡因子一致。然后得到新的最小不平衡树,就和上面提到的最简单的情况一致,进行右旋即可使二叉树平衡。然后最小不平衡节点的右子树的左子树,这种情况与第一种对称,先右旋然后再左旋。
有了上面的思路,就有了下面的代码,添加下面的代码到algorithm.c中:
void LeftBalance(Bitree *P)
{
Bitree L,Lr;
L=(*P)->lchild;
switch(L->bf)
{
case LH:
(*P)->bf=L->bf=EH;
R_Rotate(P);
break;
case RH:
Lr=L->rchild;
switch(Lr->bf){
case LH:
(*P)->bf=RH;
L->bf=EH;
break;
case EH:
(*P)->bf=L->bf=EH;
break;
case RH:
(*P)->bf=EH;
L->bf=LH;
break;
}
Lr->bf=EH;
L_Rotate(&(*P)->lchild);
R_Rotate(P);
}
}
void RightBalance(Bitree* P)
{
Bitree L,Ll;
L= (*P)->rchild;
switch (L->bf)
{
case RH:
(*P)->bf = L->bf = EH;
L_Rotate(P);
break;
case LH:
Ll = L->lchild;
switch(Ll->bf)
{
case LH:
(*P)->bf = EH;
L->bf = RH;
break;
case EH:
(*P)->bf = L->bf = EH;
break;
case RH:
(*P)->bf = EH;
L->bf = LH;
break;
}
Ll->bf = EH;
R_Rotate(&(*P)->rchild);
L_Rotate(P);
break;
}
}
有了上面的这些调整二叉树的操作,我们就可以生成一棵平衡二叉树了:
int insertAVL(Bitree *T,int e,int *taller)
{
if(!*T)
{
*T=(Bitree)malloc(sizeof(BiTNode));
(*T)->data=e;
(*T)->lchild=(*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){
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;
}
对平衡二叉树的操作,搜索和删除:
int search(Bitree *P,int e)
{
if(!*P)
return 0;
if((*P)->data==e)
return 1;
else if((*P)->data<e)
return search(&(*P)->lchild,e);
else
return search(&(*P)->rchild,e);
}
int DeleteAVL(Bitree *T, int key, int &shorter){
if (!T)
{
shorter = false;
return 0;
}
else
{
if(key == (*T)->data)
{
Bitree q = *T;
if (!(*T)->lchild)
{
q = *T;
*T = (*T)->rchild;
free(q);
shorter = 1;
return 1;
}
else if (!(*T)->rchild){
q = *T;
*T= (*T)->lchild;
free(q);
shorter = 1;
return 1;
}
else {
Bitree s = (*T)->lchild;
while(s->rchild)
s = s->rchild;
(*T)->data = s->data;
key = s->data;
}
}
if (key < (*T)->data){
if (!DeleteAVL(&(*T)->lchild, key, 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)
shorter = 0;
else
shorter = 1;
break;
}
}
}
else{
if (!DeleteAVL(&(*T)->rchild, key, 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 1;
}
然后测试平衡二叉树的生成,并进行遍历。
#include"avl.h"
#include"function.h"
#define DEBUGPRINT printf("file_name:%s;\ncurrent_line:%d;\ncompile_time:%s:%s;\n\nprogram start...:\n",__FILE__,__LINE__,__DATE__,__TIME__)
void midordertraverse(Bitree T)
{
if(T==NULL)
return ;
midordertraverse(T->lchild);
printf("%d\t",T->data);
midordertraverse(T->rchild);
}
int main()
{
DEBUGPRINT;
Bitree AVL=NULL;
int taller;
srand(time(0));
printf("please input treenode number:\n");
int n,i;
scanf("%d",&n);
for(i=0;i<=n;i++){
insertAVL(&AVL,rand()%1000,&taller);
}
midordertraverse(AVL);
printf("\n");
}
然后我们对生成的两棵树的深度进行对比,节点为20的二叉排序树与二叉平衡树的深度结果如下: