平衡二叉树

平衡二叉树

对于二叉查找树,尽管查找、插入及删除操作的平均运行时间为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型:插入位置为 左子树的右孩子,要进行两次旋转,先左旋转,再右旋转;第一次最小不平衡子树的根结点先不动,调整插入结点所在的子树,第二次再调整最小不平衡子树。

 
 由于在A的左子树B的右子树上插入了结点F,A的平衡因子由1变为了2,成为不平衡的最小二叉树根结点。第一次旋转A结点不动,先将B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。
(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


平衡二叉树的实现代码如下

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. #define TRUE  1  
  5. #define FALSE 0  
  6.   
  7. // 定义平衡二叉树的结点结构  
  8. typedef struct BiTNode  
  9. {  
  10.     int data;  
  11.     int bf;             // 平衡因子  
  12.     BiTNode *lchild, *rchild;  
  13. }BiTNode, *BiTree;  
  14.   
  15. typedef int Status;  
  16.   
  17. // 右旋处理,即LL调整  
  18. void R_Rotate( BiTree *p )  
  19. {  
  20.     BiTree L;  
  21.     L = (*p)->lchild;  
  22.     (*p)->lchild = L->rchild;  
  23.     L->rchild = *p;  
  24.     *p = L;  
  25. }  
  26.   
  27. // 左旋处理,即RR调整  
  28. void L_Rotate( BiTree *p )  
  29. {  
  30.     BiTree R;  
  31.     R = (*p)->rchild;  
  32.     (*p)->rchild = R->lchild;  
  33.     R->lchild = *p;  
  34.     *p = R;  
  35. }  
  36.   
  37. // 左平衡旋转处理,包括 LL 和 LR 调整  
  38. void LeftBalance( BiTree *T )  
  39. {  
  40.     BiTree L, Lr;  
  41.     L = (*T)->lchild;  
  42.     switch( L->bf )  
  43.     {  
  44.     case 1:        // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整  
  45.         (*T)->bf = L->bf = 0;  
  46.         R_Rotate( T );  
  47.         break;  
  48.     case -1:        // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理  
  49.         Lr = L->rchild;  
  50.         switch( Lr->bf )  
  51.         {  
  52.         case 1:  
  53.             (*T)->bf = -1;  
  54.             L->bf = 0;  
  55.             break;  
  56.         case 0:  
  57.             (*T)->bf = L->bf = 0;  
  58.             break;  
  59.         case -1:  
  60.             (*T)->bf = 0;  
  61.             L->bf = 1;  
  62.             break;  
  63.         }  
  64.         Lr->bf = 0;  
  65.         L_Rotate( &(*T)->lchild );      // 先对T的左子树进行左旋处理即RR调整  
  66.         R_Rotate( T );                  // 再对T进行右旋处理即LL调整  
  67.     }  
  68. }  
  69.   
  70. // 右平衡旋转处理,包括 RR 和 RL 调整  
  71. void RightBalance( BiTree *T )  
  72. {  
  73.     BiTree R, Rl;  
  74.     R = (*T)->rchild;  
  75.     switch( R->bf )  
  76.     {  
  77.     case -1:        // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整  
  78.         (*T)->bf = R->bf = 0;  
  79.         L_Rotate( T );  
  80.         break;  
  81.     case 1:        // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理  
  82.         Rl = R->lchild;  
  83.         switch( Rl->bf )  
  84.         {  
  85.         case 1:  
  86.             (*T)->bf = 0;  
  87.             R->bf = -1;  
  88.             break;  
  89.         case 0:  
  90.             (*T)->bf = R->bf = 0;  
  91.             break;  
  92.         case -1:  
  93.             (*T)->bf = 1;  
  94.             R->bf = 0;  
  95.             break;  
  96.         }  
  97.         Rl->bf = 0;  
  98.         R_Rotate( &(*T)->rchild );      // 先对T的左子树进行左旋即RR调整  
  99.         L_Rotate( T );                  // 再对T进行右旋即LL调整  
  100.     }  
  101. }  
  102.   
  103. // 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,  
  104. // 若因插入使二叉排序树失去平衡,则要作平衡调整,  
  105. // 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加  
  106.   
  107. Status InsertAVL( BiTree *T, int e, Status *taller )  
  108. {  
  109.     if( !*T )  
  110.     {  
  111.         *T = (BiTree)malloc(sizeof(BiTNode));  
  112.         (*T)->data = e;  
  113.         (*T)->lchild = (*T)->rchild = NULL;  
  114.         (*T)->bf = 0;  
  115.         *taller = TRUE;  
  116.     }  
  117.     else  
  118.     {  
  119.         // 树中已有和e具有相同数据的结点,则不再插入  
  120.         if( e == (*T)->data )  
  121.         {  
  122.             *taller = FALSE;  
  123.             return FALSE;  
  124.         }  
  125.   
  126.         if( e < (*T)->data )    // 继续在T的左子树进行搜索  
  127.         {  
  128.             if( !InsertAVL( &(*T)->lchild, e, taller ) ) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息  
  129.             {  
  130.                 return FALSE;  
  131.             }  
  132.   
  133.             // 如果e已插入到T的左子树中,且左子树深度增加  
  134.             if( *taller )  
  135.             {  
  136.                 switch( (*T)->bf )      // 检查T的平衡因子  
  137.                 {  
  138.                 case 1:                // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整  
  139.                     LeftBalance( T );  
  140.                     *taller = FALSE;  
  141.                     break;  
  142.                 case 0:                // 原本左右子树等高,再加上此结点,左子树增高  
  143.                     (*T)->bf = 1;  
  144.                     *taller = TRUE;  
  145.                     break;  
  146.                 case -1:                // 原本右子树比左子树高,再加上此结点,左右子树变为等高  
  147.                     (*T)->bf = 0;  
  148.                     *taller = FALSE;  
  149.                     break;  
  150.                 }  
  151.             }  
  152.         }  
  153.         else        // 在T的右子树进行搜索  
  154.         {  
  155.             if( !InsertAVL( &(*T)->rchlid, e, taller ) )  
  156.             {  
  157.                 return FALSE;  
  158.             }  
  159.   
  160.             if( *taller )  
  161.             {  
  162.                 switch( (*T)->bf )  
  163.                 {  
  164.                 case 1:                // 原先左子树比右子树高,现在左右子树等高  
  165.                     (*T)->bf = 0;  
  166.                     *taller = FALSE;  
  167.                     break;  
  168.                 case 0:                // 原先左右子树等高,现在右子树增高  
  169.                     (*T)->bf = -1;  
  170.                     *taller = TRUE;  
  171.                     break;  
  172.                 case -1:                // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整  
  173.                     RightBalance( T );  
  174.                     *taller = FALSE;  
  175.                     break;  
  176.                 }  
  177.             }  
  178.         }  
  179.     }  
  180.   
  181.     return TRUE;  
  182. }  
  183.   
  184. // 对于实例,我们可以这样创建平衡二叉树  
  185. void CreateAVL()  
  186. {  
  187.     int i;  
  188.     int a[10] = {2,1,0,3,4,5,6,9,8,7};  
  189.     BiTree T = NULL;  
  190.     Status taller;  
  191.   
  192.     for( i=0; i<10; i++ )  
  193.     {  
  194.         InsertAVL( &T, a[i], &taller );  
  195.     }  
  196. }  

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值