说明
在阅读下列代码时,为了保障能够轻松理解,请务必对指针和二叉排序树数据结构以及平衡原理有较深的了解
思路:
在每次插入结点时,去检查平衡性是否遭到破坏,若是,则进行平衡调整,平衡调整分两种情况,左子树高于右子树(右旋)和 右子树高于左子树(左旋),同时,每一种情况又存在两种情景,可直接旋转和两次旋转
(左旋和右旋代码具有对称性,各位看官好好体会理解)
定义
#include <stdio.h>
#include <stdlib.h>
//状态
typedef int status;
#define TRUE 1
#define FLASE 0
//平衡因子
#define EH 0 //等高
#define LH 1 //左子树高
#define RH -1 //右子树高
//定义一个结构体
typedef struct BiTNode
{
int data; //数据
int bf; //平衡因子
struct BiTNode *lchild,*rchild; //左子树和右子树
}BiTNode,*BiTree;
//中序遍历
void InOrderTraverse(BiTree b){
if(!b){
return;
}
InOrderTraverse(b->lchild);
printf("%d,",b->data);
InOrderTraverse(b->rchild);
}
左旋平衡调整
//左旋
void L_Rotate(BiTree *p){
printf("%d结点进行左旋\n",(*p)->data);
//定义当前结点的右子树
BiTree R;
//赋值
R = (*p)->rchild;
//将R的左子树赋值给当前节点p的右子树
(*p)->rchild = R->lchild;
//将R的左子树指向当前结点p
R->lchild = (*p);
printf("%d结点左旋完毕:",(*p)->data);
*p = R;
InOrderTraverse(*p);
printf("\n");
}
//左旋平衡处理 若b结点和左子树结点平衡因子正负相反 需要进行两次旋转
void LeftBanlance(BiTree *b){
BiTree r,rl;
r = (*b)->rchild;
//判断b的右子树的平衡因子 当和b为正负相反需要进行两次旋转
switch(r->bf){
//左子树高
case LH:
rl = r->lchild;
printf("%d结点的左子树%d结点的平衡因子%d\n",(*b)->data,r->data,r->bf);
//对r的左子树平衡因子进行判断
switch(rl->bf){
//调整平衡时 改变b结点和右子树结点的平衡因子 有以下三种情景
case LH:
(*b)->bf=LH;
r->bf = EH;
break;
case RH:
(*b)->bf=EH;
r->bf=RH;
break;
case EH:
//这种情况在我理解下应该是不会发生
(*b)->bf=r->bf=EH;
break;
}
rl->bf=EH;
R_Rotate(&(*b)->rchild); //先对b的右子树进行右旋
L_Rotate(b); //再对当前结点b进行左旋
break;
//右子树高
case RH:
(*b)->bf=r->bf=LH;
//直接左旋
L_Rotate(b);
break;
}
}
右旋平衡调整
//右旋
void R_Rotate(BiTree *p){
printf("%d结点进行右旋\n",(*p)->data);
//当前结点的左子树
BiTree L;
//赋值
L = (*p)->lchild;
//将L的右子树赋值给当前结点p的左子树
(*p)->lchild = L->rchild;
//将左子树的右子树指向当前结点p
L->rchild = (*p);
printf("%d结点右旋完毕:",(*p)->data);
//*p拿到的是指针地址 根据点重新指向L 完成右旋
*p = L;
InOrderTraverse(*p);
printf("\n");
}
//右旋平衡处理 若b结点和左子树结点平衡因子正负相反 需要进行两次旋转
void RightBanlance(BiTree *b){
BiTree L,Lr;
L = (*b)->lchild;
//判断左子树的平衡因子
switch(L->bf){
//右子树高 符号为负
case RH:
Lr = L->rchild;
printf("%d结点的左子树%d结点的平衡因子%d\n",(*b)->data,L->data,L->bf);
//对r的左子树平衡因子进行判断
switch(Lr->bf){
case LH:
(*b)->bf=RH;
L->bf = EH;
break;
case RH:
(*b)->bf=EH;
L->bf=LH;
break;
case EH:
//这种情况在我的理解下应该是不会发生 欢迎补充
(*b)->bf=L->bf=EH;
break;
}
Lr->bf=EH;
L_Rotate(&(*b)->lchild); //先对b的左子树进行左旋
R_Rotate(b); //再对当前结点b进行右旋
break;
//左子树高 符号为正
case LH:
(*b)->bf=L->bf=RH;
//直接右旋
R_Rotate(b);
break;
}
}
主要函数
//主函数
status InsertAVL(BiTree *b,int data,status *taller) //taller 表示树的高度是否增加
{
//如果当前结点不存在
if(!*b){
printf("%d开辟空间\n",data);
//则开辟内存
*b = (BiTree) malloc(sizeof(BiTNode));
//赋值
(*b)->data = data;
//当前结点左子树右子树赋值为NULL
(*b)->lchild=(*b)->rchild=NULL;
//当前结点的平衡因子为等高
(*b)->bf=EH;
//插入当前结点表示树的高度增加
*taller = TRUE;
}
else
{
//如果值相等不进行插入
if(data==(*b)->data){
printf("%d已经存在不进行插入\n",data);
return FLASE;
}
//如果小于当前结点的值进入左子树
if(data<(*b)->data){
printf("%d进入%d结点的左子树\n",data,(*b)->data);
//进入左子树进行递归添加
if(!InsertAVL(&(*b)->lchild,data,taller) ){
printf("插入失败\n");
//插入失败
return FLASE;
}
//如果树的高度变高
if(*taller){
//判断当前结点平衡因子
switch((*b)->bf){
//原本左子树高,现在左子树增高
case LH:
//进行右旋平衡处理
RightBanlance(b);
*taller = FLASE;
break;
//原本右子树高,现在左子树增高
case RH:
(*b)->bf = EH;
*taller = FLASE;
break;
//原本左右子树等高,现在左子树增高
case EH:
(*b)->bf = LH;
*taller = TRUE;
break;
}
printf("%d结点平衡因子:%d\n",(*b)->data,(*b)->bf);
}
}
//如果大于当前结点的值进入右子树
else
{
printf("%d进入%d结点的右子树\n",data,(*b)->data);
//进入右子树进行递归添加
if(!InsertAVL(&(*b)->rchild,data,taller) ){
//插入失败
return FLASE;
}
//如果树的高度变高
if(*taller){
//判断当前结点平衡因子
switch((*b)->bf){
//原本左子树高,现在右子树增高
case LH:
(*b)->bf = EH;
*taller = FLASE;
break;
//原本右子树高,现在右子树增高
case RH:
//进行左旋平衡处理
LeftBanlance(b);
*taller = TRUE;
break;
//原本左右子树等高,现在右子树增高
case EH:
(*b)->bf = RH;
*taller = TRUE;
break;
}
printf("%d结点平衡因子:%d\n",(*b)->data,(*b)->bf);
}
}
}
printf("%d结点执行完毕\n========================================================\n",(*b)->data);
return TRUE;
}
//main函数
void main(){
//printf("%d",malloc(sizeof(BiTNode)));
//BiTree b = (BiTree)malloc(sizeof(BiTNode));
int a[20]={14,45,21,20,99,35,3,1,7,25,43,75,34,63,8123,52,28,1234,64,234};
BiTree b = NULL;
status taller;
for(int i=0;i<20;i++){
InsertAVL(&b,a[i],&taller);
}
InOrderTraverse(b);
}