数据结构与算法——平衡二叉树(AVL Tree)
构建一个二叉排序树时,如果构建序列是完全有序的,则会出现这样的情况:
显然这种情况会使得二叉搜索树退化成链表。当出现这样的情况,二叉排序树的查找也就退化成了线性查找,所以我们需要合理调整二叉排序树的形态,使得树上的每个结点都尽量有两个子结点,这样整个二叉树的高度就会大约在log(n) 左右,其中 n 为结点个数。
基本性质
AVL树也称为平衡二叉树,是一种自平衡的二叉排序树,本质上仍然是一颗二叉排序树,只是增加了“平衡”的要求,平衡是指,对AVL树中任何节点的两个子树的高度之差(称为平衡因子)的绝对值不超过 1 。能保证上面这一点,AVL树的高度就能始终保持在 O(logn)级别。
数据结构
typedef struct BiTNode
{
int data;
int bf;//结点的平衡因子
BiTNode *lchild;
BiTNode *rchild;
}BiTNode,*BiTree;
右旋操作
当最小不平衡子树根结点的平衡因子BF是大于1时,就右旋:
/*
*右旋处理
*/
void R_Rotate(BiTree *p)
{
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild;
L->rchild = (*p);
*p = L;//p指向新的根结点
}
左旋操作
当最小不平衡子树根结点的平衡因子BF是小于-1时就左旋
/*
*左旋
*/
void L_Rotate(BiTree *p)
{
BiTree R;
R = (*p)->rchild;
(*p)->rchild = R->lchild;
R->lchild = (*p);
*p = R;
}
左平衡旋转
/*
*左平衡旋转
*插入结点后最小不平衡子树的BF与它的子树的BF符号相反时:
*1. 需要对结点先进行一次旋转以使符号相同
*2. 符号相同后需要再反向旋转一次
*/
#define LH +1
#define EH 0
#define RH -1
void LeftBalance(BiTree *T)
{
BiTree L, Lr;
L = (*T)->lchild;
switch (L->bf)
{//检查T的左子树的平衡度,并作相应平衡处理
case LH://
(*T)->bf = L->bf = EH;
R_Rotate(T);
break;
case RH:
Lr = L->rchild;
switch (Lr->bf)
{
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);
R_Rotate(T);
}
}
1. L->bf == LH
此时新结点插入在T的左孩子的左树上,因为L为T的lchild,说明此时与根结点的符号相同,执行右旋操作:
2. L->bf == RH
此时的结点与根节点的BF符号相反,此时需要做双旋处理:
右平衡旋转
右平衡旋转和上述的左旋转相似,实现如下:
void RightBalance(BiTree *T)
{
BiTree R, Rl;
R = (*T)->rchild;
switch (R->bf)
{//检查T的右子树的平衡度,并作相应平衡处理
case RH://
(*T)->bf = R->bf = EH;
L_Rotate(T);
break;
case LH:
Rl = R->rchild;
switch (Rl->bf)
{
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);
L_Rotate(T);
}
}
插入操作
插入操作的思想:利用递归的方式查找插入点,并在插入成功之后返回,从插入结点的父结点开始,按递归的逆顺序遍历结点,去判断该结点的二叉树是否是平衡树,如果不是,进行调整。
bool InsertAVL(BiTree *T,int e,bool *taller)
{
if (*T == nullptr)
{
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = EH;
*taller = true;
}
else
{
if (e == (*T)->data)//已存在结点,则不再插入
{
*taller = false;
return false;
}
if ( e < (*T)->data )//在T的左子树中插入
{
if (!InsertAVL(&(*T)->lchild, e, taller)) //递归插入结点
{//如果插入失败,目前失败的情况就是结点存在
return false;
}
if (*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 (!InsertAVL(&(*T)->lchild, e, taller)) //递归插入结点
{//如果插入失败,目前失败的情况就是结点存在
return false;
}
if (*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;
}