数据结构学习之用Java实现AVL树

上一篇文章写到用java实现二叉查找树,本篇来大致说一下一种特殊的二叉查找树—AVL树。
主要有以下几个定义:
1.AVL(Adelson-Velskii和Landis)树是带有平衡条件(balance condition)的二叉查找树,这个平衡条件必须容易保持,而且它保证树的深度是O(log N)。最简单的想法是要求左右子树有相同的高度。
2.一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1)。
3.在高度为h的AVL树中,**最少节点数**S(h)=S(h-1)+S(h-2)+1,其中h=0时S(0)=1,h=1时S(1)=2。

一、AVL树的插入操作:
插入操作的困难之处在于,插入一个节点可能破坏AVL树的特性(例如将6插入到图中的AVL树中将会破坏关键字为8的节点处的平衡条件)。
《数据结构学习之用Java实现AVL树》

如果发生这种情况,那么就要考虑在这一步插入完成之前回复平衡的性质。我们可以通过对树进行简单的修正来做到,那就是使用旋转(rotation)

注意:在插入以后,只有那些从插入点到根节点的路径上的节点的平衡可能被改变,因为只有这些节点的子树可能发生变化。当我们沿着这条路径上行到根并更新平衡信息时,可以发现一个节点,它的新平衡破坏了AVL条件。我们将指出如何在第一个这样的节点(即最深的节点)重新平衡这棵树。我们把重新平衡的节点叫做a(上图中的节点8即是这样一个a)

不平衡的四种情况:
1.对a的左儿子的左子树进行一次插入;
2.对a的左儿子的右子树进行一次插入;
3.对a的右儿子的左子树进行一次插入;
4.对a的右儿子的右子树进行一次插入。
情形1和4是关于a点的镜像对称,情形2和3是关于a点的镜像对称。

第一种是插入发生在“外部”(即左-左和右-右)的情况,通过单旋转(single rotation)可以调整。第二种是插入发生在“内部”(即左-右和右-左)的情况,通过双旋转(double rotation)可以调整。

单旋转:
《数据结构学习之用Java实现AVL树》
图中显示了单旋转如何调整的情形1

旋转前的图在左边,旋转后的图在右边。具体做法的分析为:节点k2不满足AVL平衡性质,因为它的左子树比右子树深两层(图中间的实线标示树的各层)。该图所描述的情况只是情形1的一种可能情况,在插入之前k2满足AVL性质,但在插入之后这种性质被破坏了。子树X已经长出一层,这使得它比子树Z深出2层。Y不可能与新X在同一水平,因为那样k2在插入之前就已经失去平衡了;Y也不可能与Z在同一层上,因为那样k1就会是在通向根的路径上破坏AVL平衡条件的第一个节点。
为了使树恢复平衡,我们把X上移一层,并把Z下移一层。
二叉查找树的性质表明:在原树中k2>k1,于是在新树中k2变成了k1的右儿子,X和Z仍然是k1的左儿子和k2的右儿子。子树Y包含原树中介于k1和k2之间的那些节点,可以将它放在新树中k2的左儿子的位置上,这样,所有对顺序的要求都得到了满足。
《数据结构学习之用Java实现AVL树》
图中显示了单旋转如何调整的情形4

双旋转:
上面描述的单旋转算法对情形2和3无效,如图所示。问题在于树Y太深,单旋转没有减低它的深度。
《数据结构学习之用Java实现AVL树》
单旋转不能修复情形2

《数据结构学习之用Java实现AVL树》
左—右双旋转修复情形2

为了重新平衡,我们看到,不能再把k3用作根了,唯一的选择就是把k2用作新的根。这迫使k1是k2的左儿子,k3是它的右儿子,从而完全确定了这四棵树的最终位置。容易看出,最后得到的树满足AVL树的性质,与单旋转的情形一样,我们也把树的高度恢复到插入以前的水平,这就保证所有的重新平衡和高度更新是完善的。

《数据结构学习之用Java实现AVL树》
右—左双旋转修复情形3

双旋转的例子:
我们在已有的树1,2,3,4,5,6,7的基础上以倒序插入关键字10-13。
1.插入16容易,因为它并不破坏平衡性质,但是插入15就会引起在节点7处的高度不平衡。这属于情形3,我们通过一次右—左双旋转来解决。此时,k1是含有项7的节点,k3是含有项16的节点,而k2是含有项15的节点。子树A,B,C,D都是空树。
《数据结构学习之用Java实现AVL树》

