AVL

AVL

目录

概述

二叉搜索树(BST)可能会造成如下图所示的问题:

性能很差的BST

查找的时间复杂度会达到O(N),这个时候AVL树就派上用场了。
一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉搜索树(空树的高度定义为-1)。

把失去平衡的节点叫做α,由于任意节点最多有两个孩子,因此出现高度不平衡的α的两棵子树的高度差为2。容易看出,这种不平衡可能会出现以下四种情况:

  • 对α的左孩子的左子树进行一次插入
  • 对α的左孩子的右子树进行一次插入
  • 对α的右孩子的左子树进行一次插入
  • 对α的右孩子的右子树进行一次插入

情形1和4是对称的,2和3也是对称的。因此,理论上只有两种情形。


单旋转

右旋转

情形1如下图所示:

情形1

在k2的左孩子k1的左子树插入一个元素,导致k2的左右子树不平衡了(插入前是平衡的),此时以k1为轴,进行一次右旋转(顺时针)即可达到平衡。

左旋转

同理,就很容易得出对称的情形4:

情形4

在k1的右孩子k2的右子树插入一个元素,导致k1的左右子树不平衡(插入前是平衡的),此时以k2为轴,进行一次左旋转(逆时针)即可达到平衡。


双旋转

左-右旋转

情形2如图所示:

情形2

假设在k3的左孩子k1的右子树上k2插入一个元素,导致B或者C比D深两层,那么此时只需要:

  1. 以k1为轴,先做一次左旋转(情形1)
  2. 再次k3为轴,做一次右旋转(情形4)

即可让整棵树再次达到平衡,也就是两次单旋转即可。

右-左旋转

同样,对于情形3,如图:

情形3

在k1的右孩子k3的左子树k2上插入一个元素,导致B或者C比A深两层,那么此时只需要:

  1. 以k3为轴,先做一次右旋转(情形4)
  2. 再以k1为轴,做一次左旋转(情形1)

即可让整棵树再次达到平衡,也是两次单旋转。


代码实现

已经明白了旋转的原理,剩下的就是编码实现了,弄清楚指针指向就行了。

public class AVLTreeTest {  
    public static void main(String[] agrs) {
        AVLTree tree = null;
        for(Integer i = 1; i < 10; i++) {
            tree = AVLTreeUtils.insert(i, tree);
        }
        System.out.println("preOrder: ");
        AVLTreeUtils.preOrder(tree);
        System.out.println();
        System.out.println("inOrder: ");
        AVLTreeUtils.inOrder(tree);
        System.out.println();
        System.out.println("postOrder: ");
        AVLTreeUtils.postOrder(tree);

    }       
}
class AVLTree {
    public Integer element;
    public AVLTree lchild;
    public AVLTree rchild;
    public Integer height;

    public AVLTree() {}
}
class AVLTreeUtils {

    public static AVLTree insert(Integer ele, AVLTree root) {
        if(root == null) {// 如果是空节点,就插入
            root = new AVLTree();
            root.element = ele;
            root.lchild = root.rchild = null;
            root.height = 0;
        } else if(ele < root.element) {// 在左子树插入
            root.lchild = insert(ele, root.lchild);
            // 插入后,检查一下是否已经失衡
            if(getHeight(root.lchild) - getHeight(root.rchild) == 2) {
                if(ele < root.lchild.element)
                    // 左孩子的左子树——情形1 
                    root = singleRotateWithLeft(root);
                else
                    // 左孩子的右子树--情形2
                    root = doubleRotateWithLeft(root);
            }           
        } else if(ele > root.element)   {
            root.rchild = insert(ele, root.rchild);
            if(getHeight(root.rchild) - getHeight(root.lchild) == 2) {
                if(ele > root.rchild.element) 
                    // 右孩子的左子树--情形3
                    root = singleRotateWithRight(root);
                else
                    // 右孩子的右子树--情形4
                    root = doubleRotateWithRight(root);
            }   
        }

        // 调整完成后,更新一下整棵树的高度
        root.height = Math.max(getHeight(root.lchild), getHeight(root.rchild)) + 1;

        return root;        
    }   

    private static Integer getHeight(AVLTree tree) {
        if (tree == null) 
            return -1;
        return tree.height;
    }

    // 对照几张示意图,即可弄明白指针的指向

    // 情形1
    private static AVLTree singleRotateWithLeft(AVLTree oldRoot) {
        AVLTree newRoot = oldRoot.lchild;;
        oldRoot.lchild = newRoot.rchild;
        newRoot.rchild = oldRoot;

        oldRoot.height = Math.max(getHeight(oldRoot.lchild), getHeight(oldRoot.rchild)) + 1;
        newRoot.height = Math.max(getHeight(newRoot.lchild), oldRoot.height) + 1;
        return newRoot;
    };

    // 情形4
    private static AVLTree singleRotateWithRight(AVLTree oldRoot) {
        AVLTree newRoot = oldRoot.rchild;;
        oldRoot.rchild = newRoot.lchild;
        newRoot.lchild = oldRoot;

        oldRoot.height = Math.max(getHeight(oldRoot.lchild), getHeight(oldRoot.rchild)) + 1;
        newRoot.height = Math.max(oldRoot.height, getHeight(newRoot.rchild)) + 1;
        return newRoot;
    };

    // 情形2
    private static AVLTree doubleRotateWithLeft(AVLTree oldRoot) {
        oldRoot.lchild = singleRotateWithRight(oldRoot.lchild);
        return singleRotateWithLeft(oldRoot);
    };  

    // 情形3
    private static AVLTree doubleRotateWithRight(AVLTree oldRoot) {
        oldRoot.rchild = singleRotateWithLeft(oldRoot.rchild);
        return singleRotateWithRight(oldRoot);
    };

    // 先序遍历
    public static void preOrder(AVLTree tree) {
        if(tree != null) {          
            System.out.print(tree.element + " ");           
            preOrder(tree.lchild);          
            preOrder(tree.rchild);
        }       
    }   

    // 中序遍历
    public static void inOrder(AVLTree tree) {      
        if(tree != null) {  
            inOrder(tree.lchild);
            System.out.print(tree.element + " ");
            inOrder(tree.rchild);
        }       
    }

    // 后序遍历
    public static void postOrder(AVLTree tree) {        
        if(tree != null) {  
            postOrder(tree.lchild);         
            postOrder(tree.rchild);
            System.out.print(tree.element + " ");
        }       
    }
}

// output
preOrder: 
4 2 1 3 6 5 8 7 9 
inOrder: 
1 2 3 4 5 6 7 8 9 
postOrder: 
1 3 2 5 7 9 8 6 4 

Summary

以前挺畏惧树的,可是仔细研究一下 – Just a pretty girl standing out there in the fog waiting to be saved…

posted @ 2016-05-11 00:18  1202zhyl  阅读(200)  评论(0编辑  收藏  举报