您的位置:首页 > 理论基础 > 数据结构算法

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

2016-08-01 22:04 746 查看
上一篇文章写到用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)可以调整。

单旋转:



图中显示了单旋转如何调整的情形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的左儿子的位置上,这样,所有对顺序的要求都得到了满足。



图中显示了单旋转如何调整的情形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>{

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: