【基础算法】 平衡二叉树的插入

               

关于平衡二叉树,其中的各项指标,比如说:LL,RR,LR,RL,这些东西都是些什么意思,请看下面的图:

对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:

  1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左

  2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右

  3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左

  4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右

  从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。

其他的文章转载如下,详细讲了旋转是咋回事,自己推一遍即可

在上一个专题中,我们在谈论二叉查找树的效率的时候。不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找)。如何解决这个问题呢?关键在于如何最大限度的减小树的深度。正是基于这个想法,平衡二叉树出现了。

平衡二叉树的定义 (AVL—— 发明者为Adel'son-Vel'skii 和 Landis)

平衡二叉查找树,又称 AVL树。 它除了具备二叉查找树的基本特征之外,还具有一个非常重要的特点: 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值平衡因子 ) 不超过1。 也就是说AVL树每个节点的平衡因子只可能是-1、0和1(左子树高度减去右子树高度)。

那么如何是二叉查找树在添加数据的同时保持平衡呢?基本思想就是:当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若 破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达 到新的平衡。所谓最小不平衡子树 指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。 

平衡二叉树的操作

1. 查找操作

       平衡二叉树的查找基本与二叉查找树相同。

2. 插入操作

       在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转 。 下面我们归纳一下平衡旋转的4中情况

1) 绕某元素左旋转  

                                 80                                    90  

                                 /  \             左旋               /    \

                               60 90          ---- ->         80     120

                                    /  \                               /  \       /

                                  85 120                    60  85 100

                                        /

                                      100     

                               a)  BST树                              b ) AVL树

     分析一下:在插入数据100之前,a图的B ST树只有80节点的平衡因子是-1(左高-右高),但整棵树还是平衡的。加入100之后,80节点的平衡因子就成为了-2,此时平衡被破坏。需要左旋转成b 图。

     当树中节点X的右孩子的右孩子上插入新元素,且平衡因子从-1变成-2后,就需要绕节点X进行左旋转。

2) 绕某元素右旋转  

                                100                                   85

                                 /  \               右旋              /    \

                              85  120         ------ ->     60    100  

                              /  \                                      \      /   \

                            60 90                                 80  90 120

                              \

                              80

                             a) B ST树                                b) AVL树

     当树中节点X的左孩子的左孩子上插入新元素,且平衡因子从1变成2后,就需要绕节点X进行右旋转。

3) 绕某元素的左子节点左旋转,接着再绕该元素自己右旋转。 此情况下就是左旋与右旋 的结合,具体操作时可以分 解成这两种操作,只是围绕点不一样而已。

                                                      

                            100                             100                                90

                             /  \             左旋            /  \              右旋           /    \

                          80  120       ------>      90  120        ------>     80   100  

                          / \                                  /                                    /  \      \

                       60 90                            80                              60  85  120

                            /                               / \

                          85                            60 85 

      当树中节点X的左孩子的右孩子上插入新元素,且 平衡因子从1变成2后,就需要 先绕X的左子节点Y左旋转,接着再绕X右旋转


4) 绕某元素的右子节点右旋转,接着再绕该元素自己左旋转。 此情况下就是 右旋与左旋 的结合,具体操作时可以分解 成这两种操作,只是围绕点不一样而已 。

                               80                               80                                       85  

                               /   \              旋          /  \                               /  \     

                            60  100      ------>      60 85            ------->          80 100

                                   /  \                                 \                                   /     /   \       

                                85  120                        100                           60    90 120

                                   \                                   /  \

                                   90                           90  120

       当树中节点X的右孩子的左孩子上插入新元素,且 平衡因子从-1变成-2后,就需要 先绕X的右子节点Y右旋转,接着再绕X左旋转

平衡二叉树性能分析


平衡二叉树的性能优势:

      很显然,平衡二叉树的优势在于不会出现普通二叉查找树的最差情况。其查找的时间复杂度为O(logN)。

平衡二叉树的缺陷:

      (1) 很遗憾的是,为了保证高度平衡,动态插入和删除的代价也随之增加。因此,我们在下一专题中讲讲《红黑树》 这种更加高效的查找结构。

      (2) 所有二叉查找树结构的查找代价都与树高是紧密相关的,能否通过减少树高来进一步降低查找代价呢。我们可以通过多路查找树的结构来做到这一点,在后面专题中我们将通过《多路查找树/B-树/B+树 》来介绍。

      (3) 在大数据量查找环境下(比如说系统磁盘里的文件目录,数据库中的记录查询 等),所有的二叉查找树结构(BST、AVL、RBT)都不合适。如此大规模的数据量(几G数据),全部组织成平衡二叉树放在内存中是不可能做到的。那么把这棵树放在磁盘中吧。问题就来了:假如构造的平衡二叉树深度有1W层。那么从根节点出发到叶子节点很可能就需要1W次的硬盘IO读写。大家都知道,硬盘的机械部件读写数据的速度远远赶不上纯电子媒体的内存。 查找效率在IO读写过程中将会付出巨大的代价。在大规模数据查询这样一个实际应用背景下,平衡二叉树的效率就很成问题了。对这一问题的解决:我们也会在《多路查找树/B-树/B+树 》 将详细分析。

      上面提到的红黑树和多路查找树都是属于深度有界查找树(depth-bounded tree —DBT)


平衡二叉树的插入操作代码(平衡旋转)

