• 【数据结构】平衡二叉树—AVL树


     

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An algorithm for the organization of information" 中发表了它。

     

    平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。

     

    基本操作

    单旋转:左旋转、右旋转

    双旋转:左旋转与右旋转结合

     

    操作判断

    1. 按照二叉搜索树的方式增加节点,新增节点称为一个叶节点。

    2. 从新增节点开始,回溯到第一个失衡节点。(如果回溯到根节点,还没有失衡节点,就说明该树已经符合AVL性质)。

    3. 找到断的边,并确定断弦的方向(左OR右)。

    4. 以断边下端为根节点,确定两个子树中的哪一个深度大(左子树还是右子树)。

    5. 如果第2和第3步中的方向一致(都为左或者都为右),需要单旋转以失衡节点为根节点的子树。否则,双旋转以失衡节点为根节点的子树。若是左左(LL),则需要一次右旋转;若是右右(RR),则需要一次左旋转;若是左右(LR),则需要先左旋转再右旋转;若是右左(RL),则需要先右旋转再左旋转。

     

    单向右旋平衡处理案例分析(LL)

    从上图可以看出,从叶子结点往上回溯,(5)是第一个失衡结点,失衡方向是左边,而(5)的左子树根结点(3)的左子树深度比右子树大,所以该情况属于LL,只需要以(5)为转轴进行一次右旋转。

     

    单向左旋平衡处理案例分析(RR

    从上图可以看出,从叶子结点往上回溯,(10)是第一个失衡结点,失衡方向是右边,而(10)的左子树根结点(13)的右子树深度比左子树大,所以该情况属于RR,只需要以(10)为转轴进行一次左旋转。

     

    双向旋转(先左后右)平衡处理案例分析(LR

    先以(5)为轴向左旋转

    后以(8)为轴向右旋转

    从上图可以看出,从叶子结点往上回溯,(8)是第一个失衡结点,失衡方向是左边,而(5)的左子树根结点(7)的右子树深度比左子树大,所以该情况属于LR,因此需要先以(5)为转轴进行一次左旋转,后以(8)为转轴进行一次右旋转。

     

    双向旋转(先右后左)平衡处理(RL

     

    先以(13)为轴向右旋转

    后以(8)为轴向左旋转

    从上图可以看出,从叶子结点往上回溯,(8)是第一个失衡结点,失衡方向是右边,而(13)的右子树根结点(10)的左子树深度比右子树大,所以该情况属于RL,因此需要先以(13)为转轴进行一次右旋转,后以(8)为转轴进行一次左旋转。

     

    插入操作

    向AVL树中插入元素可能会导致树失去平衡。但是,我们只需要调整插入点至根节点的路径上不满足平衡状态的各节点中深度最大的那个即可。假设该最深节点为X。导致X失去平衡可能有四种情况:

    (1)插入点位于X的左子结点的左子树-左左。

    (2)插入点位于X的左子结点的右子树-左右。

    (3)插入点位于X的右子结点的左子树-右左。

    (4)插入点位于X的右子结点的左子树-右右。

    情况1和4是对称的,成为外侧插入,可以通过单旋转解决。而情况2和3也是对称的,成为内侧插入。通过双旋转解决。

     

    删除操作

     

    (1)判断当前根结点是否等于要删除的节点,否则进入步骤(2),是则进入(1.1)。

    (1.1)如果是叶子结点直接删除,然后进入步骤(3),否则进入(1.2)。

    (1.2)如果该结点只有左子树,则直接删除结点把左子树的根结点往上提,再步骤(3),否则进入步骤(1.3)。

    (1.3)如果该结点只有右子树,则直接删除结点把右子树的根结点往上提,再步骤(3),否则进入步骤(1.4)。

    (1.4)如果左右子树都不为空,则从左子树获取前驱(后驱同理),替换当前删除结点,再从前驱结点的原父结点往上遍历检查高度和平衡,也就是步骤(3)。

    (2)判断当前根结点是否大于删除节点,是则进入当前根结点的右子树继续递归删除,否则进入当前根结点的左子树继续递归删除,最后重新进入步骤(1),直到遍历完整棵树。

    (3)从移除结点的父结点开始往上遍历,检查高度是否变化和失衡,若高度发生变化,则重新赋值最新高度,若失衡,则按照以上的旋转方式检查进行旋转。

     

    AVL树的实现练习(Java

      1 public class AVLTree {
      2     
      3     private TreeNode root=null;
      4     
      5     /**
      6      * 获取树的高度
      7      * @param subTree 
      8      * @return
      9      */
     10     private int height(TreeNode subTree){
     11         if(subTree == null){
     12             return 0;
     13         }else{
     14             return subTree.height;
     15         }
     16     }
     17     
     18     /**
     19      * 中序遍历  
     20      * @param subTree
     21      */
     22     public void inOrder(TreeNode subTree){  
     23         if(subTree!=null){
     24             inOrder(subTree.leftChild);
     25             visted(subTree);  
     26             inOrder(subTree.rightChild);  
     27         }  
     28     }
     29     
     30     public void visted(TreeNode subTree){  
     31         System.out.print(subTree.data+"("+subTree.height+")"+",");
     32     }
     33     
     34     /**
     35      * 右旋转(LL)
     36      * @param subTree 转轴节点
     37      */
     38     public void r_Rotate(TreeNode sn){
     39         
     40         TreeNode pn = sn.parent;
     41         TreeNode ln = sn.leftChild;
     42         
     43         sn.leftChild = ln.rightChild;
     44         if(ln.rightChild !=null){
     45             ln.rightChild.parent = sn;
     46         }
     47         ln.rightChild = sn;
     48         
     49         sn.parent = ln;
     50         ln.parent = pn;
     51         if(pn != null && pn.rightChild==sn){
     52             pn.rightChild = ln;
     53         }else if(pn != null && pn.leftChild==sn){
     54             pn.leftChild = ln;
     55         }
     56         
     57         if(pn == null){
     58             this.root = ln;
     59         }
     60         
     61         sn.height = Math.max(height(sn.leftChild), height(sn.rightChild))+1;
     62         ln.height = Math.max(height(ln.leftChild), height(ln.rightChild))+1;
     63     }
     64     
     65     /**
     66      * 左旋转(RR)
     67      * @param subTree 转轴节点
     68      */
     69     public void l_Rotate(TreeNode sn){
     70         
     71         TreeNode pn = sn.parent;
     72         TreeNode rn = sn.rightChild;
     73         
     74         sn.rightChild = rn.leftChild;
     75         if(rn.leftChild !=null){
     76             rn.leftChild.parent = sn;
     77         }
     78         rn.leftChild = sn;
     79         
     80         sn.parent = rn;
     81         rn.parent = pn;
     82         if(pn != null && pn.rightChild==sn){
     83             pn.rightChild = rn;
     84         }else if(pn != null && pn.leftChild==sn){
     85             pn.leftChild = rn;
     86         }
     87         
     88         if(pn == null){
     89             this.root = rn;
     90         }
     91         
     92         sn.height = Math.max(height(sn.leftChild), height(sn.rightChild))+1;
     93         rn.height = Math.max(height(rn.leftChild), height(rn.rightChild))+1;
     94     }
     95     
     96     /**
     97      * 插入
     98      * @param subTree
     99      * @param iv
    100      */
    101     public void insertNote(TreeNode subTree, int iv){
    102         
    103         if(subTree == null){
    104             /*空树,根结点赋值*/
    105             subTree = new TreeNode(iv);
    106             this.root = subTree;
    107             
    108         }else if(subTree.data > iv){
    109             /*插入左子树*/
    110             if(subTree.leftChild == null){
    111                 TreeNode newNode = new TreeNode(iv);
    112                 subTree.leftChild = newNode;
    113                 newNode.parent = subTree;
    114             }else{
    115                 insertNote(subTree.leftChild, iv);
    116                 /*判断是否需要旋转*/
    117                 if(compareHeight(subTree.leftChild,subTree.rightChild) == 2){
    118                     if(compareHeight(subTree.leftChild.leftChild,subTree.leftChild.rightChild)>=0){
    119                         r_Rotate(subTree);
    120                     }else{
    121                         l_Rotate(subTree.leftChild);
    122                         r_Rotate(subTree);
    123                     }
    124                 }
    125             }
    126             
    127         }else if(subTree.data < iv){
    128             /*插入右子树*/
    129             if(subTree.rightChild == null){
    130                 TreeNode newNode = new TreeNode(iv);
    131                 subTree.rightChild = newNode;
    132                 newNode.parent = subTree;
    133             }else{
    134                 insertNote(subTree.rightChild, iv);
    135                 /*判断是否需要旋转*/
    136                 if(compareHeight(subTree.rightChild, subTree.leftChild) == 2){
    137                     if(compareHeight(subTree.rightChild.rightChild, subTree.rightChild.leftChild)>=0){
    138                         l_Rotate(subTree);
    139                     }else{
    140                         r_Rotate(subTree.rightChild);
    141                         l_Rotate(subTree);
    142                     }
    143                 }
    144             }
    145         }
    146         
    147         
    148         /*重新赋值当前结点高度*/
    149         subTree.height = Math.max(height(subTree.leftChild), height(subTree.rightChild))+1;           
    150     }
    151     
    152     /**
    153      * 删除结点
    154      * @param subTree
    155      * @param dv
    156      */
    157     public void deleteNote(TreeNode subTree, int dv){
    158         if(subTree == null){
    159             System.out.println("node is not exist.");
    160             
    161         }else if(subTree.data == dv){
    162             
    163             /*叶结点删除*/
    164             if (subTree.leftChild == null && subTree.rightChild == null) {
    165                 if(subTree.parent != null){
    166                     /*非单结点树*/
    167                     if(subTree.parent.leftChild == subTree){
    168                         subTree.parent.leftChild = null;
    169                     }else{
    170                         subTree.parent.rightChild = null;
    171                     }
    172                     TreeNode cn = subTree.parent;
    173                     subTree.parent = null;
    174                     /*递归往上检查父类高度是否变化*/
    175                     deleteCheck(cn);
    176                 }else{
    177                     /*单结点树*/
    178                     this.root = null;
    179                 }
    180                 
    181             /*删除结点只存在左子树*/
    182             }else if(subTree.leftChild != null && subTree.rightChild == null){
    183                 TreeNode ln = subTree.leftChild;
    184                 ln.parent = subTree.parent;
    185                 subTree.leftChild = null;
    186                 if(subTree.parent != null){
    187                     if(subTree.parent.leftChild == subTree){
    188                         subTree.parent.leftChild = ln;
    189                     }else{
    190                         subTree.parent.rightChild = ln;
    191                     }
    192                     subTree.parent = null;
    193                     /*递归往上检查父类高度是否变化*/
    194                     deleteCheck(ln.parent);
    195                 }else{
    196                     this.root = ln;
    197                 }
    198                 
    199             /*删除结点只存在右子树*/
    200             }else if(subTree.leftChild == null && subTree.rightChild != null){
    201                 TreeNode rn = subTree.rightChild;
    202                 rn.parent = subTree.parent;
    203                 subTree.rightChild = null;
    204                 if(subTree.parent != null){
    205                     if(subTree.parent.leftChild == subTree){
    206                         subTree.parent.leftChild = rn;
    207                     }else{
    208                         subTree.parent.rightChild = rn;
    209                     }
    210                     subTree.parent = null;
    211                     /*递归往上检查父类高度是否变化*/
    212                     deleteCheck(rn.parent);
    213                 }else{
    214                     this.root = rn;
    215                 }
    216                 
    217             /*删除结点左右子树非空*/
    218             }else{
    219                 TreeNode cn; //删除
    220                 /*寻找直接前驱*/
    221                 TreeNode tn = subTree.leftChild;
    222                 
    223                 if(tn.rightChild == null){
    224                     if(subTree.parent == null){
    225                         this.root = tn;
    226                     }else if(subTree.parent.leftChild == subTree){
    227                         subTree.parent.leftChild = tn;
    228                     }else if(subTree.parent.rightChild == subTree){
    229                         subTree.parent.rightChild = tn;
    230                     }
    231                     tn.parent = subTree.parent;
    232                     tn.rightChild = subTree.rightChild;
    233                     subTree.rightChild.parent = tn;
    234                     cn = tn;
    235                 }else{
    236                     while(tn.rightChild != null){
    237                         tn = tn.rightChild;
    238                     }
    239                     /*释放前驱结点*/
    240                     cn = tn.parent;
    241                     if(tn.leftChild != null){
    242                         tn.parent.rightChild = tn.leftChild;
    243                         tn.leftChild.parent = tn.parent;
    244                         tn.parent = null;
    245                         tn.leftChild = null;
    246                     }else{
    247                         tn.parent.rightChild = null;
    248                         tn.parent = null;
    249                     }
    250                     
    251                     /*tn是删除节点的左子树最大值(即前驱),替换删除节点*/
    252                     if(subTree.parent == null){
    253                         this.root = tn;
    254                     }else if(subTree.parent.leftChild == subTree){
    255                         subTree.parent.leftChild = tn;
    256                     }else if(subTree.parent.rightChild == subTree){
    257                         subTree.parent.rightChild = tn;
    258                     }
    259                     tn.parent = subTree.parent;
    260                     tn.leftChild = subTree.leftChild;
    261                     subTree.leftChild.parent = tn;
    262                     tn.rightChild = subTree.rightChild;
    263                     subTree.rightChild.parent = tn;
    264                     tn.height = subTree.height;
    265                     
    266                 }
    267                 subTree.parent = null;
    268                 subTree.leftChild = null;
    269                 subTree.rightChild = null;
    270                 subTree = null;
    271                 
    272                 /*递归往上检查父类高度是否变化*/
    273                 deleteCheck(cn);
    274             }
    275             
    276         }else if(subTree.data > dv){
    277             /*从左子树继续递归删除*/
    278             deleteNote(subTree.leftChild, dv);
    279             
    280         }else if(subTree.data < dv){
    281             /*从右子树继续递归删除*/
    282             deleteNote(subTree.rightChild, dv);
    283         }
    284     }
    285     
    286     /**
    287      * 删除往上递归检查父节点树高度是否发生变化和是否失衡
    288      * @param subTree
    289      */
    290     public void deleteCheck(TreeNode subTree){
    291         
    292         if(subTree != null){
    293             TreeNode pn = subTree.parent;
    294             int hl = height(subTree.leftChild);
    295             int hr = height(subTree.rightChild);
    296             int height = hl>=hr?hl+1:hr+1;
    297             int cp = compareHeight(subTree.leftChild,subTree.rightChild);
    298             
    299             if(subTree.height != height){
    300                 subTree.height = height;
    301             }
    302             
    303             /*判断是否LL或LR*/
    304             if(cp == 2){
    305                 if(compareHeight(subTree.leftChild.leftChild,subTree.leftChild.rightChild)>=0){
    306                     r_Rotate(subTree);
    307                 }else{
    308                     l_Rotate(subTree.leftChild);
    309                     r_Rotate(subTree);
    310                 }
    311             }
    312             
    313             /*判断是否RR或RL*/
    314             if(cp == -2){
    315                 if(compareHeight(subTree.rightChild.rightChild, subTree.rightChild.leftChild)>=0){
    316                     l_Rotate(subTree);
    317                 }else{
    318                     r_Rotate(subTree.rightChild);
    319                     l_Rotate(subTree);
    320                 }
    321             }
    322             
    323             /*继续往上检查父类*/
    324             deleteCheck(pn);
    325         }
    326     }
    327     
    328     
    329     /**
    330      * 比较子树高度差
    331      * @param subTree1
    332      * @param subTree2
    333      * @return
    334      */
    335     public int compareHeight(TreeNode subTree1, TreeNode subTree2){
    336         int h1 = height(subTree1);
    337         int h2 = height(subTree2);
    338         return h1 - h2;
    339     }
    340     
    341     
    342     /** 
    343      * 二叉树的节点数据结构 
    344      */  
    345     private class  TreeNode{
    346         
    347         private int data;
    348         private TreeNode parent = null;
    349         private TreeNode leftChild=null;
    350         private TreeNode rightChild=null;
    351         private int height = 1;
    352         
    353         public TreeNode(int data){  
    354             this.data=data; 
    355         }  
    356     }
    357     
    358     public static void main(String[] args) {
    359         
    360         AVLTree avl = new AVLTree();
    361         
    362         avl.insertNote(avl.root, 8);
    363         avl.insertNote(avl.root, 3);
    364         avl.insertNote(avl.root, 10);
    365         avl.insertNote(avl.root, 1);
    366         avl.insertNote(avl.root, 6);
    367         avl.insertNote(avl.root, 14);
    368         avl.insertNote(avl.root, 4);
    369         avl.insertNote(avl.root, 7);
    370         avl.insertNote(avl.root, 19);
    371         avl.insertNote(avl.root, 20);
    372         avl.insertNote(avl.root, 13);
    373         
    374         System.out.print("create t over:");
    375         avl.inOrder(avl.root);
    376         System.out.println("");
    377         
    378         System.out.print("delete 1 over:");
    379         avl.deleteNote(avl.root, 1);
    380         avl.inOrder(avl.root);
    381         System.out.println("");
    382         
    383         System.out.print("delete 7 over:");
    384         avl.deleteNote(avl.root, 7);
    385         avl.inOrder(avl.root);
    386         System.out.println("");
    387         
    388         System.out.print("delete 6 over:");
    389         avl.deleteNote(avl.root, 6);
    390         avl.inOrder(avl.root);
    391         System.out.println("");
    392 
    393         System.out.print("delete 3 over:");
    394         avl.deleteNote(avl.root, 3);
    395         avl.inOrder(avl.root);
    396         System.out.println("");
    397         
    398     }
    399 }

     运行结果:

    create t over:1(1),3(3),4(1),6(2),7(1),8(4),10(2),13(1),14(3),19(2),20(1),

    delete 1 over:3(2),4(1),6(3),7(1),8(4),10(2),13(1),14(3),19(2),20(1),

    delete 7 over:3(1),4(2),6(1),8(4),10(2),13(1),14(3),19(2),20(1),

    delete 6 over:3(1),4(2),8(4),10(2),13(1),14(3),19(2),20(1),

    delete 3 over:4(1),8(3),10(2),13(1),14(4),19(2),20(1),

  • 相关阅读:
    kfx格式的复活
    HTC(HTML Component)开发简介
    window.open window.showModelDialog 打开一个新窗口/子窗口中调用父窗口的方法
    用 Firebug 动态调试和优化应用程序
    HTC——浏览器上的舞者
    innerHTML、innerText和outerHTML、outerText的区别
    window.parent与window.opener的区别与使用
    window.parent与window.opener、window.showModalDialog的区别 opener和showModalDialog刷新父页面的方法
    HTML Component(HTC)
    Hibernate下数据批量处理解决方案
  • 原文地址:https://www.cnblogs.com/wcd144140/p/5509474.html
Copyright © 2020-2023  润新知