平衡二叉树(AVL树)

来源:互联网 发布:apache tiles 教程 编辑:程序博客网 时间:2024/05/21 18:53

平衡二叉树

或者是一颗空树,或者它的左右子树都是平衡二叉树,且左子树和右子树的深度之差不超过1 。

BF(Balance Factor)

我们将二叉树上节点的左子树深度减去右子树深度的值成为平衡因子。那么平衡二叉树上所有节点的平衡因子只可能是-1,0,1.只要二叉树上有一个节点的平衡引子的绝对值大于1,则该二叉树就是不平衡的。


距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树,我们成为最小不平衡子树最小不平衡子树的树根是我们操作平衡转换的参照点,调整最小不平衡子树,然后就可以达到平衡的效果。


首先要明白,如果需要调整平衡二叉树,那么这颗二叉树肯定是刚刚开始不平衡的,我们只需要调整因为插入新节点导致的最小不平衡子树的结构即可。

树的节点结构,相比于二叉查找树,增加一bf,用来存储平衡因子。

/* 二叉树的二叉链表节点结构定义 */typedef struct BiTNode{int data;int bf;/* 相对于二叉查找树,增加了平衡因子bf */struct BiTNode *lchild, *rchild;} BiTNode, *BiTree;

两个基本操作,左旋和右旋。

右旋图示:


代码如下:

/* 对以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( BiTree *p){/*  R->lchild (节点2的左孩子) 为NULL          R->lchild != NULL  例 :   1                                例 :   9                15           \             / \              /  \           2    =>    2                         6  15     =>     9    17             \        / \                           / \          / \   /             3      1   3                        10   17       6  10 16                                                        /         16*/BiTree R;R = (*p)->rchild;(*p)->rchild = R->lchild;/* 将 P 的右孩子的左孩子转接到 p 的右孩子上,上述的将 10 接至 9 的右孩子上 */R->lchild = *p;*p = R;}


两个复杂调整:

  1. 右子树插入新节点,导致不平衡,但是不能够通过简单的左旋、右旋解决。

    由于插入节点 8 导致出现了最小不平衡子树(树根为 6 ),于是现将以 9 为根的树右旋处理,达到图15效果,然后,在将以 6 为根的树左旋处理,最后二叉树重新平衡,达到图16的效果。 


代码如下:

/* 右平衡处理 *//* 对以指针T所指节点为根的二叉树作右平衡旋转处理,与左平衡相对 *//* 本算法结束时,指针T指向新的根节点 *//* 参照左平衡处理的图示分析 */void RightBalance( BiTree *T ){BiTree R, Rl;R = (*T)->rchild;switch( R->bf ){case RH:(*T)->bf = R->bf = EH;L_Rotate(T);break;case LH:Rl = R->lchild;switch( Rl->bf ){case LH:(*T)->bf = EH;R->bf = RH;break;case EH:(*T)->bf = R->bf = EH;case RH:(*T)->bf = LH;R->bf = EH;break;}Rl->bf = EH;R_Rotate( &(*T)->rchild );L_Rotate( T );}}


  2. 另一种复杂情况是在左子树上插入了新节点,导致不平衡,于是先左旋、再右旋。详见代码。

    代码如下:

