文章目录
一. 平衡二叉树的概念
1.为什么需要平衡二叉树
为了避免这种情况发生,提高效率
性质:
- 一颗平衡二叉树或者空树,或者具有下列性质的二叉排序树:
- 左子树与右子树的高度差的绝对值小于或等于1
- 左子树与右子树也是平衡二叉排序树
2.平衡因子
BF = 左右子树高度差 = 左子树的深度-右子树深度
(BF = -1,0,1)
平衡二叉树,也被称为高度平衡树。相比于”二叉查找树”,它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。
二. 平衡二叉树的分析与调整
1.分析
当我们在一个平衡二叉排序树上插入一个结点时,有可能导致失衡,即出现平衡因子绝对值大于一的结点
2.平衡调整的四种类型
原则:
- 降低高度
- 保持二叉排序树的性质
1. LL型
- B结点带左子树一起上升
- A结点成为B的右孩子
- 原来B结点的右子树作为A的左子树
2. RR型
- B结点带右子树一起上升
- A结点成为B的左孩子
- 原来B结点的左子树作为A的右子树
图例:
调整后:
3. LR型
- C结点穿过A、B结点上升
- B结点成为C的左孩子
A结点成为C的右孩子 - 原来C结点的左子树作为B的右子树
原来C结点的右子树作为A的左子树
C原来的左子树变成上升后的左子树的右子树;原来的右子树变成上升后的右子树的左子树
4. RL型
图例:
调整后:
9原来的左子树变成了上升后的左子树的右子树
三. 平衡二叉树的实现
1. 平衡二叉树的结构
与二叉树大致相同,多了平衡因子
typedef struct BinNode
{
int data;
int bf; //平衡因子
struct BinNode* lchild;
struct BinNode* rchild;
}BinNode, * BinTree;
2. 平衡二叉树的调整
右旋:
//传入需要做右璇处理的子树的根结点
//处理完毕后 P 指向新的子树的根结点
void R_Rotate(BinTree * P)
{
BinTree L;
L = (*P)->lchild;
(*P)->lchild = L->rchild;
L->rchild = (*P);
*P = L;
}
左旋:
void L_Rotate(BinTree* P)
{
BinTree R;
R = (*P)->rchild;
(*P)->rchild = R->lchild;
R->lchild = (*P);
*P = R;
}
左子树平衡旋转代码:
//传进来需要左平衡处理的结点,结束后,T指向新的子树的根结点
//既然已经需要做左平衡处理,那么它的BF值肯定为正,只要判断它的左子树的BF值进行了
void LeftBalance(BinTree* T)
{
BinTree L, Lr;
L = (*T)->lchild;
//检查左子树的平衡度
switch (L->bf)
{
case 1://如果左边高,那么BF值都为正,只需要做右璇处理就行了
(*T)->bf = L->bf = 0;//处理完毕后,BF值都为0
R_Rotate(T);
break;
case -1://如果左子树的BF值为负,说明符号为负,需要先把符号变为相同,再进行右璇处理
Lr = L->rchild;
//后面会进行双旋操作
switch (Lr->bf)//通过判断Lr的bf值,修改T和T的左孩子的平衡因子
{
case 1://如果L的右子树是左高,那么双旋过后,T应该就是右高
(*T)->bf = -1;
L->bf = 0;
break;
case 0://如果等高,那么双旋过后应该是都是等高
(*T)->bf = L->bf = 0;
break;
case -1://如果是右高,那么双旋过后,L应该是左高
(*T)->bf = 0;
L->bf = 1;
break;
}
Lr->bf = 0;
L_Rotate(&(*T)->lchild);//对T的左子树进行左旋
R_Rotate(T);//对T进行右旋,整个是个双旋操作
}
}
右子树平衡旋转代码:
void RightBalance(BinTree* T)
{
BinTree R, Rl;
R = (*T)->rchild;
switch (R->bf)
{
case -1:
(*T)->bf = R->bf = 0;
break;
case 1:
Rl = R->lchild;
switch (Rl->bf)
{
case 1:
(*T)->bf = 0;
R->bf = -1;
case 0:
(*T)->bf = R->bf = 0;
break;
case -1:
(*T)->bf = 1;
R->bf = 0;
}
Rl->bf = 0;
R_Rotate(&(*T)->rchild);
L_Rotate(T);
}
}
3. 平衡二叉树的插入
//若在二叉排序树中不存在关键字为e的结点,则将e插入
//插入的过程通过旋转操作保持树的平衡
//插入成功返回true,否则返回false
//taller是一个判断二叉树的高度是否增加的值
//bf:左高为1 ;右高为-1 ;等高为0
int InsertAVL(BinTree* T, int e, int* taller)
{
if (!*T)//找到位置,插入结点
{
*T = (BinTree)malloc(sizeof(BinNode));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = 0;
*taller = true;
}
else
{
if (e == (*T)->data)//树中有关键字和e相同的结点,不进行插入操作
{
*taller = false;
return false;
}
if (e < (*T)->data)//在左子树中进行搜索
{
if (!InsertAVL(&(*T)->lchild, e, taller))//在左子树中递归,若最终返回false,则该步返回false
{
return false;
}
if (*taller)//这个结点插入到了左子树(到这步的时候,实际上已经从InsertAVL返回了)
{
switch ((*T)->bf)//判断T的平衡因子,这个T的bf值仍然是插入操作之前的值
{
case 1://原来是左子树高,现在左子树又增加一个结点,左子树偏高,做左子树平衡处理
LeftBalance(T);
*taller = false;//平衡完后重置标志变量
break;
case 0://原来是等高,现在左子树加了,说明左子树高了
(*T)->bf = 1;
*taller = true;//加入后,仍然左高,那么高度是真的增加了
break;
case -1://原来右边高,现在左边增加了一个结点,等高了
(*T)->bf = 0;
*taller = false;//因为是等高,所以重置标志变量
break;
}
}
}
else//在右子树中搜素,过程与上面差不多
{
if (!InsertAVL(&(*T)->rchild, e, taller))
{
return false;
}
if (*taller)
{
switch ((*T)->bf)
{
case 1://原来左边高,现在等高
(*T)->bf = 0;
*taller = false;
break;
case 0://原来等高,现在右边高
(*T)->bf = -1;
*taller = true;
break;
case -1://原来右边高,现在又加了一个,需要左右平衡处理
RightBalance(T);
*taller = false;
break;
}
}
}
}
}