目录
定义:什么叫平衡二叉树
是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵树,所以它又叫AVL树。平衡二叉树要求对于每一个节点来说,它的左右子树的高度之差不能超过1,如果插入或者删除一个节点使得高度之差大于1,就要进行节点之间的旋转,将二叉树重新维持在一个平衡状态。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
定义及原理
现在又a[8] = {1,2,3,4,5,6,7,8}需要构建二叉排序树。在没有学习平衡二叉树之前,根据二叉排序树的特性,通常会将它构建成如下左图。虽然完全符合二叉排序树的定义,但是对这样高度达到8的二叉树来说,查找是非常不利的。因此,更加期望构建出如下右图的样子,高度为4的二叉排序树,这样才可以提供高效的查找效率。
不平衡的四种情况
- 6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。
- 6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。
- 2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。
- 2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。
从图3中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
AVL数据结构
public class AVLTree {
private class Node{
int value;
int bf;
Node lchild;
Node rchild;
Node parent;
public Node(int value,int bf,Node lchild,Node rchild,Node parent){
this.value=value;
this.bf=bf;
this.lchild=lchild;
this.rchild=rchild;
this.parent=parent;
}
}
Node root=null; //根节点
final int LH =+1; /* 左高 */
final int EH= 0; /* 等高 */
final int RH=-1; /* 右高 */
final int LC=0; //在左子树插入
final int RC=1; //在右子树插入
final boolean FALSE=false;
final boolean TRUE=true;
}
旋转操作
单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的左子树X子树,所以属于左左情况。
开始时,k2节点左孩子是小x,整棵树还是平衡树,然后在小x的子节点插入一个数,小x变成大X,此时大X高度为2,k1节点不平衡,为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。
这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根节点的路径就不需要继续旋转了。
右旋转代码:
/* 对以T为根的二叉排序树作右旋处理 */
/* 处理之后T的父节点指向T的左节点 */
//右旋-顺时针旋转(如LL型就得对根结点做该旋转)
private void R_Rotate(Node T)
{
Node L,P;
P=T.parent;
L=T.lchild; /* L指向node的左子树根结点 */
T.lchild=L.rchild; /* L的右子树挂接为node的左子树 */
if(L.rchild!=null)
L.rchild.parent=T;
L.rchild=T;
L.parent=P;
T.parent=L;
if(P==null)
root=L;
else if(P.rchild==T)
P.rchild=L;
else
P.lchild=L;
}
右旋转原理:获取失去平衡结点以及左结点,为了让lchild作为根节点,将lchild的rchild挂接到之前左结点上,然后在挂接到s->rchild.
左旋转代码
/* 对以T为根的二叉排序树作左旋处理, */
/* 处理之后T的父节点指向T的右节点 */
//左旋-逆时针旋转(如RR型就得对根结点做该旋转)
private void L_Rotate(Node T)
{
Node R,P;
P=T.parent;
R = T.rchild; /* R指向T的右子树根结点 */
T.rchild = R.lchild; /* R的左子树挂接为T的右子树 */
if(R.lchild!=null)
R.lchild.parent=T;
R.lchild = T;
R.parent=P;
T.parent=R;
if(P==null)
root=R;
else if(P.rchild==T)
P.rchild=R;
else
P.lchild=R;
}
双旋转
对于左右和右左这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。
为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次z左旋转,旋转之后就变成了左左情况,所以第二步再进行一次右旋转,最后得到了一棵以k2为根的平衡二叉树树。
双旋转代码
/* 对以指针T所指结点为根的二叉树作左平衡旋转处理 */
/* 本算法结束时,指针T指向新的根结点 */
public void LeftBalance(Node T)
{
Node L,Lr;
L = T.lchild; /* L指向T的左子树根结点 */
switch(L.bf)
{
/* 检查T的左子树的平衡度,并作相应平衡处理 */
case LH: /* 新结点插入在T的左孩子的左子树上,要作单右旋处理 */
T.bf=L.bf=EH;
R_Rotate(T);
break;
case RH: /* 新结点插入在T的左孩子的右子树上,要作双旋处理 */ //
Lr=L.rchild; /* Lr指向T的左孩子的右子树根 */
switch(Lr.bf)
{
/* 修改T及其左孩子的平衡因子 */
case LH:
T.bf=RH;
L.bf=EH;
break;
case EH:
T.bf=L.bf=EH;
break;
case RH:
T.bf=EH;
L.bf=LH;
break;
}
Lr.bf=EH;
L_Rotate(T.lchild); /* 对T的左子树作左旋平衡处理 */
R_Rotate(T); /* 对T作右旋平衡处理 */
break;
case EH: //特殊情况4,这种情况在添加时不可能出现,只在移除时可能出现,旋转之后整体树高不变
L.bf = RH;
T.bf = LH;
R_Rotate(T);
break;
}
}
首先,定义三个常数变量,分别代码1、0、-1。
(1)函数被调用,传入一个需调整平衡型的子树T,根节点为k3,由于LeftBalance函数被调用时,其实是已经确认当前子树是不平衡的状态,且左子树的高度大于右子树的高度。换句话说,此时T的根结点应该是平衡因子BF的值大于1的数。k3的BF为2
(2)将T的左孩子赋值给L。L指向K1.
(3)然后是分支判断。
(4)当L(k1)的平衡因子为LH,即为1时,表明它与根结点的BF值符号相同,因此,将它们的BF值都改为0,并进行右旋(顺时针)操作,是左左情况
(5)当L的平衡因子为RH时,即为-1时,表明它与根结点的BF值符号相反,此时需要做双旋操作。针对L的右孩子k2的BF作判断,修改结点T(k3)和L(k1)的BF值。将当前的Lr的BF改为0。从图中看到K2的左结点是连接到K1的右子树上,右结点连接到K3的左子树,其中当k2结点为RH,说明K2有右结点有,左结点无,k3为0((*T)->bf=EH; ),k1就没有右结点为LH。当为Lh看程序。
(6)对根结点的左子树进行左旋,以K1为根节点进行左旋转,形成左左情况。
(7)对根结点K3进行右旋,完成平衡操作。
插入节点
插入过程也是一个二叉树查找的过程,先看图
在插入节点2之前,该树还是一颗平衡树,当插入节点2之后,节点3就成为了不平衡点,需要对节点3进行左平衡处理。
在详细的分析一下:
首先从根节点4开始搜索,发现2<4,于是搜索4的左节点3,发现2<3,就搜索1,2>1,搜索1的右节点,此时发现1的右节点为空,就执行插入操作,将2插入到1的右节点,那么怎么发现此时这棵树不平衡的呢?因为我们是通过递归寻找插入点,当找到插入点在1的右节点之后,开始往父节点回溯,回溯过程中告诉父节点,孩儿有没有长高,如果长高了,父节点就要判断左右子树高度差是否大于1,也就是处于不平衡状态,每个节点都有个平衡因子,EH=0(等高),LH=1(左边高1),RH=-1(右边高1)例如,插入2之前,节点1的BF=0,节点3BF=1,节点4BF=1,插入节点2之后,往父节点1回溯,说我长高了,1节点BF=0,现在右节点长高了,所以此时节点1BF=-1,在往父节点3回溯,告诉父节点3,我长高了,而父节点3BF=1,而现在左孩子长高了,那BF=2,此时节点3称为不平衡点,需要对节点3做左平衡处理。处理完成后,2节点变成了1,3的父节点,此时2节点的高度和没插入节点2之前3节点高度一样,于是告诉父节点4,我没有长高,此时递归结束。
代码如下:
public void insertAVL(int e){
if(root==null){
root=new Node(e,EH,null,null,null);
return;
}
TS t=new TS();
InsertAVL(root,e,t,null);
}
/* 若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个 */
/* 数据元素为e的新结点,并返回1,否则返回0。若因插入而使二叉排序树 */
/* 失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否。 */
private boolean InsertAVL(Node T,int e,TS tl,Node parent)
{
if(T==null)
{
/* 插入新结点,树“长高”,置taller为TRUE */
Node nNode=new Node(e,EH,null,null,parent);
if(e<parent.value)
parent.lchild=nNode;
else
parent.rchild=nNode;
tl.taller=TRUE;
}
else
{
if (e==T.value)
{
/* 树中已存在和e有相同关键字的结点则不再插入 */
tl.taller=FALSE;
return false;
}
else if (e<T.value)
{
/* 应继续在T的左子树中进行搜索 */
if(!InsertAVL(T.lchild,e,tl,T))
return false;
if(tl.taller) /* 已插入到T的左子树中且左子树“长高” */
switch(T.bf) /* 检查T的平衡度 */
{
case LH: /* 原本左子树比右子树高,需要作左平衡处理 */
LeftBalance(T);
tl.taller=FALSE;
break;
case EH: /* 原本左、右子树等高,现因左子树增高而使树增高 */
T.bf=LH;
tl.taller=TRUE;
break;
case RH: /* 原本右子树比左子树高,现左、右子树等高 */
T.bf=EH;
tl.taller=FALSE;
break;
}
}
else
{
/* 应继续在T的右子树中进行搜索 */
if(!InsertAVL(T.rchild,e,tl,T))
return false;
if(tl.taller) /* 已插入到T的右子树且右子树“长高” */
{
switch(T.bf) /* 检查T的平衡度 */
{
case LH: /* 原本左子树比右子树高,现左、右子树等高 */
T.bf=EH;
tl.taller=FALSE;
break;
case EH: /* 原本左、右子树等高,现因右子树增高而使树增高 */
T.bf=RH;
tl.taller=TRUE;
break;
case RH: /* 原本右子树比左子树高,需要作右平衡处理 */
RightBalance(T);
tl.taller=FALSE;
break;
}
}
}
}
return true;
}
删除节点
删除操作和二叉查找树删除一样,分为三种情况讨论
(1)删除节点没有左子树,这种情况直接将删除节点的父节点指向删除节点的右子树。
(2)删除节点没有右子树,这种情况直接将删除节点的父节点指向删除节点的左子树。
(3)删除节点左右子树都存在,可以采用两种方式,
1:让删除节点左子树的最右侧节点代替当前节点
2:让删除节点右子树的最左侧节点代替当前节点
如下图:
这里的难点是删除之后要判断该树是否还平衡?
还是先看图:
和插入操作一样,删除操作也是递归查找,然后删除,删除之后,该节点A要向父节点回溯,告诉父节点B我变矮了(因为删除了),父节点B此时要判断自己是否也变矮了,如果删除的节点是自己的左子树中的节点(右子树同理,这里只讨论左子树情况,右子树请看代码),就要分三种情况讨论:
(1)B.BF=EH ,也就是原来B节点左右子树高度一致,而现在左子树告诉我,左子树变矮了,则需要将B.BF设置为RH,即右边高,同时可知B的高度并没变化,所以再往B的父节点C回溯的时候,B的父节点C就会当啥都没发生。
(2)B.BF=LH,也就是原来B节点左子树比右子树高一层而现在左子树告诉我,左子树变矮了,则需要将B.BF设置为EH,同时可知B节点的高度也变矮了,于是再往B的父节点C回溯的是否,C也要分三种情况讨论。
(3)B.BF=RH,也就是原来B节点右子树比左子树高一层,而现在左子树告诉我,左子树变矮了,则需要对B进行右平衡处理
而这里又要分为两种情况讨论来判断,右平衡处理完成后,需要判断B的父节点C的左子树是否变矮了
1.B.rchild.BF=EH,也就是B节点(右平衡处理之前)的右子树的左右子树等高。那么这种情况,B的父节点C的左子树不变矮。
2.除了1情况,B的父节点C的左子树会变矮。
下面画图来理解一下这两种情况。
这是第一种情况,6的右节点8的BF=EH,那么旋转后高度不变。
这是第二种情况,6的右节点8的BF!=EH,那么旋转后高度变矮。
删除代码如下:
/*
若在平衡的二叉排序树t中存在和e有相同关键字的结点,则删除之
并返回TRUE,否则返回FALSE。若因删除而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量shorter反映t变矮与否
*/
private boolean deleteAVL(Node t, int key, TS ts)
{
if(t == null) //不存在该元素
{
return FALSE; //删除失败
}
else if(key == t.value) //找到元素结点
{
Node q = null;
if(t.lchild == null) //左子树为空
{
q = t.parent;
if(q==null)
root=t.rchild;
else{
if(key<q.value)
q.lchild=t.rchild;
else
q.rchild=t.rchild;
}
ts.shorter = TRUE;
}
else if(t.rchild == null) //右子树为空
{
q = t.parent;
if(q==null)
root=t.lchild;
else{
if(key<q.value)
q.lchild=t.lchild;
else
q.rchild=t.lchild;
}
ts.shorter = TRUE;
}
else //左右子树都存在,
{
q = t.lchild;
while(q.rchild!=null)
{
q = q.rchild;
}
t.value = q.value;
deleteAVL(t.lchild, q.value, ts); //在左子树中递归删除前驱结点
}
}
else if(key < t.value) //左子树中继续查找
{
if(!deleteAVL(t.lchild, key, ts))
{
return FALSE;
}
if(ts.shorter)
{
switch(t.bf)
{
case LH:
t.bf = EH;
ts.shorter = TRUE;
break;
case EH:
t.bf = RH;
ts.shorter = FALSE;
break;
case RH:
if(t.rchild.bf == EH) //注意这里,画图思考一下
ts.shorter = FALSE;
else
ts.shorter = TRUE;
RightBalance(t); //右平衡处理
break;
}
}
}
else //右子树中继续查找
{
if(!deleteAVL(t.rchild, key, ts))
{
return FALSE;
}
if(ts.shorter)
{
switch(t.bf)
{
case LH:
if(t.lchild.bf == EH) //注意这里,画图思考一下
ts.shorter = FALSE;
else
ts.shorter = TRUE;
LeftBalance(t); //左平衡处理
break;
case EH:
t.bf = LH;
ts.shorter = FALSE;
break;
case RH:
t.bf = EH;
ts.shorter = TRUE;
break;
}
}
}
return TRUE;
}
OK,到此二叉平衡树讲完了,后面把源码附上:
package wangyi;
public class AVLTree {
private class Node{
int value;
int bf;
Node lchild;
Node rchild;
Node parent;
public Node(int value,int bf,Node lchild,Node rchild,Node parent){
this.value=value;
this.bf=bf;
this.lchild=lchild;
this.rchild=rchild;
this.parent=parent;
}
}
Node root=null;
final int LH =+1; /* 左高 */
final int EH= 0; /* 等高 */
final int RH=-1; /* 右高 */
final int LC=0; //在左子树插入
final int RC=1; //在右子树插入
final boolean FALSE=false;
final boolean TRUE=true;
/* 对以T为根的二叉排序树作右旋处理 */
/* 处理之后T的父节点指向T的左节点 */
//右旋-顺时针旋转(如LL型就得对根结点做该旋转)
private void R_Rotate(Node T)
{
Node L,P;
P=T.parent;
L=T.lchild; /* L指向node的左子树根结点 */
T.lchild=L.rchild; /* L的右子树挂接为node的左子树 */
if(L.rchild!=null)
L.rchild.parent=T;
L.rchild=T;
L.parent=P;
T.parent=L;
if(P==null)
root=L;
else if(P.rchild==T)
P.rchild=L;
else
P.lchild=L;
}
/* 对以T为根的二叉排序树作左旋处理, */
/* 处理之后T的父节点指向T的右节点 */
//左旋-逆时针旋转(如RR型就得对根结点做该旋转)
private void L_Rotate(Node T)
{
Node R,P;
P=T.parent;
R = T.rchild; /* R指向T的右子树根结点 */
T.rchild = R.lchild; /* R的左子树挂接为T的右子树 */
if(R.lchild!=null)
R.lchild.parent=T;
R.lchild = T;
R.parent=P;
T.parent=R;
if(P==null)
root=R;
else if(P.rchild==T)
P.rchild=R;
else
P.lchild=R;
}
/* 对以指针T所指结点为根的二叉树作左平衡旋转处理 */
/* 本算法结束时,指针T指向新的根结点 */
public void LeftBalance(Node T)
{
Node L,Lr;
L = T.lchild; /* L指向T的左子树根结点 */
switch(L.bf)
{
/* 检查T的左子树的平衡度,并作相应平衡处理 */
case LH: /* 新结点插入在T的左孩子的左子树上,要作单右旋处理 */
T.bf=L.bf=EH;
R_Rotate(T);
break;
case RH: /* 新结点插入在T的左孩子的右子树上,要作双旋处理 */ //
Lr=L.rchild; /* Lr指向T的左孩子的右子树根 */
switch(Lr.bf)
{
/* 修改T及其左孩子的平衡因子 */
case LH:
T.bf=RH;
L.bf=EH;
break;
case EH:
T.bf=L.bf=EH;
break;
case RH:
T.bf=EH;
L.bf=LH;
break;
}
Lr.bf=EH;
L_Rotate(T.lchild); /* 对T的左子树作左旋平衡处理 */
R_Rotate(T); /* 对T作右旋平衡处理 */
break;
case EH: //特殊情况4,这种情况在添加时不可能出现,只在移除时可能出现,旋转之后整体树高不变
L.bf = RH;
T.bf = LH;
R_Rotate(T);
break;
}
}
/* 对以指针T所指结点为根的二叉树作右平衡旋转处理, */
/* 本算法结束时,指针T指向新的根结点 */
public void RightBalance(Node T)
{
Node R,Rl;
R=T.rchild; /* R指向T的右子树根结点 */
switch(R.bf)
{
/* 检查T的右子树的平衡度,并作相应平衡处理 */
case RH: /* 新结点插入在T的右孩子的右子树上,要作单左旋处理 */
T.bf=R.bf=EH;
L_Rotate(T);
break;
case LH: /* 新结点插入在T的右孩子的左子树上,要作双旋处理 */ //最小不平衡树的根结点为负,其右孩子为正
Rl=R.lchild; /* Rl指向T的右孩子的左子树根 */
switch(Rl.bf)
{
/* 修改T及其右孩子的平衡因子 */
case RH:
T.bf=LH;
R.bf=EH;
break;
case EH:
T.bf=R.bf=EH;
break;
case LH:
T.bf=EH;
R.bf=RH;
break;
}
Rl.bf=EH;
R_Rotate(T.rchild); /* 对T的右子树作右旋平衡处理 */
L_Rotate(T); /* 对T作左旋平衡处理 */
break;
case EH: //特殊情况4,这种情况在添加时不可能出现,只在移除时可能出现,旋转之后整体树高不变
R.bf = LH;
T.bf = RH;
L_Rotate(T);
break;
}
}
public void insertAVL(int e){
if(root==null){
root=new Node(e,EH,null,null,null);
return;
}
TS t=new TS();
InsertAVL(root,e,t,null);
}
/* 若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个 */
/* 数据元素为e的新结点,并返回1,否则返回0。若因插入而使二叉排序树 */
/* 失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否。 */
private boolean InsertAVL(Node T,int e,TS tl,Node parent)
{
if(T==null)
{
/* 插入新结点,树“长高”,置taller为TRUE */
Node nNode=new Node(e,EH,null,null,parent);
if(e<parent.value)
parent.lchild=nNode;
else
parent.rchild=nNode;
tl.taller=TRUE;
}
else
{
if (e==T.value)
{
/* 树中已存在和e有相同关键字的结点则不再插入 */
tl.taller=FALSE;
return false;
}
else if (e<T.value)
{
/* 应继续在T的左子树中进行搜索 */
if(!InsertAVL(T.lchild,e,tl,T))
return false;
if(tl.taller) /* 已插入到T的左子树中且左子树“长高” */
switch(T.bf) /* 检查T的平衡度 */
{
case LH: /* 原本左子树比右子树高,需要作左平衡处理 */
LeftBalance(T);
tl.taller=FALSE;
break;
case EH: /* 原本左、右子树等高,现因左子树增高而使树增高 */
T.bf=LH;
tl.taller=TRUE;
break;
case RH: /* 原本右子树比左子树高,现左、右子树等高 */
T.bf=EH;
tl.taller=FALSE;
break;
}
}
else
{
/* 应继续在T的右子树中进行搜索 */
if(!InsertAVL(T.rchild,e,tl,T))
return false;
if(tl.taller) /* 已插入到T的右子树且右子树“长高” */
{
switch(T.bf) /* 检查T的平衡度 */
{
case LH: /* 原本左子树比右子树高,现左、右子树等高 */
T.bf=EH;
tl.taller=FALSE;
break;
case EH: /* 原本左、右子树等高,现因右子树增高而使树增高 */
T.bf=RH;
tl.taller=TRUE;
break;
case RH: /* 原本右子树比左子树高,需要作右平衡处理 */
RightBalance(T);
tl.taller=FALSE;
break;
}
}
}
}
return true;
}
public boolean deleteAVL(int key){
if(root==null)
throw new RuntimeException("这是空树!");
TS ts=new TS();
return deleteAVL(root,key,ts);
}
/*
若在平衡的二叉排序树t中存在和e有相同关键字的结点,则删除之
并返回TRUE,否则返回FALSE。若因删除而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量shorter反映t变矮与否
*/
private boolean deleteAVL(Node t, int key, TS ts)
{
if(t == null) //不存在该元素
{
return FALSE; //删除失败
}
else if(key == t.value) //找到元素结点
{
Node q = null;
if(t.lchild == null) //左子树为空
{
q = t.parent;
if(q==null)
root=t.rchild;
else{
if(key<q.value)
q.lchild=t.rchild;
else
q.rchild=t.rchild;
}
ts.shorter = TRUE;
}
else if(t.rchild == null) //右子树为空
{
q = t.parent;
if(q==null)
root=t.lchild;
else{
if(key<q.value)
q.lchild=t.lchild;
else
q.rchild=t.lchild;
}
ts.shorter = TRUE;
}
else //左右子树都存在,
{
q = t.lchild;
while(q.rchild!=null)
{
q = q.rchild;
}
t.value = q.value;
deleteAVL(t.lchild, q.value, ts); //在左子树中递归删除前驱结点
}
}
else if(key < t.value) //左子树中继续查找
{
if(!deleteAVL(t.lchild, key, ts))
{
return FALSE;
}
if(ts.shorter)
{
switch(t.bf)
{
case LH:
t.bf = EH;
ts.shorter = TRUE;
break;
case EH:
t.bf = RH;
ts.shorter = FALSE;
break;
case RH:
if(t.rchild.bf == EH) //注意这里,画图思考一下
ts.shorter = FALSE;
else
ts.shorter = TRUE;
RightBalance(t); //右平衡处理
break;
}
}
}
else //右子树中继续查找
{
if(!deleteAVL(t.rchild, key, ts))
{
return FALSE;
}
if(ts.shorter)
{
switch(t.bf)
{
case LH:
if(t.lchild.bf == EH) //注意这里,画图思考一下
ts.shorter = FALSE;
else
ts.shorter = TRUE;
LeftBalance(t); //左平衡处理
break;
case EH:
t.bf = LH;
ts.shorter = FALSE;
break;
case RH:
t.bf = EH;
ts.shorter = TRUE;
break;
}
}
}
return TRUE;
}
//前序遍历
private void preOrder(Node r){
if(r==null)
return;
System.out.print(r.value+",");
preOrder(r.lchild);
preOrder(r.rchild);
}
public void preOrder(){
preOrder(root);
}
//中序遍历
private void inOrder(Node r){
if(r==null)
return;
inOrder(r.lchild);
System.out.print(r.value+",");
inOrder(r.rchild);
}
public void inOrder(){
inOrder(root);
}
public static void main(String[] args) {
int[] a={5,3,4,1,2,8,6,7};
AVLTree tree=new AVLTree();
for(int i=0;i<a.length;i++){
tree.insertAVL(a[i]);
}
System.out.println("前序遍历:");
tree.preOrder();
System.out.println();
System.out.println("中序遍历:");//二叉排序树中序遍历是递增序列
tree.inOrder();
System.out.println();
tree.deleteAVL(5);
System.out.println("前序遍历:");
tree.preOrder();
System.out.println();
System.out.println("中序遍历:");//二叉排序树中序遍历是递增序列
tree.inOrder();
System.out.println();
}
}
class TS{
public boolean taller=false;
public boolean shorter=false;
}