/* 左平衡旋转处理的函数代码 *//* 对以指针T所指节点为根的二叉树作左平衡旋转处理 *//* 本算法结束时,指针T指向新的根节点 *//* 注意:函数被调用,传入一个需要调整平衡性的子树T。由于LeftBalance函数被调用时候 *//* 其实是已经确认当前子树是不平衡状态,且左子树高度大于右子树高度。 *//* 换句话说,此时T的根节点应该是平衡因子BF的值大于 1 的数 */void LeftBalance( BiTree *T ){BiTree L, Lr;L = (*T)->lchild;/* L指向T的左子树根节点 */switch( L->bf ){/* 检查T的左子树的平衡度,并做相应的平衡处理 */case LH:/* 新节点插入在T的左孩子的左子树上,要作单向右旋处理 *//*8(*T)                  5                   8                            5   / \                    / \                 / \                         /  \   (L)5   9                  4   8      or       5   9                       4    8     / \     =>(直接右旋)   /   / \             / \         =>(直接右旋)      \  / \    4   6                  3   6   9           4   6                           3 6  9   /                                            \   3(新插入)                                      3(新插入)   */(*T)->bf = L->bf = EH;R_Rotate( T );break;case RH:/* 新节点插入在T的左孩子的右子树上,要做双旋处理 */Lr = L->rchild;switch( Lr->bf ){case LH:/*8                                     8                                     7       / \                                   / \                                  /  \       5   9                                 7   9                                5    8     / \        =>(先以5为根,单向左旋)    /          =>(以8为根,单向右旋)     / \    \    4   7                                 5                                    4   6    9       /                                 / \       6                                 4   6  */(*T)->bf = RH;L->bf = EH;break;case EH:(*T)->bf = L->bf = EH;break;case RH:/* 8                                     8                                     6   / \                                   / \                                   / \      5   9                                 6   9                                 5   8     / \        =>(先以5为跟,单向左旋)    / \         =>(以8为跟,单向右旋)     /   / \     4   6                                 5   7                                 4   7   9         \                               /             7                             4*/(*T)->bf = EH;L->bf = LH;break;}Lr->bf = EH;L_Rotate( &(*T)->lchild );R_Rotate( T );}}


插入:(Insert_AVL)

    插入函数结合了上面的做右平衡处理的方法,现在平衡二叉树中查找是否已经存在该元素,如果已经存在,那么不插入,否则,插入新节点,再通过对应的处理,使二叉树保持平衡状态,更新每个结点的平衡因子。

