AVL Trees: Self-Balancing Binary Search Trees
Master AVL tree rotations, balance factors, and rebalancing logic. Learn when to use AVL vs Red-Black trees for your use case.
AVL Trees: Self-Balancing Binary Search Trees
An AVL tree is a binary search tree where the heights of left and right subtrees of any node differ by at most one. This balance condition guarantees O(log n) for search, insertion, and deletion. Named after inventors Adelson-Velsky and Landis (1962), AVL trees were the first balanced binary search tree data structure invented.
The balance factor (height of left subtree minus height of right subtree) is always -1, 0, or +1 for every node. When an insertion or deletion violates this property, rotations rebalance the tree.
Introduction
An AVL tree is a self-balancing binary search tree where the heights of left and right subtrees differ by at most one for every node. This strict balance constraint—named after inventors Adelson-Velsky and Landis—guarantees O(log n) search, insertion, and deletion operations regardless of insertion order. When a node is inserted or deleted, rotations restore the balance property if violated.
AVL trees were the first balanced binary search tree invented (1962) and remain a good choice for read-heavy workloads. The balance factor (left_height minus right_height) is always -1, 0, or +1, enabling efficient height tracking and rebalancing decisions. AVL trees enforce stricter balance than Red-Black trees (height bound 1.44 × log₂(n) vs 2 × log₂(n)), which means faster search but slightly more rebalancing work on insertions and deletions.
In this guide, you’ll implement AVL insertion and deletion with all four rotation cases (LL, RR, LR, RL), understand why insertion requires at most one rotation while deletion may require O(log n), and learn when AVL trees are the right choice. We’ll compare against Red-Black trees, skip lists, and hash tables across multiple dimensions.
AVL Node Implementation
class AVLNode:
def __init__(self, key, value=None):
self.key = key
self.value = value
self.left = None
self.right = None
self.height = 1 # New node starts at height 1
self.balance_factor = 0 # Left height - Right height
class AVLTree:
def __init__(self):
self.root = None
def _update_height(self, node):
"""Update node height from children's heights."""
if node:
left_height = node.left.height if node.left else 0
right_height = node.right.height if node.right else 0
node.height = 1 + max(left_height, right_height)
node.balance_factor = left_height - right_height
def _rotate_right(self, y):
"""Right rotation for Left-Left imbalance."""
x = y.left
t2 = x.right
# Perform rotation
x.right = y
y.left = t2
# Update heights (y must be updated before x)
self._update_height(y)
self._update_height(x)
return x
def _rotate_left(self, x):
"""Left rotation for Right-Right imbalance."""
y = x.right
t2 = y.left
# Perform rotation
y.left = x
x.right = t2
# Update heights
self._update_height(x)
self._update_height(y)
return y
def _rebalance(self, node):
"""Rebalance node after insertion/deletion."""
self._update_height(node)
# Left-heavy
if node.balance_factor > 1:
# Left-Right case: left child is right-heavy
if node.left and node.left.balance_factor < 0:
node.left = self._rotate_left(node.left)
return self._rotate_right(node)
# Right-heavy
if node.balance_factor < -1:
# Right-Left case: right child is left-heavy
if node.right and node.right.balance_factor > 0:
node.right = self._rotate_right(node.right)
return self._rotate_left(node)
return node
def insert(self, key, value=None):
self.root = self._insert(self.root, key, value)
def _insert(self, node, key, value):
if not node:
return AVLNode(key, value)
if key < node.key:
node.left = self._insert(node.left, key, value)
elif key > node.key:
node.right = self._insert(node.right, key, value)
else:
node.value = value # Update existing
return node
return self._rebalance(node)
AVL Deletion
def _find_min(self, node):
"""Find node with minimum key in subtree."""
while node.left:
node = node.left
return node
def delete(self, key):
self.root = self._delete(self.root, key)
def _delete(self, node, key):
if not node:
return None
if key < node.key:
node.left = self._delete(node.left, key)
elif key > node.key:
node.right = self._delete(node.right, key)
else:
# Found node to delete
if not node.left:
return node.right
if not node.right:
return node.left
# Two children: replace with inorder successor
successor = self._find_min(node.right)
node.key = successor.key
node.value = successor.value
node.right = self._delete(node.right, successor.key)
return self._rebalance(node)
When to Use AVL Trees
Use AVL trees when:
- You need guaranteed O(log n) search, insert, delete
- You do more searches than insertions/deletions
- You need a simpler implementation than Red-Black trees
- You need strict balance (AVL is more balanced than RB trees)
Don’t use AVL trees when:
- You do many more insertions/deletions than searches (overhead of rebalancing)
- You need weaker balance guarantees (Red-Black trees have less strict balance)
- You need the tree to also be an efficient priority queue (use a heap instead)
AVL vs Red-Black Trees
| Aspect | AVL Trees | Red-Black Trees |
|---|---|---|
| Balance | Stricter (heights differ by ≤1) | Weaker (path lengths differ by ≤2×) |
| Search Performance | Better (more balanced) | Slightly worse |
| Insert/Delete Overhead | Higher (more rotations) | Lower (fewer rotations) |
| Height | ≤ 1.44 × log₂(n) | ≤ 2 × log₂(n) |
| Use Case | Read-heavy workloads | Write-heavy workloads |
Trade-off Analysis
AVL Trees vs Alternative Data Structures
| Attribute | AVL Tree | Red-Black Tree | Skip List | Hash Table (Chaining) |
|---|---|---|---|---|
| Search Complexity | O(log n) guaranteed | O(log n) guaranteed | O(log n) expected | O(1) average, O(n) worst |
| Insert Complexity | O(log n) with ≤ 1 rotation | O(log n) with ≤ 2 rotations | O(log n) expected | O(1) average |
| Delete Complexity | O(log n) with O(log n) rotations worst-case | O(log n) with O(1) rotations + recoloring | O(log n) expected | O(1) average |
| Memory per Node | Key + 2 pointers + height (int) | Key + 2-3 pointers + color bit | Key + 1 pointer + ~2 forward ptrs avg | Key + value + next pointer |
| Ordered Traversal | O(n) inorder | O(n) inorder | O(n) sorted order | Not supported |
| Range Queries | O(k + log n) | O(k + log n) | O(k + log n) | O(n) scan |
| Implementation Complexity | Moderate | Complex (deletion especially) | Simple | Simple |
| Balance Guarantee | Strictest: height diff ≤ 1 | Weak: max ≤ 2× min path | Probabilistic: expected O(log n) | None |
| Cache Friendliness | Good (tree locality) | Good | Poor (indirection via levels) | Poor (pointer chains) |
| Best Workload | Read-heavy, ordered data | Write-heavy, ordered data | Concurrent environments | Simple key-value, unordered |
When Each Structure Wins
- AVL Trees — Search-optimized workloads: database indexes (read replica), lookup tables, compiler symbol tables, and any scenario where queries dominate mutations.
- Red-Black Trees — General-purpose balanced trees: Linux kernel Completely Fair Scheduler, Java TreeMap, C++ std::map. Best when insert/delete frequency is comparable to search.
- Skip Lists — Concurrent systems (no global rebalancing lock needed): Redis sorted sets, LevelDB memtables. Randomized structure avoids adversarial input issues.
- Hash Tables — Fastest for exact-match lookups when order doesn’t matter: caches, dictionaries, symbol tables in interpreters. Degrades to O(n) with poor hash distribution.
Rotations Summary
| Case | Condition | Rotation |
|---|---|---|
| LL | Left child’s left subtree is too deep | Single right rotation |
| RR | Right child’s right subtree is too deep | Single left rotation |
| LR | Left child’s right subtree is too deep | Left rotation then right rotation |
| RL | Right child’s left subtree is too deep | Right rotation then left rotation |
AVL Rotation Types
graph LR
subgraph "LL Rotation (Right-Right Imbalance)"
LLA["(imbalanced node) y"]
LLB["(left child) x"]
LLC["(x's right subtree) t2"]
LLA --> LLB
LLB --> LLA
LLB --> LLC
LLStyle1["Before: y has balance factor +2, x has +1"]
LLStyle2["After: Single right rotation at y"]
end
subgraph "RR Rotation (Left-Left Imbalance)"
RRA["(imbalanced node) y"]
RRB["(right child) x"]
RRC["(x's left subtree) t2"]
RRA --> RRB
RRB --> RRA
RRB --> RRC
RRStyle1["Before: y has balance factor -2, x has -1"]
RRStyle2["After: Single left rotation at y"]
end
subgraph "LR Rotation (Left-Right Imbalance)"
LRA["(imbalanced node) z"]
LRB["(left child) y"]
LRC["(y's right subtree) x"]
LRD["(x's left subtree) t2"]
LRE["(x's right subtree) t3"]
LRA --> LRB
LRB --> LRC
LRC --> LRD
LRC --> LRE
LRStyle1["Before: z has +2, y has -1 (LR case)"]
LRStyle2["After: Left rotate y, then right rotate z"]
end
subgraph "RL Rotation (Right-Left Imbalance)"
RLA["(imbalanced node) z"]
RLB["(right child) y"]
RLC["(y's left subtree) x"]
RLD["(x's left subtree) t2"]
RLE["(x's right subtree) t3"]
RLA --> RLB
RLB --> RLC
RLC --> RLD
RLC --> RLE
RLStyle1["Before: z has -2, y has +1 (RL case)"]
RLStyle2["After: Right rotate y, then left rotate z"]
end
Production Failure Scenarios
-
Skipping height updates after rotations: This is the one I see most often in debugging sessions. You rotate, get the tree looking right, but forget to update heights up the chain. Next operation reads stale balance factors and makes bad decisions. The tree doesn’t fail catastrophically—it just quietly loses its guarantees over time. Searches that should take microseconds start taking milliseconds, then longer, and nobody knows why until they dig into the heights.
-
Getting the balance factor sign wrong: It should be
left_height - right_height. A negative sign flip means you’re rebalancing the wrong side at every invocation. The tree might actually look balanced visually for a while, but searches return wrong results. That’s a data corruption bug hiding inside what looks like correct code. -
Inconsistent duplicate handling: If your application allows duplicate keys, the insertion logic has to pick a direction and stick with it. When it waffles—sometimes going left, sometimes right—on equal keys, the tree’s structural invariants break. Searches for keys that exist start returning nothing.
-
Height field overflow in very deep trees: AVL trees with millions of nodes can approach the integer limits of a small height field. In embedded contexts or languages with small integer types, this wraps around and makes the tree think it’s short when it’s actually tall. Operations degrade without obvious cause.
Failure Recovery
- Monitor tree height and rebalance operations per second
- Validate AVL property periodically: for every node, |left_height - right_height| ≤ 1
- Log rotation counts—if rotation frequency spikes, the tree may be in a degraded state
Quick Recap Checklist
- AVL trees guarantee O(log n) operations via balance factor constraint
- Balance factor = left_height - right_height (must be -1, 0, or +1)
- Four rotation cases: LL, RR, LR, RL
- LR = left rotate child, then right rotate at node
- RL = right rotate child, then left rotate at node
- Insert rebalances once; delete may rebalance multiple times up the tree
Observability Checklist
Track AVL tree operations so you can catch balance degradation before it becomes a production incident.
Core Metrics
- Operation count (inserts, deletes, searches) per second
- Rotation count per operation—should be low on average
- Tree height over time—should stay near 1.44 × log₂(n)
- Balance factor distribution across all nodes
- Cache hit rate for tree node accesses
Health Signals
- Rotation count spikes: the tree is likely under a pathological input pattern
- Height approaching O(n): the AVL property is broken somewhere
- Balance factor violations: the rebalance logic has a bug
- Search latency creeping up: tree height is growing beyond expected bounds
- Memory usage growing without bound: node leak or duplicate allocation
Alerting Thresholds
- Height > 2 × log₂(n) for your node count: tree is unbalanced
- Average rotations per operation > 2: implementation bug or attack
- Any balance factor with absolute value > 1: alert immediately, the AVL invariant is violated
- Rotation count trending up without an input pattern change: investigate
Distributed Tracing
- Trace each tree operation with node count, tree height, and rotation count as span attributes
- Include balance factor statistics in span metadata
- Track rebalance path length to identify problematic input patterns
- Correlate slow operations with specific tree shapes—skewed, deep chains
Security Notes
AVL trees have specific security concerns when input is attacker-controlled.
Height exhaustion via pathological insertions: An attacker who controls insertion order can craft sequences that maximize tree height at every step, approaching 1.44 × log₂(n) bound. While this is still O(log n), the constant factor matters—if the attacker can predict the tree’s state, they can craft inputs that hit worst-case performance bounds, causing latency spikes.
Fix: Use randomized input shuffling for adversarial environments, or switch to skip lists or hash-based alternatives with better worst-case guarantees.
Balance factor oracle via timing attacks: If the AVL tree implementation uses timing differences in balance factor checks to infer tree structure, an attacker could use timing attacks to learn the tree’s shape. This matters in security-sensitive contexts where tree structure itself is sensitive.
Fix: Ensure balance operations take constant time regardless of tree shape. Use branchless implementations where possible to eliminate timing side channels.
Integer overflow in height field: When the height field is a small integer (e.g., 8-bit or 16-bit), an attacker could craft insertions that cause height overflow, wrapping to small values and breaking the tree’s balance guarantees.
Fix: Use 32-bit or larger integers for height fields. Validate height values are within expected bounds after each rebalance operation.
Interview Questions
AVL trees enforce that for every node, the heights of left and right subtrees differ by at most 1—this is a strict balance. Red-Black trees maintain only that no path is more than twice as long as any other, which is a weaker guarantee. The stricter AVL balance means faster search but more rebalancing work on insert/delete. Red-Black trees trade slightly worse search for less rotation overhead during modifications.
LL (Left-Left): Left child's left subtree is too deep—single right rotation at the imbalanced node. RR (Right-Right): Right child's right subtree is too deep—single left rotation. LR (Left-Right): Left child's right subtree is too deep— left rotate the left child, then right rotate at the node. RL (Right-Left): Right child's left subtree is too deep— right rotate the right child, then left rotate at the node.
At most one rotation for insertion. After inserting the new leaf, we trace back up the path, rebalancing at most once at the lowest node that became imbalanced. Deletion, however, can require O(log n) rotations because after a rotation, we may need to continue checking ancestors for balance. This is why AVL trees are better for read-heavy workloads where insertions are less frequent.
The maximum height is approximately 1.44 × log₂(n), which comes from the Fibonacci-like relationship in AVL tree structure. This contrasts with Red-Black trees (≤ 2 × log₂(n)) and unbalanced BSTs (which can degrade to O(n)). The formula comes from the recurrence: N(h) = 1 + N(h-1) + N(h-2) with N(0) = 1, N(1) = 2, which gives the tight bound on minimum nodes for a given height.
The balance factor (BF) for a node is left subtree height minus right subtree height. During insertion, after adding a new leaf, we recalculate BF up the ancestor chain. The first node where |BF| > 1 must be rebalanced with a rotation. Only one rotation (single or double) is needed per insertion because the subtree height after rotation returns to its pre-insertion value, propagating no further imbalance upward. During deletion, we also recalculate BF up the chain, but a rotation may reduce the subtree height, potentially causing imbalance at a higher ancestor. This means deletion may require O(log n) rebalance operations in the worst case.
Standard AVL trees typically do not allow duplicate keys. The standard insertion logic (key < left, key > right) treats equal keys as an update to the existing node. If duplicates are required, there are several strategies:
- Consistent Direction: Allow duplicates consistently to the left or right subtree, and ensure searches traverse both sides.
- Count Field: Add a count field to each node to track duplicates without inserting extra nodes.
- Linked List at Node: Store a linked list or dynamic array of values at each key node.
The key risk with duplicates is inconsistency: if on insertion the duplicate goes left but on search it goes right, the tree may miss the key entirely. The count-field approach is usually preferred because it preserves the tree's balance properties better than inserting many equal-key nodes.
A valid AVL tree must satisfy three invariants:
- BST Property: For every node, all keys in the left subtree are smaller and all keys in the right subtree are larger.
- AVL Balance Property: For every node, |left_height - right_height| ≤ 1.
- Height Accuracy: Each node's stored height equals 1 + max(left.height, right.height).
The verification algorithm uses a recursive post-order traversal. For each node, compute left and right heights recursively (return -1 for null), verify the balance factor constraint, and check that the stored height (if any) matches. This runs in O(n) time and O(h) space for the recursion stack.
When deleting a node with two children, AVL trees typically use the inorder successor (or predecessor) replacement strategy:
- Find the inorder successor (the minimum node in the right subtree).
- Copy the successor's key and value to the node being "deleted."
- Then delete the successor from the right subtree (which is always a leaf or has at most one child).
- Finally, rebalance starting from the parent of the removed successor all the way up to the root.
This is where deletion differs from insertion: a deletion may trigger rotations at multiple nodes along the path back to the root, because each rotation reduces the subtree height, potentially creating a new imbalance at a higher ancestor.
If the workload is read-heavy (many more searches than insertions/deletions), choose an AVL tree. The stricter balance (height ≤ 1.44 × log₂(n) vs ≤ 2 × log₂(n)) means searches are ~30% faster in the worst case. For 10 million nodes (log₂(10⁷) ≈ 24), an AVL tree has at most ~35 levels vs ~48 for a Red-Black tree — 13 fewer pointer dereferences per search.
If the workload is write-heavy (frequent insertions/deletions), choose a Red-Black tree. The weaker balance means fewer rotations per modification. AVL deletion can trigger O(log n) rotations in the worst case, while Red-Black deletion requires at most 3 rotations plus O(log n) recoloring operations.
If the data changes infrequently and searches dominate (e.g., a lookup table loaded at startup), AVL is clearly superior. For a general-purpose balanced tree with mixed operations, many production systems (Linux kernel, Java TreeMap, C++ std::map) prefer Red-Black trees.
LR (Left-Right) case: The left child of the imbalanced node is right-heavy (balance factor = -1). To fix it:
- Step 1: Left-rotate the left child (converts LR → LL case).
- Step 2: Right-rotate at the imbalanced node.
RL (Right-Left) case: The right child of the imbalanced node is left-heavy (balance factor = +1). To fix it:
- Step 1: Right-rotate the right child (converts RL → RR case).
- Step 2: Left-rotate at the imbalanced node.
They're called "double rotations" because they compose two single rotations into one atomic rebalancing operation. The intermediate state after the first rotation is itself a valid single-rotation case. The double rotation's name reflects the direction of the two rotations: LR means "rotate Left child then Right rotate at parent."
All three trees store O(n) nodes, so the asymptotic space complexity is O(n) for all of them. The constant factors differ:
- Regular BST: key + 2 child pointers = 3 fields per node.
- AVL Tree: key + 2 child pointers + height (integer) = 4 fields per node.
- Red-Black Tree: key + 2 child pointers + parent pointer (optional) + color bit = 4.5–5 fields per node.
In practice, AVL trees are memory-efficient compared to Red-Black trees because the height field is typically a small integer (1 or 2 bytes if packed), while Red-Black trees often use a parent pointer for efficient recoloring during deletion. Some implementations trade memory for performance, making Red-Black trees larger.
Expected answer points:
- Single rotation (LL or RR): O(1) - just pointer swaps and height updates for 2 nodes
- Double rotation (LR or RL): O(1) - two single rotations, 3 nodes affected
- Height update: O(1) per node, visiting only ancestors on the search path back to root
- Total rebalance per operation: O(log n) worst case for traversal, but O(1) for the actual rotation work
Expected answer points:
- For n = 1,000,000 nodes: AVL max height ≈ 35, RB max height ≈ 48 (37% fewer levels for AVL)
- For n = 1,000,000,000: AVL max height ≈ 55, RB max height ≈ 90 (39% fewer levels)
- Each level means one more pointer dereference and cache miss for disk/memory access
- AVL's 1.44 × log₂(n) bound comes from the Fibonacci-like recurrence N(h) = 1 + N(h-1) + N(h-2)
- The "1.44×" factor means AVL trees are strictly tighter balanced, but the Big-O is still O(log n) for both
Expected answer points:
- Yes, range query can be implemented using inorder traversal with early termination
- Algorithm: Start from smallest key >= low, do inorder, stop when key > high
- Time complexity: O(k + log n) where k = number of keys in range
- The log n component comes from finding the starting node (binary search down the tree)
- Each node in range is visited exactly once during the inorder traversal
- Space complexity: O(h) = O(log n) for the recursion stack
Expected answer points:
- AVL trees provide worst-case O(log n) for all operations - this is a hard guarantee, not amortized
- Some data structures like splay trees give amortized O(log n) - meaning the average per operation is bounded, but any single operation could be O(n)
- AVL trees never degrade to O(n) - the balance invariant is always maintained
- The "worst-case" distinction matters for real-time systems or adversarial input scenarios
- Red-Black trees also provide worst-case O(log n) but with a weaker constant factor
Expected answer points:
- AVL trees are not naturally concurrent-friendly due to structural modifications during rotations
- Coarse-grained locking: Lock the entire tree for write operations, allows multiple readers
- Fine-grained locking: Lock individual nodes, but rotations affect multiple nodes requiring careful lock ordering to avoid deadlock
- Optimistic locking: Each thread works on a private copy, validate AVL property before committing (copy-on-write)
- Read-write locks: Allow multiple readers or single writer, but writers still block each other
- Skip lists or lock-free data structures may be better choices for highly concurrent scenarios
Expected answer points:
- Add a `size` field to each node: size = 1 + size(left) + size(right)
- Update size during insert, delete, and rotation operations
- Finding k-th smallest: recursively compute rank = size(left) + 1, if k == rank return node, else recurse left or right
- Time complexity: O(log n) worst case
- Space overhead: one extra integer per node
- Balance factor and size must both be maintained consistently after every operation
Expected answer points:
- Floor: Find the largest key <= target; Ceiling: Find the smallest key >= target
- Floor algorithm: Start at root, track best candidate. If node.key <= target and node.key > best, update best. If target < node.key, go left; else go right. Return best when reaching null.
- Time complexity: O(log n) - visits at most one node per level
- Symmetric for ceiling operation
- These are useful for range queries, nearest-neighbor searches, and gap finding
Expected answer points:
- B-trees excel when nodes don't fit in a single cache line or memory page (typically > 1000 keys per node for disk storage)
- For in-memory operations with typical CPU caches, AVL trees are usually better up to millions of nodes
- When each node access costs a disk I/O (databases, file systems), B-trees reduce I/O by storing more keys per node (fewer tree levels = fewer disk seeks)
- AVL trees have better inorder traversal locality within a single node but more pointer chasing between nodes
- Rule of thumb: If your working set doesn't fit in memory and you're doing disk storage, use B-trees. If everything fits in RAM, AVL or Red-Black are typically faster.
Expected answer points:
- After many deletions, an AVL tree can become "suboptimal" - structurally valid but not well-balanced in practice
- Rebuilding produces a perfectly balanced tree with height closer to log₂(n) rather than 1.44 × log₂(n)
- Scapegoat trees use this idea: rebuild entire subtree when it becomes too deep (balance factor > α)
- Partial rebuilding costs O(n) every log n deletions but yields better long-term height guarantees
- The trade-off: O(n) rebuild cost vs improved search performance going forward
- Useful when you know batch deletions are happening and future reads will dominate
Further Reading
Related Data Structures
- Red-Black Trees — Weaker balance, fewer rotations, preferred for write-heavy workloads.
- B-Trees / B+ Trees — Disk-optimized balanced trees for database storage systems.
- Treap (Randomized BST) — Combines BST and heap properties using random priorities.
- Splay Trees — Self-adjusting BST with amortized O(log n) operations.
- Scapegoat Trees — Partial rebuilding strategy instead of rotations for rebalancing.
Deeper Dives
- AVL Tree Visualization (visualgo.net) — Interactive tool to see rotations in action.
- Tree Rotations Explained — Step-by-step visual guide to single and double rotations.
- Fibonacci Numbers and AVL Trees — The mathematical relationship: N(h) = N(h-1) + N(h-2) + 1 mirrors Fibonacci recurrence, giving the 1.44 × log₂(n) height bound.
Practical Resources
- CLRS (Introduction to Algorithms), Chapter 12 — Canonical textbook coverage of binary search trees and AVL trees.
- Algorithm Design Manual, Chapter 3 — Practical guidance on choosing between balanced tree variants.
- LeetCode Problem Set — Practice problems on balanced BST construction and validation.
Conclusion
AVL trees enforce strict balance—left and right subtree heights differ by at most one—which guarantees O(log n) search, insert, and delete. The tradeoff is more rotations during insertions compared to Red-Black trees. Four rotation cases handle imbalances: LL (single right rotation), RR (single left rotation), LR (left rotate child then right rotate at node), and RL (right rotate child then left rotate at node). Insertion needs at most one rotation; deletion may require multiple rotations up the tree. AVL trees suit read-heavy workloads where guaranteed search performance matters more than insert/delete speed. If your workload modifies frequently, Red-Black trees trade slightly worse search performance for fewer rotations.
Category
Related Posts
Red-Black Trees: Balanced Search Trees with Simpler Rotations
Master red-black tree properties, insertion fixup cases, and color flip mechanics. Learn when RB trees outperform AVL and when they don't.
Bellman-Ford Algorithm: Shortest Paths with Negative Weights
Learn the Bellman-Ford algorithm for single-source shortest paths including negative edge weights and negative cycle detection.
Binary Trees and Binary Search Trees: Hierarchical Data
Master tree traversal (inorder, preorder, postorder), BST operations, and when trees outperform other data structures.