Java代码   收藏代码
  1. package net.hr.algorithm.search;  
  2. /**平衡因子枚举类*/  
  3. enum B  
  4. alanceFactor{  
  5.     LH("左子树高"),EH("左右等高"),RH("右子树高");  
  6.       
  7.     private String illustration="";  
  8.       
  9.     private BalanceFactor(String s){  
  10.         this.illustration=s;  
  11.     }  
  12.       
  13.     public String toString(){  
  14.         return this.illustration;  
  15.     }  
  16. }  
  17. /** 
  18.  * 平衡二叉树结点 
  19.  */  
  20. class AVLNode<E extends Comparable<E>>{  
  21.     /**结点关键字*/  
  22.     E key=null;  
  23.     /**结点的平衡因子*/  
  24.     BalanceFactor bFactor=BalanceFactor.EH;  
  25.     /**结点的直接父亲*/  
  26.     AVLNode<E> parent=null;  
  27.     /**结点的左右孩子*/  
  28.     AVLNode<E> lchild,rchild=null;  
  29.       
  30.     AVLNode(E k){  
  31.         this.key=k;  
  32.     }  
  33.     /** 
  34.      * 格式输出结点 
  35.      */  
  36.     public String toString(){  
  37.         //String fomateStr="";  
  38.         //if(this.lchild==null)  
  39.         String lchildStr=(this.lchild==null)?"null":this.lchild.key.toString();  
  40.         String rchildStr=(this.rchild==null)?"null":this.rchild.key.toString();  
  41.         return this.key+"[lchild="+lchildStr+",rchild="+rchildStr+"]";  
  42.     }  
  43.   
  44. }  
  45. /** 
  46.  * 平衡二叉查找树 
  47.  * @author heartraid 
  48.  */  
  49. public class AVL<E extends Comparable<E>> {  
  50.   
  51.     /**树根*/  
  52.     private AVLNode<E> root=null;  
  53.     /**当前树是否变高*/  
  54.     public boolean isTaller=false;  
  55.       
  56.     public AVL(){  
  57.     }  
  58.       
  59.       
  60.     public boolean insert(E key){  
  61.         System.out.print("插入["+key+"]:");  
  62.         if(key==nullreturn false;  
  63.         if(root==null){  
  64.             System.out.println("插入到树根。");  
  65.             root=new AVLNode<E>(key);  
  66.             return true;  
  67.         }  
  68.         else{  
  69.             System.out.print("搜索路径[");  
  70.             return insertAVL(key,root);  
  71.         }  
  72.     }  
  73.       
  74.     private boolean insertAVL(E key,AVLNode<E> node){  
  75.         System.out.print(node.key+" —>");  
  76.         // 树中存在相同的key,不需要插入  
  77.         if(node.key.compareTo(key)==0){  
  78.             System.out.println("].  搜索有相同关键字,插入失败");  
  79.             isTaller=false;  
  80.             return false;  
  81.         }  
  82.         else{  
  83.             //左子树搜索  
  84.             if(node.key.compareTo(key)>0){  
  85.                 //当前node的左孩子为空,则插入到结点的做孩子并修改结点的平衡因子为LH  
  86.                 if(node.lchild==null){  
  87.                     System.out.println("].  插入到"+node.key+"的左孩子");  
  88.                     AVLNode<E> newNode=new AVLNode<E>(key);  
  89.                     node.lchild=newNode; //设置左孩子结点  
  90.                     newNode.parent=node; //设置父亲结点  
  91.                     isTaller=true//树长高了  
  92.                 }  
  93.                 //左孩子不为空,则继续搜索下去  
  94.                 else{  
  95.                     insertAVL(key,node.lchild);  
  96.                 }  
  97.                 //当前如果树长高了,说明是因为左孩子的添加改变了平衡因子(左高)。  
  98.                 if(isTaller){  
  99.                     System.out.print("          树变化了,"+node.key+"的平衡因子变化");  
  100.                     switch(node.bFactor){  
  101.                         //原来结点平衡因子是LH(bf=1),则左高以后bf=2,因此需要做左平衡旋转  
  102.                         case LH: {  
  103.                             System.out.println("[LH=1 ——> LH=2]. 出现了不平衡现象[左比右高2]");  
  104.                             System.out.println("          ★ 以"+node.key+"为根将树进行左平衡处理");  
  105.                             leftBalance(node);  
  106.                             isTaller=false;   
  107.                             break;  
  108.                         }  
  109.                         //原来结点平衡因子是EH(bf=0),则左高了以后bf=1,不需要平衡处理。  
  110.                         case EH:{  
  111.                             System.out.println("[EH=0 ——> LH=1]. 没有不平衡现象");  
  112.                             node.bFactor=BalanceFactor.LH;  
  113.                             isTaller=true;  
  114.                             break;  
  115.                         }  
  116.                         //原来结点平衡因子是RH(bf=-1),则左高以后bf=0,不需要平衡处理。  
  117.                         case RH:{  
  118.                             System.out.println("[RH=-1 ——> EH=0]. 没有不平衡现象");  
  119.                             node.bFactor=BalanceFactor.EH;  
  120.                             isTaller=false;  
  121.                             break;  
  122.                         }  
  123.                     }//end switch  
  124.                 }//end if  
  125.             }//end if  
  126.             //右子树搜索  
  127.             else{  
  128.                 if(node.rchild==null){  
  129.                     System.out.println("].  插入到"+node.key+"的右孩子");  
  130.                     AVLNode<E> newNode=new AVLNode<E>(key);  
  131.                     node.rchild=newNode; //设置右孩子结点  
  132.                     newNode.parent=node; //设置父亲结点  
  133.                     isTaller=true//树长高了  
  134.                 }  
  135.                 else{  
  136.                     insertAVL(key,node.rchild);  
  137.                 }  
  138.                 //当前如果树长高了,说明是因为右孩子的添加改变了平衡因子(右高)。  
  139.                 if(isTaller){  

猜你喜欢

转载自blog.csdn.net/qq_44919293/article/details/89493745