/* 若在平衡的二叉排序树T中不存在和e有相同关键字的节点,则插入一个 *//* 数据元素为e的新节点并返回1,否则返回0 。若因插入而使二叉排序树 *//* 失去平衡,则作平衡旋转处理,布尔变量taller反应T是否长高 */int Insert_AVL( BiTree *T, int e, int *taller){if( !(*T) ){/* 递归调用Insert_AVL函数,如果进入if语句,说明原二叉排序树中不存在与e相等的结点 *//* 故而,插入新结点,树“长高”,置taller为TRUE */*T = ( BiTree )malloc( sizeof(BiTNode) );( *T )->data = e;( *T )->lchild = ( *T )->rchild = NULL;( *T )->bf = EH;*taller = TRUE;}else{if( e == (*T)->data ){/* 书中已存在与e相等的节点,不需要再次插入 */*taller = FALSE;return FALSE;}else if( e < (*T)->data ){/* 应该继续在T的左子树中进行搜索 */if( !Insert_AVL( &(*T)->lchild, e, taller) )/*说明插入失败,已经存在与e相等的节点,无需再次插入 */return FALSE;if(*taller){/* 已经插入到 T 的左子树,且左子树“长高” */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{/* 应该继续在T的右子树中进行搜索 */if( !Insert_AVL( &(*T)->rchild, e, taller) )/*说明插入失败,已经存在与e相等的节点,无需再次插入 */return FALSE;if(*taller){/* 已经插入到 T 的右子树且右子树“长高” */switch( (*T)->bf ){/* 检查 T 的平衡度 */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;}

完整代码:

#include<stdio.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( BiTree *p){/*  R->lchild (节点2的左孩子) 为NULL          R->lchild != NULL  例 :   1                                例 :   9                15           \                 / \              /  \           2    =>    2                         6  15     =>     9    17             \        / \                           / \          / \   /             3      1   3                        10   17       6  10 16                                                        /             16*/BiTree R;R = (*p)->rchild;(*p)->rchild = R->lchild;/* 将 P 的右孩子的左孩子转接到 p 的右孩子上,上述的将 10 接至 9 的右孩子上 */R->lchild = *p;*p = R;}/* 左平衡旋转处理的函数代码 *//* 对以指针T所指节点为根的二叉树作左平衡旋转处理 *//* 本算法结束时,指针T指向新的根节点 *//* 注意:函数被调用,传入一个需要调整平衡性的子树T。由于LeftBalance函数被调用时候 *//* 其实是已经确认当前子树是不平衡状态,且左子树高度大于右子树高度。 *//* 换句话说,此时T的根节点应该是平衡因子BF的值大于 1 的数 */void LeftBalance( BiTree *T ){BiTree L, Lr;L = (*T)->lchild;/* L指向T的左子树根节点 */switch( L->bf ){/* 检查T的左子树的平衡度,并做相应的平衡处理 */case LH:/* 新节点插入在T的左孩子的左子树上,要作单向右旋处理 *//*8(*T)                  5                   8                            5       / \                    / \                 / \                         /  \   (L)5   9                  4   8      or       5   9                       4    8     / \     =>(直接右旋)   /   / \             / \         =>(直接右旋)      \  / \    4   6                  3   6   9           4   6                           3 6  9   /                                            \   3(新插入)                                      3(新插入)   */(*T)->bf = L->bf = EH;R_Rotate( T );break;case RH:/* 新节点插入在T的左孩子的右子树上,要做双旋处理 */Lr = L->rchild;switch( Lr->bf ){case LH:/*8                                     8                                     7       / \                                   / \                                  /  \       5   9                                 7   9                                5    8     / \        =>(先以5为根,单向左旋)    /          =>(以8为根,单向右旋)     / \    \    4   7                                 5                                    4   6    9       /                                 / \       6                                 4   6  */(*T)->bf = RH;L->bf = EH;break;case EH:(*T)->bf = L->bf = EH;break;case RH:/* 8                                     8                                     6       / \                                   / \                                   / \      5   9                                 6   9                                 5   8     / \        =>(先以5为跟,单向左旋)    / \         =>(以8为跟,单向右旋)     /   / \     4   6                                 5   7                                 4   7   9         \                               /             7                             4*/(*T)->bf = EH;L->bf = LH;break;}Lr->bf = EH;L_Rotate( &(*T)->lchild );R_Rotate( T );}}/* 右平衡处理 *//* 对以指针T所指节点为根的二叉树作右平衡旋转处理,与左平衡相对 *//* 本算法结束时,指针T指向新的根节点 *//* 参照左平衡处理的图示分析 */void RightBalance( BiTree *T ){BiTree R, Rl;R = (*T)->rchild;switch( R->bf ){case RH:(*T)->bf = R->bf = EH;L_Rotate(T);break;case LH:Rl = R->lchild;switch( Rl->bf ){case LH:(*T)->bf = EH;R->bf = RH;break;case EH:(*T)->bf = R->bf = EH;case RH:(*T)->bf = LH;R->bf = EH;break;}Rl->bf = EH;R_Rotate( &(*T)->rchild );L_Rotate( T );}}/* 若在平衡的二叉排序树T中不存在和e有相同关键字的节点,则插入一个 *//* 数据元素为e的新节点并返回1,否则返回0 。若因插入而使二叉排序树 *//* 失去平衡,则作平衡旋转处理,布尔变量taller反应T是否长高 */int Insert_AVL( BiTree *T, int e, int *taller){if( !(*T) ){/* 递归调用Insert_AVL函数,如果进入if语句,说明原二叉排序树中不存在与e相等的结点 *//* 故而,插入新结点,树“长高”,置taller为TRUE */*T = ( BiTree )malloc( sizeof(BiTNode) );( *T )->data = e;( *T )->lchild = ( *T )->rchild = NULL;( *T )->bf = EH;*taller = TRUE;}else{if( e == (*T)->data ){/* 书中已存在与e相等的节点,不需要再次插入 */*taller = FALSE;return FALSE;}else if( e < (*T)->data ){/* 应该继续在T的左子树中进行搜索 */if( !Insert_AVL( &(*T)->lchild, e, taller) )/*说明插入失败,已经存在与e相等的节点,无需再次插入 */return FALSE;if(*taller){/* 已经插入到 T 的左子树,且左子树“长高” */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{/* 应该继续在T的右子树中进行搜索 */if( !Insert_AVL( &(*T)->rchild, e, taller) )/*说明插入失败,已经存在与e相等的节点,无需再次插入 */return FALSE;if(*taller){/* 已经插入到 T 的右子树且右子树“长高” */switch( (*T)->bf ){/* 检查 T 的平衡度 */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;}void main(){int i;int a[] = {3,2,1,4,5,6,7,10,9,8};BiTree T = NULL;int taller;for( i = 0; i < 10; i++)Insert_AVL(&T, a[i], &taller);order(T);printf("\n");}

总结,待续……



0 0