2.下面我们插入14,它也需要一个双旋转。此时修复该树的双旋转还是右—左双旋转,它将涉及6,15,7。在这种情况下,k1是含有项6的节点,k2是含有项7的节点,而k3是含有15的节点。子树A的根在项为5的节点上,子树B是空子树,它是项7的节点原先的左儿子,子树C置根于项14的节点上,最后,子树D的根在项为16的节点上。
《数据结构学习之用Java实现AVL树》

3.如果现在插入13,那么在根处就会产生一个不平衡。由于13不在4和7之间,因此我们知道一次单旋转就能完成修正的工作。此种情形属于右—右的情况。
《数据结构学习之用Java实现AVL树》

二、编程思路
为了将项是X的一个新节点插入到一颗AVL树T中去,我们递归的将X插入到T的相应的子树中去,如果子树的高度不变,那么插入完成。否则,如果在T中出现高度不平衡,则根据X以及T和其子树中的项做适当的单旋转或者双旋转,更新这些高度(并解决好与树的其余部分的链接),从而完成插入。
由于一次旋转总能足以解决问题,因此仔细的编写非递归程序一般说来要比编写非递归程序快的多。
然而,想要编写非递归程序是相当困难的,因此还是使用递归方法实现AVl树比较简单。

1.创建节点类:

private static class AvlNode<Integer>{

        Integer element;    //the data in the node
        AvlNode<Integer> left;  //left child
        AvlNode<Integer> right; //right child
        int height;        //height

        public AvlNode(Integer theElement){
            this(theElement,null,null);
        }

        public AvlNode(Integer theElement, AvlNode<Integer> lt, AvlNode<Integer> rt) {
            element = theElement;
            left = lt;
            right = rt;
        }

    }

2.插入方法:

//Internal method to insert into a subtree
    //x the item to insert
    //t the node that roots the subtree
    //return the new root of the subtree
    //注意递归的使用,要逐步分析!!!
    private AvlNode<Integer> insert (Integer x,AvlNode<Integer> t){

        if(t==null)
            return new AvlNode<Integer>(x,null,null);

        int compareResult = x.compareTo(t.element);

        if(compareResult<0){

            t.left = insert(x, t.left);   
            if(height(t.left)-height(t.right)==2)
                if(x.compareTo(t.left.element)<0)
                    t = rotateWithLeftChild(t);  //左—左单旋转
                else
                    t = doubleWithLeftChild(t);  //左—右双旋转 
        }
        else if(compareResult>0){

            t.right = insert(x, t.right);
            if(height(t.right)-height(t.left)==2)
                if(x.compareTo(t.right.element)>0)
                    t = rotateWithRightChild(t);  //右—右单旋转
                else
                    t = doubleWithRightChild(t);  //右—左双旋转 
        }
        else
            ;

        t.height = Math.max(height(t.left), height(t.right))+1;

        return t;       
    }

3.左-左单旋转:

//rotate binary tree node with left child
    //for avl trees, this is a single rotation for case1
    //update heights, then return new root
    private AvlNode<Integer> rotateWithLeftChild(AvlNode<Integer> k2) {

        AvlNode<Integer> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        k2.height = Math.max(height(k2.left), height(k2.right))+1;
        k1.height = Math.max(height(k1.left), k2.height)+1;

        return k1;
    }

4.右-右单旋转:

//rotate binary tree node with right child
    //for avl trees, this is a single rotation for case4
    //update heights, then return new root
    private AvlNode<Integer> rotateWithRightChild(AvlNode<Integer> k1){

        AvlNode<Integer> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;

        k1.height = Math.max(height(k1.left),height(k1.right))+1;
        k2.height = Math.max(height(k2.right), k1.height)+1;

        return k2;

    }

5.左-右双旋转:

//double rotate binary tree node:first left child
    //with its right child;then node k3 with new left child.
    //for all avl trees, this is a double rotation for case 2.
    //update heights, then return new root.
    private AvlNode<Integer> doubleWithLeftChild(AvlNode<Integer> k3){

        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);     
    }

6.右-左双旋转:

//double rotate binary tree node:first right child
    //with its left child;then node k1 with new right child.
    //for all avl trees, this is a double rotation for case 3.
    //update heights, then return new root.
    private AvlNode<Integer> doubleWithRightChild(AvlNode<Integer> k1){
        k1.right = rotateWithLeftChild(k1.right);
        return rotateWithRightChild(k1);
    }

