一、概念
平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。
平衡因子(BF):结点的左子树深度减去右子树深度的值称为平衡因子。
最小不平衡子树:插入进来一个元素后,变得不平衡的最小部分。
二、实现原理
当最小不平衡子树根结点的平衡因子BF大于1时,则右旋,小于-1时,则左旋;
插入结点后,最小不平衡子树的BF与它的子树的BF符号相反时,先将结点进行一次旋转使得符号相同后,再反方向旋转一次。
将图1中的二叉排序树变为图二中的平衡二叉树:
1、左旋情况
2、右旋情况
3、需要先旋转一次将符号变成一致,在反方向旋转一次得到平衡(分开写的话这里包含两种情况)
三、代码实现
当一个元素插入进左子树后,有三种情况:1、插入进来后树的左比右高2,则需要做左平衡;2、插入进来后左比右高1或者等高,不做平衡变换;
同理:当一个元素插入进右子树后,也有三种情况。
注意:做左平衡时有两种情况,一种是只做一次单右旋;一种是做一次单左旋,再做一次单右旋。
//1、右旋函数
void r_Rotate(tNode *t) //*t指向根结点
{
tNode temp;
temp = *t;
*t = (*t)->lchild;
temp->lchild = (*t)->rchild;
(*t)->rchild = temp;
}
//2、左旋函数
void l_Rotate(tNode *t) //*t指向根结点
{
tNode temp;
temp = *t;
*t = (*t)->rchild;
temp->rchild = (*t)->lchild;
(*t)->lchild = temp;
}
//3、最小不平衡子树的BF与它的子树的BF符号相反时,先将结点进行一次旋转使得符号相同后,再反方向旋转一次
//3.1根结点bf为正2,左子树的bf为-1:先将左子树左旋,再整个树右旋
void l_r_Rotate(tNode *t)
{
//左子树左旋
l_Rotate( &((*t)->lchild) );
//整个树右旋
r_Rotate(t);
}
void r_l_Rotate(tNode *t)
{
//左子树左旋
r_Rotate( &((*t)->rchild) );
//整个树右旋
l_Rotate(t);
}
注意:不能上述三个函数里面处理bf,因为双旋函数调用了单旋函数,而双旋函数中bf不能那样处理
总代码:
typedef struct treeNode node;
typedef struct treeNode
{
int data;
int bf; //平衡因子
node *rchild, *lchild;
}node, *tNode;
#define LH +1 //左高
#define EH 0 //等高
#define RH -1 //右高
#define TRUE 1
#define FALSE 0
//创建平衡二叉树
void createAVL(tNode *T)
{
int e;
scanf("%d", &e);
if(65535 != e)
{
insertAVL(T, e);
scanf("%d", &e);
}
}
//向平衡二叉树中插入元素
int insertAVL(tNode *T, int e)
{
if(!(*T))
{/*插入新节点,树长高*/
*T = (tNode)malloc(sizeof(node));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = EH; //默认为等高
}
else
{
if(e == (*T)->data)
{//树中已存在和e相同的关键字则结点不再插入
return FALSE;
}
if(e < (*T)->data)
{//则沿着左子树向下找插入点
if(!insertAVL( &((*T)->lchild), e )) //未插入
return FALSE;
else //已经插入,并且左子树长高了
{
switch((*T)->bf) //检查T的平衡度 , 其实真正的bf = (*T)-bf+1
{
case LH: //原本左子树比右子树高,现在插入一个元素后,左子树bf比右子树大2,所以要做做平衡处理
leftBalance(T);
break;
case EH: //原本左右等高,现在插入一个元素后,左比右高1
(*T)->bf = LH;
break;
case RH: //原本右子树比左子树高,现在等高
(*T)->bf = EH;
break;
}
}
}
else
{//则沿着右子树向下找插入点
if(!insertAVL( &((*T)->rchild), e )) //未插入
return FALSE;
else //已经插入,并且左子树长高了
{
switch((*T)->bf) //检查T的平衡度 , 其实真正的bf = (*T)-bf+1
{
case LH: //原本左子树比右子树高,现在等高
(*T)->bf = EH;
break;
case EH: //原本左右等高,现在插入一个元素后,右比左高1
(*T)->bf = RH;
break;
case RH: //原本右子树比左子树高,现在插入一个元素后,右子树bf比左子树大2,所以要做做平衡处理
rightBalance(T);
break;
}
}
}
}
return TRUE;
}
void leftBalance(tNode *T)
{
tNode L, Lr;
L = (*T)->lchild;
switch(L->bf) //不可能是等高EH
{//检查左子树的平衡度,由此做出相应的平衡处理
case LH: //表明新节点是插入在T的左孩子的左子树上面,所以做右旋
(*T)->bf = ((*T)->lchild)->bf = EH; //旋转后的平衡因子bf
r_Rotate(T);
break;
case RH: //表明新节点是插入在T的左孩子的右子树上面,所以做双旋
/*处理旋转后平衡因子bf的值*/
Lr = L->rchild;
switch(Lr->bf) //修改对应的t及其左孩子的平衡因子bf
{
case LH:
(*T)->bf = RH;
L->bf = EH;
break;
case EH:
(*T)->bf = L->bf = EH;
case RH:
(*T)->bf = EH;
L->bf = LH;
break;
}
Lr->bf = EH;
/*bf处理结束*/
l_r_Rotate(T);
break;
}
}
void rightBalance(tNode *T)
{
tNode R, Rl;
R = (*T)->rchild;
switch(R->bf) //不可能是等高EH
{//检查左子树的平衡度,由此做出相应的平衡处理
case RH: //表明新节点是插入在T的右孩子的右子树上面,所以做左旋
(*T)->bf = ((*T)->rchild)->bf = EH; //旋转后的平衡因子bf
l_Rotate(T);
break;
case LH: //表明新节点是插入在T的右孩子的左子树上面,所以做双旋
/*处理旋转后平衡因子bf的值*/
Rl = R->lchild;
switch(Rl->bf) //修改对应的t及其右孩子的平衡因子bf
{
case RH:
(*T)->bf = LH;
R->bf = EH;
break;
case EH:
(*T)->bf = R->bf = EH;
case LH:
(*T)->bf = EH;
R->bf = RH;
break;
}
Rl->bf = EH;
/*bf处理结束*/
l_r_Rotate(T);
break;
}
}
//1、右旋函数, 注意:不能再这里面处理bf,因为双旋函数调用了它,而双旋函数中bf不能这样处理
void r_Rotate(tNode *t) //*t指向根结点
{
tNode temp;
temp = *t;
*t = (*t)->lchild;
temp->lchild = (*t)->rchild;
(*t)->rchild = temp;
}
//2、左旋函数
void l_Rotate(tNode *t) //*t指向根结点
{
tNode temp;
temp = *t;
*t = (*t)->rchild;
temp->rchild = (*t)->lchild;
(*t)->lchild = temp;
}
//3、最小不平衡子树的BF与它的子树的BF符号相反时,先将结点进行一次旋转使得符号相同后,再反方向旋转一次
//3.1根结点bf为正2,左子树的bf为-1:先将左子树左旋,再整个树右旋
void l_r_Rotate(tNode *t)
{
//左子树左旋
l_Rotate( &((*t)->lchild) );
//整个树右旋
r_Rotate(t);
}
void r_l_Rotate(tNode *t)
{
//左子树左旋
r_Rotate( &((*t)->rchild) );
//整个树右旋
l_Rotate(t);
}
void PreOrderTraverse(tNode T) /*前序遍历算法*/
{
if( NULL == T )
return;
printf("%d-->", T->data); //先显示根结点
PreOrderTraverse(T->lchild); //再遍历左子树
PreOrderTraverse(T->rchild); //最后遍历右子树
}