二叉查找树的主要缺点:
很多时候输入得序列都是有序或基本有序的,二叉查找树(BST)在最坏的情况下会退化成链表。树的高度会变成N, 而BST的性能又依赖于树的高度。插入、删除、查找等操作的时间复杂度都变成了O(N)。所以我们需要通过一定的方式来降低树的高度。
AVL树是一种自平衡的二叉查找树,AVL树要求它的每个节点的左右子树的高度差不超过1. 它保证了树的深度是O(logN), 所以它不会出现上述BST的缺点。 不管输入序列是什么样的,都能保持良好的性能。树的高度都为O(logN)。
AVL树的缺点是频繁的旋转、需要维护树的节点的平衡以及总体的复杂性,尤其是删除操作。这些缺点使得AVL树没有成为实现字典的标准结构。
AVL树解决了BST在最坏情况下效率低下的问题。换句话说,BST不能保证查询和插入操作花费log时间,而AVL树提供了这种保证。
具体的原理就不再重复了,书上和网上有非常详细的解释。
这里仅提供我自己实现AVL树的代码和注释。
package trees;
/** * AVL树,重构~ * @author earayu * @param <K> * @param <V> */
public class AVL <K extends Comparable<? super K>, V> {
private class AVLNode{
K key;
V val;
int height;
AVLNode leftChild;
AVLNode rightChild;
AVLNode(K key, V val, AVLNode leftChild, AVLNode rightChild){
this.key = key;
this.val = val;
this.height = 0;//height为该节点到叶子节点的最大距离
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
private AVLNode root;
public AVL(){
root = null;
}
/** * 返回AVL树是否为空 * @return */
public boolean isEmpty(){
return root == null;
}
/** * 用键值对创建一个节点插入AVL树,将自动平衡 * @param key * @param val */
public void insert(K key, V val){
root = insert(root, key, val);
}
/** * 删除键为key的元素 * @param key */
public void remove(K key){
root = remove(root, key);
}
/** * 先序遍历AVL树 */
public void prePrint(){
prePrint(root);
}
/**************************************************************************************************/
/** * 返回该节点的高度,空节点返回-1. * @param t * @return */
private int height(AVLNode t){
return t == null ? -1 : t.height;
}
/** * 将key,val插入树t中,然后返回树t。除了最后要平衡一下,代码跟BST完全相同。 * @param t * @param key * @param val * @return */
private AVLNode insert(AVLNode t, K key, V val){
if(t == null)
return new AVLNode(key, val, null, null);
int cmp = key.compareTo(t.key);
if(cmp < 0)
t.leftChild = insert(t.leftChild,key,val);
else if(cmp > 0)
t.rightChild = insert(t.rightChild,key,val);
else
t.val = val;
return balance(t);
}
/** * AVL树的四种旋转,这里只提供代码。 * @param t * @return */
private AVLNode balance( AVLNode t )
{
if( t == null )
return t;
if( height( t.leftChild ) - height( t.rightChild ) > 1 )
if( height( t.leftChild.leftChild ) >= height( t.leftChild.rightChild ) )
t = rotateLL( t );
else
t = rotateLR( t );
else
if( height( t.rightChild ) - height( t.leftChild ) > 1 )
if( height( t.rightChild.rightChild ) >= height( t.rightChild.leftChild ) )
t = rotateRR( t );
else
t = rotateRL( t );
t.height = Math.max( height( t.leftChild ), height( t.rightChild ) ) + 1;
return t;
}
/** * 删除t中键为key的元素,返回t。除了balance,跟BST的代码也几乎相同 * @param t * @param key * @return */
private AVLNode remove(AVLNode t, K key){
if(t == null)
return t;
int cmp = key.compareTo(t.key);
if( cmp < 0 )
t.leftChild = remove(t.leftChild, key);
else if( cmp > 0 )
t.rightChild = remove(t.rightChild, key );
else
if(t.leftChild == null || t.rightChild == null)
t = t.leftChild==null?t.rightChild:t.leftChild;
else{
AVLNode minNode = findMin(t.rightChild);
t.key = minNode.key;
t.val = minNode.val;
t.rightChild = remove(t.rightChild, minNode.key);
}
return balance(t);
}
/** * 返回AVL树t中的键最小的节点 * @param t * @return */
private AVLNode findMin(AVLNode t) {
if (t == null) {
return null;
}
while (t.leftChild != null) {
t = t.leftChild;
}
return t;
}
private AVLNode rotateLL(AVLNode k1){
AVLNode k2 = k1.leftChild;
k1.leftChild = k2.rightChild;
k2.rightChild = k1;
k1.height = Math.max(height(k1.leftChild), height(k1.rightChild) ) + 1;
k2.height = Math.max(height(k2.leftChild), k1.height) + 1;
return k2;
}
private AVLNode rotateRR(AVLNode k1){
AVLNode k2 = k1.rightChild;
k1.rightChild = k2.leftChild;
k2.leftChild = k1;
k1.height = Math.max(height(k1.leftChild), height(k1.rightChild)) + 1;
k2.height = Math.max(k1.height, height(k2.rightChild)) + 1;
return k2;
}
private AVLNode rotateLR(AVLNode k1){
AVLNode k2 = k1.leftChild;
k1.leftChild = rotateRR(k2);
return rotateLL(k1);
}
private AVLNode rotateRL(AVLNode k1){
AVLNode k2 = k1.rightChild;
k1.rightChild = rotateLL(k2);
return rotateRR(k1);
}
private void prePrint(AVLNode t){
if(t != null){
System.out.println(t.val +": " + t.height );
prePrint(t.leftChild);
prePrint(t.rightChild);
}
}
public static void main(String[] args) {
AVL<Integer,Integer> a = new AVL<>();
//for(int i=0;i<11;i++)
// a.insert(i, i);
a.insert(4, 4);
a.insert(1, 1);
a.insert(5, 5);
a.insert(0, 0);
a.insert(3, 3);
a.insert(2, 2);
a.remove(4);
a.remove(5);
a.prePrint();
}
}