您的位置:首页 > 其它

树04-自平衡二叉查找树-AVL树

2012-06-28 13:30 330 查看
一)AVL定义

计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-VelskyE.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

出自维基百科:http://zh.wikipedia.org/wiki/AVL%E6%A0%91

二)AVL的实现(java)

package com.fox.avl;

import java.util.ArrayList;
import java.util.List;

public class AVLTree03<T extends Comparable<? super T>> {
private static class AvlNode<T> {
T e;
AvlNode<T> l;
AvlNode<T> r;
int h;

public AvlNode(T e, AvlNode<T> l, AvlNode<T> r) {
super();
this.e = e;
this.l = l;
this.r = r;
this.h = 0;
}

public AvlNode(T e) {
this(e, null, null);
}

}

AvlNode<T> root = null;

private int height(AvlNode<T> t) {
return (t == null) ? -1 : t.h;
}

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

private AvlNode<T> insert(T x, AvlNode<T> t) {
if (t == null)
return new AvlNode<T>(x);
int cr = x.compareTo(t.e);
if (cr < 0) {
t.l = insert(x, t.l);
if (height(t.l) - height(t.r) == 2)
if (x.compareTo(t.l.e) < 0)
t = rotateWithLeftChild(t);
else
t = doubleWithLeftChild(t);
} else if (cr > 0) {
t.r = insert(x, t.r);
if (height(t.r) - height(t.l) == 2)
if (x.compareTo(t.r.e) > 0)
t = rotateWithRightChild(t);
else
t = doubleWithRightChild(t);
} else
;
t.h = Math.max(height(t.l), height(t.r)) + 1;
return t;
}

// RL
private AvlNode<T> doubleWithRightChild(AvlNode<T> t) {
t.r = rotateWithLeftChild(t.r);
return rotateWithRightChild(t);
}

// RR
private AvlNode<T> rotateWithRightChild(AvlNode<T> t) {
AvlNode<T> t1 = t.r;
t.r = t1.l;
t1.l = t;
t.h = Math.max(height(t.l), height(t.r)) + 1;
t1.h = Math.max(height(t1.r), t.h) + 1;
return t1;
}

// LR
private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
t.l = rotateWithRightChild(t.l);
return rotateWithLeftChild(t);
}

// LL
private AvlNode<T> rotateWithLeftChild(AvlNode<T> t) {
AvlNode<T> t1 = t.l;
t.l = t1.r;
t1.r = t;
t.h = Math.max(height(t.l), height(t.r)) + 1;
t1.h = Math.max(height(t1.l), t.h) + 1;
return t1;
}

public boolean contain(T x) {
return contain(x, root);
}

private boolean contain(T x, AvlNode<T> t) {
if (t == null)
return false;
int cr = x.compareTo(t.e);
if (cr < 0)
return contain(x, t.l);
else if (cr > 0)
return contain(x, t.r);
else
return true;
}

public void display() {
List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
ts.add(root);
displayLevel(ts);
}

// 中序遍历
private void display(AvlNode<T> t) {
if (t == null)
return;
System.out.println(t.e + "[" + t.h + "]");
display(t.l);
display(t.r);
}

// 层次遍历
private void displayLevel(List<AvlNode<T>> t) {
if (t.size() == 0)
return;
List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
for (AvlNode<T> t_ : t) {
if (t_ == null)
continue;
System.out.print(t_.e + "[" + t_.h + "]\t");
if (t_.l != null)
ts.add(t_.l);
if (t_.r != null)
ts.add(t_.r);
}
System.out.println();
displayLevel(ts);
}

public static void main(String[] f) {
AVLTree03<Integer> avl = new AVLTree03<Integer>();
avl.insert(1);
avl.insert(12);
avl.insert(3);
avl.insert(8);
avl.insert(2);
avl.insert(11);
avl.display();
System.out.println(avl.contain(1));
System.out.println(avl.contain(10));
}
}


这里注意插入节点的四种情况,下面一一细说。

1)左左情况



t1=t.l;

step1:t.l = t1.r ;

step2: t1.r = t ;

经过“旋转”后如下图:



2)右右情况



t1 = t.r ;

step1: t.r = t1.l ;

step2: t1.l = t ;

经过旋转后如下图:



3)左右情况



t1 = t.l ;

t2 = t.r ;

step1: t1.r = t2.l ;

step2: t2.l = t1 ;

以上步骤和右右情况一致,因此可以看成是对t1做右右操作,旋转后如下图:



这个又和左左一模一样,因此在对t节点做左左操作。从代码中可以清楚的看出:

// LR
private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
t.l = rotateWithRightChild(t.l);//先对t1做右右操作
return rotateWithLeftChild(t);//在对t做左左操作
}


4)右左情况

和3)类似,不再复述。

AVL的插入操作有点小复杂,但是删除操作比插入更复杂,如果偷懒那么还是选用标记删除吧!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: