一)AVL定义
在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.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的插入操作有点小复杂,但是删除操作比插入更复杂,如果偷懒那么还是选用标记删除吧!