zoukankan      html  css  js  c++  java
  • 查找->动态查找表->平衡二叉树

    文字描述

    平衡二叉树(Balanced Binary Tree或Height-Balanced Tree)

      因为是俄罗斯数学家G.M.Adel’son-Vel’skii和E.M.Landis在1962年提出来的,所以又称AVL树。它或者是一颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。若将二叉树上结点的平衡因子BF(Balanced Factor)定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1,0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

      那么如何使二叉排序树成为平衡树呢?即在一颗二叉排序树中因插入一个结点后失去平衡的话,怎么调整才能使之重新平衡呢?

      一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针a(即a是离插入结点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行调整的规律可归纳为下面4中情况:

      (1)单向右旋平衡处理,图9.13(a)所示:在*a的左子树根结点的左子树上插入结点后,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次向右的顺时针旋转操作。

      (2)双向旋转(先左后右),图9.13(b)所示:在*a的左子树根结点的右子树上插入结点后,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。

      (3)单向左旋平衡处理,图9.13(c)所示:在*a的右子树根结点的右子树上插入结点后,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次向左的逆时针旋转操作。

      (4)双向旋转(先右后左),图9.13(d)所示:在*a的右子树根结点的左子树上插入结点后,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次选择(先右旋后左旋)

    上诉4种情况中,(1)和(3)对称,(2)和(4)对称。它们旋转后依然能保持二叉排序树的特性且由不平衡变为平衡。可以用二叉排序树的特性(”对于二叉排序树,中序遍历所得关键字序列自小至大有序”)证明之。

    示意图

    算法分析

      在平衡二叉排序树上查找的时间复杂度为logn, 不会出现最差的情况。

    代码实现

      1 //./a.out 45 12 53 3 37 100 24 61 90 78
      2 //./a.out 45 12 53 100 61
      3 //测试
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 
      8 #define DEBUG
      9 #define TRUE     1
     10 #define FALSE    0
     11 #define LH        +1    //左高
     12 #define EH        0    //等高
     13 #define RH        -1    //右高
     14 #define EQ(a,b)    ((a)==(b))
     15 #define LT(a,b)    ((a)< (b))
     16 #define LQ(a,b)    ((a)<=(b))
     17 #define LCHILD 1
     18 #define RCHILD 2
     19 
     20 typedef int ElemType;
     21 typedef int Boolean;
     22 //平衡二叉树采用二叉链表作为存储结构
     23 typedef struct BSTNode{
     24     ElemType data;
     25     //结点的平衡因子
     26     int bf;
     27     //左,右孩子指针
     28     struct BSTNode *lchild, *rchild;
     29 }BSTNode, *BSTree; 
     30 
     31 /*右旋平衡处理算法
     32  *
     33  *对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,即旋转之前的左子树的根结点。f始终指向*p的父亲结点
     34  *提示:建议结合图9.13(a)看,此处*p, lc相当于图中A、B结点。
     35  */
     36 void R_Rotate(BSTree *p, BSTree f){
     37     //*p是其父亲结点f的左孩子结点还是右孩子结点?
     38     int flag = -1;
     39     if(f && (f->lchild == (*p))){
     40         //*p是f的左孩子结点
     41         flag = LCHILD;
     42     }
     43     if(f && (f->rchild == (*p))){
     44         //*p是f的右孩子结点
     45         flag = RCHILD;
     46     }
     47     
     48     //lc指向*p的左子树根结点
     49     BSTNode *lc = (BSTNode*)(*p)->lchild;
     50     //lc的右子树挂接为*p的左子树
     51     (*p)->lchild = lc->rchild;
     52     //p指向新的根结点
     53     lc->rchild = *p;
     54     *p = lc;
     55 
     56     //更新父亲结点f的孩子结点指针
     57     if(f && (flag==LCHILD)){
     58         f->lchild = *p;
     59     }
     60     if(f && (flag==RCHILD)){
     61         f->rchild = *p;
     62     }
     63 }
     64 
     65 /*左旋平衡处理算法
     66  *
     67  *提示:和右旋平衡算法是对称的,建议结合图9.13(c)看,此处*p,rc相当图图中的A、B结点。
     68  */
     69 void L_Rotate(BSTree *p, BSTree f){
     70     int flag = -1;
     71     if(f && (f->lchild == (*p))){
     72         flag = LCHILD;
     73     }
     74     if(f && (f->rchild == (*p))){
     75         flag = RCHILD;
     76     }
     77     
     78     BSTNode *rc = (BSTNode*)(*p)->rchild;
     79     (*p)->rchild = rc->lchild;
     80     rc->lchild = *p;
     81     *p = rc;
     82     
     83     if(f && (flag==LCHILD)){
     84         f->lchild = *p;
     85     }
     86     if(f && (flag==RCHILD)){
     87         f->rchild = *p;
     88     }
     89 }
     90 
     91 //对指针T所指结点为根的二叉树作左平衡选择处理,本算法结束时,指针T指向新的根结点,f为*T的父亲结点。
     92 void LeftBalance(BSTree *T, BSTree f){
     93     //lc指向*T的左子树根结点
     94     BSTNode *lc = (BSTNode*)(*T)->lchild;
     95     BSTNode *rd;
     96     //检查*T的左子树的平衡度,并作相应平衡处理
     97     switch(lc->bf){
     98         //新结点插在了*T的左孩子的左子树上,要做单右旋处理
     99         case LH:
    100             lc->bf = (*T)->bf = EH;
    101             R_Rotate(T, f);
    102             break;
    103         //新结点插在了*T的左孩子的右子树上,要做双旋处理
    104         case RH:
    105             //rd指向*T的左孩子的右子树根
    106             rd = lc->rchild;
    107             switch(rd->bf){
    108                 //修改*T及其左孩子的平衡因子。
    109                 //提示:建议结合图9.13(b)看,此处*T, lc, rd相当于图中A、B、C结点。
    110                 case LH:
    111                     (*T)->bf = RH;
    112                     lc->bf = EH;
    113                     break;
    114                 case EH:
    115                     (*T)->bf = EH;
    116                     lc->bf = EH;
    117                     break;
    118                 case RH:
    119                     (*T)->bf = EH;
    120                     lc->bf = LH;
    121                     break;
    122             }
    123             rd->bf = EH;        
    124             //对*T的左子树lc做左旋平衡处理
    125             L_Rotate(&lc, *T);
    126             //对*T左右旋平衡处理
    127             R_Rotate(T, f);
    128             break;
    129         default:
    130             break;
    131     }
    132     return ;
    133 }
    134 
    135 //和左平衡算法是对称的,此处不再赘述
    136 void RightBalance(BSTree *T, BSTree f){
    137     BSTNode *rc = (BSTNode*)(*T)->rchild;
    138     BSTNode *ld;
    139     switch(rc->bf){
    140         case LH:
    141             //提示:建议结合图9.13(d)看,此处*T, rc, ld相当于图中的A、B、C结点。
    142             ld = rc->lchild;
    143             switch(ld->bf){
    144                 case LH:
    145                     (*T)->bf = EH;
    146                     rc->bf = RH;
    147                     break;
    148                 case EH:
    149                     (*T)->bf = EH;
    150                     rc->bf = EH;
    151                     break;
    152                 case RH:
    153                     (*T)->bf = LH;
    154                     rc->bf = EH;
    155                     break;
    156             }
    157             ld->bf = EH;
    158             R_Rotate(&rc, *T);
    159             L_Rotate(T, f);
    160             break;
    161         case RH:
    162             rc->bf = (*T)->bf = EH;
    163             L_Rotate(T, f);
    164             break;
    165         default:
    166             break;
    167     }
    168     return ;
    169 }
    170 
    171 /*平衡二叉树的插入算法
    172  *
    173  *若在平衡二叉排序树中T不存在和e有相同关键字的结点,则插入一个数据元素为e
    174  *的新结点点,并返回TRUE;否则返回FALSE。若因插入而使二叉排序树失去平衡,则
    175  *作平衡选择处理,布尔变量taller反映T长高与否。
    176  */
    177 int InsertAVL(BSTree *T,BSTree f, ElemType e, Boolean *taller){
    178     if(!(*T)){
    179         //插入新结点,树"长高",置taller为TRUE,并返回TRUE
    180         (*T) = (BSTree)malloc(sizeof(BSTNode));
    181         (*T)->data = e;
    182         (*T)->bf = EH;
    183         (*T)->lchild = (*T)->rchild = NULL;
    184         *taller = TRUE;
    185         return TRUE;
    186     }else{
    187         if(EQ((*T)->data, e)){  
    188             //树中已经存在和e相同的结点,不再插入,并返回FALSE
    189             *taller = FALSE;
    190             return FALSE;
    191         }
    192         if(LT(e, (*T)->data)){
    193             //应该继续在*T的左子树上进行搜索
    194             BSTree *p = malloc(sizeof(BSTree));
    195             *p = (BSTree)((*T)->lchild);
    196             if(!InsertAVL(p, *T, e, taller)){
    197                 //未插入
    198                 free(p);
    199                 return FALSE;
    200             }
    201             //已插入到*T的左子树中, 更新*T的左子树结点
    202             (*T)->lchild = *p;
    203             if(*taller){
    204                 //左子树"长高",检查*T的平衡度
    205                 switch((*T)->bf){
    206                     case LH:
    207                         //原本左子树比右子树高,现在左子树上又长高了,需要作左平衡处理
    208                         LeftBalance(T, f);
    209                         (*T)->bf = EH;
    210                         *taller = FALSE;
    211                         break;
    212                     case EH:
    213                         //原本左子树和右子树等高,现在左子树上又长高了,现在*T的左子树比右子树高
    214                         (*T)->bf = LH;
    215                         *taller = TRUE;
    216                         break;
    217                     case RH:
    218                         //原本左子树和右子树矮,现在左子树上又长高了,现在*T的左子树比右子树等高
    219                         (*T)->bf = EH;
    220                         *taller = FALSE;
    221                         break;
    222                 }
    223             }
    224             free(p);
    225             return TRUE;
    226         }else{
    227             //应该继续在*T的右子树上进行搜索
    228             BSTree *p2 = malloc(sizeof(BSTree));
    229             *p2= (BSTree)((*T)->rchild);
    230             if(!InsertAVL(p2, *T, e, taller)){
    231                 //未插入
    232                 free(p2);
    233                 return FALSE;
    234             }
    235             //已插入到*T的右子树中, 更新*T的右子树结点
    236             (*T)->rchild = *p2;
    237             if(*taller){
    238                 //右子树"长高",检查*T的平衡度
    239                 switch((*T)->bf){
    240                     case LH:
    241                         //原本左子树比右子树高,现在右子树上长高了,现在*T的左子树比右子树等高
    242                         (*T)->bf = EH;
    243                         *taller = FALSE;
    244                         break;
    245                     case EH:
    246                         //原本左子树和右子树等高,现在右子树上长高了,现在*T的左子树比右子树矮
    247                         (*T)->bf = RH;
    248                         *taller = TRUE;
    249                         break;
    250                     case RH:
    251                         //原本左子树和右子树矮,现在右子树上长高了,需要作右平衡处理
    252                         RightBalance(T, f);
    253                         (*T)->bf = EH;
    254                         *taller = FALSE;
    255                         break;
    256                 }
    257             }
    258             free(p2);
    259             return TRUE;
    260         }
    261     }
    262 }
    263 //二叉树先根遍历算法的函数声明
    264 int PreOrderTraverse(BSTree T);
    265 //二叉树中根遍历算法的函数声明
    266 int InOrderTraverse(BSTree T);
    267 //二叉树后根遍历算法的函数声明
    268 int PostOrderTraverse(BSTree T);
    269 //分别以先、中、后根遍历算法依次打印二叉树中的结点元素的函数声明
    270 void print(BSTree T);
    271 
    272 int main(int argc, char *argv[])
    273 {
    274     if(argc < 2)
    275         return FALSE;
    276     int i = 0;
    277     ElemType e;
    278     Boolean taller;
    279     BSTree Tree = NULL;
    280     for(i=1; i<argc; i++){
    281         e = atoi(argv[i]);
    282         printf("插入数据: %d
    ", e);
    283         InsertAVL(&Tree, NULL, e, &taller);
    284         print(Tree);
    285         
    286     }
    287     return TRUE;
    288 }
    289 
    290 
    291 //分别以先、中、后根遍历算法依次打印二叉树中的结点元素的函数实现
    292 void print(BSTree T){
    293     printf("先根遍历:	");    
    294     PreOrderTraverse(T);    
    295     printf("
    ");    
    296 
    297     printf("中根遍历:	");    
    298     InOrderTraverse(T);    
    299     printf("
    ");    
    300     
    301     printf("后根遍历:	");    
    302     PostOrderTraverse(T);    
    303     printf("
    ");
    304 }
    305 
    306 
    307 
    308 //二叉树先根遍历算法的函数实现
    309 int PreOrderTraverse(BSTree T){
    310     if(T){
    311         printf("[%-3d(%-2d)]  ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf);
    312         PreOrderTraverse((BSTree)T->lchild);
    313         PreOrderTraverse((BSTree)T->rchild);
    314     }
    315     return 0;
    316 }
    317 
    318 //二叉树中根遍历算法的函数实现
    319 int InOrderTraverse(BSTree T){
    320     if(T){
    321         InOrderTraverse((BSTree)T->lchild);
    322         printf("[%-3d(%-2d)]  ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf);
    323         InOrderTraverse((BSTree)T->rchild);
    324     }
    325     return 0;
    326 }
    327 
    328 //二叉树后根遍历算法的函数实现
    329 int PostOrderTraverse(BSTree T){
    330     if(T){
    331         PostOrderTraverse((BSTree)T->lchild);
    332         PostOrderTraverse((BSTree)T->rchild);
    333         printf("[%-3d(%-2d)]  ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf);
    334     }
    335     return 0;
    336 }
    平衡二叉树

    运行

  • 相关阅读:
    jQuery中的prop和attr区别
    echarts 不同区域背景色不同 废了我一天的时间
    echarts 饼状图 改变折线长度
    jqGrid有关问题 小知识点
    echarts页面中多图自适应
    bootstrap详解 见网址
    前端自适应布局方法总结
    前端页面中如何在窗口缩放时让两个div始终在同一行显示
    Vue2.0 v-for 中 :key 到底有什么用?
    sublime中安装package control总是失败
  • 原文地址:https://www.cnblogs.com/aimmiao/p/9538293.html
Copyright © 2011-2022 走看看