一)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的插入操作有点小复杂,但是删除操作比插入更复杂,如果偷懒那么还是选用标记删除吧!