AVL Trees & Self-Balancing Rotations
Height-balance factor BF in {-1, 0, 1}, and the 4 fundamental rotation algorithms: Left-Left, Right-Right, Left-Right, and Right-Left.
Reinforce your mental model: run, pause, and inspect live pointers with our interactive visualizer:
An AVL Tree enforces strict logarithmic height through rigid local invariants, guaranteeing worst-case search, insertion, and deletion. By detecting imbalances immediately along the recursive call stack, four atomic tree rotations restore structural equilibrium in pointer adjustments.
1. Executive Summary & Learning Objectives
#An AVL Tree (named after Soviet mathematicians Georgy Adelson-Velsky and Evgenii Landis, 1962) is an augmented self-balancing Binary Search Tree where the heights of the left and right subtrees of every node differ by at most 1.
By the end of this chapter, you will be able to:
- Derive the AVL balance factor invariant and reproduce the Fibonacci-based proof bounding tree height to .
- Classify tree imbalances into the four canonical configurations: Left-Left (LL), Right-Right (RR), Left-Right (LR), and Right-Left (RL).
- Execute single and double tree rotations through exact pointer reassignments and height recalculations in time.
- Trace an AVL insertion and rebalancing sequence step-by-step using a structured state execution table.
- Contrast AVL trees with Red-Black trees regarding rotation frequency, lookup performance, and memory overhead.
2. The Balance Factor () Invariant & Height Bound Proof
#Definition: Balance Factor
#For every node in an AVL tree, the Balance Factor is defined as:
| Balance Factor | Structural State | Operational Implication |
|---|---|---|
| Left-heavy | Left subtree is 1 level taller than right subtree. Invariant satisfied. | |
| Symmetrically balanced | Left and right subtrees have identical heights. Invariant satisfied. | |
| Right-heavy | Right subtree is 1 level taller than left subtree. Invariant satisfied. | |
| or | Critically unbalanced | Invariant violated! Must be restored via immediate local tree rotation. |
(Note: In by-convention implementations, may be defined as ; the mathematical properties are fully symmetric).
Formal Mathematical Proof: Worst-Case Height is Strictly
#Let denote the minimum number of nodes required to construct an AVL tree of height :
- For : (a single root node).
- For : (root with one child).
- For height , to minimize total nodes while maintaining the AVL property, one child must have height and the other must have height :
This recurrence is closely related to the Fibonacci sequence ():
Using Binet's formula for Fibonacci numbers with the Golden Ratio :
Taking the base- logarithm:
Since :
3. The Four Canonical Rebalancing Rotations
#When an insertion or deletion causes a node to violate the AVL invariant (), the structural imbalance falls into one of four mutually exclusive cases:
| Imbalance Type | Condition at Node | Condition at Heavy Child | Rebalancing Action |
|---|---|---|---|
| Left-Left (LL) | Single Right Rotation on | ||
| Right-Right (RR) | Single Left Rotation on | ||
| Left-Right (LR) | Double Rotation: Left-Rotate child , then Right-Rotate root | ||
| Right-Left (RL) | Double Rotation: Right-Rotate child , then Left-Rotate root |
Single Right Rotation (LL Case)
#Applied when an insertion occurs in the left subtree of the left child of :
| Topological Role | Before Rotation | After Right Rotation | Pointer Adjustment |
|---|---|---|---|
| Subtree Root | Node () | Node () | becomes the new parent/root of this subtree |
| Left Child of Root | Node | Node (Unchanged) | remains |
| Right Child of Root | Subtree | Node () | is set to |
| Transferred Subtree | ('s original right child) | 's new left child | is set to |
Single Left Rotation (RR Case)
#Applied when an insertion occurs in the right subtree of the right child of :
| Topological Role | Before Rotation | After Left Rotation | Pointer Adjustment |
|---|---|---|---|
| Subtree Root | Node () | Node () | becomes the new parent/root of this subtree |
| Left Child of Root | Subtree | Node () | is set to |
| Right Child of Root | Node | Node (Unchanged) | remains |
| Transferred Subtree | ('s original left child) | 's new right child | is set to |
Double Rotations: Left-Right (LR) and Right-Left (RL)
#A single rotation cannot resolve an "inner" child imbalance (zigzag shape). Instead, two consecutive single rotations are executed:
| Step | Left-Right (LR) Procedure | Right-Left (RL) Procedure |
|---|---|---|
| Phase 1 | Perform Left Rotation on 's left child (). This transforms the LL/LR zigzag into a purely linear LL chain. | Perform Right Rotation on 's right child (). This transforms the RR/RL zigzag into a purely linear RR chain. |
| Phase 2 | Perform Right Rotation on unbalanced node . Node becomes the new subtree root. | Perform Left Rotation on unbalanced node . Node becomes the new subtree root. |
| Result | Subtree height is restored to its pre-insertion baseline with zero invariant violations. | Subtree height is restored to its pre-insertion baseline with zero invariant violations. |
4. Complete Implementation: AVL Tree Node, Rotations, and Insertion
#export class AVLNode<T> {
key: T;
height: number = 1;
left: AVLNode<T> | null = null;
right: AVLNode<T> | null = null;
constructor(key: T) {
this.key = key;
}
}
function getHeight<T>(node: AVLNode<T> | null): number {
return node ? node.height : 0;
}
function getBalanceFactor<T>(node: AVLNode<T> | null): number {
return node ? getHeight(node.left) - getHeight(node.right) : 0;
}
function updateHeight<T>(node: AVLNode<T>): void {
node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
}
export function rotateRight<T>(y: AVLNode<T>): AVLNode<T> {
const x = y.left!;
const T2 = x.right;
// Perform rotation
x.right = y;
y.left = T2;
// Update heights (order matters: bottom node first)
updateHeight(y);
updateHeight(x);
return x; // New root of rotated subtree
}
export function rotateLeft<T>(x: AVLNode<T>): AVLNode<T> {
const y = x.right!;
const T2 = y.left;
// Perform rotation
y.left = x;
x.right = T2;
// Update heights (order matters: bottom node first)
updateHeight(x);
updateHeight(y);
return y; // New root of rotated subtree
}
export function insertAVL<T>(node: AVLNode<T> | null, key: T): AVLNode<T> {
// Step 1: Standard recursive BST insertion
if (node === null) return new AVLNode(key);
if (key < node.key) {
node.left = insertAVL(node.left, key);
} else if (key > node.key) {
node.right = insertAVL(node.right, key);
} else {
return node; // Duplicate keys disallowed
}
// Step 2: Update height of ancestor node
updateHeight(node);
// Step 3: Check Balance Factor
const balance = getBalanceFactor(node);
// Step 4: Rebalance if required
// Case 1: Left-Left (LL)
if (balance > 1 && key < node.left!.key) {
return rotateRight(node);
}
// Case 2: Right-Right (RR)
if (balance < -1 && key > node.right!.key) {
return rotateLeft(node);
}
// Case 3: Left-Right (LR)
if (balance > 1 && key > node.left!.key) {
node.left = rotateLeft(node.left!);
return rotateRight(node);
}
// Case 4: Right-Left (RL)
if (balance < -1 && key < node.right!.key) {
node.right = rotateRight(node.right!);
return rotateLeft(node);
}
return node;
}5. Step-by-Step Dry Run State Trace: Left-Right (LR) Double Rotation
#Consider inserting keys in sequence: into an initially empty AVL tree.
| Step | Operation | Current Tree State | Balance Factors | Identified Violation | Rebalancing Execution |
|---|---|---|---|---|---|
| 1 | Insert | Root | None | Height = 1. Valid. | |
| 2 | Insert | , | None | Height = 2. Left-heavy but within . | |
| 3 | Insert | attached | Critical Violation at Node 30: with Child . | Left-Right (LR) Case detected! Requires 2 rotations. | |
| 4 | Phase 1: Left-Rotate on Child | Left child transforms: becomes left child of ; becomes left child of . | , , | Intermediate state: purely Left-Left (LL) chain. | Height of is updated to 2. |
| 5 | Phase 2: Right-Rotate on Root | Node becomes new root; is left child, is right child. | None! Invariant fully restored. | Tree height reduced from 3 to 2. Perfectly balanced. |
6. Asymptotic Complexity Matrix
#| Operation | Best Case | Average Case | Worst Case | Space Complexity |
|---|---|---|---|---|
| Search | ||||
| Insertion | call stack | |||
| Deletion | call stack | |||
| Single Rotation |
Engineering Trade-Off: AVL Trees vs. Red-Black Trees
#| Feature / Metric | AVL Tree | Red-Black Tree |
|---|---|---|
| Balance Rigidity | Stricter: Height | Looser: Height |
| Lookup Speed | Faster (due to flatter height) | Slightly slower (due to potentially deeper paths) |
| Insertion Rotations | At most 1 single or double rotation | At most 2 rotations |
| Deletion Rotations | Up to rotations up the tree | At most 3 rotations |
| Metadata Overhead | 1 integer or byte for height | 1 bit for color (RED / BLACK) |
| Optimal Use Case | Read-heavy workloads (dictionaries, static lookups) | Write-heavy workloads (standard map/set library engines) |
7. Common Traps, Edge Cases & Implementation Pitfalls
#- Height Update Ordering:
- In both single and double rotations, the child node that moves down must have its height updated before the new root node. Inverting the order computes stale parent heights.
- Deletion Rebalancing Propagation:
- Unlike insertion where at most one rotation sequence restores the global tree, deletion can cause balance factor violations to propagate all the way up to the tree root, requiring rebalancing rotations.
- Integer Subtraction for Balance Factor:
- Always ensure empty/leaf subtrees return height (or depending on node vs. edge counting conventions) consistently. Mixing -based and -based height representations corrupts balance factor calculations.
8. Real-World Applications & Practice Problems
#Production Systems
#- In-Memory Ordered Dictionaries: Used in high-performance trading systems where lookup latency predictability is prioritized over insertion speed.
- Database Query Optimizers: Used for keeping in-memory index range scans strictly bounded with minimal variance.
Standard Practice Problems
#- Balance a Binary Search Tree (LeetCode 1382) — Inorder extraction followed by median-based recursive tree reconstruction.
- AVL Tree Implementation — Implement full
insertanddeleteroutines with automatic rotation dispatch.
9. References & Academic Attribution
#- Adelson-Velsky, G. M., & Landis, E. M. (1962). An algorithm for the organization of information. Proceedings of the USSR Academy of Sciences, 146, 263–266.
- Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.), Chapter 13. MIT Press.
- Knuth, D. E. (1998). The Art of Computer Programming, Volume 3: Sorting and Searching (2nd ed.), Section 6.2.3. Addison-Wesley.