一、基本含义
1.AVL树
AVL树是一种特殊形式的二叉查找树,其任何结点的两个子树的高度差不超过1,因此也被称为高度平衡树
2.平衡因子
结点的左子树与右子树的高度差称为平衡因子,AVL树中所有结点的平衡因子可取:1,0,-1
3.AVL树结点的类型定义
//AVL树的结点类型定义
typedef struct avlNode {
int ket;
int h;//以当前结点为根结点的AVL树的高度
avlNode* lc, * rc;
avlNode():h(1),lc(NULL),rc(NULL){}
}*avlTree;
//获取子树高度
int avl_height(avlTree avl) {
return avl == NULL ? 0 : avl->h;
}
3.不平衡结点
假设出现不平衡的结点为A,则平衡处理一般涉及结点A、A的某个孩子结点B以及B的某个孩子结点C。这三个结点有如下性质:
(1)如果结点A的另一个孩子结点为
B
1
B_1
B1,则
∣
h
e
i
g
h
t
(
B
)
−
h
e
i
g
h
t
(
B
1
)
∣
=
2
|height(B)-height(B_1)|=2
∣height(B)−height(B1)∣=2
(2)如果结点B的另一个孩子结点为
C
1
C_1
C1,
h
e
i
g
h
t
(
C
)
>
=
h
e
i
g
h
t
(
C
1
)
height(C)>=height(C_1)
height(C)>=height(C1)
二、AVL树的平衡处理
1.LL旋转
1.含义
当B为A的左孩子且C为B的左孩子时进行的平衡处理类型称为LL旋转
2.方法
将结点A的左孩子重新设置为B的右孩子
B
r
B_r
Br,将B的右孩子重新设置为结点A,此时以结点A和B为根结点的子树的高度发生变化。LL旋转后子树的根结点变为B
//AVL树的LL旋转操作,返回旋转后子树的根结点
avlTree LL(avlTree A) {
avlTree B = A->lc;
A->lc = B->rc;
B->rc = A;
A->h = max(avl_height(A->lc), avl_height(A->rc)) + 1;//更新结点A的高度
B->h = max(avl_height(B->lc), avl_height(B->rc)) + 1;//更新结点B的高度
return B;
}
2.RR旋转
1.含义
当B为A的右孩子且C为B的右孩子时进行的平衡处理类型称为RR旋转,此时A的平衡因子为-2
2.方法
将结点A的右孩子重新设置为结点B的左孩子
B
l
B_l
Bl,将结点B的左孩子重新设置为结点A,并更新结点A和B为根结点的子树的高度。RR旋转后子树的根结点变为B\
//AVL树的RR旋转操作,返回旋转后子树的根结点
avlTree RR(avlTree A) {
avlTree B = A->rc;
A->rc = B->lc;
B->lc = A;
A->h = max(avl_height(A->lc), avl_height(A->rc)) + 1;
B->h = max(avl_height(B->lc), avl_height(B->rc)) + 1;
return B;
}
3.LR旋转
1.含义
当B为A的左孩子且C为B的右孩子时进行的平衡处理类型称为LR旋转
2.方法
先对结点B和C进行RR旋转,然后对结点A和C进行LL旋转。LR旋转后子树的根结点为C
//AVL树的LR旋转操作,返回旋转后子树的根结点
avlTree LR(avlTree A) {
A->lc = RR(A->lc);
return LL(A);
}
4.LR旋转
1.含义
当B为A的右孩子且C为B的左孩子时进行的平衡处理类型称为RL旋转,此时A的平衡因子为-2
2.方法
先对B结点和C进行LL旋转,然后对结点A和C进行RR旋转,RL旋转后子树的根结点为C
//AVL树的RL旋转操作
avlTree RL(avlTree A) {
A->rc = LL(A->rc);
return RR(A);
}
三、AVL树的插入操作
1.算法1-1:AVL树的插入操作
功能
在AVL树t中插入一个键值为k的结点
算法步骤
(1)创建一个新的结点tmp,其键值为k,高度为1,按照BST插入结点的方法,将tmp插入AVL树中
(2)从tmp出发,沿根结点方向向上回溯,在回溯过程中,更新所经过结点的高度,并检查其平衡因子,如果平衡因子不为2或-2,继续向上回溯,否则进入第三步
(3)假设当前结点为A,分为两种情况
(3.1)A的前一个结点B为A的左孩子,分为两种情况
(3.1.1)如果k<结点B的键值,则插入结点的位置为B的左子数,对A和B进行LL旋转
(3.1.2)如果k>结点B的键值,则插入结点的位置为B的右子树,设B的右孩子为C,对A,B,C进行LR旋转
(3.2)A的前一个结点B为A的右孩子,分为两种情况
(3.2.1)如果k>结点B的键值,则插入结点的位置为B的右子数,对A和B进行RR旋转
(3.2.2)如果k<结点B的键值,则插入结点的位置为B的左子树,设B的左孩子为C,对A,B,C进行RL旋转
//向AVL树t中插入结点,结点的键值为k,插入成功,返回true,否则,返回false
bool avl_insert(avlTree& t, int k) {
if (t == NULL) {
t = new avlNode;
t->key = k;
return true;
}
if (k < t->key) {//插入结点位于t的左子树
avl_insert(t->lc, k);//插入t的左子数
if (avl_height(t->lc) - avl_height(t->rc) > 1) {//回退时检查t子树是否平衡
//不平衡,A为t,B为t->lc,因为此处属于t的左子树回退部分
if (t->lc->key > k)t = LL(t);//C为B的左子数
else t = LR(t);//C为B的右子树
}
}
else if (k > t->key) {//插入结点位于t的右子树
avl_insert(t->rc, k);//插入t的右子树
if (avl_height(t->rc) - avl_height(t->lc) > 1) {//回退时检测t子树是否平衡
//不平衡,A为t,B为t->rc,因为此处属于t的右子树回退部分
if (t->rc->key < k)t = RR(t);//C为B的右子树
else t = RL(t);//C为B的左子树
}
}
else
return false;
t->h = max(avl_height(t->lc), avl_height(t->rc)) + 1;//在回退时更新t的高度
}
四、AVL树的删除操作
1.算法1-2:AVL树的删除
功能
在AVL树t中删除键值为k的结点
算法步骤
(1)按照BST删除结点的方法,删除AVL树中键值为k的结点,最终删除的结点一定是叶结点,假设为tmp
(2)从tmp出发,沿着结点方向向上回溯,在回溯过程中,更新所经过结点的高度,并检查平衡因子,如果平衡因子不为2或-2,继续向上回溯,否则进入第三步
(3)假设当前结点为A,分为如下两种情况
(3.1)A的前一个结点为A的左孩子,设B为A的右孩子,又分为如下情况
(3.1.1)如果B的右子树的高度>=B的左子树的高度,对A和B进行RR旋转
(3.1.2)如果B的右子树的高度<B的左子树的高度,则设B的左孩子为C,对A,B,C进行RL旋转
(3.2)A的前一个结点为A的右孩子,设B为A的左孩子,又分为如下两种情况
(3.2.1)如果B的左子树的高度>=B的右子树的高度,对A和B进行LL旋转
(3.2.2)如果B的左子树的高度<B的右子树的高度,设B的右孩子为C,对A,B,C进行LR旋转
//删除AVL树t中键值为k的结点。删除成功返回true,否则返回false
bool avl_delete(avlTree& t, int k) {
static bool ret = true;
if (t == NULL)return false;
else if (k < t->key) {//删除结点在t的左分支
avl_delete(t->lc, k);
if (avl_height(t->rc) - avl_height(t->lc) > 1) {//判断t是否平衡
//不平衡,A为t,B为A的右孩子,因为此处属于t的左子树回退部分
avlTree B = t->rc;
if (avl_height(B->rc) >= avl_height(B->lc))t = RR(t);//C为B的右孩子
else t = RL(t);//C为B的左孩子
}
}
else if (k > t->key) {//删除结点在t的右分支
avl_delete(t->rc, k);
if (avl_height(t->lc) - avl_height(t->rc) > 1) {//判断t是否平衡
avlTree B = t->lc;
if (avl_height(B->lc) >= avl_height(t->rc))t = LL(t);//C为B的左孩子
else t = LR(t);//C为B的右孩子
}
}
else {//结点t的键值等于k,删除结点t
if (t->lc == NULL || t->rc == NULL) {
delete t;
t = NULL;//叶结点直接删除
}
else if (t->rc == NULL) {//t没有右孩子
avlNode* tmp = t->rc;
int k1 = tmp->key;
avl_delete(t, k1);//删除t的左孩子
t->key = k1;//用t的左孩子的键值取代t的键值
}
else {//t的右子树不为空
avlNode* tmp = t->rc;
while (tmp->lc)tmp = tmp->lc;//获取t的中跟序列的直接前驱
int k1 = tmp->key;
avl_delete(t, k1);//删除tmp
t->key = k1;//用tmp的键值取代t的键值
}
ret = true;
}
if (t)
t->h = max(avl_height(t->lc), avl_height(t->rc)) + 1;
return ret;
}