简介
普通的二分搜索树是有可能退化成链表的,这意味着时间复杂度从 O ( l o g 2 n ) O(log_2{n}) O(log2n)降至 O ( n ) O(n) O(n),为了规避这种现象,平衡二叉树的概念应运而生。
在计算机科学中,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);
}