以上是几个主要的方法,下面是全部的实验代码,其中删除方法以后更加熟悉AVl树之后再补上。

三、实验及运行结果

public class AVLTree {

    private AvlNode<Integer> root;

    private static class AvlNode<Integer>{

        Integer element;    //the data in the node
        AvlNode<Integer> left;  //left child
        AvlNode<Integer> right; //right child
        int height;        //height

        public AvlNode(Integer theElement){
            this(theElement,null,null);
        }

        public AvlNode(Integer theElement, AvlNode<Integer> lt, AvlNode<Integer> rt) {
            element = theElement;
            left = lt;
            right = rt;
        }

    }

    public AVLTree(){
        root = null;
    }

    public void insert(Integer x)
    {
        root = insert(x,root);
    }

    private int height(){

        return height(root);
    }

    //return the height of node t, or -1, if null.
    private int height(AvlNode<Integer> t){
        return t == null?-1:t.height;
    }

    //Internal method to insert into a subtree
    //x the item to insert
    //t the node that roots the subtree
    //return the new root of the subtree
    //注意递归的使用,要逐步分析!!!
    private AvlNode<Integer> insert (Integer x,AvlNode<Integer> t){

        if(t==null)
            return new AvlNode<Integer>(x,null,null);

        int compareResult = x.compareTo(t.element);

        if(compareResult<0){

            t.left = insert(x, t.left);   
            if(height(t.left)-height(t.right)==2)
                if(x.compareTo(t.left.element)<0)
                    t = rotateWithLeftChild(t);  //左—左单旋转
                else
                    t = doubleWithLeftChild(t);  //左—右双旋转 
        }
        else if(compareResult>0){

            t.right = insert(x, t.right);
            if(height(t.right)-height(t.left)==2)
                if(x.compareTo(t.right.element)>0)
                    t = rotateWithRightChild(t);  //右—右单旋转
                else
                    t = doubleWithRightChild(t);  //右—左双旋转 
        }
        else
            ;

        t.height = Math.max(height(t.left), height(t.right))+1;

        return t;       
    }

    //rotate binary tree node with left child
    //for avl trees, this is a single rotation for case1
    //update heights, then return new root
    private AvlNode<Integer> rotateWithLeftChild(AvlNode<Integer> k2) {

        AvlNode<Integer> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        k2.height = Math.max(height(k2.left), height(k2.right))+1;
        k1.height = Math.max(height(k1.left), k2.height)+1;

        return k1;
    }

    //rotate binary tree node with right child
    //for avl trees, this is a single rotation for case4
    //update heights, then return new root
    private AvlNode<Integer> rotateWithRightChild(AvlNode<Integer> k1){

        AvlNode<Integer> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;

        k1.height = Math.max(height(k1.left),height(k1.right))+1;
        k2.height = Math.max(height(k2.right), k1.height)+1;

        return k2;

    }

    //double rotate binary tree node:first left child
    //with its right child;then node k3 with new left child.
    //for all avl trees, this is a double rotation for case 2.
    //update heights, then return new root.
    private AvlNode<Integer> doubleWithLeftChild(AvlNode<Integer> k3){

        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);     
    }

    //double rotate binary tree node:first right child
    //with its left child;then node k1 with new right child.
    //for all avl trees, this is a double rotation for case 3.
    //update heights, then return new root.
    private AvlNode<Integer> doubleWithRightChild(AvlNode<Integer> k1){
        k1.right = rotateWithLeftChild(k1.right);
        return rotateWithRightChild(k1);
    }

    public void printTree()
    {

            printTree(root);        

    }

    private void printTree(AvlNode<Integer> t)
    {
        if(t!=null)
        {
            printTree(t.left);
            System.out.println(t.element);
            printTree(t.right);
        }

    }

    public static void main(String[] args) {

        AVLTree avl = new AVLTree();
        avl.insert(1);
        avl.insert(5);
        avl.insert(7);
        avl.insert(9);
        avl.insert(2);

        avl.printTree();

        System.out.println(avl.height());

    }

}

结果:
1
2
5
7
9
树的高度为:2

    原文作者:AVL树
    原文地址: https://blog.csdn.net/qq_27606119/article/details/52089586
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