【数据结构】平衡二叉树—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)

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

 

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

 

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

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

 

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

 

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

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

 

先以(5)为轴向左旋转

 

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

后以(8)为轴向右旋转

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

 

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

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

 

先以(13)为轴向右旋转

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

后以(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),

 

    原文作者:wc的一些事一些情
    原文地址: https://www.cnblogs.com/wcd144140/p/5509474.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