Trees and heaps · 15 min

Self-balancing trees: lift the taller side

Keep every node's two sides within one level of each other: how an AVL tree finds a node at ±2 on the way back up, how one or two rotations fix it without breaking the order, and why sorted keys no longer make a chain.

01Why it matters

Sorted keys, short tree

The binary search trees lesson ended on its worst case: insert keys in sorted order and each one goes right of the last, so the tree becomes a chain and a search walks past every key. Real data often arrives sorted, so a plain search tree can't promise O(log n).

A self-balancing tree keeps that promise by repairing its shape as keys arrive. The AVL tree, published by Adelson-Velsky and Landis in 1962, watches how much taller one side of each node is than the other. When an insert makes one side two levels taller, a rotation lifts that side up: a few links change, the keys stay in order, and the seven sorted keys that made a chain of 7 levels come out 3 levels tall.

02Key parts

The pieces you will see

height
The number of levels in a subtree, as in the last lesson: a leaf has height 1 and an empty subtree 0. Each AVL node stores its own.
balance factor
A node's left height minus its right height: +1 means its left side is one level taller, −1 its right side, 0 level. (Some books subtract the other way round.)
AVL rule
On top of the search-tree rule, every node's balance factor is −1, 0 or +1. A node at +2 or −2 must be fixed.
rotation
Lifting one child of a node into the node's place and moving the node down to the other side. A few links change; the keys stay in order.
the four cases
Named by the path from the node at ±2 down its taller side: left-left and right-right are straight, left-right and right-left are bent.

03See it work

Lift the taller side

These are the seven keys that made a chain in the plain tree, inserted in the same order. To the right of each node are its balance factor, above, and its height, below (h1, h2 …).

10 to 70, inserted in sorted order: 3 levels, 40 at the root, and every balance factor 0. Search for 70: it is compared with 40, 60 and 70, three comparisons where the chain took seven.

The repairs happen one insert at a time. In the smallest case, 30 has a left child, 20, and nothing on its right, so it already leans left by one.

Insert 10: it goes left at 30 and at 20, and becomes 20's left child. On the way back up, 20's height becomes 2 and its balance factor +1, which is allowed; 30's height becomes 3 and its balance factor +2, which is not. One right rotation lifts 20 into 30's place, with 10 and 30 as its children.

Insert walks down as in a plain search tree. The new part is the walk back up the same path: each node recomputes its height as 1 + its taller child's height, then its balance factor. Nodes off the path gained nothing, so they can't have changed.

At 30 the path went left and left again, so 20 moved up and 30 moved down to its right. The keys still read 10, 20, 30 from left to right, and the subtree is 2 levels tall again, as before 10 arrived, so nothing above it changes.

That path was straight. If it bends, left and then right, one rotation is not enough.

Insert 25: left at 30, then right at 20. 30 is at +2 again, but its left child 20 now leans right (−1). So 20 is rotated left first, which lifts 25 above it and straightens the path; then 30 is rotated right, and 25 takes 30's place with 20 and 30 as its children. Two rotations.

So there are four cases. Left-left takes one right rotation and right-right one left rotation. Left-right takes a left rotation at the child, then a right rotation at the node, and right-left is its mirror. In the pseudo-code: if the node leans left (bf > 1) and its left child leans right (bf(left) < 0), straighten the child first.

04Predict

Your turn: which rotation?

Before each step, predict which way the walk goes, and at a node that reaches ±2, which rotation fixes it. This tree starts empty. Insert 10, then 30, then 20. Then undo the 20, insert 40 instead, and compare the two fixes.

Make 7 predictions: step forward and answer each question before the next step plays.

0 of 7 made

05Try it

Rotations in code, and deleting

A rotation is a few assignments. This program builds the chain from the first rotation, 30 above 20 above 10, and rotates it right. lifted is the child that moves up.

class BstNode:
    def __init__(self, key, left):
        self.key = key
        self.left = left
        self.right = None

def rotate_right(node):
    lifted = node.left
    node.left = lifted.right
    lifted.right = node
    return lifted

root = BstNode(30, BstNode(20, BstNode(10, None)))
print('root ' + str(root.key))
root = rotate_right(root)
print('root ' + str(root.key))
print(str(root.left.key) + ' < ' + str(root.key) + ' < ' + str(root.right.key))

Two links change inside the subtree, and the caller stores the returned node where 30 hung (here root): three links, however big the subtrees below, so a rotation is O(1). The last line prints 10 < 20 < 30: the order survives. Besides the two nodes that trade places, only the lifted child's inner subtree changes parent, and its keys all lie between those two. An AVL node also stores its height, so recompute the lowered node's, then the lifted node's. A left rotation is the mirror image.

Why does a bent case need two rotations? Think back to the insert of 25.

Delete works the same way: remove the key as in a plain search tree, then walk back up, updating heights and rotating wherever a node reaches ±2.

Delete 20, then 40, then 30. After the first two, every balance factor is still within ±1. The third leaves 50 with nothing on its left and two levels on its right: −2. Its right child, 70, is level (0), so one left rotation lifts 70 to the root, and 60, the key between 50 and 70, moves across to become 50's right child.

One rotation was enough here, but after a delete the rotated subtree can end up a level shorter than before, which can tip the node above it, and so on: at worst one rotation per level, still O(log n). The walk checks every node on the path, because the rule covers every node.

Within 1 is enough: the fewest keys an AVL tree of a given height can hold grow like the Fibonacci numbers, so its height stays below about 1.44log⁡2n1.44 \log_2 n. A million keys take at most 28 levels; no binary tree fits them in fewer than 20, and the sorted chain took a million. Red-black trees, behind Java's TreeMap and the usual C++ std::map, balance more loosely (height up to about 2log⁡2n2 \log_2 n) but need at most three rotations per insert or delete: cheaper updates, slightly slower lookups.

06Check yourself

Check yourself

  1. Question 1 of 5

    In a binary search tree, the root’s two subtrees both have height 4. The root’s left child has subtrees of heights 3 and 1. Is it an AVL tree?

    Your answer comes back in Review in a few days, so it sticks.

  2. Question 2 of 5

    After an insert, 50 is at +2 and its left child, 30, is at −1. What restores the balance?

  3. Question 3 of 5

    A right rotation at 30 lifts its left child, 20, which has children 10 and 25. Where does 25 end up?

  4. Question 4 of 5

    Why is insert into an AVL tree O(log n) in the worst case, even though it rotates?

  5. Question 5 of 5

    How many rotations can one delete in an AVL tree need?

07Explain it back

Say it in your own words

What does an AVL tree keep true at every node, and how does it restore that after an insert? Why does this keep search and insert O(log n) whatever order the keys arrive in?

08Recap

What to remember

  • An AVL tree is a binary search tree whose every node has a balance factor (left height minus right height) of −1, 0 or +1.
  • Insert and delete work as in a plain search tree, then walk back up updating heights and rotating at any node at ±2. Straight cases need one rotation; bent cases rotate the child first.
  • A rotation rewires three links and keeps the keys in order: O(1). An insert needs at most one single or double rotation; a delete may need one per level.
  • The height stays below about 1.44 log₂ n, so search, insert and delete are O(log n) in the worst case, whatever the order of the keys.
  • Next: B-trees, which pack many keys into each node (try the B-tree visualiser), and storage engines, where a variant, the B+ tree, holds database indexes. Practise with Check a Binary Tree Is Balanced and Depth of a Binary Tree, or open the AVL tree visualiser.
Back to path