Balanced Trees: AVL and Red-Black
This chapter covers self-balancing binary search trees: AVL trees and red-black trees. Both maintain O(log n) worst-case height.
Why This Matters
A plain BST degrades to O(n) on sorted input. Balanced trees guarantee O(log n) search/insert/delete regardless of input order, which is essential for ordered maps/sets in production systems (databases, kernels, runtimes).
Prerequisites
c.ds.6— BSTs.
Core Concept
Both trees keep height balanced via rotations after insert/delete:
- AVL tree: strictly balanced — the height of any node's two subtrees
differs by at most 1. It rebalances with single/double rotations.
- Red-black tree: loosely balanced — each node is red or black, with rules
guaranteeing height ≤ 2 log₂(n+1). Fewer rotations than AVL.
AVL Tree
typedef struct Node {
int value;
int height;
struct Node *left;
struct Node *right;
} Node;
Balance factor = height(left) - height(right). When a node's factor is outside [-1, 1], rotate.
Rotations
y x
/ \ / \
x C ----> A y
/ \ / \
A B B C
Left and right rotations restore balance; left-right/right-left cases need two rotations.
Red-Black Tree
Each node has a color (red/black). The invariants:
1. Every node is red or black. 2. The root is black. 3. No two red nodes are adjacent. 4. Every path from a node to its descendant NULL leaves has the same number of black nodes.
Insert/delete fix violations with rotations and recoloring.
Examples
AVL height and balance
static int height(Node *n) { return n ? n->height : 0; }
static int balance(Node *n) { return height(n->left) - height(n->right); }
static Node *rotate_right(Node *y)
{
Node *x = y->left;
Node *T2 = x->right;
x->right = y;
y->left = T2;
y->height = 1 + (height(y->left) > height(y->right) ? height(y->left) : height(y->right));
x->height = 1 + (height(x->left) > height(x->right) ? height(x->left) : height(x->right));
return x;
}
AVL insert (skeleton)
Node *avl_insert(Node *n, int value)
{
if (!n) return new_node(value);
if (value < n->value) n->left = avl_insert(n->left, value);
else if (value > n->value) n->right = avl_insert(n->right, value);
else return n; /* no duplicates */
n->height = 1 + max(height(n->left), height(n->right));
int b = balance(n);
if (b > 1 && value < n->left->value) return rotate_right(n);
if (b < -1 && value > n->right->value) return rotate_left(n);
if (b > 1 && value > n->left->value) { n->left = rotate_left(n->left); return rotate_right(n); }
if (b < -1 && value < n->right->value){ n->right = rotate_right(n->right); return rotate_left(n); }
return n;
}
How It Works
Rotations restructure a small local subtree without violating the BST ordering, reducing height. AVL rebalances immediately and strictly; red-black rebalances lazily with colors, doing fewer rotations per insert.
Variations
AVL vs. red-black
- AVL: more rigidly balanced → faster lookups, more rotations on insert.
- Red-black: fewer rotations → faster insert/delete, slightly less balanced.
Splay trees, B-trees
Other balanced structures trade different properties; B-trees are the basis of databases and filesystems.
Common Mistakes
- Forgetting to update heights after rotation.
- Returning the wrong subtree root after rebalancing.
- Not handling the left-right/right-left double rotation cases.
- Violating the BST ordering in a rotation.
Undefined Behavior
- Dereferencing a NULL child.
VERIFIED - Use-after-free during delete.
Portability
- Plain C, fully portable.
Under the Hood
Rotations are pointer rewrites; the compiler keeps the hot nodes in cache. Balanced trees still chase pointers, so they are less cache-friendly than arrays, but they guarantee logarithmic behavior.
Practical Usage
- Use balanced trees for ordered maps/sets with worst-case guarantees.
- Prefer red-black for insert/delete-heavy workloads; AVL for lookup-heavy.
- Use existing libraries (e.g.,
<search.h>tsearch, or GLib) when possible.
Exercises
1. Implement AVL insert with all four rotation cases. 2. Implement AVL delete (the hardest part of the chapter). 3. Implement red-black insert with recoloring/rotations. 4. Verify height stays balanced after many random inserts.
Deep Challenge
Implement AVL delete, explaining each rebalance case and why delete requires more rotations than insert. Then test with a random sequence and verify the AVL invariant after every step.
Related Concepts
c.ds.6— BSTs.c.alg.1— sorting and searching.
References
- Standard data-structure literature (CLRS, Sedgewick).
Verification
- AVL/red-black invariants and rotations.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] AVL rotations
- [ ] Red-black invariants
- [ ] Insert rebalancing
- [ ] Delete rebalancing
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.ds.avl | 0 | 6 |
| c.ds.redblack | 0 | 6 |