AVL樹

1. 簡(jiǎn)介

AVL樹得名于它的發(fā)明者---前蘇聯(lián)的數(shù)學(xué)家G.M. Adelson-VelskyE.M. Landis,他們?cè)?1962 年的論文 "An algorithm for the organization of information" 中發(fā)表了它。

2. 定義

AVL樹其實(shí)是一棵高度平衡的二叉搜索樹,它是依靠平衡因子來(lái)維持樹的高度。

  • 對(duì)于每個(gè)結(jié)點(diǎn)來(lái)說(shuō),它的左右子樹高度差的絕對(duì)值(平衡因子)不會(huì)超過(guò)1。
  • 它具有和二叉搜索樹一樣的性質(zhì),也就是說(shuō)除了二叉搜索樹中那些會(huì)改變樹的高度的操作(插入,刪除),其他的操作都可以用在AVL樹中。

3. 實(shí)現(xiàn)

因?yàn)锳VL樹除了插入,刪除這些可能改變樹高度的操作之外,其他操作的與二叉搜索樹無(wú)異,所以這里只講插入和刪除操作。

  • 每個(gè)葉子結(jié)點(diǎn)的高度都為1
  • 每個(gè)結(jié)點(diǎn)都有高度,其高度為左右孩子中最高孩子的高度加上1
  • 每個(gè)結(jié)點(diǎn)的高度差為左子樹的高度減去右子樹的高度
  • 每當(dāng)插入或刪除一個(gè)結(jié)點(diǎn)后,可能導(dǎo)致某個(gè)結(jié)點(diǎn)的平衡因子超過(guò)1,這時(shí)候就需要對(duì)以這個(gè)結(jié)點(diǎn)進(jìn)行旋轉(zhuǎn)操作來(lái)維持平衡。

旋轉(zhuǎn)操作:

1. 左左旋轉(zhuǎn):

需要進(jìn)行左左旋轉(zhuǎn)

如上圖,當(dāng)插入一個(gè)0001這個(gè)結(jié)點(diǎn)后,導(dǎo)致0003的平衡因子超過(guò)1,此時(shí)0003結(jié)點(diǎn)需要通過(guò)左左旋轉(zhuǎn)來(lái)維持平衡。因?yàn)槠茐钠胶獾慕Y(jié)點(diǎn)在發(fā)現(xiàn)不平衡的結(jié)點(diǎn)的左孩子的左孩子上,取名左左旋轉(zhuǎn),旋轉(zhuǎn)后的結(jié)果如下圖:
左左旋轉(zhuǎn)后

代碼實(shí)現(xiàn):

//左左旋轉(zhuǎn)
private Node rotationLeft(Node node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        updateHeight(node);
        return x;
    }

2. 右右旋轉(zhuǎn):

需要進(jìn)行右右旋轉(zhuǎn)

如上圖,當(dāng)插入一個(gè)0003這個(gè)結(jié)點(diǎn)后,導(dǎo)致0001的平衡因子超過(guò)1,此時(shí)0003結(jié)點(diǎn)需要通過(guò)右右旋轉(zhuǎn)來(lái)維持平衡。因?yàn)槠茐钠胶獾慕Y(jié)點(diǎn)在發(fā)現(xiàn)不平衡的結(jié)點(diǎn)的右孩子的右孩子上,取名右右旋轉(zhuǎn),旋轉(zhuǎn)后的結(jié)果如下圖:
右右旋轉(zhuǎn)后

代碼實(shí)現(xiàn):

//右右旋轉(zhuǎn)
    private Node rotationRight(Node node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        updateHeight(node);
        return x;
    }

3. 右左旋轉(zhuǎn):

需要進(jìn)行右左旋轉(zhuǎn)

