定义
一棵AVL树是每个节点的左子树和右子树的高度最多差1的二叉查找树
(空树高度定义为-1)
旋转
1.单旋转
AVL树本质是一棵二叉查找树,所以AVL树的插入和删除其实和二叉查找树一致,只是每次插入和删除后要做平衡性调整。AVL树比较难的地方就是旋转维持树的平衡性。先以右旋为例,分析一下如何进行右旋(左旋是对称操作)。
/**
* Rotates the subtree so that its root's left child is the new root.
*/
private void rotateRight(Node<V> Y) {
Node<V> X = Y.left;
Node<V> right = Y.right;
Node<V> α = X.left;
Node<V> β = X.right;
//第一步:将当前节点的左孩子修改为其之前左孩子的右孩子,同时修改parent指针
Y.left = β;
if (β != null) {
β.parent = Y;
}
//第二步:将当前节点左孩子的parent修改为当前节点的parent
replaceInParent(Y, X);
//第三步:将左孩子的右节点指向自己,并将自己的parent指向左孩子
X.right = Y;
Y.parent = X;
//第四步:更新节点的高度
Y.height = Math.max(right != null ? right.height : 0,
β != null ? β.height : 0) + 1;
X.height = Math.max(Y.height,
α != null ? α.height : 0) + 1;
}
2.双旋转
双旋转是单旋转的组合操作,一些情形下一次单旋转并不能平衡,所以需要一次双循环。假设在下图中的B或C
处的一次插入操作导致K3
处两棵子树高度差为2。那插入节点后应该是B或C
的深度比D
高2,A
和B或C
不可能在同一层上,否则插入之前就已经不平衡了。A
和D
也不可能在同一层上,如果A
和D
在同一层,那么K1
就是第一个导致不平衡的节点了。
假设我们只对K3
进行一次右旋转,则会变成如下效果:
由于B或C
的深度没有变,而A
的深度少了1,由于之前分析的一样,A
之前的深度就已经比插入后的B或C
深度小,因此旋转并没有平衡。针对这种情况,我们一次需要双旋转,只能让K2
做根,迫使K1
是K2
的左孩子。因此,首先针对K1
进行一次左旋。然后再对K2
进行右旋。
rotateLeft(root.left) //左旋K1
rotateRight(root) //左旋K3
旋转判断条件
假设添加节点后,节点α是沿着当前节点向根查找发现的第一个破坏平衡条件的点,针对α点不平衡可能出现下面四种情况和处理方式:
- 对α的左儿子的左子树进行了一次插入 //rorateRight(α)
- 对α的左儿子的右子树进行了一次插入 //rorateLeft(α.left) ,rorateRight(α)
- 对α的右儿子的左子树进行了一次插入 //rorateRight(α.right) ,rorateLeft(α)
- 对α的右儿子的右子树进行了一次插入 //rorateLeft(α)
代码实现
Java源码中TreeMap
是一个标准的AVL树实现,因此将源码做了简单修改,总结AVL树的操作(现在的TreeMap源码已经改为红黑树实现)
/**
* Created by 默默 on 2018/3/6.
*/
public class AVLTree<V extends Comparable<? super V>> {
Node<V> root;
int size = 0;
int modCount = 0;
public AVLTree() {
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public Node<V> insert(V value) {
if (root == null) {
root = new Node<V>(null, value);
size = 1;
modCount++;
return root;
}
Node<V> nearest = root;
while (true) {
int comparison = value.compareTo(nearest.value);
/*
* We found the requested value.
*/
if (comparison == 0) {
return nearest;
}
Node<V> child = (comparison < 0) ? nearest.left : nearest.right;
if (child != null) {
nearest = child;
continue;
}
if (comparison < 0) { // nearest value is higher
Node<V> created = new Node<>(nearest, value);
nearest.left = created;
size++;
modCount++;
rebalance(nearest, true);
return created;
} else { // comparison > 0, nearest value is lower
Node<V> created = new Node<>(nearest, value);
nearest.right = created;
size++;
modCount++;
rebalance(nearest, true);
return created;
}
}
}
public void clear() {
root = null;
size = 0;
modCount++;
}
public V remove(V value) {
Node<V> node = find(value);
if (node != null) {
removeInternal(node);
}
return node != null ? node.value : null;
}
public Node<V> find(V value) {
if (root == null) {
return null;
}
Node<V> nearest = root;
while (true) {
int comparison = value.compareTo(nearest.value);
/*
* We found the requested key.
*/
if (comparison == 0) {
return nearest;
}
Node<V> child = (comparison < 0) ? nearest.left : nearest.right;
if (child != null) {
nearest = child;
continue;
}
/*
* We found a nearest node. Every value not in the tree has up to two
* nearest nodes, one lower and one higher.
*/
if (comparison < 0) { // nearest value is higher
return null;
} else { // comparison > 0, nearest value is lower
return null;
}
}
}
/**
* Removes {@code node} from this tree, rearranging the tree's structure as
* necessary.
*/
void removeInternal(Node<V> node) {
Node<V> left = node.left;
Node<V> right = node.right;
Node<V> originalParent = node.parent;
if (left != null && right != null) {
/*
* To remove a node with both left and right subtrees, move an
* adjacent node from one of those subtrees into this node's place.
*
* Removing the adjacent node may change this node's subtrees. This
* node may no longer have two subtrees once the adjacent node is
* gone!
*/
Node<V> adjacent = (left.height > right.height) ? left.last() : right.first();
removeInternal(adjacent); // takes care of rebalance and size--
int leftHeight = 0;
left = node.left;
if (left != null) {
leftHeight = left.height;
adjacent.left = left;
left.parent = adjacent;
node.left = null;
}
int rightHeight = 0;
right = node.right;
if (right != null) {
rightHeight = right.height;
adjacent.right = right;
right.parent = adjacent;
node.right = null;
}
adjacent.height = Math.max(leftHeight, rightHeight) + 1;
replaceInParent(node, adjacent);
return;
} else if (left != null) {
replaceInParent(node, left);
node.left = null;
} else if (right != null) {
replaceInParent(node, right);
node.right = null;
} else {
replaceInParent(node, null);
}
rebalance(originalParent, false);
size--;
modCount++;
}
private void replaceInParent(Node<V> node, Node<V> replacement) {
Node<V> parent = node.parent;
node.parent = null;
if (replacement != null) {
replacement.parent = parent;
}
if (parent != null) {
if (parent.left == node) {
parent.left = replacement;
} else {
assert (parent.right == node);
parent.right = replacement;
}
} else {
root = replacement;
}
}
/**
* Rebalances the tree by making any AVL rotations necessary between the
* newly-unbalanced node and the tree's root.
*
* @param insert true if the node was unbalanced by an insert; false if it
* was by a removal.
*/
private void rebalance(Node<V> unbalanced, boolean insert) {
for (Node<V> node = unbalanced; node != null; node = node.parent) {
Node<V> left = node.left;
Node<V> right = node.right;
int leftHeight = left != null ? left.height : 0;
int rightHeight = right != null ? right.height : 0;
int delta = leftHeight - rightHeight;
if (delta == -2) {
Node<V> rightLeft = right.left;
Node<V> rightRight = right.right;
int rightRightHeight = rightRight != null ? rightRight.height : 0;
int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
int rightDelta = rightLeftHeight - rightRightHeight;
if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
rotateLeft(node); // AVL right right
} else {
//assert (rightDelta == 1);
rotateRight(right); // AVL right left
rotateLeft(node);
}
if (insert) {
break; // no further rotations will be necessary
}
} else if (delta == 2) {
Node<V> leftLeft = left.left;
Node<V> leftRight = left.right;
int leftRightHeight = leftRight != null ? leftRight.height : 0;
int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
int leftDelta = leftLeftHeight - leftRightHeight;
if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
rotateRight(node); // AVL left left
} else {
//assert (leftDelta == -1);
rotateLeft(left); // AVL left right
rotateRight(node);
}
if (insert) {
break; // no further rotations will be necessary
}
} else if (delta == 0) {
//删除一个节点后,高度一致,需要更新parent所有节点高度并判断是否产生不平衡
node.height = leftHeight + 1; // leftHeight == rightHeight
if (insert) {
//如果是插入一个节点,节点高度没有发生变化,不需要再往上更新,因为节点高度没有发生变化
break;
}
} else {
//assert (delta == -1 || delta == 1);
//插入一个节点后,高度发生变化,需要更新parent所有节点高度并判断是否发生了不平衡
node.height = Math.max(leftHeight, rightHeight) + 1;
if (!insert) {
//如果删除一个节点,高度差一,说明之前高度一致,不需要再往上更新,因为删除后各个节点高度没有发生变化
break;
}
}
}
}
/**
* Rotates the subtree so that its root's right child is the new root.
*/
private void rotateLeft(Node<V> root) {
Node<V> left = root.left;
Node<V> pivot = root.right;
Node<V> pivotLeft = pivot.left;
Node<V> pivotRight = pivot.right;
// move the pivot's left child to the root's right
root.right = pivotLeft;
if (pivotLeft != null) {
pivotLeft.parent = root;
}
replaceInParent(root, pivot);
// move the root to the pivot's left
pivot.left = root;
root.parent = pivot;
// fix heights
root.height = Math.max(left != null ? left.height : 0,
pivotLeft != null ? pivotLeft.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotRight != null ? pivotRight.height : 0) + 1;
}
/**
* Rotates the subtree so that its root's left child is the new root.
*/
private void rotateRight(Node<V> root) {
Node<V> pivot = root.left;
Node<V> right = root.right;
Node<V> pivotLeft = pivot.left;
Node<V> pivotRight = pivot.right;
// move the pivot's right child to the root's left
root.left = pivotRight;
if (pivotRight != null) {
pivotRight.parent = root;
}
replaceInParent(root, pivot);
// move the root to the pivot's right
pivot.right = root;
root.parent = pivot;
// fixup heights
root.height = Math.max(right != null ? right.height : 0,
pivotRight != null ? pivotRight.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotLeft != null ? pivotLeft.height : 0) + 1;
}
static class Node<V> {
Node<V> parent;
Node<V> left;
Node<V> right;
V value;
int height;
Node(Node<V> parent, V value) {
this.parent = parent;
this.value = value;
this.height = 1;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
/**
* Returns the next node in an inorder traversal, or null if this is the
* last node in the tree.
*/
Node<V> next() {
if (right != null) {
return right.first();
}
Node<V> node = this;
Node<V> parent = node.parent;
while (parent != null) {
if (parent.left == node) {
return parent;
}
node = parent;
parent = node.parent;
}
return null;
}
/**
* Returns the previous node in an inorder traversal, or null if this is
* the first node in the tree.
*/
public Node<V> prev() {
if (left != null) {
return left.last();
}
Node<V> node = this;
Node<V> parent = node.parent;
while (parent != null) {
if (parent.right == node) {
return parent;
}
node = parent;
parent = node.parent;
}
return null;
}
/**
* Returns the first node in this subtree.
*/
public Node<V> first() {
Node<V> node = this;
Node<V> child = node.left;
while (child != null) {
node = child;
child = node.left;
}
return node;
}
/**
* Returns the last node in this subtree.
*/
public Node<V> last() {
Node<V> node = this;
Node<V> child = node.right;
while (child != null) {
node = child;
child = node.right;
}
return node;
}
}
public void preOrder() {
if (root==null) {
return;
}
System.out.println(root.value);
preOrder(root.left);
preOrder(root.right);
}
public static void main(String[] args) {
AVLTree<Integer> tree =new AVLTree<>();
int[] a= {1,2,3,4,5,6,7,16,15,14,13,12,11,10,8,9};
/**
* 7
* / \
* / \
* 4 13
* / \ / \
* 2 6 11 15
* / \ / / \ / \
* 1 3 5 9 12 14 16
* / \
* 8 10
*/
for (int i : a) {
tree.insert(i);
}
tree.preOrder();
}
}