平衡二叉树的定义
平衡二叉树(Balance Binary Tree),简称平衡树(AVL树),树上任意结点的左子树和右子树的高度之差不超过1。
平衡因子=左子树高-右子树高
平衡二叉树结点的平衡因子的值只可能是1、-1、0。只要有任意结点的平衡因子不等于这三个值,那么二叉树不平衡。
平衡二叉树的插入
插入一个新结点,可能会破坏二叉树的平衡。
引入最小不平衡子树的概念:从插入结点往回找到第一个不平衡的结点,以该结点为根的子树就是最小不平衡子树。之后所有的操作都是针对最小不平衡子树的。
对于根结点为A的最小不平衡子树,分四种情况:
- LL平衡旋转(右单旋转)。A的左孩子的左子树上插入了新结点,导致失衡,应进行右旋操作,将A的左孩子B向右上旋转,代替A成为根结点,将A右下旋成为B的右子树的根结点,而B的原右子树作为A的左子树。
- RR平衡旋转(左单旋转)。A的右孩子的右子树上插入了新结点,导致失衡,应进行左旋操作,将A的右孩子B向左上旋转,代替A成为根结点,将A左下旋转成B的左子树的根结点,而B的原左子树作为A的右子树。
- LR平衡旋转(先左后右双旋转)。A的左孩子的右子树上插入了新结点,导致失衡,这种情况需要两次旋转。将A结点的左孩子B的右子树的根结点C左上旋转提升到B的位置,再将C右上旋转提升到A的位置。
- RL平衡旋转(先右后左双旋转)。A的右孩子的左子树上插入了新结点,导致失衡,方法类似上面的做法,将A的右孩子B的左子树的根结点C右上旋转提升到B的位置,再将C左上旋转提升到A的位置。
通过分析以上四种情况,我们可以看出:
1.只有左孩子才可以右上旋转,只有右孩子才可以左上旋转。
2.每次的操作都一定都是在最小不平衡子树当中进行的。
3.调整了最小不平衡子树使其平衡之后,该路径上的所有结点的都会恢复平衡。
查找效率分析
1.假设Nh为深度为h的平衡树中含有的最少结点数,那么可以得到N0=0,N1=1,N2=2,递推得到公式Nh=Nh-1+Nh-2+1。
2.含有N个结点的平衡二叉树最大深度为O(log2 n),平均查找长度为O(log2 n)。
附加:代码实现
#include<stdio.h>
#include<stdlib.h>
#define LH +1 /* 左高 */
#define EH 0 /* 等高 */
#define RH -1 /* 右高 */
#define TRUE 1
#define FALSE 0
/* 二叉树的二叉链表节点结构定义 */
typedef struct BiTNode
{
int data;
int bf; /* 相对于二叉查找树,增加了平衡因子bf */
struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;
void order(BiTree t)//中序输出
{
if (t == NULL)
return;
order(t->lchild);
printf("%d ", t->data);
order(t->rchild);
}
/* 对以p为根的二叉排序树作右旋处理 */
/* 处理之后p指向新的树根节点,即旋转处理之前的左子树的根节点 */
void R_Rotate(BiTree* p)
{
/*
L->rchild (节点2的右孩子) 为NULL L->rchild != NULL
例 : 3 例 : 9 6
/ / \ / \
2 => 2 6 10 => 5 9
/ / \ / \ / / \
1 1 3 5 7 4 7 10
/
4
*/
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild; /* 容易忽视,将*p的左子树的右子树作为*p的左子树(如果 L->rchild 不为空) */
L->rchild = (*p);
*p = L;
}
/* 对以p为根的二叉排序树作左旋处理 */
/* 处理之后 p 指向新的树根节点,即旋转处理之前的右子树的根节点 */
void L_Rotate