如上圖,當(dāng)插入一個(gè)0002這個(gè)結(jié)點(diǎn)后,導(dǎo)致0001的平衡因子超過(guò)1,此時(shí)0001結(jié)點(diǎn)需要通過(guò)右左旋轉(zhuǎn)來(lái)維持平衡。因?yàn)槠茐钠胶獾慕Y(jié)點(diǎn)在發(fā)現(xiàn)不平衡的結(jié)點(diǎn)的右孩子的左孩子上,取名右左旋轉(zhuǎn),旋轉(zhuǎn)后的結(jié)果如下圖:
右左旋轉(zhuǎn)后

代碼實(shí)現(xiàn):

//右左旋轉(zhuǎn)
    private Node rotationRightLeft(Node node){
        node.right = rotationLeft(node.right);
        updateHeight(node.right);
        return rotationRight(node);
    }

4.左右旋轉(zhuǎn):

image.png

如上圖,當(dāng)插入一個(gè)0002這個(gè)結(jié)點(diǎn)后,導(dǎo)致0003的平衡因子超過(guò)1,此時(shí)0003結(jié)點(diǎn)需要通過(guò)左右旋轉(zhuǎn)來(lái)維持平衡。因?yàn)槠茐钠胶獾慕Y(jié)點(diǎn)在發(fā)現(xiàn)不平衡的結(jié)點(diǎn)的左孩子的右孩子上,取名左右旋轉(zhuǎn),旋轉(zhuǎn)后的結(jié)果如下圖:
左右旋轉(zhuǎn)后

代碼實(shí)現(xiàn):

//左右旋轉(zhuǎn)
private Node rotationLeftRight(Node node){
        node.left = rotationRight(node.left);
        updateHeight(node.left);
        return rotationLeft(node);
    }

4. 時(shí)間復(fù)雜度

由于AVL樹是一個(gè)高度平衡的二叉搜索樹,所以樹的高度幾乎是lgN,所以無(wú)論查找,插入還是刪除操作最壞情況的時(shí)間復(fù)雜度為O(lgN)。

5. 代碼實(shí)現(xiàn)

其中的插入刪除操作都是用遞歸來(lái)實(shí)現(xiàn)的


import java.util.*;

public class AVL <Key extends Comparable<Key>, Value>{

    private class Node{
        Key key;
        Value value;
        int height;
        Node left;
        Node right;

        public Node(Key key, Value value, int height, Node left, Node right){
            this.key = key;
            this.value = value;
            this.height = height;
            this.left = left;
            this.right = right;
        }
    }

    private Node root;
    private int size;

    public int size(){
        return size;
    }

    //獲取樹高度
    public int height(Node node){
        return node == null ? 0 : node.height;
    }

    //高度差
    private int altitude(Node node){
        return height(node.left) - height(node.right);
    }

    //更新樹高度
    private void updateHeight(Node node){
        node.height = Math.max(height(node.left), height(node.right)) + 1;
    }

    //右旋
    private Node rotationRight(Node node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        updateHeight(node);
        return x;
    }

    //左旋
    private Node rotationLeft(Node node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        updateHeight(node);
        return x;
    }

    //左右旋轉(zhuǎn)
    private Node rotationLeftRight(Node node){
        node.left = rotationRight(node.left);
        updateHeight(node.left);
        return rotationLeft(node);
    }

    //右左旋轉(zhuǎn)
    private Node rotationRightLeft(Node node){
        node.right = rotationLeft(node.right);
        updateHeight(node.right);
        return rotationRight(node);
    }

    //平衡
    private Node balance(Node node, int altitude){
        if (altitude == 2)
            node =  height(node.left.left) > height(node.left.right) ? rotationLeft(node) : rotationLeftRight(node);
        else if (altitude == -2)
            node =  height(node.right.left) > height(node.right.right) ? rotationRightLeft(node) : rotationRight(node);

        updateHeight(node);
        return node;
    }

    //插入
    public void put(Key key, Value value){
        root = put(key, value, root);
        ++size;
    }

