2021-07-01

数据结构学习之用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的节点处的平衡条件)。

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

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

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

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

单旋转:

在这里插入图片描述图中显示了单旋转如何调整的情形4

双旋转:
上面描述的单旋转算法对情形2和3无效,如图所示。问题在于树Y太深,单旋转没有减低它的深度。

在这里插入图片描述
单旋转不能修复情形2

在这里插入图片描述
左—右双旋转修复情形2

为了重新平衡,我们看到,不能再把k3用作根了,唯一的选择就是把k2用作新的根。这迫使k1是k2的左儿子,k3是它的右儿子,从而完全确定了这四棵树的最终位置。容易看出,最后得到的树满足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都是空树。

在这里插入图片描述
2.下面我们插入14,它也需要一个双旋转。此时修复该树的双旋转还是右—左双旋转,它将涉及6,15,7。在这种情况下,k1是含有项6的节点,k2是含有项7的节点,而k3是含有15的节点。子树A的根在项为5的节点上,子树B是空子树,它是项7的节点原先的左儿子,子树C置根于项14的节点上,最后,子树D的根在项为16的节点上。

在这里插入图片描述
3.如果现在插入13,那么在根处就会产生一个不平衡。由于13不在4和7之间,因此我们知道一次单旋转就能完成修正的工作。此种情形属于右—右的情况。

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

1.创建节点类:

private static class AvlNode{

    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 insert (Integer x,AvlNode 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 rotateWithLeftChild(AvlNode 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 rotateWithRightChild(AvlNode 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 doubleWithLeftChild(AvlNode 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 doubleWithRightChild(AvlNode k1){
k1.right = rotateWithLeftChild(k1.right);
return rotateWithRightChild(k1);
}

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

三、实验及运行结果

public class AVLBalanceTree{

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 AVLBalanceTree(){
    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) {
    long startTime = System.nanoTime();
    AVLBalanceTree avl = new AVLBalanceTree();
    for (int i = 0; i < 1000000; i++) {
        avl.insert(i);
    }
    avl.printTree();
    long endTime = System.nanoTime();
    System.out.println((endTime - startTime) / 1000000000.0);
    System.out.println(avl.height());
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值