AVL树的插入、删除、旋转

来源:互联网 发布:九品网络电视 是什么 编辑:程序博客网 时间:2024/05/21 13:59

什么是AVL树?

计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(logn)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-VelskyE.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

节点的平衡因子是它的右子树的高度减去它的左子树的高度。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

插入

向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.5乘logn个节点,而每次AVL旋转都耗费恒定的时间,插入处理在整体上耗费O(log n) 时间。

删除

从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费恒定的时间,删除处理在整体上耗费O(logn) 时间。

一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查询而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)

实现描述

假设平衡因子是左子树的高度减去右子树的高度所得到的值,又假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行的规律可归纳为下列四种情况:

  1. 单向右旋平衡处理RR:由于在*a的左子树根结点的左子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次右旋转操作;
  2. 单向左旋平衡处理LL:由于在*a的右子树根结点的右子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次左旋转操作;
  3. 双向旋转(先左后右)平衡处理LR:由于在*a的左子树根结点的右子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。
  4. 双向旋转(先右后左)平衡处理RL:由于在*a的右子树根结点的左子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。

在平衡的二叉排序树BBST (Balancing Binary Search Tree)上插入一个新的数据元素e的递归算法可描述如下:

  1. 若BBST为空树,则插入一个数据元素为e的新结点作为BBST的根结点,树的深度增1;
  2. 若e的关键字和BBST的根结点的关键字相等,则不进行;
  3. 若e的关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:
    1. BBST的根结点的平衡因子为-1(右子树的深度大于左子树的深度,则将根结点的平衡因子更改为0,BBST的深度不变;
    2. BBST的根结点的平衡因子为0(左、右子树的深度相等):则将根结点的平衡因子更改为1,BBST的深度增1;
    3. BBST的根结点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根结点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根结点和其右子树根结点的平衡因子更改为0,树的深度不变;
  4. 若e的关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e有相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。

AVL树的旋转

typedef struct BSTNode {
ElemType data ;
int bf ; //结点的平衡因子
struct BSTNode*lchild , *rchild ; //左、右孩子指针
}BSTNode , *BSTree ;

void R_Rotate (BSTree&p) {
//对以*p为根的二叉排序树作右旋转处理,处理之后p指向新的树根结点,即旋转
//处理之前的左子树的根结点
lc=p->lchild ;//lc指向的*p的左子树根结点
p->lchild=lc->rchild ; //lc的右子树挂接为*p的左子树
lc->rchild=p ; p=lc ; //p指向新的根结点
}// R_Rotate
void L_Rotate (BSTree&p) {
//对以*p为根的二叉排序树作左旋处理,处理之后p指向新的树根结点,即旋转
//处理之前的右子树的根结点
rc=p->rchild ;//rc指向的*p的右子树根结点
p->rchild=rc->lchild ; //rc左子树挂接为*p的右子树
rc->lchild=p ; p=rc ; //p指向新的根结点
}//L_Rotate

#define LH +1//左高
#define EH 0//等高
#define RH -1//右高
Status InsertAVL (BSTree
&T , ElemType e , Boolean&taller ) {
//若在平衡二叉排序树T中不存在和e有相同关键字的结点,则插入一个数据元素
//为e的新结点,并返回1,否则返回0。若因插入而使二叉排序树失去平衡,则作平衡
//旋转处理,布尔变量taller反映T长高与否
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.key , T->data.key))//树中已存在和e有相同关键字的结点
{ taller=FALSE ;return 0 ; } //则不插入
if (LT(e.key , T->data.key)) {//继续在*T的左子树中进行搜索
if (!InsertAVL(T->lchild , e , taller)) return0; //未插入
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 ; break ;
}
//switch(T->bf)
}//if
else {//继续在*T的左子树中进行搜索
if(!InsertAVL(T->rchild , e , taller)) return0 ; //未插入
if (taller)//已插入到*T的右子树中且右子树“长高”
switch ( T->bf ) {//检查*T的平衡度
case LH://原本左子树比右子树高,现左、右子树等高
T->bf=EH ; taller=FALSE ; break ;
case EH: //原本左、右子树等高,现因右子树增高而使树增高
T->bf=RH ; taller=FALSE ; break ;
case RH: //原本右子树比左子树高,需要作右平衡处理
RightBalance(T) ; taller=FALSE ;break ;
}
//switch(T->bf)
}//else
}//else
return1;
}
//InsertAVL
void LeftBalance (BSTree&T) {
//对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时,指针T指向
//新的根结点
lc=T->lchild ;//lc指向*T的左子树根结点
switch (lc->bf) {//检查*T的左子树的平衡高度,并作相应平衡处理
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 ;
}
// switch (rd->bf)
rd->bf=EH;
L_Rotate(T
->lchild) ;//对*T的左子树作左旋转处理
R_Rotate(T) ; //对*T作右旋转处理
}// switch (lc->bf)
}//LeftBalance

原创粉丝点击