zoukankan      html  css  js  c++  java
  • [数据结构与算法]平衡二叉树实现

    由于程序太长,分成了几部分,后面附上源码。

     1 /**
     2  * 平衡二叉搜索(排序)树
     3  * 
     4  * 平衡二叉搜索树双称为AVL树,它也是一棵二叉搜索树,是对二叉搜索树的一种改进,或都是具有下列性质的二叉树:它
     5  * 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
     6  * 
     7  * 平衡因子(Balance Factor,BF)定义为该节点的左子树的深度减去其右子树的深度,则平衡二叉树上所有节点的平
     8  * 衡因子只可能是-1、0和1。只要树上有一个节点的平衡因子的绝对值大于1,则该二叉树就是不平衡的了。
     9  * 
    10  * 使用二叉排序树保持平衡的基本思想是:每当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若
    11  * 是,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子s树中节点之间的关系,以达
    12  * 到新的平衡。所谓最小不平衡子树指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。
    13  * 
    14  * 对于平衡二叉搜索树,保持树的平衡的基本机制就是旋转。旋转是对树的元素顺序进行调节。旋转的目的是消除由于临
    15  * 时插入和删除对树的平衡产生的影响。
    16  * 
    17  * 有四种旋转:
    18  * 1)绕某元素左旋转
    19  *          80 ← p                    90
    20  *          /                        /
    21  *         60 90 ← r        →        80 120
    22  *            /                     /  /
    23  *          85 120                 60 85 100
    24  *              /
    25  *             100          
    26  *             
    27  * 2)绕某元素右旋转
    28  *      p → 100                        85
    29  *          /                         /
    30  *     l → 85 120          →         60 100
    31  *         /                           /
    32  *        60 90                      80 90 120
    33  *         
    34  *         80
    35  * 
    36  * 3)绕某元素的左子节点左旋转,接着再绕该元素自己右旋转。此情况下就是 左旋与右旋 的结合,具体操作时可以分
    37  * 解成这两种操作,只是围绕点不一样而已
    38  * 
    39  * 先绕100的左子节点80左旋转,接着再绕100左旋转
    40  * 
    41  *                左旋                   右旋
    42  *         100     →     p → 100         →         90
    43  *         /                 /                   /
    44  *    p → 80 120        l → 90 120                80 100
    45  *        /                 /                    /   
    46  *       60 90 ← r          80                   60 85 120
    47  *          /               /
    48  *         85              60 85
    49  *         
    50  * 4)绕某元素的右子节点右旋转,接着再绕该元素自己左旋转。此情况下就是 右旋与左旋 的结合,具体操作时可以分解
    51  * 成这两种操作,只是围绕点不一样而已
    52  * 
    53  * 先绕80的右子节点80右旋转,接着再绕80左旋转
    54  *                     右旋               左旋
    55  *          80          →      80 ← p     →       85
    56  *          /                 /                 /
    57  *         60 100 ← p         60 85 ← r         80 100
    58  *            /                               /  /
    59  *       l → 85 120               100          60 90 120
    60  *                               /
    61  *            90                 90 120
    62  *            
    63  * 平衡二叉树实现类 AVLTree 中的很多方法与排序二叉树是一新的,详细请参考 BinSearchTree 类中相应方法
    64  * 
    65  * AVLTree中的Entry对象与BinSearchTree中的Entry对象是有区别的,所以AVLTree类不能是BinSearchTree的
    66  * 了类,虽然很多的方法是一样的(如:contains、getEntry、successor、iterator),还有一些方法(add、de
    67  * leteEntry)只是在操作后增加了节点平衡因子调整动作,但不能直接继承于它。
    68  * 
    69  * 二叉搜索树与平衡二叉搜索树没有在Java集合框架中实现,但RED-BLACK树在TreeMap实现过,当然TreeSet也是,
    70  * 因为它是基于TreeMap实现的,TreeSet对象基本上就是一个忽略了每个元素值部分的TreeMap对象。
    71  * 
    72  */
      1 package tree.avl;
      2 
      3 import java.util.AbstractSet;
      4 import java.util.Iterator;
      5 
      6 /**
      7  * 平衡二叉搜索(排序)树
      8  * 
      9  * @author jzj
     10  * @data 2009-12-25
     11  */
     12 public class AVLTree<E extends Comparable<E>> extends AbstractSet<E> {
     13     private Entry<E> root;//根节点
     14     private int size;//树节点个数
     15 
     16     private static class Entry<E> {
     17         E elem;//数据域
     18         Entry<E> parent;//父节点
     19         Entry<E> left;//左节点
     20         Entry<E> right;//右节点
     21         /*
     22          * 树的平衡因子,等号表示左子树和右子树有同样的高度。如果L,左子树比右子树高1。如果为R,则意味着右
     23          * 子树比左高1。刚创建时是平衡的,所以为=
     24          *             50
     25          *             /R
     26          *            20  80
     27          *            /L  /R
     28          *           10  70 100
     29          *           =   =  /=
     30          *                 92 103
     31          *                 =  =
     32          */
     33         char balanceFactor = '=';
     34 
     35         //构造函数只有两个参数,左右节点是调用add方法时设置
     36         public Entry(E elem, Entry<E> parent) {
     37             this.elem = elem;
     38             this.parent = parent;
     39         }
     40     }
     41 
     42     private class TreeIterator implements Iterator<E> {
     43 
     44         private Entry<E> lastReturned = null;
     45         private Entry<E> next;
     46 
     47         TreeIterator() {
     48 
     49             next = root;
     50             if (next != null)
     51                 while (next.left != null)
     52                     next = next.left;
     53 
     54         }
     55 
     56         public boolean hasNext() {
     57 
     58             return next != null;
     59 
     60         }
     61 
     62         public E next() {
     63 
     64             lastReturned = next;
     65             next = successor(next);
     66             return lastReturned.elem;
     67 
     68         }
     69 
     70         public void remove() {
     71 
     72             if (lastReturned.left != null && lastReturned != null)
     73                 next = lastReturned;
     74             deleteEntry(lastReturned);
     75             lastReturned = null;
     76 
     77         }
     78     }
     79 
     80     /**
     81      * 左旋转:结果就是将p移到它的左子节点的位置,而p的右子节点被移到该元素原来位置
     82      * @param p 旋转元素
     83      */
     84     private void rotateLeft(Entry<E> p) {
     85         /*
     86         * 围绕50左旋转:
     87         *p → 50                 90
     88         *                      /
     89         * r → 90      →       50 100
     90         *      
     91         *     100
     92         * 
     93         * 围绕80左旋转:r的左子树变成p的右子树。因为位于r的左子树上的任意一个元素值大于p且小于r,所以r的左子
     94         * 树可以成为p的右子树这是没有问题的。其实上面也发生了同样的现象,只是r的左子树为空罢了
     95         *  p → 80                  90
     96         *      /                  / 
     97         *     60 90 ← r     →     80 120
     98         *        /               /   /
     99         *      85 120           60 85 100
    100         *          /
    101         *         100
    102         * 
    103         * 围绕80在更大范围内旋转:元素不是围绕树的根旋转。旋转前后的树都是二叉搜索树。且被旋转元素80上的所
    104         * 有元素在旋转中不移动(50、30、20、40这四个元素还是原来位置)
    105         *       50                         50
    106         *       /                         / 
    107         *     30   80 ← p                 30  90
    108         *     /   /                     /  / 
    109         *   20 40 60 90 ← r      →      20 40 80 120
    110         *            /                       /   /
    111         *           85 120                  60 85 100
    112         *              /
    113         *             100
    114         * 
    115         * 节点数据结构:
    116         *  +------------------+
    117         *  | elem | p | l | r |
    118         *  +------------------+
    119         *  
    120         *    +------------------+
    121         *    | 50 |NULL|NULL| r |
    122         *    +------------------+
    123         *                 ↖⑥ ↘④
    124         *         +---------------+
    125         *         |80| p | l  | r |   ← p 
    126         *         +---------------+
    127         *         ↗       ↙     ↖③ ↘①      
    128         *  +----------------+ +---------------+
    129         *  |60| p |NULL|NULL| |90| p |  l | r |   ← r
    130         *  +----------------+ +---------------+
    131         *                     ↗②       ↙⑤    ↖↘ 
    132         *              +----------------+ +---------------+
    133         *              |85| p |NULL|NULL| |90| p | l |NULL|
    134         *              +----------------+ +---------------+
    135         *                                  ↗      ↙  
    136         *                           +-----------------+
    137         *                           |100| p |NULL|NULL|
    138         *                           +-----------------+
    139         */
    140 
    141         Entry<E> r = p.right;//旋转元素的右子节点
    142         //把旋转元素的右子节点的左子节点接到旋转元素的右子树
    143         p.right = r.left;//144         //如果旋转元素的右子节点存在左子节点的话,同时修改该节点的父节指针指向
    145         if (r.left != null) {
    146             //把旋转元素的右子节点的左子节点的父设置成旋转元素
    147             r.left.parent = p;//
    148         }
    149         //将旋转元素的右子节点的父设置成旋转元素的父,这里有可以p为根节点,那么旋转后p就成根节点
    150         r.parent = p.parent;//151 
    152         //如果旋转元素为根元素,则旋转后,旋转元素的右子节点r将成为根节点
    153         if (p.parent == null) {
    154             root = r;
    155         }//否则p不是根节点,如果p是他父节点的左节点时
    156         else if (p.parent.left == p) {
    157             //让p的父节点的左指针指向r
    158             p.parent.left = r;
    159         }//否则如果p是他父节点的右节点时
    160         else {
    161             //让p的父节点的右指针指向r
    162             p.parent.right = r;//
    163         }
    164         //让旋转元素的左节点指向旋转元素p
    165         r.left = p;//166         //让旋转元素的父节点指向旋转元素右节点r
    167         p.parent = r;//
    168     }
    169 
    170     /**
    171      * 右旋转:结果就是将p移到它的右子节点的位置,而p的左子节点被移到该元素原来位置
    172      * 与左旋转是完全对称的,将左旋转中的lef与rigth互换即可得到,这里就不详细解释了
    173      * @param p 旋转元素
    174      */
    175     private void rotateRight(Entry<E> p) {
    176 
    177         /*
    178         * 围绕100右旋转:
    179         *  p → 100              90
    180         *       /               /
    181         * l → 90      →       50 100
    182         *     /
    183         *    50
    184         * 
    185         * 围绕80右旋转:l的右子树变成p的左子树。因为位于l的右子树上的任意一个元素值小于p且小于l,所以l的右
    186         * 子树可以成为p的左子树这是没有问题的。其实上面也发生了同样的现象,只是l的右子树为空罢了
    187         * 
    188         *  p → 80                  60
    189         *      /                  / 
    190         * l → 60 90         →     50 80
    191         *     /                     /
    192         *    50 70                55 70 90   
    193         *        
    194         *     55
    195         */
    196 
    197         Entry<E> l = p.left;
    198         p.left = l.right;
    199         if (l.right != null) {
    200             l.right.parent = p;
    201         }
    202         l.parent = p.parent;
    203 
    204         if (p.parent == null) {
    205             root = l;
    206         } else if (p.parent.right == p) {
    207             p.parent.right = l;
    208         } else {
    209             p.parent.left = l;
    210         }
    211         l.right = p;
    212         p.parent = l;
    213     }
    214 
    215     /**
    216      * AVLTree类的add方法类似于BinSerrchTree类的add方法,但是沿着根向下前进到插入点时,需记录这样一个被插
    217      * 入Entry对象最近的祖先:该祖先的平衡因子balanceFactor值是L或R(即不歼),且让ancestor指向这个祖先节
    218      * 点,该祖先节有什么用呢,从ancestor的子开始到新增节点路径上的所有祖先节点都是平衡,这些祖先节点会因为
    219      * 新增节点而变得不平衡了,需要重新调整平衡因子,这个分界点在调整平衡因子时非常有用
    220      * 
    221      * @param elem 要新增元素的数据域
    222      * @return
    223      */
    224     public boolean add(E elem) {
    225         //如果树为空,则直接插入
    226         if (root == null) {
    227             root = new Entry<E>(elem, null);
    228             size++;
    229             return true;
    230         } else {
    231             Entry<E> tmp = root;//新增节点的父节点,从根节点下面开始找插入点
    232             Entry<E> ancestor = null;//平衡因子不为 = 的最近祖先节点
    233             int comp; //比较结果
    234             while (true) {//死循环,直接找到插入点退出循环
    235                 comp = elem.compareTo(tmp.elem);
    236                 //如果已存在该元素,则直接返回失败
    237                 if (comp == 0) {
    238                     return false;
    239                 }
    240 
    241                 //记录不平衡的祖先节点
    242                 if (tmp.balanceFactor != '=') {
    243                     //如果哪个祖先节点不平衡,则记录,当循环完后,ancestor指向的就是最近一个
    244                     //不平衡的祖先节点
    245                     ancestor = tmp;
    246                 }
    247 
    248                 //如果小于当前比较节点,则在其左边插入
    249                 if (comp < 0) {
    250 
    251                     //如果左子树不为空,继续循环在左边找插入点
    252                     if (tmp.left != null) {
    253                         tmp = tmp.left;
    254                     } else {//否则插入
    255                         tmp.left = new Entry<E>(elem, tmp);
    256                         //插入后要进行平衡调整
    257                         fixAfterInsertion(ancestor, tmp.left);
    258                         size++;
    259                         return true;
    260                     }
    261                 } else {//在右边插入
    262 
    263                     //如果右子树不为空,继续循环在右边找插入点
    264                     if (tmp.right != null) {
    265                         tmp = tmp.right;
    266                     } else {//否则插入
    267                         tmp.right = new Entry<E>(elem, tmp);
    268                         //插入后要进行平衡调整
    269                         fixAfterInsertion(ancestor, tmp.right);
    270                         size++;
    271                         return true;
    272                     }
    273                 }
    274 
    275             }
    276         }
    277     }
    278 
    279     /**
    280      * 当新增节点后,会改变某些节点的平衡因子,所以添加节点后需重新调整平衡因子
    281      * 
    282      * 根据前人们的分析与研究,可分为6种情况
    283      * 
    284      * @param ancestor 新增元素的最近一个不平衡的祖先节点
    285      * @param inserted 新增元素
    286      */
    287     protected void fixAfterInsertion(Entry<E> ancestor, Entry<E> inserted) {
    288         E elem = inserted.elem;
    289 
    290         if (ancestor == null) {
    291             /*
    292              * 情况1:ancestor的值为null,即被插入Entry对象的每个祖先的平衡因子都是 =,此时新增节点后
    293              * ,树的高度不会发生变化,所以不需要旋转,我们要作的就是调整从根节点到插入节点的路径上的所有
    294              * 节点的平衡因子罢了
    295              * 
    296              *                       新增55后调整
    297              *             50            →           50
    298              *             /=                       /R
    299              *           25   70                   25   70
    300              *          /=   /=                 /=   /L
    301              *         15 30 60 90               15 30 60 90
    302              *         =  =  =   =               =  =  /L =
    303              *                                        55
    304              *                                        =
    305              */
    306             //根节点的平衡因子我们单独拿出来调整,因为adjustPath的参数好比是一个开区间,它不包括两头参数
    307             //本身,而是从nserted.paraent开始到to的子节点为止,所以需单独调整,其他分支也一样
    308             if (elem.compareTo(root.elem) < 0) {
    309                 root.balanceFactor = 'L';
    310             } else {
    311                 root.balanceFactor = 'R';
    312             }
    313             //再调用adjustPath方法调整新增节点的父节点到root的某子节点路径上的所有祖先节点的
    314             //平衡因子
    315             adjustPath(root, inserted);
    316         } else if ((ancestor.balanceFactor == 'L' && elem.compareTo(ancestor.elem) > 0)
    317                 || (ancestor.balanceFactor == 'R' && elem.compareTo(ancestor.elem) < 0)) {
    318             /*
    319              * 情况2:
    320              * ancestor.balanceFactor的值为 L,且在ancestor对象的右子树插入,或ancestor.balanceFac
    321              * tor的值为 R,且在ancestor对象的左子树插入,这两插入方式都不会引起树的高度发生变化,所以我们
    322              * 也不需要旋转,直接调整平衡因子即可
    323              *                      新增55后调整
    324              *  ancestor → 50           →              50
    325              *            /L                         /=
    326              *          25   70                     25   70
    327              *         /R   /=                   /R   /L
    328              *        15 30 60 90                 15 30 60 90
    329              *           /L                          /L /L
    330              *          28                          28 55
    331              *                      新增28后调整
    332              *  ancestor → 50            →             50
    333              *            /R                         /=
    334              *          25   70                     25   70
    335              *         /=   /L                   /R   /L
    336              *        15 30 60 90                 15 30 60 90
    337              *              /L                       /L /L
    338              *             55                       28 55
    339              */
    340             //先设置ancestor的平衡因子为 平衡
    341             ancestor.balanceFactor = '=';
    342             //然后按照一般策略对inserted与ancestor间的元素进行调整
    343             adjustPath(ancestor, inserted);
    344         } else if (ancestor.balanceFactor == 'R'
    345                 && elem.compareTo(ancestor.right.elem) > 0) {
    346             /*
    347              * 情况3:
    348              * ancestor.balanceFactor的值为 R,并且被插入的Entry位于ancestor的右子树的右子树上, 此
    349              * 种情况下会引起树的不平衡,所以先需绕ancestor进行旋转,再进行平衡因子的调整
    350              *
    351              * 新增93                          先调整因子再绕70左旋
    352              *   →         50                    →           50
    353              *            /R                                /R
    354              *          25   70  ← ancestor                25  90
    355              *         /L    /R                          /L   /=
    356              *        15    60 90                        15  70   98
    357              *        =     =  /=                       =  /=   /L
    358              *                80  98                       60 80 93
    359              *                =   /=                       =  =  =
    360              *                   93
    361              *                   =
    362              */
    363             ancestor.balanceFactor = '=';
    364             //围绕ancestor执行一次左旋
    365             rotateLeft(ancestor);
    366             //再调整ancestor.paraent(90)到新增节点路径上祖先节点平衡
    367             adjustPath(ancestor.parent, inserted);
    368         } else if (ancestor.balanceFactor == 'L'
    369                 && elem.compareTo(ancestor.left.elem) < 0) {
    370             /*
    371              * 情况4:
    372              * ancestor.balanceFactor的值为 L,并且被插入的Entry位于ancestor的左子树的左子树上,
    373              * 此种情况下会引起树的不平衡,所以先需绕ancestor进行旋转,再进行平衡因子的调整
    374              * 
    375              * 新增13                         先调整因子再绕50右旋
    376              *   →         50 ← ancestor        →            20
    377              *            /L                                /=
    378              *          20   70                            10   50
    379              *         /=   /=                          /R   /=
    380              *        10 30 60 90                        5  15 30  70
    381              *       /= /==  =                         = /L /=  /=
    382              *      5 15 25 35                            13 25 35 60 90
    383              *      = /= = =                              =  =  =  =  = 
    384              *       13         
    385              *       =        
    386              */
    387             ancestor.balanceFactor = '=';
    388             //围绕ancestor执行一次右旋
    389             rotateRight(ancestor);
    390             //再调整ancestor.paraent(20)到新增节点路径上祖先节点平衡
    391             adjustPath(ancestor.parent, inserted);
    392         } else if (ancestor.balanceFactor == 'L'
    393                 && elem.compareTo(ancestor.left.elem) > 0) {
    394             /*
    395              * 情况5:
    396              * ancestor.balanceFactor的值为 L,并且被插入的Entry位于ancestor的左子树的右子树上。此
    397              * 种情况也会导致树不平衡,此种与第6种一样都需要执行两次旋转(执行一次绕ancestor的左子节点左
    398              * 旋,接着执行一次绕ancestor执行一次右旋)后,树才平衡,最后还需调用 左-右旋 专有方法进行平衡
    399              * 因子的调整
    400              */
    401             rotateLeft(ancestor.left);
    402             rotateRight(ancestor);
    403             //旋转后调用 左-右旋 专有方法进行平衡因子的调整
    404             adjustLeftRigth(ancestor, inserted);
    405         } else if (ancestor.balanceFactor == 'R'
    406                 && elem.compareTo(ancestor.right.elem) < 0) {
    407 
    408             /*
    409              * 情况6:
    410              * ancestor.balanceFactor的值为 R,并且被插入的Entry位于ancestor的右子树的 左子树上,此
    411              * 种情况也会导致树不平衡,此种与第5种一样都需要执行两次旋转(执行一次绕ancestor的右子节点右旋
    412              * ,接着执行一次绕ancestor执行一次左旋)后,树才平衡,最后还需调用 右-左旋 专有方法进行平衡因
    413              * 子的调整
    414              */
    415             rotateRight(ancestor.right);
    416             rotateLeft(ancestor);
    417             //旋转后调用 右-左旋 专有方法进行平衡因子的调整
    418             adjustRigthLeft(ancestor, inserted);
    419         }
    420     }
      1     /**
      2      * 调整指定路径上的节点的平衡因子
      3      * 
      4      * 注,指定的路径上的所有节点一定是平衡的,因此如果被插入元素小于某个祖先节点,
      5      * 则这个祖先节点新的平衡因子是 L,反之为 R。
      6      * 
      7      * @param inserted 从哪里元素开始向上调整,但不包括该,即从父开始)
      8      * @param to 直到哪个元素结束,但不包括该元素,一般传进来的为ancestor
      9      */
     10     protected void adjustPath(Entry<E> to, Entry<E> inserted) {
     11         E elem = inserted.elem;
     12         Entry<E> tmp = inserted.parent;
     13         //从新增节点的父节点开始向上回溯调整,直到结尾节点to止
     14         while (tmp != to) {
     15             /*
     16              * 插入30,则在25右边插入,这样父节点平衡会被打破,右子树就会比左子树高1,所以平衡因子为R;祖
     17              * 先节点50的平衡因子也被打破,因为在50的左子树上插入的,所以对50来说,左子树会比右子树高1,所
     18              * 以其平衡因子为L
     19              *    50                      50
     20              *    /=       插入30        /L
     21              *   25  70       →         25  70
     22              *   =   =                   R  =
     23              *                            30
     24              *                            = 
     25              */
     26             //如果新增元素比祖先节点小,则是在祖先节点的左边插入,则自然该祖先的左比右子树会高1            
     27             if (elem.compareTo(tmp.elem) < 0) {
     28 
     29                 tmp.balanceFactor = 'L';
     30             } else {
     31                 //否则会插到右边,那么祖先节点的右就会比左子树高1
     32                 tmp.balanceFactor = 'R';
     33             }
     34             tmp = tmp.parent;//再调整祖先的祖先
     35         }
     36     }
     37 
     38     /**
     39      * 进行 左-右旋转 后平衡因子调整
     40      * 分三种情况
     41      * @param ancestor
     42      * @param inserted
     43      */
     44     protected void adjustLeftRigth(Entry<E> ancestor, Entry<E> inserted) {
     45         E elem = inserted.elem;
     46         if (ancestor.parent == inserted) {
     47             /*
     48              * 第1种,新增的节点在旋转完成后为ancestor父节点情况:
     49              * 
     50              * 新增40                          绕30左旋                绕50右旋
     51              *   →      50 ← ancestor         →         50         →
     52              *          /L                              /L         
     53              *         30                              40  
     54              *         =                             /=
     55              *          40                           30
     56              *          =                            =
     57              *          
     58              *                    调整平衡因子
     59              *          40            →            40
     60              *          /=                        /=
     61              *         30 50                      30 50
     62              *         =  L                       =   =
     63              *         
     64              * 注,这里的 左-右旋 是在fixAfterInsertion方法中的第5种情况中完成的,在这里只是平衡因子的
     65              * 调整,图是为了好说明问题而放在这个方法中的,下面的两个分支也是一样      
     66              */
     67             ancestor.balanceFactor = '=';
     68         } else if (elem.compareTo(ancestor.parent.elem) < 0) {
     69             /*
     70              * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要小
     71              * 的情况
     72              * 
     73              * 由于插入元素(35)比旋转后ancestor(50)的父节点(40)要小, 所以新增节点会在其左子树中
     74              * 
     75              * 新增35                         绕20左旋
     76              *   →      50 ← ancestor        →                 50
     77              *          /L                                    /L
     78              *        20   90                                40   90
     79              *       /=   /=                              /=   /=
     80              *     10  40 70  100                          20 45 70 100
     81              *    /=  /==   =                            /=    
     82              *   5  15 30 45                              10  30   
     83              *   =  =  = =                              /=   =
     84              *          35                              5  15   35
     85              *          =                               =  =    =
     86              *          
     87              *  绕50右旋                      调整平衡因子
     88              *     →        40                →                40
     89              *              /=                                /=
     90              *             20  50                            20   50
     91              *            /=  /L                          /=   /R
     92              *          10 30 45 90                        10 30 45  90
     93              *         /=  =   /=                      /=  R    /=
     94              *        5  15  35 70 100                   5  15  35  70  100
     95              *        =  =   =  =  =                     =  =   =   =   =
     96              *          
     97              */
     98             ancestor.balanceFactor = 'R';
     99             //调整ancestor兄弟节点到插入点路径上节点平衡因子
    100             adjustPath(ancestor.parent.left, inserted);
    101         } else {
    102             /*
    103              * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要大的
    104              * 情况
    105              * 
    106              * 由于插入元素(42)比旋转后ancestor(50)的父节点(40)要大,所以新增节点会在其右子树中
    107              * 
    108              * 新增42                           绕20左旋
    109              *   →      50 ← ancestor          →               50
    110              *          /L                                    /L
    111              *        20   90                                40   90
    112              *       /=   /=                              /=    /=
    113              *     10  40 70  100                          20  45 70 100
    114              *    /=  /==   =                           /=  /=
    115              *   5  15 30 45                             10 30 42  
    116              *   =  =  =  /=                             /==  =
    117              *           42                             5  15   
    118              *           =                              =  =    
    119              *          
    120              * 绕50右旋                        调整平衡因子
    121              *   →          40                 →               40
    122              *              /=                                /=
    123              *             20  50                            20   50
    124              *            /=  /L                          /L   /=
    125              *          10 30 45 90                        10 30 45  90
    126              *         /=   /=  /=                      /=    /L  /=
    127              *        5  15 42  70 100                    5 15  42  70 100
    128              *        =  =  =   =  =                      =  =  =   =  =
    129              *          
    130              */
    131             ancestor.parent.left.balanceFactor = 'L';
    132 
    133             ancestor.balanceFactor = '=';
    134             //调整ancestor节点到插入点路径上节点平衡因子
    135             adjustPath(ancestor, inserted);
    136         }
    137     }
    138 
    139     /**
    140      * 进行 右-左旋转 后平衡因子调整
    141      * 
    142      * 与adjustLeftRigth方法一样,也有三种情况,这两个方法是对称的
    143      * @param ancestor
    144      * @param inserted
    145      */
    146     protected void adjustRigthLeft(Entry<E> ancestor, Entry<E> inserted) {
    147         E elem = inserted.elem;
    148         if (ancestor.parent == inserted) {
    149             /*
    150              * 第1种,新增的节点在旋转完成后为ancestor父节点情况: 
    151              * 
    152              * 新增40                          绕50右旋                绕30左旋 
    153              *   →       30 ← ancestor        →        30          →
    154              *           R                            R         
    155              *            50                            40  
    156              *           /=                              =
    157              *          40                                50
    158              *          =                                 =
    159              *          
    160              *          40         调整平衡因子         40
    161              *          /=           →            /=
    162              *         30 50                      30  50
    163              *         R  =                       =   =
    164              *         
    165              * 注,这里的 右-左旋 是在fixAfterInsertion方法中的第6种情况中完成的,这里只是 平衡因子的调
    166              * 整,图是为了好说明问题而放在这个方法中的,下面的两个分支也是一样      
    167              */
    168             ancestor.balanceFactor = '=';
    169         } else if (elem.compareTo(ancestor.parent.elem) > 0) {
    170             /*
    171              * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后
    172              * ancestor的父节点要大的情况
    173              * 
    174              * 由于插入元素(73)比旋转后ancestor(50)的父节点(70)要大, 所以新增节点会
    175              * 在其右子树中
    176              * 
    177              * 新增73                          绕90右旋
    178              *   →       50 ← ancestor       →                  50
    179              *          /R                                    /R
    180              *        20   90                                20   70
    181              *       /=   /=                              /=   /=
    182              *     10  40 70  95                          10  40 65 90
    183              *     =   = /=  /=                         =   =  =  /= 
    184              *          65 75 93 97                                75  95
    185              *          =  /= =  =                                 /=  /=
    186              *            73                                      73  93 97
    187              *            =    
    188              *                          
    189              * 绕50左旋                       调整平衡因子
    190              *   →          70                →                70
    191              *              /=                                /=
    192              *            50   90                            50   90
    193              *           /R   /=                          /L   /=
    194              *          20 65 75  95                       20 65 75  95
    195              *         /= =  /=  /=                     /= =  /L  /=
    196              *        10  40 73  93 97                   10  40 73  93 97
    197              *        =   =   =   =  =                   =   =  =   =   =
    198              *          
    199              */
    200             ancestor.balanceFactor = 'L';
    201             adjustPath(ancestor.parent.right, inserted);
    202         } else {
    203             /*
    204              * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要小
    205              * 的情况
    206              * 
    207              * 由于插入元素(73)比旋转后ancestor(50)的父节点(70)要大, 所以新增节点会在其右子树中
    208              * 
    209              * 新增63                          绕90右旋
    210              *   →       50 ← ancestor       →                 50
    211              *          /R                                    /R
    212              *        20   90                                20   70
    213              *       /=   /=                              /=   /=
    214              *     10  40 70  95                          10  40 65 90
    215              *     =   = /=  /=                         =   =  /= /= 
    216              *          65 75 93 97                             63 75 95
    217              *          /= =  =  =                              =  =  /=
    218              *         63                                            93 97
    219              *         =                                             =  =
    220              *                          
    221              * 绕50左旋                       调整平衡因子
    222              *   →          70                →                70
    223              *              /=                                /=
    224              *            50   90                            50   90
    225              *           /R   /=                          /=   /R
    226              *         20  65 75 95                       20  65 75 95
    227              *        /=  /= =  /=                     /=  /L    /=
    228              *       10 40 63   93 97                   10 40 63   93 97
    229              *       =  =  =    =  =                    =  =  =    =  =         
    230              */
    231             ancestor.parent.right.balanceFactor = 'R';
    232             ancestor.balanceFactor = '=';
    233             adjustPath(ancestor, inserted);
    234         }
    235     }
    236 
    237     /**
    238      * 删除指定节点
    239      * 
    240      * @param elem
    241      * @return boolean
    242      */
    243     public boolean remove(E elem) {
    244         Entry<E> e = getEntry(elem);
    245         if (e == null)
    246             return false;
    247         deleteEntry(e);
    248         return true;
    249     }
    250 
    251     private Entry<E> getEntry(E e) {
    252         Entry<E> tmp = root;
    253         int c;
    254         while (tmp != null) {
    255             c = e.compareTo(tmp.elem);
    256             if (c == 0) {
    257                 return tmp;
    258             } else if (c < 0) {
    259                 tmp = tmp.left;
    260             } else {
    261                 tmp = tmp.right;
    262             }
    263         }
    264         return null;
    265     }
    266 
    267     private void deleteEntry(Entry<E> p) {
    268         if (p.left != null && p.right != null) {
    269 
    270             Entry<E> s = successor(p);
    271 
    272             p.elem = s.elem;
    273 
    274             p = s;
    275         }
    276 
    277         if (p.left == null && p.right == null) {
    278 
    279             if (p.parent == null) {
    280                 root = null;
    281             } else if (p == p.parent.left) {
    282                 p.parent.left = null;
    283             } else {
    284                 p.parent.right = null;
    285             }
    286 
    287         } else {
    288 
    289             Entry<E> rep;
    290             if (p.left != null) {
    291                 rep = p.left;
    292             } else {
    293                 rep = p.right;
    294             }
    295 
    296             rep.parent = p.parent;
    297 
    298             if (p.parent == null) {
    299                 root = rep;
    300             } else if (p == p.parent.left) {
    301                 p.parent.left = rep;
    302             } else {
    303                 p.parent.right = rep;
    304             }
    305 
    306         }
    307         fixAfterDeletion(p.elem, p.parent);
    308 
    309         p.parent = null;
    310         p.left = null;
    311         p.right = null;
    312 
    313         size--;
    314     }
    315 
    316     /**
    317      * 查找指定节点的中序遍历序列的直接后继节点,此方法的实现与二叉搜索树类(BinSearchTree.java)的此方法是
    318      * 一样的,具体的思路请参考二叉搜索树类中的相应方法
    319      * 
    320      * @param e 需要查找哪个节点的直接后继节点
    321      * @return Entry<E> 直接后继节点
    322      */
    323     private Entry<E> successor(Entry<E> e) {
    324         if (e == null) {
    325             return null;
    326         }//如果待找的节点有右子树,则在右子树上查找
    327         else if (e.right != null) {
    328             //默认后继节点为右子节点(如果右子节点没有左子树时即为该节点)
    329             Entry<E> p = e.right;
    330             while (p.left != null) {
    331                 //注,如果右子节点的左子树不为空,则在左子树中查找,且后面找时要一直向左拐
    332                 p = p.left;
    333             }
    334             return p;
    335         }//如果待查节点没有右子树,则要在祖宗节点中查找后继节点 
    336         else {
    337 
    338             //默认后继节点为父节点(如果待查节点为父节点的左子树,则后继为父节点)
    339             Entry<E> p = e.parent;
    340             Entry<E> c = e;//当前节点,如果其父不为后继,则下次指向父节点
    341             //如果待查节点为父节点的右节点时,继续向上找,一直要找到c为左子节点,则p才是后继
    342             while (p != null && c == p.right) {
    343                 c = p;
    344                 p = p.parent;
    345             }
    346             return p;
    347         }
    348     }
      1     /**
      2      * 删除节点后平衡调整实现
      3      * 
      4      * @param elem 被删除节点的数据域
      5      * @param ancestor 被删除节点的祖先节点,从父节点向上迭代
      6      */
      7     protected void fixAfterDeletion(E elem, Entry<E> ancestor) {
      8 
      9         boolean heightHasDecreased = true;//树的高度是否还需要减小
     10 
     11         /*
     12          * 1、如果删除的是根节点,则ancestor为空,此时不需调整了,直接退出
     13          * 2、如果删除的不是根节点,且根节点都已调整,则退出
     14          * 3、如果删除的不是根节点,且树的高度不需再减小(heightHasDecreased为false),退出
     15          */
     16         while (ancestor != null && heightHasDecreased) {
     17 
     18             int comp = elem.compareTo(ancestor.elem);
     19 
     20             /*
     21              * 当要删除的节点有左右子树时,comp就会等于0,比如下面要删除33这个节点,删除方法deleteEntry
     22              * 会用36替换掉33节点中的数据的elem,删除后会调用fixAfterDeletion(p.elem, p.parent)方
     23              * 法来调整平衡因子,p又是指向的36,所以p.elem与p.parent.elem是相等的,都是36
     24              * 
     25              *            82
     26              *           /L
     27              *         42   95
     28              *        /=   R
     29              *       33 48   96
     30              *      /=  /=
     31              *     29 36 43 75
     32              */
     33 
     34             //从ancestor的右子树中删除节点
     35             if (comp >= 0) {
     36                 // ancestor 的平衡因子为 '='
     37                 if (ancestor.balanceFactor == '=') {
     38 
     39                     /* 删除15       调整因子
     40                      *      20       →           20
     41                      *      /L                  /L
     42                      *   → 10 50                10 50
     43                      *     /=                  /L
     44                      *    5  15                5
     45                      */
     46                     ancestor.balanceFactor = 'L';
     47                     heightHasDecreased = false;
     48 
     49                 } // ancestor 的平衡因子为 'R'
     50                 else if (ancestor.balanceFactor == 'R') {
     51                     /* 删除15       调整因子                    下次循环调整20的因子
     52                      *      20       →         → 20 ← ancestor   → ...
     53                      *      /L                  /L
     54                      *   → 10 50                10 50
     55                      *     /R R               /= R
     56                      *    5  15 60              5 18 60
     57                      *        R
     58                      *         18
     59                      */
     60                     ancestor.balanceFactor = '=';
     61                     ancestor = ancestor.parent;
     62 
     63                 }// ancestor 的平衡因子为 'L'
     64                 else if (ancestor.balanceFactor == 'L') {
     65                     // ancestor 的左子节点平衡因子为 '='
     66                     if (ancestor.left.balanceFactor == '=') {
     67 
     68                         /* 删除60       调整因子              绕50右旋
     69                          *      20       →     → 20         →        20
     70                          *      /R              /                  /R
     71                          *     10 50 ← ancestor 10 50 ←             10 45
     72                          *     /L /L           /  /L              /L /R
     73                          *    5  45 60         5  45 60            5  35 50 ← 
     74                          *      /=              /R                     /L
     75                          *     35 48            35 48                   48
     76                          */
     77                         ancestor.left.balanceFactor = 'R';
     78                         ancestor.balanceFactor = 'L';
     79                         rotateRight(ancestor);
     80                         heightHasDecreased = false;
     81 
     82                     }// ancestor 的左子节点平衡因子为 'L' 
     83                     else if (ancestor.left.balanceFactor == 'L') {
     84 
     85                         /* 删除60       调整因子           绕50右旋   下次循环调整20的因子
     86                          *      20       →     → 20     →    20 ← p     → ...
     87                          *      /R              /          /R
     88                          *     10 50 ← ancestor 10 50 ←     10 45
     89                          *     /L /L           /  /=      /L /=
     90                          *    5  45 60         5  45 60    5  35 50 ← ancestor
     91                          *      /L               /=              =
     92                          *     35               35                      
     93                          */
     94                         Entry<E> p = ancestor.parent;
     95                         ancestor.balanceFactor = '=';
     96                         ancestor.left.balanceFactor = '=';
     97                         rotateRight(ancestor);
     98                         ancestor = p;
     99 
    100                     } // ancestor 的左子节点平衡因子为 'R'
    101                     else if (ancestor.left.balanceFactor == 'R') {
    102 
    103                         Entry<E> p = ancestor.parent;
    104 
    105                         // ancestor 的左子节点的右子节点的平衡因子为 'L'
    106                         if (ancestor.left.right.balanceFactor == 'L') {
    107 
    108                             /* 删除60        调整因子               
    109                              *      20        →       20             
    110                              *      /R               / 
    111                              *    10   50 ← ancestor 10  50 ← ancestor
    112                              *    /L   /L          /   /R
    113                              *   5  12 45 60        5  12 45 70
    114                              *  /L    /R  R      /     /=  
    115                              * 3     42 48  70    3     42 48  
    116                              *          /L                 /= 
    117                              *         46                 46
    118                              *         
    119                              *  绕45左旋         绕50右旋           下次循环调整20的因子
    120                              *    →     20       →         20 ← p      → ...
    121                              *          /R                /R
    122                              *         10  50 ←          10   48
    123                              *         /L  /R          /L   /=
    124                              *        5 12 48 70        5  12 45 50 ← ancestor
    125                              *       /L   /=           /L    /=  R
    126                              *      3    45           3     42 46  70
    127                              *          /=
    128                              *         42 46
    129                              */
    130                             ancestor.balanceFactor = 'R';
    131                             ancestor.left.balanceFactor = '=';
    132 
    133                         }// ancestor 的左子节点的右子节点的平衡因子为 'R' 
    134                         else if (ancestor.left.right.balanceFactor == 'R') {
    135 
    136                             /* 删除60        调整因子               
    137                              *      20        →       20             
    138                              *      /R               / 
    139                              *    10   50 ← ancestor 10  50 ← 
    140                              *    /L   /L          /   /=
    141                              *   5  12 45 60        5  12 45 70
    142                              *  /L    /R  R      /     /L  
    143                              * 3     42 48  70    3     42 48  
    144                              *           R                 = 
    145                              *            49                 49
    146                              *         
    147                              *  绕45左旋         绕50右旋           下次循环调整20的因子
    148                              *    →     20       →         20 ← p      → ...
    149                              *          /R                /R
    150                              *         10  50 ←          10   48
    151                              *         /L  /=          /L   /=
    152                              *        5 12 48 70        5  12 45 50 ← ancestor
    153                              *       /L   /=          /L    /L  /=
    154                              *      3    45 49        3     42  49 70
    155                              *          /L
    156                              *         42 
    157                              */
    158                             ancestor.balanceFactor = '=';
    159                             ancestor.left.balanceFactor = 'L';
    160 
    161                         }// ancestor 的左子节点的右子节点的平衡因子为 '=' 
    162                         else {
    163                             /* 删除60        调整因子               
    164                              *      20        →       20             
    165                              *      /R               / 
    166                              *    10   50 ← ancestor 10  50 ← 
    167                              *    /L   /L          /   /=
    168                              *   5  12 45 60        5  12 45 70
    169                              *  /L    /R  R      /     /=  
    170                              * 3     42 48  70    3     42 48  
    171                              *          /=                /= 
    172                              *         46 49              46 49
    173                              *         
    174                              *  绕45左旋         绕50右旋           下次循环调整20的因子
    175                              *    →     20       →         20 ← p      → ...
    176                              *          /R                /R
    177                              *         10  50 ←          10   48
    178                              *         /L  /=          /L   /=
    179                              *        5 12 48 70        5  12 45  50 ← ancestor
    180                              *       /L   /=          /L    /=   /=
    181                              *      3    45 49        3     42 46 49 70
    182                              *          /=
    183                              *         42 46
    184                              */
    185                             ancestor.balanceFactor = '=';
    186                             ancestor.left.balanceFactor = '=';
    187 
    188                         }
    189                         ancestor.left.right.balanceFactor = '=';
    190                         rotateLeft(ancestor.left);
    191                         rotateRight(ancestor);
    192                         ancestor = p;
    193                     }
    194                 }
    195 
    196             }
    197             //从ancestor的左子树中删除节点,与上面是对称的
    198             else if (comp < 0) {
    199 
    200                 if (ancestor.balanceFactor == '=') {
    201 
    202                     ancestor.balanceFactor = 'R';
    203                     heightHasDecreased = false;
    204                 } else if (ancestor.balanceFactor == 'L') {
    205 
    206                     ancestor.balanceFactor = '=';
    207                     ancestor = ancestor.parent;
    208 
    209                 } else if (ancestor.balanceFactor == 'R') {
    210 
    211                     if (ancestor.right.balanceFactor == '=') {
    212 
    213                         ancestor.balanceFactor = 'R';
    214                         ancestor.right.balanceFactor = 'L';
    215                         rotateLeft(ancestor);
    216                         heightHasDecreased = false;
    217 
    218                     } else if (ancestor.right.balanceFactor == 'R') {
    219 
    220                         Entry<E> p = ancestor.parent;
    221                         ancestor.balanceFactor = '=';
    222                         ancestor.right.balanceFactor = '=';
    223                         rotateLeft(ancestor);
    224                         ancestor = p;
    225 
    226                     } else if (ancestor.right.balanceFactor == 'L') {
    227 
    228                         Entry<E> p = ancestor.parent;
    229                         if (ancestor.right.left.balanceFactor == 'R') {
    230 
    231                             ancestor.balanceFactor = 'L';
    232                             ancestor.right.balanceFactor = '=';
    233 
    234                         } else if (ancestor.right.left.balanceFactor == 'L') {
    235 
    236                             ancestor.balanceFactor = '=';
    237                             ancestor.right.balanceFactor = 'R';
    238 
    239                         } else {
    240 
    241                             ancestor.balanceFactor = '=';
    242                             ancestor.right.balanceFactor = '=';
    243 
    244                         }
    245                         ancestor.right.left.balanceFactor = '=';
    246                         rotateRight(ancestor.right);
    247                         rotateLeft(ancestor);
    248                         ancestor = p;
    249 
    250                     }
    251                 }
    252             }
    253         }
    254     }
    255 
    256     public boolean contains(E o) {
    257 
    258         Entry<E> e = root;
    259 
    260         int comp;
    261 
    262         while (e != null) {
    263 
    264             comp = o.compareTo(e.elem);
    265             if (comp == 0)
    266                 return true;
    267             else if (comp < 0)
    268                 e = e.left;
    269             else
    270                 e = e.right;
    271 
    272         }
    273         return false;
    274     }
    275 
    276     //验证树是否是平衡二叉树
    277     public boolean isAVL() {
    278 
    279         return checkAVL(root);
    280 
    281     }
    282 
    283     /**
    284      * 验证指定的树是否是平衡二叉树
    285      * @param p
    286      * @return
    287      */
    288     public static <E extends Comparable<E>> boolean checkAVL(Entry<E> p) {
    289 
    290         if (p == null)
    291             return true;
    292         //左子树与右子树绝对值不能超过 1,并且左右子树也是平衡二叉树
    293         return (Math.abs(h(p.left) - h(p.right)) <= 1 && checkAVL(p.left) && checkAVL(p.right));
    294 
    295     }
    296 
    297     /**
    298      * 求指定节点的高度
    299      * @param <E>
    300      * @param p
    301      * @return
    302      */
    303     protected static <E extends Comparable<E>> int h(Entry<E> p) {
    304 
    305         if (p == null)
    306             return -1;
    307         return 1 + Math.max(h(p.left), h(p.right));
    308     }
    309 
    310     /**
    311      * 树的高度
    312      * @return
    313      */
    314     public int height() {
    315 
    316         return h(root);
    317 
    318     }
    319 
    320     //树的高度非递归求法
    321     public int heightIter() {
    322 
    323         int height = -1;
    324         for (Entry<E> temp = root; temp != null; height++)
    325             if (temp.balanceFactor == 'L')
    326                 temp = temp.left;
    327             else
    328                 temp = temp.right;
    329         return height;
    330     }
    331 
    332     @Override
    333     public Iterator<E> iterator() {
    334         return new TreeIterator();
    335     }
    336 
    337     @Override
    338     public int size() {
    339         return size;
    340     }
    341 }

    测试:

     1 package tree.avl;
     2 
     3 import java.util.Iterator;
     4 import java.util.Random;
     5 
     6 public class AVLTreeTest {
     7     public static void main(String[] args) {
     8         AVLTree myTree = new AVLTree();
     9         Random random = new Random();
    10         System.out.print("随机产生的节点为:");
    11         int num = 0;
    12         //直到树的节点数为n止
    13         while (myTree.size() < 10) {
    14             num = new Integer(random.nextInt(100));
    15             myTree.add(num);
    16             System.out.print(num + " ");
    17         }
    18         System.out.println("");
    19         if (myTree.isAVL()) {
    20             System.out.println("这棵平衡二叉树的总节点数:" + myTree.size());
    21             System.out.println("这棵平衡二叉树的高度是:" + myTree.height());
    22             System.out.println("在树中查找 " + num + " 节点:"
    23                     + myTree.contains(new Integer(num)));
    24             System.out.println("在树中查找 100 节点:" + myTree.contains(new Integer(100)));
    25             System.out.print("中序遍历:");
    26             Iterator itr = myTree.iterator();
    27             while (itr.hasNext()) {
    28                 System.out.print(itr.next() + " ");
    29             }
    30             System.out.println("");
    31 
    32             myTree.remove(num);
    33             System.out.print("删除节点 " + num + " 后遍历:");
    34             itr = myTree.iterator();
    35             while (itr.hasNext()) {
    36                 System.out.print(itr.next() + " ");
    37             }
    38             System.out.println("");
    39 
    40             System.out.println("使用迭代器删除所有节点");
    41             itr = myTree.iterator();
    42             while (itr.hasNext()) {
    43                 itr.next();
    44                 itr.remove();
    45             }
    46             System.out.println("删除后树的总节点数:" + myTree.size());
    47         } else {
    48             System.out.println("failure");
    49         }
    50     }
    51 }
  • 相关阅读:
    MTGA天梯利用Ummored Ego进行针对核心卡列表
    三日狂欢_THDN_简介
    Unity_Dungeonize 随机生成迷宫
    Match3 Module For Game(THDN)
    UNITY->(width*height)style Inventory
    Mysql基本配置以及常见问题
    C++||变量
    c++||OOP
    c++||template
    实用的js函数
  • 原文地址:https://www.cnblogs.com/jiangzhengjun/p/4289792.html
Copyright © 2011-2022 走看看