平衡二叉树的定义
平衡二叉查找树( Balanced Binary Sort Tree,BBST)简称平衡二叉树。平衡二又树有很多种,其中最著名的是由前苏联数学家 Adele- Veliki和 Landis在1962年提出的高度平衡的二叉树。根据提出者的英文名字首字母简称为AVL树 。
平衡二叉树或者是棵空树,或者是具有下列性质的二叉查找树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度之差的绝对值不超过1。若将二叉树结点的平衡因子( Balance Factor)定义为该结点的左子树的高度减去它的右子树的高度,则所有结点的平衡因子只可能为-1、0、1。只要有一个结点的平衡因子的绝对值大于1,那么这棵树就失去了平衡。
平衡二叉树的存储结构及宏定义
#define LH +1 // 左子树比右子树高
#define EH 0 // 左、右子树等高
#define RH -1 // 右子树比左子树高
typedef int KeyType; // 关键字类型为整数类型
typedef struct {
KeyType key; // 关键字项
} RecordType, RcdType; // 记录类型
typedef struct BBSTNode {
RcdType data;
int bf; // 结点平衡因子
struct BBSTNode *lchild, *rchild; // 左右孩子
} * BBSTree; // 平衡二叉树
平衡二叉树的失衡及调整
最小失衡子树
在平衡二叉树中插入一个新结点后,从该结点起向上寻找第一个不平衡的结点(平衡因子bf变为了-2或2),以确定该树是否失衡。若找到,则以该结点为根的子树称为最小失衡子树。
平衡二叉树失衡后则需要调整,需要对失衡的情况进行分类,分为以下四种。
(1). LL型
LL型指的是在最小失衡子树的左孩子的左子树上插入了新的结点。失衡调整如下图所示,先找到最小失衡子树的根A,以其左孩子结点B为轴,对不平衡结点A进行顺时针旋转(也称为右旋)。右旋是让B顶替A的位置,并置A为B的右孩子,如果B存在右子树 ,则置 为A的左子树。
代码实现
/** * 对最小失衡子树p作右旋调整 * * @param p 最小失衡子树 */
void R_Rotate(BBSTree &p) {
BBSTree lc = p->lchild; // lc指向p结点的左孩子
p->lchild = lc->rchild; // lc结点的右子树置为p结点的左子树
lc->rchild = p; // 置p结点为lc结点的右孩子
p = lc; // p指向新的根结点
}
(2). RR型
对于RR型。正好与LL型对称,对以A为根的最小失衡子树进行逆时针旋转(左旋),如下图所示。
实现代码
/** * 对最小失衡子树p作左旋调整 * * @param p 最小失衡子树 */
void L_Rotate(BBSTree &p) {
BBSTree rc = p->rchild;
p->rchild = rc->lchild;
rc->lchild = p;
p = rc;
}
(3). LR型
LR型指的是最小失衡子树的左孩子的右子树上插入了新的结点。处理办法如下图所示,首先找到以A为根的小失衡子树,以该子树的左孩子结点B为轴,对右子树结点C进行左旋调整,使之变为LL型。再以C为轴,对不平衡结点A进行右旋调整。
(4). RL型
RL型与LR型对称,需依次进行右旋处理和左旋处理,如下图所示
平衡二叉树的插入
构造平衙二又树可采用依次插人结点的方式,其递归步骤如下:
- 若是空树,则插人结点作为根结点,树的高度增加1。
- 若待插入结点和根结点相等,则无须插入。
- 若待插人结点小于根结点,且在左子树中也不存在相等的结点,则在左子树插入,且若插入后的左子树高度增加1,分情况处理。
- 原根结点的平衡因子为-1(右子树高于左子树),更改为0,树的高度不变。
- 原根结点的平衡因子为0(左右子树高度相等),更改为1,树的高度增加1。
- 原根结点的平衡因子为1(左子树高于右子树):若左子树根结点的平衡因子为1,则属于LL型,需进行右旋平衡调整,并在调整后将根结点及其右孩子的平衡因子更改为0,树的高度不变;若左子树根结点的平衡因子为-1,则属于LR型,需依次进行左旋和右旋处理,并在调整后修改根结点及其左、右孩子的平衡因子,树的高度不变。
- 若待插入结点大于根结点,且在右子树中不存在相等的结点,则在右子树插入,且当插入之后的右子树高度加1时,分情况处理
- 原根结点的平衡因子为1(左子树高于右子树),更改为0,树的高度不变。
- 原根结点的平衡因子为0(左右子树高度相等),更改为-1,树的高度加1。
- 原根结点的平衡因子为-1(右子树高于左子树):若右子树根结点的平衡因子为1,则属于RL型,需依次进行右旋和左旋处理,并且在旋转处理之后,修改根结点及其左、右子树根结点的平衡因子,树的高度不变;若右子树根结点的平衡因子为-1,则属于RR型,需要进行一次左旋处理,并且在左旋之后,更新根结点和其左、右子树根结点的平衡因子,树的高度不变。
左平衡处理操作
/** * 树T的左平衡处理 * * @param T 树T */
void LeftBalance(BBSTree &T) {
BBSTree lc, rd;
lc = T->lchild;
switch (lc->bf) {
case LH: {
T->bf = lc->bf = EH;
R_Rotate(T);
break;
} case RH: {
rd = lc->rchild;
switch (rd->bf) {
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;
}
}
rd->bf = EH;
L_Rotate(T->lchild);
R_Rotate(T);
break;
}
}
}
右平衡处理操作
/** * 树T的右平衡处理 * * @param T 树T */
void RightBalance(BBSTree &T) {
BBSTree rc, ld;
rc = T->rchild;
switch (rc->bf) {
case RH:
T->bf = rc->bf = EH;
L_Rotate(T);
break;
case LH:
ld = rc->lchild;
switch (ld->bf) {
case RH: {
T->bf = LH;
rc->bf = EH;
break;
} case EH: {
T->bf = rc->bf = EH;
break;
} case LH: {
T->bf = EH;
rc->bf = RH;
break;
}
}
ld->bf = EH;
R_Rotate(T->rchild);
L_Rotate(T);
break;
}
}
插入代码实现
/** * 平衡二叉树的插入操作 * * @param T 平衡二叉树T * @param e 待插入元素 * @param taller 高度是否变高 * @return 成功返回TRUE */
Status InsertAVL(BBSTree &T, RcdType e, Status &taller) {
if (NULL == T) {
// T为空,树长高
T = (BBSTree)malloc(sizeof(BBSTNode));
T->data = e;
T->bf = EH;
T->lchild = NULL;
T->rchild = NULL;
taller = TRUE;
} else if (e.key == T->data.key) {
// 已经存在该点
taller = FALSE;
return FALSE;
} else if (e.key < T->data.key) {
// 插入到左子树
if (FALSE == InsertAVL(T->lchild, e, taller)) {
return FALSE;
}
if (TRUE == taller) {
switch (T->bf) {
case LH: {
LeftBalance(T);
taller = FALSE;
break;
} case EH: {
T->bf = LH;
taller = TRUE;
break;
} case RH: {
T->bf = EH;
taller = FALSE;
break;
}
}
}
} else {
if (FALSE == InsertAVL(T->rchild, e, taller)) {
return FALSE;
}
if (TRUE == taller) {
switch (T->bf) {
case LH: {
T->bf = EH;
taller = FALSE;
break;
} case EH: {
T->bf = RH;
taller = TRUE;
break;
} case RH: {
RightBalance(T);
taller = FALSE;
break;
}
}
}
}
return TRUE;
}
平衡二叉树的删除
平衡二叉树的删除可以看做插入的反过程,所以调整的方向也是相反的,所以操作起来并不难。
其中的查找前驱结点的依据是平衡二叉树的中序遍历是有序序列。且被删结点的直接前驱位于其左子树的右下角。用前驱结点的值代替被删结点的值,然后删除前驱结点,这样就跟简单的操作一样了。
代码实现
/** * 平衡二叉树的删除操作 * * @param T * @param key * @param shorter * @return */
Status DeleteAVL(BBSTree &T, KeyType key, Status &shorter) {
if (NULL == T) {
// 树为空
return FALSE;
} else if (T->data.key == key) {
// 找到元素结点
BBSTree p = NULL;
if (NULL == T->lchild) {
// 左子树为空
p = T;
T = T->rchild;
free(p);
shorter = TRUE; // 高度变矮
} else if (T->rchild == NULL) {
// 右子树为空
p = T;
T = T->lchild;
free(p);
shorter = TRUE; // 高度变矮
} else {
// 左右子树都存在
p = T->lchild;
while (p->rchild != NULL) {
p = p->rchild;
}
T->data = p->data;
// 在左子树中删除前驱结点
DeleteAVL(T->lchild, p->data.key, shorter);
}
} else if (T->data.key > key) {
if (DeleteAVL(T->lchild, key, shorter) == FALSE) {
return FALSE;
}
if (shorter == TRUE) {
switch (T->bf) {
case LH: {
T->bf = EH;
shorter = TRUE;
break;
} case EH: {
T->bf = RH;
shorter = FALSE;
break;
} case RH: {
RightBalance(T);
if (T->rchild->bf == EH) {
shorter = FALSE;
} else {
shorter = TRUE;
}
break;
}
}
}
} else {
if (DeleteAVL(T->rchild, key, shorter) == FALSE) {
return FALSE;
}
if (shorter == TRUE) {
switch (T->bf) {
case LH: {
LeftBalance(T);
if (T->lchild->bf == EH) {
shorter = FALSE;
} else {
shorter = TRUE;
}
break;
} case EH: {
T->bf = LH;
shorter = FALSE;
break;
} case RH: {
T->bf = EH;
shorter = TRUE;
break;
}
}
}
}
return TRUE;
}
平衡二叉树的分裂和合并
平衡二叉树的分裂和合并是根据插入操作来实现的,具体请看以下代码。
/** * 分裂平衡二叉树,分成大于key和小于key两棵树 * * @param T 待分裂的平衡二叉树 * @param key 分裂的关键字 * @param T1 关键字全部小于key的平衡二叉树 * @param T2 关键字全部大于key的平衡二叉树 */
void SpiltBBST(BBSTree T, KeyType key, BBSTree &T1, BBSTree &T2) {
Status taller = FALSE;
if (T != NULL) {
SpiltBBST(T->lchild, key, T1, T2); // 递归访问左子树
if(T->data.key > key) {
InsertAVL(T1, T->data, taller);
} else {
InsertAVL(T2, T->data, taller);
}
SpiltBBST(T->rchild, key, T1, T2);
}
}
/** * 合并平衡二叉树 * * @param T1 合并后的平衡二叉树 * @param T2 待合并的平衡二叉树 */
void MergeBBST(BBSTree &T1, BBSTree T2) {
Status taller = FALSE;
if (T2 != NULL) {
MergeBBST(T1, T2->lchild);
InsertAVL(T1, T2->data, taller);
MergeBBST(T1, T2->rchild);
}
}
如需获取完整的代码以及测试函数,请到https://github.com/zhengjunming/ds/tree/master/avl-tree 中clone。