平衡二叉树
对于二叉查找树,尽管查找、插入及删除操作的平均运行时间为O(logn),但是它们的最差运行时间都是O(n),原因在于对树的形状没有限制。
平衡二叉树又称为AVL树,它或者是一棵空树,或者是有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左右子树的深度之差的绝对值不超过1。二叉树的的平衡因子BF为:该结点的左子树的深度减去它的右子树的深度,则平衡二叉树的所有结点的平衡因子为只可能是:-1、0和1
一棵好的平衡二叉树的特征:
(1)保证有n个结点的树的高度为O(logn)
(2)容易维护,也就是说,在做数据项的插入或删除操作时,为平衡树所做的一些辅助操作时间开销为O(1)
一、平衡二叉树的构造
在一棵二叉查找树中插入结点后,调整其为平衡二叉树。若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树
1.调整方法
(1)插入点位置必须满足二叉查找树的性质,即任意一棵子树的左结点都小于根结点,右结点大于根结点
(2)找出插入结点后不平衡的最小二叉树进行调整,如果是整个树不平衡,才进行整个树的调整。
2.调整方式
(1)LL型
LL型:插入位置为左子树的左结点,进行向右旋转
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1变为2,成为不平衡的最小二叉树根结点。此时A结点顺时针右旋转,旋转过程中遵循“旋转优先”的规则,A结点替换D结点成为B结点的右子树,D结点成为A结点的左孩子。
(2)RR型
RR型:插入位置
为右子树的右孩子,进行向左旋转
由于在A的右子树C的右子树插入了结点F,A的平衡因子由-1变为-2,成为不平衡的最小二叉树根结点。此时,A结点逆时针左旋转,遵循“旋转优先”的规则,A结点替换D结点成为C的左子树,D结点成为A的右子树。
(3)LR型:可以理解为先左旋成LL型,再按照LL型处理
LR型:插入位置为
左子树的右孩子,要进行两次旋转,先左旋转,再右旋转;第一次最小不平衡子树的根结点先不动,调整插入结点所在的子树,第二次再调整最小不平衡子树。
(4)RL型
RL型:插入位置为右子树的左孩子,进行两次调整,先右旋转再左旋转;处理情况与LR类似。
构造实例:
选取一组数据分别为2,1,0,3,4,5,6,9,8,7的10个结点来构造平衡二叉树。
(1)首先数据为2的结点作为根结点插入,接着插入1,仍是平衡的,再插入0是,2的平衡因子变为2,此时出现了不平衡,因此需要进行调整,最低不平衡结点为2,属于LL型,根据上述网址的内容,则调整过程如图1所示。
图 1
(2)接着插入3,是平衡的,再插入4,此时出现了不平衡,结点 1 和 2 的平衡因子都为 -2,结点2为最低不平衡结点,属于RR型,调整过程如图2所示。
图 2
(3)接着插入5,此时结点 1 的平衡因子为 -2,导致不平衡,结点1为最低不平衡结点,属于RR型,调整如图3所示。
图 3
(4)接着插入6,此时结点4的平衡因子为 -2,导致不平衡,结点4为最低不平衡结点,属于RR型,调整如图4所示。
图 4
(5)接着插入9,是平衡的,再插入8,此时结点 3、5、6 的平衡因子都为 -2,导致不平衡,结点6为最低不平衡结点,属于RL型,调整如图5所示。
图 5
(6)插入7,此时结点3、5的平衡因子为 -2,导致不平衡,最低不平衡结点为5,属于RL型,调整如图6所示。
图 6
平衡二叉树的实现代码如下
- #include <stdio.h>
- #include <stdlib.h>
- #define TRUE 1
- #define FALSE 0
- // 定义平衡二叉树的结点结构
- typedef struct BiTNode
- {
- int data;
- int bf; // 平衡因子
- BiTNode *lchild, *rchild;
- }BiTNode, *BiTree;
- typedef int Status;
- // 右旋处理,即LL调整
- void R_Rotate( BiTree *p )
- {
- BiTree L;
- L = (*p)->lchild;
- (*p)->lchild = L->rchild;
- L->rchild = *p;
- *p = L;
- }
- // 左旋处理,即RR调整
- void L_Rotate( BiTree *p )
- {
- BiTree R;
- R = (*p)->rchild;
- (*p)->rchild = R->lchild;
- R->lchild = *p;
- *p = R;
- }
- // 左平衡旋转处理,包括 LL 和 LR 调整
- void LeftBalance( BiTree *T )
- {
- BiTree L, Lr;
- L = (*T)->lchild;
- switch( L->bf )
- {
- case 1: // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整
- (*T)->bf = L->bf = 0;
- R_Rotate( T );
- break;
- case -1: // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理
- Lr = L->rchild;
- switch( Lr->bf )
- {
- case 1:
- (*T)->bf = -1;
- L->bf = 0;
- break;
- case 0:
- (*T)->bf = L->bf = 0;
- break;
- case -1:
- (*T)->bf = 0;
- L->bf = 1;
- break;
- }
- Lr->bf = 0;
- L_Rotate( &(*T)->lchild ); // 先对T的左子树进行左旋处理即RR调整
- R_Rotate( T ); // 再对T进行右旋处理即LL调整
- }
- }
- // 右平衡旋转处理,包括 RR 和 RL 调整
- void RightBalance( BiTree *T )
- {
- BiTree R, Rl;
- R = (*T)->rchild;
- switch( R->bf )
- {
- case -1: // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整
- (*T)->bf = R->bf = 0;
- L_Rotate( T );
- break;
- case 1: // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理
- Rl = R->lchild;
- switch( Rl->bf )
- {
- case 1:
- (*T)->bf = 0;
- R->bf = -1;
- break;
- case 0:
- (*T)->bf = R->bf = 0;
- break;
- case -1:
- (*T)->bf = 1;
- R->bf = 0;
- break;
- }
- Rl->bf = 0;
- R_Rotate( &(*T)->rchild ); // 先对T的左子树进行左旋即RR调整
- L_Rotate( T ); // 再对T进行右旋即LL调整
- }
- }
- // 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,
- // 若因插入使二叉排序树失去平衡,则要作平衡调整,
- // 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加
- Status InsertAVL( BiTree *T, int e, Status *taller )
- {
- if( !*T )
- {
- *T = (BiTree)malloc(sizeof(BiTNode));
- (*T)->data = e;
- (*T)->lchild = (*T)->rchild = NULL;
- (*T)->bf = 0;
- *taller = TRUE;
- }
- else
- {
- // 树中已有和e具有相同数据的结点,则不再插入
- if( e == (*T)->data )
- {
- *taller = FALSE;
- return FALSE;
- }
- if( e < (*T)->data ) // 继续在T的左子树进行搜索
- {
- if( !InsertAVL( &(*T)->lchild, e, taller ) ) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
- {
- return FALSE;
- }
- // 如果e已插入到T的左子树中,且左子树深度增加
- if( *taller )
- {
- switch( (*T)->bf ) // 检查T的平衡因子
- {
- case 1: // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整
- LeftBalance( T );
- *taller = FALSE;
- break;
- case 0: // 原本左右子树等高,再加上此结点,左子树增高
- (*T)->bf = 1;
- *taller = TRUE;
- break;
- case -1: // 原本右子树比左子树高,再加上此结点,左右子树变为等高
- (*T)->bf = 0;
- *taller = FALSE;
- break;
- }
- }
- }
- else // 在T的右子树进行搜索
- {
- if( !InsertAVL( &(*T)->rchlid, 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: // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整
- RightBalance( T );
- *taller = FALSE;
- break;
- }
- }
- }
- }
- return TRUE;
- }
- // 对于实例,我们可以这样创建平衡二叉树
- void CreateAVL()
- {
- int i;
- int a[10] = {2,1,0,3,4,5,6,9,8,7};
- BiTree T = NULL;
- Status taller;
- for( i=0; i<10; i++ )
- {
- InsertAVL( &T, a[i], &taller );
- }
- }