AVL trees: balance factor and rotations
The AVL rule that keeps a BST at O(log n), the LL/RR/LR/RL rotations, and the classic exam question: what does the tree look like after inserting in this order?
The binary search tree (BST) you saw last week is nice but fragile. If you insert keys in sorted order (1, 2, 3, …), the tree turns into a chain to the right and search becomes O(n). The AVL tree (Adelson-Velsky and Landis) is the first self-balancing BST that solves this problem. Almost every exam has the same question: “Insert these keys in order into an empty AVL tree and draw the final tree.” Someone who knows the four rotations on this page by their logic, not by memory, will not miss this question.
Golden rules:
- The AVL rule: at every node, the heights of the left and right subtrees differ by at most 1.
- Height: an empty tree is -1, a leaf is 0. The balance factor is bf = height(left) - height(right).
- Go up after inserting. Stop at the first (deepest) node whose bf is +2 or -2 and fix it. In insertion one fix is enough.
- The direction question: which two steps did the new key take from that node? Left-left, right-right, left-right, right-left. The names of the four cases come from here.
Height and balance factor
The height of a node is the number of edges on the path to its farthest leaf. Always compute it from the bottom up:
30 h=2, bf=1-0=1
/ \
20 40 h(20)=1 bf=1, h(40)=0
/
10 h=0, bf=0
This tree is AVL: all bf values are -1, 0 or 1. If you insert 5 under 10, the bf of 20 becomes 2 and the rule is broken.
PITFALL · do not confuse height with depth. Depth is counted from the root down, height from the leaf up. The bf computation always uses height. Some sources call a leaf 1; then all values shift by 1 but the bf comes out the same. In the exam, check the instructor’s definition.
Why O(log n)?
If you want to build an AVL tree of height h with the fewest nodes, you make one subtree height h-1 and the other h-2 (a difference of 1 is allowed). From this:
| h | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| N(h) minimum nodes | 1 | 2 | 4 | 7 | 12 | 20 | 33 | 54 |
This sequence grows exponentially like Fibonacci. If the number of nodes grows exponentially with the height, the height grows logarithmically with the number of nodes: . Search, insert and delete take as many steps as the height, so all are O(log n). A rotation changes only a few pointers, O(1).
The four cases and rotations
Insertion is done like a normal BST insertion. Then the ancestors of the inserted leaf are checked going up. Call the first unbalanced node k. The two steps that the new key took after k determine the case.
LL and RR: single rotation
In the LL case the new key went into the left subtree of the left child of k. The left child (k1) moves up and k2 moves down to the right. X, Y, Z are subtrees:
LL: right rotation at k2
k2 k1
/ \ / \
k1 Z ==> X k2
/ \ / \
X Y Y Z
Note: the subtree Y on the right of k1 goes to the left of k2. Every key in Y is larger than k1 and smaller than k2. So the BST order is preserved in its new place. The inorder sequence X, k1, Y, k2, Z is the same in both drawings.
RR is the mirror image: a left rotation at k1, the right child k2 moves up to the root, and the left subtree of k2 goes to the right of k1.
RR: left rotation at 10 (10, 20, 30 inserted)
10 20
\ / \
20 ==> 10 30
\
30
LR and RL: double rotation
In the LR case the new key went to the right of the left child of k. A single right rotation does not help here; it only moves the slant to the other side. The solution takes two steps: first a left rotation at the left child (which turns the case into LL), then a right rotation at k. In the end the middle node (between k1 and k3) goes to the top.
LR: 30, 10, 20 inserted
30 30 20
/ / / \
10 ==> 20 ==> 10 30
\ /
20 10
left at 10 right at 30
RL is the mirror image: first a right rotation at the right child, then a left rotation at k.
TIP · the shortcut for a double rotation. Sort k, its child and its grandchild (the three nodes on the path of the new key). The median value becomes the new root, the smaller goes left, the larger goes right. The old two subtrees of the grandchild are distributed to the left and right sides in order.
| Case | Path of the new key | bf(k) | Fix |
|---|---|---|---|
| LL | left, left | +2 | single right rotation at k |
| RR | right, right | -2 | single left rotation at k |
| LR | left, right | +2 | left at the left child, then right at k |
| RL | right, left | -2 | right at the right child, then left at k |
AVL insertion in Java
The node stores its own height. Insertion is recursive: on the way back, each node updates its height and rebalances if needed. The balance method compares heights, so it works for both insertion and deletion.
static class AvlNode {
int key, height; // leaf: height = 0
AvlNode left, right;
AvlNode(int key) { this.key = key; }
}
static int height(AvlNode t) { return t == null ? -1 : t.height; }
static void update(AvlNode t) {
t.height = 1 + Math.max(height(t.left), height(t.right));
}
static AvlNode rotateRight(AvlNode k2) { // LL
AvlNode k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
update(k2);
update(k1);
return k1;
}
static AvlNode rotateLeft(AvlNode k1) { // RR
AvlNode k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
update(k1);
update(k2);
return k2;
}
static AvlNode insert(AvlNode t, int x) {
if (t == null) return new AvlNode(x);
if (x < t.key) t.left = insert(t.left, x);
else if (x > t.key) t.right = insert(t.right, x);
else return t; // duplicates are not inserted
return balance(t);
}
static AvlNode balance(AvlNode t) {
update(t);
int bf = height(t.left) - height(t.right);
if (bf > 1) {
if (height(t.left.left) < height(t.left.right))
t.left = rotateLeft(t.left); // LR: first turn it into LL
return rotateRight(t);
}
if (bf < -1) {
if (height(t.right.right) < height(t.right.left))
t.right = rotateRight(t.right); // RL
return rotateLeft(t);
}
return t;
}
Usage is always of the form root = insert(root, x);. Because a rotation changes the root of the subtree, assigning the returned value is required.
Worked example: 15, 20, 24, 10, 13, 7, 30, 36, 25
Let’s insert these nine keys into an empty AVL tree. At each step follow the path and check bf going up.
+15, +20: no problem. +24: the bf of 15 is -2, the path is right-right: RR, a left rotation at 15.
15 20
\ / \
20 ==> 15 24
\
24
+10: to the left of 15, balanced. +13: 13 → 20 → 15 → 10 → right. The bf of 15 is 2 (left height 1, right -1). The path is left-right: LR. Left at 10, right at 15. 13 comes to the middle.
20 20
/ \ / \
15 24 ==> 13 24
/ / \
10 10 15
\
13
+7: 7 → 20 → 13 → 10 → left. Look upward: the bf of 10 is 1, the bf of 13 is 1, the bf of 20 is height(13) - height(24) = 2 - 0 = 2. The path (from 20) is left-left: LL, a right rotation at 20. 13 moves up to the root, and the right child of 13, which is 15, goes to the left of 20.
13
/ \
10 20
/ / \
7 15 24
+30: to the right of 24, balanced. +36: the bf of 24 is -2, right-right: RR, a left rotation at 24, 30 moves up.
13
/ \
10 20
/ / \
7 15 30
/ \
24 36
+25: 25 → 13 → 20 → 30 → 24 → right. The bf of 24 is -1, the bf of 30 is 1, the bf of 20 is 0 - 2 = -2. The path (from 20) is right-left: RL. First a right rotation at 30 (24 moves up), then a left rotation at 20 (24 takes the place of 20).
13
/ \
10 24
/ / \
7 20 30
/ / \
15 25 36
Program output (the level order after each insertion and the fix that was made):
+15 -> 15
+20 -> 15 20
+24 -> 20 15 24 [RR at 15]
+10 -> 20 15 24 10
+13 -> 20 13 24 10 15 [LR at 15]
+7 -> 13 10 20 7 15 24 [LL at 20]
+30 -> 13 10 20 7 15 24 30
+36 -> 13 10 20 7 15 30 24 36 [RR at 24]
+25 -> 13 10 24 7 20 30 15 25 36 [RL at 20]
The height of the final tree is 3. The bf of the root is 1 - 2 = -1, so the rule holds.
Deletion in brief
Deletion from an AVL tree is first a normal BST deletion (with two children, the smallest of the right subtree, the inorder successor, takes its place). Then balance is called at every node from the deleted position up to the root. The difference from insertion: in deletion a rotation can reduce the height of the subtree, and the imbalance can move upward. So more than one rotation up to the root may be needed. The choice of case is again made by looking at the heights of the children; the balance method above already does this. The time is again O(log n).
Common mistakes
| Wrong idea | The truth |
|---|---|
| I start from the root and fix the first unbalanced node | Go up from the new leaf; fix the deepest unbalanced node |
| A single right rotation is enough for LR | A single rotation just mirrors the slant; a double rotation is needed |
| In a rotation the subtree Y disappears or stays in place | Y always moves from the node that goes up to the node that goes down |
| A leaf has height 1, an empty tree 0 | In this course: leaf 0, null -1 |
| Several rotations may be needed after insertion | In insertion at most one fix (single or double) is enough; in deletion there may be several |
Writing rotateRight(t); is enough | Assign the returned new root: t = rotateRight(t); |
| I update the heights in any order | First the node that went down, then the new root |
| I compute bf from the number of nodes in the subtree | bf is a height difference, not a node count |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
In the tree below, what is the balance factor of the root (height(left) - height(right)), and is the tree an AVL tree?
20
/ \
10 30
/ \
5 15
/
3Show answer
Compute the heights from the bottom up (empty = -1, leaf = 0). 3: 0. 5: 1. 15: 0. 10: 1 + max(1, 0) = 2. 30: 0.
Root: height(10) - height(30) = 2 - 0 = 2. Since |bf| > 1 it is not AVL. The nodes 10 and 5 have bf 1, so they are not a problem.
Insert 10, 20, 30 (in this order) into an empty AVL tree. Which key is the root, and which rotation was used?
Show answer
10 and 20 are fine. When 30 is inserted, the bf of 10 becomes -2. The new key went to the right of the right of 10: the RR case.
A single left rotation at 10: 20 moves up to the root, with 10 as its left child and 30 as its right child.
Keys 30, 10, 20 are inserted into an empty AVL tree in this order. Which case occurs at node 30 and what kind of rotation fixes it?
Show answer
20 goes to the right of the left child (10) of 30: left-right, that is, LR. A single rotation does not fix this.
First a left rotation at 10 (20 moves up), then a right rotation at 30. Result: root 20, children 10 and 30.
What is the worst-case time complexity of search and insert in an AVL tree with n nodes? Compare with an ordinary BST.
Show answer
The height of an AVL tree always stays below about 1.44 log2 n. Search and insert take as many steps as the height, and rotations are O(1). So the cost is O(log n).
An ordinary BST with sorted data (1, 2, 3, ...) becomes a linked list, and the height is n - 1: O(n).
Insert 1, 2, 3, 4, 5, 6, 7 into an empty AVL tree. Give the level-order traversal of the final tree and the number of rotations performed.
Show answer
All are the RR case, all single left rotations:
+3: imbalance at 1, the root becomes 2.
+5: imbalance at 3, 4 moves up: 2(1, 4(3,5)).
+6: imbalance at the root (2), 4 moves up to the root, and the subtree 3 goes to the right of 2: 4(2(1,3), 5(-,6)).
+7: imbalance at 5, 6 moves up: 4(2(1,3), 6(5,7)).
The result is a perfect tree of height 2. In a normal BST the same order would give a chain of height 6.
Insert 60, 50, 40, 30, 20, 10 into an empty AVL tree. Give the level-order traversal of the result.
Show answer
Decreasing order, all LL and single right rotations:
+40: LL at 60, the root becomes 50: 50(40,60).
+30: no imbalance. +20: LL at 40, 30 moves up: 50(30(20,40),60).
+10: LL at the root (50). 30 moves up to the root, and the right subtree of 30 (40) goes to the left of 50: 30(20(10), 50(40,60)).
Level order: 30 20 50 10 40 60.
Insert 40, 20, 60, 50, 70, 55 into an empty AVL tree. Which case occurs and what is the level-order traversal of the final tree?
Show answer
The first five insertions are balanced: 40(20, 60(50,70)).
When 55 is inserted, the path is 40 → 60 → 50 → right. The bf of 40: 0 - 2 = -2. The new key went to the left of the right child (60) of 40: RL.
First a right rotation at 60 (50 moves up, 55 goes to the left of 60), then a left rotation at 40 (50 moves up to the root).
Result: 50(40(20,-), 60(55,70)). Level order 50 40 60 20 55 70.
Insert 50, 30, 70, 20, 40, 45 into an empty AVL tree. Give the level-order traversal of the final tree.
Show answer
After five insertions: 50(30(20,40),70). 45 → 50 → 30 → 40 → right.
The bf of the root: height(30) - height(70) = 2 - 0 = 2. 45 went to the right of the left child (30) of 50: LR.
A left rotation at 30: 40 moves up, with 30 as its left child. Then a right rotation at 50: 40 moves up to the root, and the old right child of 40, which is 45, goes to the left of 50.
Result: 40(30(20), 50(45,70)).
What is the minimum number of nodes in an AVL tree of height 5? (A single node has height 0.)
Show answer
In the sparsest AVL tree one subtree has height h-1 and the other h-2: , , .
N(2) = 2 + 1 + 1 = 4, N(3) = 4 + 2 + 1 = 7, N(4) = 7 + 4 + 1 = 12, N(5) = 12 + 7 + 1 = 20.
Insert 5, 2, 8, 1, 4, 3 into an empty AVL tree. Where does the imbalance appear, which case is it, and what is the level-order traversal of the result?
Show answer
After five insertions: 5(2(1,4),8), balanced. 3 → 5 → 2 → 4 → left.
The bf of 2 is 0 - 1 = -1 (no problem). The bf of 5 is 2 - 0 = 2. 3 went into the right subtree of the left child (2) of 5: LR.
A left rotation at 2: 4 moves up, 3 goes to the right of 2: 4(2(1,3)). Then a right rotation at 5: 4 moves up to the root.
Result: 4(2(1,3), 5(-,8)).
Insert 12, 8, 16, 4, 10, 14, 2, 6, 1 into an empty AVL tree. Which node is rebalanced and what is the level-order traversal of the final tree?
Show answer
The first eight insertions create no imbalance: 12(8(4(2,6),10), 16(14)).
1 → 12 → 8 → 4 → 2 → left. Checking bf going up: 2: 1, 4: 2 - 1 = 1, 8: height(4) - height(10) = 2 - 0 = 2. The first unbalanced node is 8.
1 is in the left of the left of 8: LL, a single right rotation at 8. 4 moves up, and the right child of 4, which is 6, goes to the left of 8: 4(2(1), 8(6,10)).
The bf of the root 12 is now 2 - 1 = 1, so the fixing is done. The resulting level order: 12 4 16 2 8 14 1 6 10.
Write a Java method AvlNode rotateRight(AvlNode k2) that performs a single right rotation (LL case) and returns the new subtree root. Assume AvlNode has fields key, height, left, right and a helper height(t) that returns -1 for null.
Show answer
Three pointers change, then the heights are updated from the bottom up. Since k2 is now a child of k1, k2 must be updated first.
static AvlNode rotateRight(AvlNode k2) {
AvlNode k1 = k2.left;
k2.left = k1.right; // the middle subtree (Y) moves to k2
k1.right = k2;
k2.height = 1 + Math.max(height(k2.left), height(k2.right));
k1.height = 1 + Math.max(height(k1.left), height(k1.right));
return k1; // the new root; the caller writes t = rotateRight(t)
}A common mistake: forgetting to return the new root, or forgetting the assignment t.left = rotateRight(t.left) on the caller side. Then the rotation gets detached from the tree.Verification: All insertion traces, rotation types, level orders and N(h) values were produced with Java (verify/SEN2212/W05.java, java W05.java). The AVL code on the page was run in the same file and compared with a second key-based implementation on 8 sequences; all outputs were identical.