簡介
普通的二分搜索樹是有可能退化成鏈表的,這意味着時間複雜度從降至,爲了規避這種現象,平衡二叉樹的概念應運而生。
在計算機科學中,AVL樹
是最先發明的自平衡二叉查找樹。AVL樹
得名於它的發明者G. M. Adelson-Velsky
和E. M. Landis
,他們在1962年的論文《An algorithm for the organization of information》
中發表了它。
特點
-
本身首先是一棵二叉搜索樹。
-
帶有平衡條件:每個結點的左右子樹的高度之差的絕對值(平衡因子)最多爲1。
相關概念
平衡因子
左右子樹高度差。只要高度差不超過1,則爲平衡狀態。否則就得想辦法使其平衡。這個方法叫旋轉👇
旋轉
增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。
右旋
左旋
與右旋相反
組合
左旋右旋是調整平衡的最小操作,這意味着在更復雜的情況下需要進行左旋右旋組合操作才能達到目的,什麼情況下適合什麼樣的操作呢?
-
LL:由於在當前節點左子樹的左子樹上插入節點,導致平衡因子由1增至2,此時只需進行一次右旋操作
-
RR:與LL相反
-
LR:在左子樹的右子樹上插入節點導致失衡,需要先左旋後右旋
-
RL:與LR相反
實現
節點
維護平衡的前提是通過旋轉,而旋轉的本體是節點,因此將其放在節點類中。
-
平衡因子
private int getBalanceFactor() { return getHeight(left) - getHeight(right); } private static int getHeight(Node node) { return node == null ? 0 : node.height; }
-
旋轉(以右旋爲例),同時刷新高度
刷新高度操作不需要擔心空指針問題,交給調用者👇
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; }
-
平衡
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);
}