    private Node put(Key key, Value value, Node node) {
        if (node == null)
            return new Node(key, value, 1, null, null);

        int cmp = key.compareTo(node.key);

        if (cmp < 0)
            node.left = put(key, value, node.left);
        else if (cmp > 0)
            node.right = put(key, value, node.right);
        else {
            node.value = value;
            return node;
        }

        return balance(node, altitude(node));
    }

    private Node max(Node node){
        if (node == null)
            return null;
        while (node.right != null)
            node = node.right;
        return node;
    }


    public Value max() {
        return root == null ? null : max(root).value;
    }



    public void deleteMax(){
        if (root != null) {
            root = deleteMax(root);
            --size;
        }
    }

    private Node deleteMax(Node node){
        if (node.right == null)
            return node.left;

        node.right = deleteMax(node.right);
        return balance(node, altitude(node));
}



    private Node deleteMin(Node node){
        if (node.left == null)
            return node.right;

        node.left = deleteMin(node.left);
        return balance(node, altitude(node));
    }

    public void deleteMin(){
        if (root != null) {
            root = deleteMin(root);
            --size;
        }
    }

    public Value delete(Key key){
        Node node = delete(key, root);
        if (node != null)
            return node.value;
        return null;
    }

    private Node delete(Key key, Node node){
        if (node == null)
            return null;

        int cmp = key.compareTo(node.key);

        if (cmp < 0)
            node.left = delete(key, node.left);
        else if (cmp > 0)
            node.right = delete(key, node.right);
        else {
            --size;
            if (node.left == null)
                return node.right;
            if (node.right == null)
                return node.left;
            Node x = max(node.right);
            node.key = x.key;
            node.value = x.value;
            node.right = deleteMax(node.right);
        }

        return balance(node, altitude(node));
    }

    //中序遍歷
    private void inorderTraverse(Node node, Set<Key> keySet){
        if (node == null)
            return;
        inorderTraverse(node.left, keySet);
        keySet.add(node.key);
        inorderTraverse(node.right, keySet);
    }

    //返回一個(gè)把鍵從小到大排序的迭代器
    public Iterable<Key> keySet(){
        Set<Key> keySet = new TreeSet<>();
        inorderTraverse(root, keySet);
        return keySet;
    }




    public static void main(String[] args){
        AVL<Integer, String> tree = new AVL<>();
        Random random = new Random();

        for (int i = 0; i < 500; ++i)
            tree.put(random.nextInt(), "naoko" + i);


        tree.deleteMax();
        tree.deleteMin();

        for (int i : tree.keySet())
            System.out.println(i);

        System.out.println("符號(hào)表的大?。? + tree.size());
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 更新:經(jīng)過(guò)很多朋友的提醒, 本文的 insert() 和 delete() 兩個(gè)算法存在一些問題, 由于筆者最近略...
    eric_lai閱讀 33,311評(píng)論 18 80
  • 這篇文章收錄在我的 Github 上 algorithms-tutorial,另外記錄了些算法題解,感興趣的可以看...
    Lindz閱讀 2,620評(píng)論 3 11
  • 數(shù)據(jù)結(jié)構(gòu)與算法--從平衡二叉樹(AVL)到紅黑樹 上節(jié)學(xué)習(xí)了二叉查找樹。算法的性能取決于樹的形狀,而樹的形狀取決于...
    sunhaiyu閱讀 7,807評(píng)論 4 32
  • 什么是AVL樹? AVL樹,又稱為平衡二叉樹,它是一種特殊的二叉查找樹(Binary Search Tree, B...
    wqbu閱讀 894評(píng)論 0 0
  • 此時(shí),這個(gè)時(shí)點(diǎn),其實(shí)是困倦的,可面對(duì)著白花花的病床,腦子是臨亂的,明天的手術(shù)讓我思緒萬(wàn)千,從未想過(guò),有一天自己會(huì)有...
    澄籽閱讀 148評(píng)論 0 0

友情鏈接更多精彩內(nèi)容