數據結構-AVL

簡介

普通的二分搜索樹是有可能退化成鏈表的,這意味着時間複雜度從O(log2n)O(log_2{n})降至O(n)O(n),爲了規避這種現象,平衡二叉樹的概念應運而生。

在計算機科學中,AVL樹是最先發明的自平衡二叉查找樹。AVL樹得名於它的發明者G. M. Adelson-VelskyE. M. Landis,他們在1962年的論文《An algorithm for the organization of information》中發表了它。

特點

  1. 本身首先是一棵二叉搜索樹。

  2. 帶有平衡條件:每個結點的左右子樹的高度之差的絕對值(平衡因子)最多爲1。

相關概念

平衡因子

左右子樹高度差。只要高度差不超過1,則爲平衡狀態。否則就得想辦法使其平衡。這個方法叫旋轉👇

旋轉

增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。

右旋

圖片來源

左旋

與右旋相反

組合

左旋右旋是調整平衡的最小操作,這意味着在更復雜的情況下需要進行左旋右旋組合操作才能達到目的,什麼情況下適合什麼樣的操作呢?

  • LL:由於在當前節點左子樹的左子樹上插入節點,導致平衡因子由1增至2,此時只需進行一次右旋操作

  • RR:與LL相反

  • LR:在左子樹的右子樹上插入節點導致失衡,需要先左旋後右旋

  • RL:與LR相反

實現

完整代碼

節點

維護平衡的前提是通過旋轉,而旋轉的本體是節點,因此將其放在節點類中。

  1. 平衡因子

            private int getBalanceFactor() {
                return getHeight(left) - getHeight(right);
            }
            private static int getHeight(Node node) {
                return node == null ? 0 : node.height;
            }
    
  2. 旋轉(以右旋爲例),同時刷新高度

    刷新高度操作不需要擔心空指針問題,交給調用者👇

            public Node<K, V> rightRotate() {
                Node x = this.left;
                Node t = x.right;
                x.right = this;
                this.left = t;
                return x.refreshHight();
            }
            private Node<K, V> refreshHight() {
                height = 1 + Math.max(getHeight(this.left), getHeight(this.right));
                return this;
            }
    
  3. 平衡

            public Node<K, V> balance() {
                int balanceFactor = getBalanceFactor();
                if (balanceFactor > 1 && left.getBalanceFactor() >= 0) {
                    return rightRotate();
                }
                if (balanceFactor < -1 && right.getBalanceFactor() <= 0) {
                    return leftRotate();
                }
                if (balanceFactor > 1 && left.getBalanceFactor() < 0) {
                    left = left.leftRotate();
                    return rightRotate();
                }
                if (balanceFactor < -1 && right.getBalanceFactor() > 0) {
                    right = right.rightRotate();
                    return leftRotate();
                }
                return this;
            }
    

AVL

在二分搜索樹的基礎上增加平衡操作

    private Node<K, V> balance(Node<K, V> node) {
        return node == null ? null : node.balance();
    }

在增刪等可能影響平衡的方法中調用平衡:

    public Node<K, V> add(Node<K, V> node, K k, V v) {
        if (node == null) {
            size++;
            return new Node<>(k, v);
        }
        Comparator<K> kComparator = Comparator.naturalOrder();
        if (Objects.compare(k, node.key, kComparator) < 0) {
            node.left = add(node.left, k, v);
        } else if (Objects.compare(k, node.key, kComparator) > 0) {
            node.right = add(node.right, k, v);
        } else {
            node.value = v;
        }
        return node.balance();
    }
    public Node<K, V> remove(Node<K, V> node, K k) {
        if (node == null) {
            return null;
        }
        if (k.compareTo(node.key) < 0) {
            node.left = remove(node.left, k);
            return node.balance();
        } else if (k.compareTo(node.key) > 0) {
            node.right = remove(node.right, k);
            return node.balance();
        }
        size--;
        if (node.left == null) {
            Node<K, V> right = node.right;
            node.right = null;
            return balance(right);
        }
        if (node.right == null) {
            Node<K, V> left = node.left;
            node.left = null;
            return balance(left);
        }
        Node<K, V> successor = minimum(node.right);
        successor.right = remove(node.right, successor.key);
        size++;
        successor.left = node.left;
        node.left = node.right = null;
        return balance(successor);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章