7. 数据结构进阶七平衡二叉树
“金钱的贪求(这个毛病,目前我们大家都犯得很凶)和享乐的贪求,促使我们成为它们的奴隶,也可以说,把我们整个身心投入深渊。唯利是图,是一种痼疾,使人卑鄙,但贪求享乐,更是一种使人极端无耻,不可救药的毛病。 —郎加纳斯”
我们来看下传说中的平衡二叉树
1. 平衡二叉树
平衡二叉树(BalancedBinary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法平衡二叉树的常用算法有红黑树、AVL、Treap等。 最小二叉平衡树的节点的公式如下F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。
2. 代码实现
2.1 main
输入1进行调用CreatBST函数来创建平衡二叉树,输入2 调用SearchBST函数进行查找,输入3调用InsertAVL函数进行插入,输入4调用DeleteAVL进行删除,输入5退出。
输入其他则提出输入错误,请重新选择。
简单图1示如下
2.2 R_Rotate(BSTree &p)
//对以*p为根的二叉排序树作右旋处理
2.3 L_Rotate(BSTree &p)
//对以*p为根的二叉排序树作左旋处理
2.4 LeftBalance(BSTree &T)
该函数实现对以指针T所指结点为根的二叉树作左平衡旋转处理
输入的那个节点的平衡度为1,但是又再其左边插入了一个节点,使失去了平衡。
获得该节点的子节点。判断该子节点的平衡度。
如果子节点平衡度为1则,将该节点和子节点的平衡度设置为0,然后调用R_Rotate函数进行单向右旋处理(LL型)。
如果子节点平衡度为-1则,先进行左旋,然后右旋处理(双向旋转 LR型)。
2.5 R_Rotate
为根的二叉排序树作右旋处理。
2.6 L_Rotate
为根的二叉排序树作左旋处理。
2.7 RightBalance(BSTree &T)
//对以指针T所指结点为根的二叉树作右平衡旋转处理
同LeftBalance函数。有两种类型。(RR型和RL型)。
2.8 InsertAVL(BSTree &T,inte,bool &taller)
如果为空,则创建节点,插入成功。
如果不为空,
则判断是否在平衡二叉树中已经存在。如果存在则设置taller=false.然后退出返回0。
如果再平衡二叉树中不存在,则判断插入的值与根节点大小,如果小于则递归调用InsertAVL函数从左节点开始,如果大于则递归调用InsertAVL函数从右节点插入。(最后是肯定要一个为空的节点上进行插入的,不存在替换的情况,如果替换说明相等,但是相等时不会插入的)
如果插入成功,则设置taller为true,然后计算平衡度。
平衡度是倒退。在左子节点插入后,判断当前该节点的平衡值,如果之前是LH,说明原本是左边高现在又往左边插入,需要调用leftbalance函数进行处理。其他情况类似,如果再在右子节点插入后,判断当前该节点的平衡值,如果之前是RH,说明原本是右边高现在又往右边插入,也需要调用RightBalance函数进行处理
2.9 SearchBST(BSTree &T,intkey)
该函数用户查找元素key是否在树T中
存在则返回,否则判断是否递归进行左子树后者右子树查询。
2.10 CreatBST(BSTree &T)
以输入-1为二叉树建立的结束。
然后调用InsertAVL函数插入到平衡二叉树中。
2.11 LeftBalance_div(BSTree&p,int &shorter)
//删除结点时左平衡旋转处理
2.12 RightBalance_div(BSTree&p,int &shorter)
//删除结点时右平衡旋转处理
2.13 Delete(BSTree q,BSTree &r,int &shorter)
//删除结点
如果删除节点的右子节点的右子节点为空,则直接将右子节点提升上来,将右子节点的的左子节点放置到之前右子节点的位置即可。
如果删除节点的右子节点的右子节点不为空,则递归调用Delete函数,然后调用RightBalance_div。
2.14 DeleteAVL(BSTree &p,intx,int &shorter)
对平衡二叉树的删除操作。
如果删除点的右子树为空,则直接重接它的左子树即可。
如果左子树为空个,则重接它的右子树即可。
如果都不为空,则调用Delete函数。最后调用LeftBalance_div。
如果本次没找到则进行即可查找。
2.15 PrintBST
该函数用于输出二叉树。
递归调用进行输出。
3. 源码
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#defineEQ(a,b)((a)==(b))
#defineLT(a,b) ((a)<(b))
#defineLQ(a,b) ((a)>(b))
#defineLH+1 //左高
#defineEH0 //等高
#defineRH-1 //右高
#defineNULL 0
///////////////////////////// 定义结构体 ///////////////////////////
typedefstructBSTNode
{
intdata;
intbf; //结点的平衡因子
structBSTNode*lchild,*rchild;//左、右孩子指针
}BSTNode,*BSTree;
/////////////////////////// 函数声明 ///////////////////////////
void R_Rotate(BSTree&p); //对以*p为根的二叉排序树作右旋处理
void L_Rotate(BSTree&p); //对以*p为根的二叉排序树作左旋处理
void LeftBalance(BSTree&T);//对以指针T所指结点为根的二叉树作左平衡旋转处理
void RightBalance(BSTree&T);//对以指针T所指结点为根的二叉树作右平衡旋转处理
bool InsertAVL(BSTree&T,int e,bool&taller);//插入结点e
bool SearchBST(BSTree&T,int key);//查找元素key是否在树T中
void PrintBST(BSTreeT,int m);//按树状打印输出二叉树的元素
void CreatBST(BSTree&T); //创建平衡二叉树,(注意:以输入-1为二叉树建立的结束)
void LeftBalance_div(BSTree&p,int &shorter);//删除结点时左平衡旋转处理
void RightBalance_div(BSTree&p,int &shorter);//删除结点时右平衡旋转处理
void Delete(BSTreeq,BSTree &r,int &shorter);//删除结点
int DeleteAVL(BSTree&p,int x,int&shorter);//平衡二叉树的删除操作
/////////////////////////////////////////////////////////////////////
void main()
{
intinput,search,m;
booltaller=false;
intshorter=0;
BSTreeT,T1,T2;
T=(BSTree)malloc(sizeof(BSTNode));
T=T1=T2=NULL;
while(1)
{ system(“cls”);
printf(” ******************************************\n”);
printf(” *1.创建\t2.查找\t3.插入\t4.删除\t5.退出*\n”);
printf(” ******************************************\n”);
printf(“请输入您所需的操作功能:\t”);
scanf(“%d”,&input);getchar();
switch(input)
{
case 1:
CreatBST(T);break;
case 2:
printf(“请输入你要查找的关键字“);
scanf(“%d”,&search); getchar();
if(SearchBST(T,search)) printf(“该二叉树中存在关键字%d,查找成功!\n”,search);
else printf(“查找失败!\n”);
break;
case 3:
printf(“请输入你要插入的关键字“);
scanf(“%d”,&search); getchar();
InsertAVL(T,search,taller); m = 0;
PrintBST(T,m);break;
case 4:
printf(“请输入你要删除的关键字“);
scanf(“%d”,&search);getchar();
DeleteAVL(T,search,shorter);
m=0; PrintBST(T,m);break;
case 5:
printf(“\t\tbyebye!\n”);break;
default:printf(“输入错误,请重新选择。“);break;
}
if(input== 5)break;
printf(“\t\t按任意键继续…”);getchar();
}
}
//对以*p为根的二叉排序树作右旋处理
void R_Rotate(BSTree&p)
{
BSTreelc;
lc = p->lchild; //lc指向的*p左子树根结点
p->lchild= lc->rchild;//rc的右子树挂接为*p的左子树
lc->rchild = p;p =lc;//p指向新的结点
}
//对以*p为根的二叉排序树作左旋处理
void L_Rotate(BSTree&p)
{
BSTreerc;
rc = p->rchild; //rc指向的*p右子树根结点
p->rchild= rc->lchild;//rc的左子树挂接为*p的右子树
rc->lchild = p;p =rc;//p指向新的结点
}
//对以指针T所指结点为根的二叉树作左平衡旋转处理
void LeftBalance(BSTree&T)
{
BSTreelc,rd;
lc = T->lchild; //lc指向*T的左子树根结点
switch(lc->bf) //检查*T的左子树的平衡度,并作相应平衡处理
{
caseLH: //新结点插入在*T的左孩子的左子树上,要作单右旋处理
T->bf= lc->bf =EH;
R_Rotate(T); break;
caseRH: //新结点插入在*T的左孩子的右子树上,要作双旋处理
rd =lc->rchild; //rd指向*T的左孩子的右子树根
switch(rd->bf) //修改*T及其左孩子的平衡因子
{
caseLH:T->bf=RH; lc->bf =EH;break;
caseEH:T->bf= lc->bf = EH;break;
caseRH:T->bf=EH; lc->bf =LH;break;
}
rd->bf = EH;
L_Rotate(T->lchild);//对*T的左子树作左旋平衡处理
R_Rotate(T); //对*T作右旋平衡处理
}
}
//对以指针T所指结点为根的二叉树作右平衡旋转处理
void RightBalance(BSTree&T)
{
BSTreerc,ld;
rc = T->rchild; //rc指向*T的左子树根结点
switch(rc->bf) //检查*T的右子树的平衡度,并作相应平衡处理
{
caseRH: //新结点插入在*T的右孩子的右子树上,要作单左旋处理
T->bf= rc->bf =EH;
L_Rotate(T);break;
caseLH: //新结点插入在*T的右孩子的左子树上,要作双旋处理
ld =rc->lchild; //ld指向*T的右孩子的左子树根
switch(ld->bf) //修改*T及其右孩子的平衡因子
{
caseLH:T->bf=EH; rc->bf =RH;break;
caseEH:T->bf= rc->bf =EH;break;
caseRH:T->bf=LH; rc->bf =EH;break;
}
ld->bf = EH;
R_Rotate(T->rchild);//对*T的右子树作左旋平衡处理
L_Rotate(T); //对*T作左旋平衡处理
}
}
//插入结点e,若T中存在和e相同关键字的结点,则插入一个数据元素为e的新结点,并返回1,否则返回0
bool InsertAVL(BSTree&T,inte,bool&taller)
{
if(!T)//插入新结点,树“长高”,置taller为true
{
T =(BSTree)malloc(sizeof(BSTNode));
T->data=e;
T->lchild=T->rchild =NULL;
T->bf=EH;taller =true;
}
else
{
if(EQ(e,T->data)) //树中已存在和有相同关键字的结点
{ taller =false;printf(“已存在相同关键字的结点\n”);return0; }//则不再插入
if(LT(e,T->data)) //应继续在*T的左子树中进行搜索
{
if(!InsertAVL(T->lchild,e,taller))return0;//未插入
if(taller) //已插入到*T的左子树中且左子树“长高”
switch(T->bf) //检查*T的平衡度
{
caseLH: //原本左子树比右子树高,需要作左平衡处理
LeftBalance(T);taller =false;break;
caseEH: //原本左子树、右子等高,现因左子树增高而使树增高
T->bf =LH;taller =true;break;
caseRH: //原本右子树比左子树高,现左、右子树等高
T->bf=EH;taller =false;break;
}//switch(T->bf)
}//if
else //应继续在*T的右子树中进行搜索
{
if(!InsertAVL(T->rchild,e,taller))return0;//未插入
if(taller) //已插入到*T的右子树中且右子树“长高”
switch(T->bf) //检查*T的平衡度
{
caseLH: //原本左子树比右子树高,现左、右子树等高
T->bf=EH;taller =false;break;
caseEH: //原本左子树、右子等高,现因右子树增高而使树增高
T->bf=RH;taller =true;break;
caseRH: //原本右子树比左子树高,需要作右平衡处理
RightBalance(T);taller =false;break;
}//switch(T->bf)
}//else
}//else
return1;
}//InsertAVL
//查找元素key是否在树T中
bool SearchBST(BSTree&T,intkey)
{
if(!T)returnfalse;
elseif(EQ(key,T->data))returntrue;
elseif(LT(key,T->data))returnSearchBST(T->lchild,key);
elsereturnSearchBST(T->rchild,key);
}
//按树状打印输出二叉树的元素,m表示结点所在层次,初次调用时m=o
void PrintBST(BSTreeT,intm)
{
inti;
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);
}
//创建平衡二叉树,(注意:以输入-1为二叉树建立的结束)
void CreatBST(BSTree&T)
{
inte,m;
booltaller=false;
T =NULL;
printf(“\n请输入关键字(以-1结束建立平衡二叉树):”);
scanf(“%d”,&e);getchar();
while(e!= -1)
{
InsertAVL(T,e,taller);
printf(“\n请输入关键字(以-1结束建立平衡二叉树):”);
scanf(“%d”,&e);getchar();taller=false;
}
m=0;
printf(“平衡二叉树创建结束,横向打印出树状结构:\n”);
if(T) PrintBST(T,m);
else printf(“这是一棵空树.\n”);
}
//删除结点时左平衡旋转处理
void LeftBalance_div(BSTree&p,int&shorter)
{
BSTree p1,p2;
if(p->bf==1)//p结点的左子树高,删除结点后p的bf减1,树变矮
{ p->bf=0;shorter=1;}
elseif(p->bf==0)//p结点左、右子树等高,删除结点后p的bf减1,树高不变
{ p->bf=-1;shorter=0;}
else //p结点的右子树高
{
p1=p->rchild;//p1指向p的右子树
if(p1->bf==0)//p1结点左、右子树等高,删除结点后p的bf为-2,进行左旋处理,树高不变
{
L_Rotate(p);
p1->bf=1;p->bf=-1;shorter=0;
}
elseif(p1->bf==-1)//p1的右子树高,左旋处理后,树变矮
{
L_Rotate(p);
p1->bf=p->bf=0;shorter=1;
}
else//p1的左子树高,进行双旋处理(先右旋后左旋),树变矮
{
p2=p1->lchild;
p1->lchild=p2->rchild;p2->rchild=p1;p->rchild=p2->lchild;p2->lchild=p;
if(p2->bf==0)
{p->bf=0;p1->bf=0; }
elseif(p2->bf==-1)
{p->bf=1;p1->bf=0;}
else
{p->bf=0;p1->bf=-1;}
p2->bf=0; p=p2;shorter=1;
}
}
}
//删除结点时右平衡旋转处理
void RightBalance_div(BSTree&p,int&shorter)
{
BSTree p1,p2;
if(p->bf==-1)
{ p->bf=0;shorter=1;}
elseif(p->bf==0)
{ p->bf=1;shorter=0;}
else
{
p1=p->lchild;
if(p1->bf==0)
{
R_Rotate(p);
p1->bf=-1; p->bf=1;shorter=0;
}
elseif(p1->bf==1)
{
R_Rotate(p);
p1->bf=p->bf=0;shorter=1;
}
else
{
p2=p1->rchild;
p1->rchild=p2->lchild; p2->lchild=p1;p->lchild=p2->rchild;p2->rchild=p;
if(p2->bf==0)
{ p->bf=0;p1->bf=0; }
elseif(p2->bf==1)
{ p->bf=-1;p1->bf=0; }
else
{ p->bf=0;p1->bf=1; }
p2->bf=0; p=p2;shorter=1;
}
}
}
//删除结点
void Delete(BSTreeq,BSTree &r,int&shorter)
{
if(r->rchild==NULL)
{
q->data=r->data;q=r;
r=r->lchild;free(q);
shorter=1;
}
else
{
Delete(q,r->rchild,shorter);
if(shorter==1)
RightBalance_div(r,shorter);
}
}
//平衡二叉树的删除操作
int DeleteAVL(BSTree&p,intx,int&shorter)
{
intk;
BSTreeq;
if(p==NULL) { printf(“不存在要删除的关键字!!\n”);return0;}
elseif(x<p->data)//在p的左子树中进行删除
{
k=DeleteAVL(p->lchild,x,shorter);
if(shorter==1)
LeftBalance_div(p,shorter);
returnk;
}
elseif(x>p->data)//在p的右子树中进行删除
{
k=DeleteAVL(p->rchild,x,shorter);
if(shorter==1)
RightBalance_div(p,shorter);
returnk;
}
else
{
q=p;
if(p->rchild==NULL)//右子树空则只需重接它的左子树
{p=p->lchild;free(q);shorter=1; }
elseif(p->lchild==NULL)//左子树空则只需重接它的右子树
{p=p->rchild;free(q);shorter=1; }
else//左右子树均不空
{
Delete(q,q->lchild,shorter);
if(shorter==1)
LeftBalance_div(p,shorter);
p=q;
}
return1;
}
}