When to use a balanced tree
Use a balanced search tree when you need:
- Ordered data that changes: insert, delete and search in
O(log n)while keeping everything sorted. - Guaranteed
O(log n)even for bad input such as already-sorted data, where a plain binary search tree degrades toO(n). - Ordered queries a hash map cannot answer: the next larger key, the previous smaller key, everything in a range, or the k-th smallest.
Use something else when:
- You only need exact lookups: use a hash table, which is
O(1)on average. - The data is built once and then only read: a sorted array with binary search is simpler and faster.
- The data lives on disk: use a B-tree, which packs many keys per node to cut disk reads.
- You only need the smallest or largest item: use a heap.
Do not write your own balanced tree in production. Use TreeMap, std::map or the like (see the real-world section). Writing an AVL tree by hand is for learning and interviews.
How a balanced tree works
A plain binary search tree keeps smaller keys on the left and larger keys on the right, which makes search fast only while the tree is short. Insert keys in sorted order and every new key goes to the right, so the tree becomes a straight line and every search costs O(n).
Intuition. Think of a phone book. You open it in the middle and halve the pages again and again, and that only works because the names are spread evenly. Now imagine every new name is stapled on at the end, in alphabetical order. The book is one long chain and you must read from the front. A balanced tree is a phone book that re-spreads itself as names arrive.
Here is the problem in three inserts. Put 1, 2 and 3 into a plain tree and you get a line:
1
\
2
\
3 height 3, a straight line
A balanced tree turns the same keys into this, with the same left-to-right order 1, 2, 3:
2
/ \
1 3 height 2
A self-balancing tree repairs itself after each insert or delete so its height stays about log n. The repair is a rotation: re-linking a few nodes to move a subtree up one level without breaking the ordering rule.
AVL rule: at every node, the heights of the left and right subtrees differ by at most 1. The difference is called the balance factor and is always -1, 0 or +1.
Counting convention. The height of an empty subtree is 0 and the height of a leaf is 1. The balance factor of a node is the height of its left subtree minus the height of its right subtree. So +2 means "too heavy on the left" and -2 means "too heavy on the right".
If an insert makes a balance factor +2 or -2, a rotation (or two) restores it. A rotation touches only a few nodes, so it costs O(1). After the repair, the rebalanced subtree has the same height it had before the insert, so no node higher up changes and nothing more needs fixing. That is why an insert causes at most one rebalancing: either a single rotation or a double rotation (two rotations).
There are two common families:
- AVL trees keep the strictest balance. Height is at most about 1.44 × log2(n), so searches are fastest, and inserts do slightly more rotating.
- Red-black trees keep a looser rule using node colours (red or black) so that the longest path is at most twice the shortest. They rotate less on inserts and deletes, so updates are cheaper. Java's
TreeMapand C++'sstd::maptypically use them.
Try it
Insert the same numbers into a plain tree and an AVL tree and compare the heights.
Balanced Trees
Step 1 of 17 · Press Space to play, arrow keys to stepThe tree is empty.
Initial state. Insert 1, 2, 3, 4, 5, 6, 7 one at a time into an AVL tree. Watch the height.
- step:
- 0
- height:
- 0
- rotations:
- 0
- comparisons:
- 0
- nodes:
- 0
Things to try:
- Choose Plain binary search tree and 1 2 3 4 5 6 7 (sorted). The height grows to 7: a straight line, so a search may take 7 comparisons.
- Choose AVL tree with the same input. After 3 inserts it rotates, and at the end the height is 3 with 4 rotations in all. Every search takes at most 3 comparisons.
- Choose AVL tree and 30 10 20 50 40 60. Inserting 20 makes a zig-zag, which needs a double rotation: first straighten, then rotate. The tones show which node is out of balance and which becomes the new top. Over the whole run the tree rotates 5 times: two double rotations (when 20 and 40 arrive) and one single rotation (when 60 arrives). The final height is 3.
- Choose AVL tree and 4 2 6 1 3 5 7. No rotation is needed and the height is 3. The insert order matters more than the tree type, but only the AVL tree is safe for every order.
What to watch. Before you press Next on an "out of balance" step, work out the balance factors yourself: height of the left subtree minus height of the right subtree. Then predict which of the four cases it is and whether one or two rotations follow. Compare your answer with the caption.
Balanced tree operations: search, insert, delete and rotate
Search. The same as in a plain BST: go left if the target is smaller, right if larger. Height is O(log n), so this is O(log n).
Insert. Insert like a plain BST, then walk back up to the root, updating heights. At the first node whose balance factor is +2 or -2, rotate. One rotation (or one double rotation) restores the balance for the whole tree.
Delete. Delete like a plain BST, then walk back up, updating heights and rotating wherever the balance is broken. Unlike insert, a delete may need a rotation at several levels, still O(log n) in total.
The four imbalance cases (the node z is out of balance):
- Left-left (left child is heavy on its left): rotate right at
z. - Right-right (right child is heavy on its right): rotate left at
z. - Left-right (left child is heavy on its right): rotate left at the child, then right at
z. - Right-left (right child is heavy on its left): rotate right at the child, then left at
z.
A rotation keeps the in-order sequence unchanged. This is the interview fact: it only changes shape, never the order of keys, which is why the tree is still a valid search tree afterwards.
Worked example: reading and fixing an imbalance by hand
The recipe is always the same. Walk back up from the new leaf, find the first node z with balance factor +2 or -2, then look at the balance factor of its heavy child. Same sign on both: one rotation. Opposite signs: two.
Example A: insert 10, 20, 30. After 30 is added the tree is a line, and node 10 has balance factor 0 - 2 = -2. Its heavy child, 20, has factor -1: same side, so this is right-right. Rotate left at 10.
10 20
\ / \
20 --> 10 30
\
30
Example B: insert 30, 10, 20. After 20 is added, node 30 has factor 2 - 0 = +2. Its heavy child, 10, has factor -1: opposite sides, so this is left-right. First rotate left at 10, which straightens the zig-zag into a line. Then rotate right at 30.
30 30 20
/ / / \
10 --> 20 --> 10 30
\ /
20 10
In both examples the in-order sequence is 10, 20, 30 before and after. Only the shape changed.
Example C: a longer run. Insert 10, 20, 30, 40, 50, 25 into an empty AVL tree. The tree rotates when 30 arrives (right-right at 10), when 50 arrives (right-right at 30) and when 25 arrives (right-left at 20, a double rotation). That is 4 rotations in total and the final tree has root 30, with 20 (children 10 and 25) on the left and 40 (right child 50) on the right. Its height is 3, where a plain tree would have a height of 5 for the first five keys alone.
Balanced tree time complexity
| Operation | Plain BST (average) | Plain BST (worst) | AVL tree | Red-black tree |
|---|---|---|---|---|
| Search | O(log n) | O(n) | O(log n) | O(log n) |
| Insert | O(log n) | O(n) | O(log n), at most 2 rotations | O(log n), at most 2 rotations |
| Delete | O(log n) | O(n) | O(log n), up to O(log n) rotations | O(log n), at most 3 rotations |
| Height | Average search path about 1.39 log2 n for random data | n | At most about 1.44 log2 n | At most 2 log2 (n + 1) |
| Space | O(n) | O(n) | O(n) plus a height or factor per node | O(n) plus a colour bit per node |
n is the number of keys. The worst case of a plain BST happens when keys arrive sorted. Balancing removes it at the cost of a small extra field per node and a few rotations.
Why the height is logarithmic. Ask how few nodes an AVL tree of height h can have. The sparsest one has a root, one subtree of height h - 1 and one of height h - 2, so its size is N(h) = N(h-1) + N(h-2) + 1. That grows like the Fibonacci numbers, which is exponential in h. So n nodes force h to be at most about 1.44 × log2(n), never close to n.
Balanced trees vs other data structures
| Job | AVL / red-black tree | Plain BST | Hash table | Sorted array | Skip list |
|---|---|---|---|---|---|
| Exact lookup | O(log n) | O(n) worst | O(1) average | O(log n) | O(log n) |
| Insert | O(log n) guaranteed | O(n) worst | O(1) average | O(n) | O(log n) |
| Delete | O(log n) guaranteed | O(n) worst | O(1) average | O(n) | O(log n) |
| In-order walk | O(n) | O(n) | O(n log n), must sort | O(n) | O(n) |
| Next larger / smaller key | O(log n) | O(n) worst | O(n) | O(log n) | O(log n) |
| Range query | O(log n + k) | O(n) worst | O(n) | O(log n + k) | O(log n + k) |
| Guaranteed worst case | Yes | No | No | Yes for reads | No (probabilistic) |
n is the number of keys and k the number of results in a range. A balanced tree wins when you need ordered operations and updates together, and it never beats a hash table at exact lookups alone. A skip list is a layered linked list that gets the same ordered behaviour from random coin flips instead of rotations.
Edge cases
- Empty tree. The first insert becomes the root with height 1 and no rotation.
- One or two keys. Balance factors stay within -1 to +1, so the first rotation can only happen on the third key.
- Duplicate keys. The simulation sends an equal key to the right. Library maps such as
TreeMapkeep one entry per key and replace its value, and a multiset allows copies. Rotations keep the in-order order either way, but decide your duplicate rule before you write the insert. - Reverse-sorted input. The mirror image of sorted input: left-left cases and right rotations. Inserting 7, 6, 5, 4, 3, 2, 1 also ends with height 3 after 4 rotations.
- Delete a node with two children. Replace it with its in-order successor (or predecessor), remove that node, then walk up and rebalance.
- Delete where the heavy child has factor 0. This cannot happen on insert. A single rotation fixes it, and the height of that subtree does not shrink, so the walk up can stop.
- Very large inputs. The height stays about log2(n), so even a million keys need only about 20 to 30 levels of recursion.
Common mistakes
- Forgetting to update heights after a rotation. Update the lower node first, then the new top, or the balance factors above will be stale.
- Picking the case from
zalone. You also need the balance factor of the heavy child, otherwise you cannot tell a single rotation from a double one. - Rotating at the wrong node. Rotate at the first (lowest) out-of-balance node on the way back up, not at the root.
- Stopping the walk up after a delete. An insert can stop after one rebalancing, but a delete may shrink a subtree and unbalance a node higher up.
- Thinking "balanced" means "perfect". The tree only needs its height to stay
O(log n). Its two sides may still differ by one level. - Believing a rotation changes the order. It changes only the shape. The in-order sequence is always the same.
How to spot a balanced-tree problem
Look for these signals in a problem statement:
- Items arrive and leave over time and you must keep them in sorted order.
- You are asked for the next larger, previous smaller, floor or ceiling of a value, or for everything in a range.
- The input may arrive already sorted, or an adversary may choose it, so you need a guaranteed
O(log n)and not an average. - The statement sounds like a calendar or booking check ("does this new interval overlap an existing one?") or a running median over a sliding window.
If none of these appear, a hash table (exact lookups), a heap (only the smallest or largest) or a sorted array (read-only data) is simpler. In an interview, reach for the language's ordered map or set, such as TreeMap or std::set, and be ready to explain how it stays balanced.
How to explain balanced trees in an interview
- State the problem first: "a plain BST can become a line, so search is
O(n). A balanced tree keeps the heightO(log n)." - State the AVL invariant: "left and right heights differ by at most 1 at every node." Then name the four cases and say that each is fixed with one or two rotations in
O(1). - Say that rotations preserve the in-order order, and that insert needs at most one rebalancing, which is one or two rotations.
- Be ready to trace a short insert sequence by hand, naming the case (left-left, left-right and so on) at each rotation.
- Compare AVL and red-black trees: "AVL is stricter, so lookups are faster; red-black rotates less, so updates are cheaper. Standard libraries mostly use red-black trees or B-trees."