概念
平衡二叉树(Balanced binary tree)是由苏联数学家Adelson-Velskii and Landis于1962年首先提出的,所以又称为AVL树。
定义:平衡二叉树或为空树,或满足如下性质的二叉树:
(1)本身首先是一棵二叉搜索树
(2)左右子树深度之差的绝对值不超过1;
(3)左右子树仍然为平衡二叉树.
平衡因子BF=左子树深度-右子树深度.
平衡二叉树每个结点的平衡因子只能是1,0,-1。若其绝对值超过1,则该二叉排序树就是不平衡的。
通过下面幅图可以看出,平衡二叉树可以保证最差查找效率为O(logN),而二叉查找树的最差查找效率为O(N)。
![](https://img-blog.csdn.net/20140328231036687?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
插入节点的调整方法
若向平衡二叉树中插入一个节点后破坏了平衡二叉树的平衡性,首先从根节点到该新插入节点的路径之逆向根节点方向找到第一个失去平衡的节点,然后以该失衡节点和它相邻的刚查找过的两个节点构成调整子树,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原来其他所有不平衡子树无需调整,整个二叉排序树就又成了一个平衡二叉树。
失去平衡的最小子树是指以离插入节点最近,且平衡因子绝对值大于1的节点作为根的子树。
(1)LL型平衡旋转法
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。
![](https://img-blog.csdn.net/20140328231352953?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(2)RR型平衡旋转法
由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
![](https://img-blog.csdn.net/20140328231411593?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(3)LR型平衡旋转法
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
![](https://img-blog.csdn.net/20140328231432203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。
(4)RL型平衡旋转法
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。
![](https://img-blog.csdn.net/20140328231454562?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。
平衡化靠的是旋转。参与旋转的是3个节点(其中一个可能是外部节点NULL),旋转就是把这3个节点转个位置。注意的是,左旋的时候p->right一定不为空,右旋的时候p->left一定不为空,这是显而易见的。
如果从空树开始建立,并时刻保持平衡,那么不平衡只会发生在插入删除操作上,而不平衡的标志就是出现bf == 2或者 bf == -2的节点。
删除节点的调整方法
在平衡二叉树上删除节点x(假定有且仅有一个节点等于x)的过程如下:
1、采用二叉排序树的删除方法找到节点x并删除之;
2、沿着根到被删除节点的路线之逆逐层向上查找,必要时修改x祖先节点的平衡因子,因为删除后会使得某些子树的高度降低;
3、查找途中,一旦发现x的某个祖先*p失衡,就要进行调整。不妨设x节点在*p的左子树中,在*p节点失衡后,要做何种调整,取决于*p节点的右孩子,*p1,若*p1的平衡因子是1,则做RL调整;若*p1的平衡因子是-1,则做RR调整;若*p1的平衡因子是0,则做RL或者RR调整均可。如果x在*p右子树中,调整过程类似。
4、如果调整后,子树的高度降低了,这个过程还要继续,直到根节点为止。即,删除一个节点可能会引起多次调整,而不是插入时的至多一次调整。
节点查找
和二叉排序树完全相同。
C语言实现一
由于没有删除某一个节点的功能,故在数据结构的定义中没有提供父节点指针。
- #include <stdio.h>
- #include <malloc.h>
-
- typedef enum
- {
- EH = 0,
- LH = 1,
- RH = -1
- }bh_t;
-
-
- typedef enum
- {
- FALSE = 0,
- TRUE = 1
- }bool_t;
-
- typedef int ElemType;
-
- typedef struct BSTNode
- {
- ElemType key;
- int bf;
- struct BSTNode *lchild,*rchild;
- }BSTNode,*BSTree;
-
-
- void InOrderTraverse(BSTree root)
- {
- if(NULL != root)
- {
- InOrderTraverse(root->lchild);
- printf("%d\t",root->key);
- InOrderTraverse(root->rchild);
- }
- }
-
-
- void PreOrderTraverse(BSTree root)
- {
- if(NULL != root)
- {
- printf("%d\t",root->key);
- PreOrderTraverse(root->lchild);
- PreOrderTraverse(root->rchild);
- }
- }
-
-
- void R_Rotate(BSTree *p)
- {
- BSTree lc=(*p)->lchild;
- (*p)->lchild=lc->rchild;
- lc->rchild=*p;
- *p=lc;
- }
-
-
- void L_Rotate(BSTree *p)
- {
- BSTree rc=(*p)->rchild;
- (*p)->rchild=rc->lchild;
- rc->lchild=*p;
- *p=rc;
- }
-
-
- void LeftBalance(BSTree *T)
- {
- BSTree lc=(*T)->lchild;
- BSTree rd = lc->rchild;
- switch(lc->bf)
- {
- case LH:
- (*T)->bf=lc->bf=EH;
- R_Rotate(T);
- break;
- case RH:
- switch(rd->bf)
- {
- case LH:
- (*T)->bf=RH;
- lc->bf=EH;
- break;
- case EH:
- (*T)->bf=lc->bf=EH;
- break;
- case RH:
- (*T)->bf=EH;
- lc->bf=LH;
- break;
- }
- rd->bf=EH;
- L_Rotate(&((*T)->lchild));
- R_Rotate(T);
- break;
- }
- }
-
-
- void RightBalance(BSTree *T)
- {
- BSTree rc=(*T)->rchild;
- BSTree ld=rc->lchild;
- switch(rc->bf)
- {
- case RH:
- (*T)->bf=rc->bf=EH;
- L_Rotate(T);
- break;
- case LH:
- switch(ld->bf)
- {
- case RH:
- (*T)->bf=LH;
- rc->bf=EH;
- break;
- case EH:
- (*T)->bf=rc->bf=EH;
- break;
- case LH:
- (*T)->bf=EH;
- rc->bf=RH;
- break;
- }
- ld->bf=EH;
- R_Rotate(&((*T)->rchild));
- L_Rotate(T);
- break;
- }
- }
-
-
- bool_t InsertAVL(BSTree *t,ElemType e,bool_t *taller)
- {
- if(NULL == t)
- return FALSE;
- if(NULL == *t)
- {
- *t=(BSTree)malloc(sizeof(BSTNode));
- if(NULL == *t)
- return FALSE;
- (*t)->key=e;
- (*t)->lchild=(*t)->rchild=NULL;
- (*t)->bf=EH;
- *taller=TRUE;
- }
- else
- {
- if(e==(*t)->key)
- {
- *taller=FALSE;
- return FALSE;
- }
- if(e<(*t)->key)
- {
- if(FALSE == 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(FALSE == InsertAVL(&((*t)->rchild),e,taller))
- return FALSE;
- if(*taller)
- {
- switch((*t)->bf)
- {
- case RH:
- RightBalance(t);
- *taller=FALSE;
- break;
- case EH:
- (*t)->bf=RH;
- *taller=TRUE;
- break;
- case LH:
- (*t)->bf=EH;
- *taller=FALSE;
- break;
- }
- }
- }
- }
- return TRUE;
- }
-
-
- BSTree searchAVL(BSTree t,ElemType key)
- {
- BSTree p=t;
- while(p)
- {
- if(p->key==key)
- return p;
- else if(p->key<key)
- p=p->rchild;
- else
- p=p->lchild;
- }
- return p;
- }
-
- static void destroy(BSTree *t)
- {
- if(NULL != *t)
- {
- destroy(&((*t)->lchild));
- destroy(&((*t)->rchild));
- free(*t);
- *t = NULL;
- }
- return;
- }
- void destroyAVL(BSTree root)
- {
- if(NULL != root)
- {
- destroy(&root);
- }
- return;
- }
-
- int main(int argc,char *argv[])
- {
- BSTree root=NULL,r;
- bool_t taller=FALSE;
- int array[]={13,24,37,90,53};
- int i = 0;
- for(i=0;i<5;i++)
- InsertAVL(&root,array[i],&taller);
- printf("inorder traverse\n");
- InOrderTraverse(root);
-
- printf("\npreorder traverse\n");
- PreOrderTraverse(root);
-
- printf("\nsearch key\n");
- r=searchAVL(root,37);
- if(r)
- {
- printf("%d\n",r->key);
- }
- else
- {
- printf("not find!\n");
- }
- destroyAVL(root);
- root = NULL;
- }
![](https://img-blog.csdn.net/20140329113058640?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG5neW5naGF5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)