平衡二叉树

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(2)左右子树仍然为平衡二叉树.
平衡因子:平衡因子bf=左子树深度-右子树深度,
每个结点的平衡因子只能是1,0,-1。若其绝对值超过1,则该二叉排序树就是不平衡的。增加一个元素后,平衡二叉树有可能变成不平衡了,所以需要旋转平衡调整。
平衡二叉树算法思想:
若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树。假设用A表示失去平衡的最小子树的根结点,则调整该子树的操作可归纳为下列四种情况:(可以断定插入一个节点一定是在叶子节点上)
{
*taller=0;
return 0;
}
if(data < T->data)
{
if(!InsertAVL(T->lchild,data,taller))
return 0;
if(*taller)
switch(T->bf)
{
case LH://插入前做左子树比右子树高,插入后,左子树已经长高,排序树失去平衡
*taller=0;
}
}
}
return 1;
}
2、判断是否需要平衡化
if (current->bf == -2) L_Balance(c_root);
else if (current->bf == 2) R_Balance(c_root);
3、是否要继续向上修改父节点的平衡因子
(1)插入节点时if (!current->bf) break;这时,以current为根的子树的高度和插入前的高度相同。
*taller=0;
break;
case EH: //这里标识有所长高实际上此时父节点或者祖父节点的平衡因子的绝对值已经大于1
T->bf=RH;
*taller=1;
break;
case RH: //插插入前做右子树比左子树高,插入后,右子树已经长高,排序树失去平衡
RightBalance(T); //对排序树进行右平衡操作
5.查找比较关键字序列:设含有n个节点的平衡二叉树,1.求出其最大高度,和叶子节点最小层数,则比较次数最大为最大高度值其一定能找到该节点(存在),所以序列个数一定小于最大高度大于或等于叶子节点最小层数2.符合二叉排序树排序,用此排除一些选项
五、相关代码实现
1.基本结构体及变量
#define LH +1 //左高
};
2.右旋操作:
void R_Rotate(AVLNode *&p)//LL型算法
{
AVLNode *lc=p->lchild; // lc指向p的左子树根结点
p->lchild=lc->rchild; // lc的右子树挂接为p(之前跟节点)的左子树
lc->rchild=p;
pBiblioteka Baidulc; // p指向新的根结点
{
AVLNode *rc,*rd;
rc=T->rchild;
switch(rc->bf)
{
case RH:
T->bf=rc->bf=EH;
L_Rotate(T);
break;
case LH:
rd=rc->lchild;
switch(rd->bf)
{
case RH:
T->bf=LH;
rc->bf=EH;
4.对树进行左平衡操作:
void LeftBalance(AVLNode *&T)
{
AVLNode *lc,*rd;
lc=T->lchild; // lc指向T的左子树根结点
switch(lc->bf)//1为左高、0等高、-1为右高
{
case LH: // 1新结点插入在*T的左孩子的左子树上,要作单右旋处理
(1)插入节点时current->bf += (current->data > *p)?1:-1;
(2)删除节点时current->bf -= (current->data > *p)?1:-1;
(3)current指向插入节点或者实际删除节点的父节点,这是普通二叉搜索树的插入和删除操作带来的结果。*p初始值是插入节点或者实际删除节点的data。因为删除操作可能实际删除的不是data。
T->bf=lc->bf=EH;
R_Rotate(T);
break;
case RH: // -1新结点插入在*T的左孩子的右子树上,要作双旋处理
rd=lc->rchild; // rd指向*T的左孩子的右子树根
switch(rd->bf)
{ //根据旋转后的效果去修改T及其左孩子的平衡因子以下右旋转类似
(3)LR型平衡旋转法
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。
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;
}
rd->bf=EH;
L_Rotate(T->lchild);
R_Rotate(T);
}
}
5.对树进行右平衡操作:
void RightBalance(AVLNode *&T)
break;
case EH:
T->bf=rc->bf=EH;
break;
case LH:
T->bf=EH;
rc->bf=RH;
}
rd->bf=EH;
R_Rotate(T->rchild);
L_Rotate(T);
}
}
6.插入操作:
int InsertAVL(AVLNode *&T,int data,int *taller)
插入和删除:
插入删除是互为镜像的操作。我们可以采用前面对二叉排序树的删除操作来进行。然后,在删除掉结点后,再对平衡树进行平衡化处理。删除之所以删除操作需要的平衡化可能比插入时次数多,就是因为平衡化不会增加子树的高度,但是可能会减少子树的高度,在有有可能使树增高的插入操作中,一次平衡化能抵消掉增高;在有可能使树减低的删除操作中,平衡化可能会带来祖先节点的不平衡。AVL树体现了一种平衡的美感,两种旋转是互为镜像的,插入删除是互为镜像的操作,没理由会有那么大的差别。实际上,平衡化可以统一的这样来操作:
(2)删除节点时if (current->bf) break;这时,以current为根的子树的高度和删除前的高度相同
4、当前节点移动到父节点,转1。
p = &(current->data); current = current->parent;
习题分析:
1.含有n个节点的平衡二叉树的最大高度为O(log2n)
{
if(!T) //此时为初始情况或已找到适当位置插入新数据
{
T=(AVLNode *)malloc(sizeof(AVLNode));
T->data=data;
T->lchild=T->rchild=NULL;
T->bf=EH;
*taller=1;
}
else {
if(data == T->data)
2.设Nh表示高度为h的平衡二叉树中含有的最少节点数:有N1=1、N2=2、Nh=Nh-1+Nh-2+1
3.已知节点个数N,有N1=1、N2=2、Nh=Nh-1+Nh-2+1,可以求出树的最大高度,
4.在一棵高度为h(h≧3)的平衡二叉树,最小叶子节点所在层次为h,最小叶子节点所在层次为min(h),显然有,min(1)=1,min(2)=2,min(1)={min(h-1),min(h-2)}+1
(1)LL型平衡旋转法
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。
(2)RR型平衡旋转法
由于在A的右孩子C的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
}
3.左旋操作:
void L_Rotate(AVLNode *&p)//RR型算法
{
AVLNode *rc=p->rchild; // rc指向p的右子树根结点
p->rchild=rc->lchild; // rc的左子树挂接为p的右子树
rc->lchild=p;
p=rc; // p指向新的根结点
}
查找:先按关键字找到删除的节点*p
删除:分为:1.叶子节点:直接删除该节点
2.单分子节点:用*p节点的左边或右孩子节点替代*p
3.双分子节点:用*p节点的左子树的最右下节点*r的值替代*p节点值,用*p
节点的右子数的最左下节点*r的值替代*p节点的值,再删除*r节点
调整:
1、while (current != NULL)修改current的平衡因子。
平衡化靠的是旋转。参与旋转的是3个节点(其中一个可能是外部节点NULL),旋转就是把这3个节点转个位置。
注意的:左旋的时候p->right一定不为空,右旋的时候p->left一定不为空,这是显而易见的。
如果从空树开始建立,并时刻保持平衡,那么不平衡只会发生在插入删除操作上,而不平衡的标志就是出现bf == 2或者bf == -2的节点。
#define EH 0 //等高
#define RH -1 //右高
//平衡二叉树的类型
struct AVLNode
{
int data;
int bf; //bf结点的平衡因子,只能够取0,-1,1,为左子树的深度减去右子树的深度
struct AVLNode *lchild,*rchild; //左、右孩子指针
LeftBalance(T); //对排序树进行右平衡操作
*taller=0;
break;
case EH:
T->bf=LH;
*taller=1; //这里标识有所长高实际上此时父节点或者祖父结点的平衡因子的绝对值已经大于1
break;
case RH: //插入前右子树比左子树高,插入后左右子树深度相等
(4)RL型平衡旋转法
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。
T->bf=EH;
*taller=0; //标志没长高
}
}
else {
if(!InsertAVL(T->rchild,data,taller))
return 0;
if(*taller)
switch(T->bf)
{ //插入前左子树比右子树高,插入后左右子树深度相等
case LH:
T->bf=EH; //标志没长高
二叉树:
左子树都小于根节点,右子树都大于根节点。可以动态维护这棵树(因为是树结构,可以有限步完成插入),所以是动态查找算法。时间复杂度为O(logn)在46.5%的情况下,需要把二叉树平衡化成“平衡二叉树”。
平衡二叉树:定义:平衡二叉树或为空树,或为如下性质的二叉排序树:
(1)左右子树深度之差的绝对值不超过1;
相关文档
最新文档