平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法 平衡二叉树的常用算法有红黑树、AVL、Treap等。 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。
AVL是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
如何生成一个二叉树呢,我们可以来看看下面这一幅图:
一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是)离插入结点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行的调整规律有四种:
(1)**单向右旋平衡处理**;
使用在插入结点位置为*a的**左子树根结点的左子树**,且树失去平衡。
(2)**单向左旋平衡处理**;
使用在插入结点位置为*a的**右子树根结点的右子树**,且树失去平衡。
(3)**双向旋转(先左后右)平衡处理**;
使用在插入结点位置为*a的**左子树根结点的右子树**,且树失去平衡。
(4)**双向旋转(先右后左)平衡处理**;
使用在插入结点位置为*a的**右子树根结点的左子树**,且树失去平衡。
下面**有图有真相**
下面显示程序代码:
功能有:
(1)插入数组中的元素生成AVL树。
(2)中序遍历AVL树得到正序数据。
(3)查找是否AVL树中是否存在要求的关键字。
(4)不存在要求的关键字的时候选择是否插入关键字到AVL树中。
(5)中序遍历新的AVL树。
#include<iostream>
#include<stdlib.h>
using namespace std;
//测试元素数量
#define N 5
//定义平衡状态
#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 RT(a,b) ((a)>(b))
//数据元素类型定义
struct ElemType
{
int key;
int order;
};
//AVL树的定义
typedef struct AVLNode
{
ElemType data;
int bf; //结点的平衡因子
struct AVLNode *lchild,*rchild; //左右孩子指针
}AVLNode,*AVLTree;
//下面是AVL树常用操作
//单右旋处理
void R_Rotate(AVLTree &p)
{
//对以*p为根的二叉排序树做右旋处理,处理后p指向新的树根节点,即旋转处理之前的左子树的根节点
AVLTree lc;
lc=p->lchild; //lc指向*p的左子树根节点
p->lchild=lc->rchild; //lc的右子树挂接为*p的左子树
lc->rchild=p;
p=lc; //p指向新的根结点
}
//单左旋处理
void L_Rotate(AVLTree &p)
{
//对以*p为根的二叉排序树做左旋处理,处理后p指向新的树根节点,即旋转处理之前的右子树的根节点
AVLTree rc;
rc=p->rchild; //rc指向*p的右子树根节点
p->rchild=rc->lchild; //lc的左子树挂接为*p的右子树
rc->lchild=p;
p=rc; //p指向新的根结点
}
//左平衡旋转处理
void LeftBalance (AVLTree &T)
{
//对以指针T所指结点为根的二叉树做左平衡旋转处理,算法结束后,指针T指向新的根节点
AVLTree lc,rd;
lc=T->lchild; //lc指向*T的左子树根节点
switch (lc->bf)
{
case LH: //新结点插入在*T的左孩子的左子树上,要做单右旋处理。
T->bf=lc->bf=EH;
R_Rotate(T);break;
case RH: //新结点插入在*T的左孩子的右子树上,要做双旋处理
rd=lc->rchild; //rd指向*T的左孩子的右子树根
switch (rd->bf)
{
//修改*T及其左孩子的平衡因子
case LH:
T->bf=RH;
lc->bf=EH;
break;
case EH:
T->bf=lc->bf=EH;
break;
case RH:
T->bf=EH;
lc->bf=LH;
break;
default:
break;
}
//rd->bf=EH;
L_Rotate(T->lchild); // 对*T的左子树做左旋平衡处理
R_Rotate(T); // 对*T做右旋平衡处理
default:
break;
}
}
//右平衡旋转处理
void RightBalance (AVLTree &T)
{
//对以指针T所指结点为根的二叉树做右平衡旋转处理,算法结束后,指针T指向新的根节点
AVLTree rc,rd;
rc=T->rchild; //rc指向*T的右子树根节点
switch (rc->bf)
{
case RH: //新结点插入在*T的右孩子的右子树上,要做单左旋处理。
T->bf=rc->bf=EH;
L_Rotate(T);break;
case LH: //新结点插入在*T的右孩子的左子树上,要做双旋处理
rd=rc->lchild; //rd指向*T的右孩子的左子树根
switch (rd->bf)
{
//修改*T及其右孩子的平衡因子
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;
default:
break;
}
rd->bf=EH;
R_Rotate(T->rchild); //对*T的右子树做右旋平衡处理
L_Rotate(T); // 对*T做左旋平衡处理
default:
break;
}
}
//插入操作
int InsertAVL(AVLTree &T,ElemType e,bool &taller)
{
//若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个数据元素
//为e的新结点,并返回1,否则返回0.若因插入而使二叉排序树失去平衡,则做平衡二叉树
//旋转处理,布尔变量taller反映T长高与否
if(!T)
{
//插入新结点,树长高,置taller为TRUE
T=(AVLTree)malloc(sizeof(AVLNode));
T->data=e;
T->lchild=T->rchild=NULL;
T->bf=EH;
taller=TRUE;
}
else
{
if(EQ(e.key,T->data.key )) //树中已经存在和e相同关键字的结点
{
taller=FALSE;
return 0; //跳出程序,不再插入
}
else if(LT (e.key,T->data.key)) //在左子树寻找
{
if(!InsertAVL(T->lchild,e,taller)) return 0; //递归调用插入函数,无插入则跳出
if(taller) // 已插入到*T的左子树中且左子树“增高”
switch(T->bf) // 检查*T的平衡度
{
case LH: // 原本左子树比右子树高,需要做左平衡处理
LeftBalance(T);taller =FALSE; break;
case EH: // 原本左右子树等高,现在因为左子树增高而使树增高
T->bf=LH; taller=TRUE;break;
case RH: // 原本右子树比左子树高,现在左右子树等高
T->bf=EH;
taller=FALSE;
}
}
else
{
if(!InsertAVL(T->rchild,e,taller)) return 0; //递归调用插入函数
if(taller) // 已插入到*T的左子树中且左子树“增高”
switch(T->bf) // 检查*T的平衡度
{
case LH: // 原本左子树比右子树高,现在左右子树等高
T->bf=EH;
taller=FALSE;
case EH: // 原本左右子树等高,现在因为右子树增高而使树增高
T->bf=RH; taller=TRUE;break;
case RH: // 原本右子树比左子树高,现在做右平衡处理
RightBalance(T);taller =FALSE; break;
}
}
}
return TRUE;
}
//visit 函数
void visit(ElemType e)
{
cout<<"( "<< e.key<<", "<<e.order<<" ) ";
}
//中序遍历二叉树
void InOrderTraverse(AVLTree T,void(*visit)(ElemType e))
{
//二叉树存在;visit函数是对结点进行操作
//对二叉树进行先序遍历,每一个结点有且只有一次调用visit
if(T) //树非空
{
InOrderTraverse(T->lchild,visit); //对左子树进行遍历
visit(T->data);
InOrderTraverse(T->rchild,visit); //对右子树进行遍历
}
}
//查找关键字
int searchAVL(AVLTree &T,int key)
{
//AVLTree p,f;
if(!T)
{
char i;
bool k;
cout<<"没有此关键字"<<endl;
cout<<"是否需要插入此关键字进AVL?(Y/N)"<<endl;
cin>>i;
if(i==89||i==121)
{
ElemType temp;
temp.key=key;
temp.order=N+1;
InsertAVL(T,temp,k);
}
return FALSE;
}
else if(EQ(key,T->data.key))
cout<<"( "<< T->data.key<<", "<<T->data.order<<" ) "<<endl;
else if(LT(key,T->data.key))
searchAVL(T->lchild,key);
else
searchAVL(T->rchild,key);
}
//初始化空树(构造空树)
void InitAVL(AVLTree &T)
{
T=NULL;
}
int main()
{
AVLTree at;
int i,j;
bool k;
ElemType e[N]={{13,1},{24,2},{37,3},{90,4},{53,5}};
InitAVL(at);
for(i=0;i<N;i++)
{
InsertAVL(at,e[i],k);
}
InOrderTraverse(at,visit);
//PreOrderTraverse(at,visit);
cout<<endl;
cout<<"请输入要寻找的关键字"<<endl;
cin>>j;
searchAVL(at,j);
cout<<"重新中序遍历"<<endl;
InOrderTraverse(at,visit);
cout<<endl;
system("pause");
return 0;
}
结果截图
这里代码参考的是上面一幅图——平衡树生成过程的数组。
ps:这里只是讲到AVL树的生成插入过程,删除操作下回细说。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-12 21:16:03