31 Balanced Trees — AVL and Red-Black
32 Balanced Trees — AVL and Red-Black
Week 5, Session 1. CSS 343.
32.1 Warmup
Validate Binary Search Tree (#98). Verify a tree satisfies the BST property.
class Solution {
public:
bool isValidBST(TreeNode* root) {
return check(root, LLONG_MIN, LLONG_MAX);
}
private:
bool check(TreeNode* node, long long lo, long long hi) {
if (node == nullptr) return true;
if (node->val <= lo || node->val >= hi) return false;
return check(node->left, lo, node->val)
&& check(node->right, node->val, hi);
}
};Pass min/max bounds down. Each node’s value must lie strictly between them. Note the use of long long for bounds — the problem allows INT_MIN and INT_MAX as values, so the bounds must be wider.
32.2 Learning objectives
- Calculate the balance factor of every node after an insertion into an AVL tree.
- Identify which rotation case (LL, RR, LR, RL) is required and execute it.
- State the four Red-Black tree properties and explain the O(log n) guarantee.
- Choose between AVL and Red-Black trees for a given use case.
- Use
std::mapandstd::setcorrectly, knowing their O(log n) guarantees.
32.3 Motivation
Plain BSTs degenerate. AVL trees and red-black trees do not — they keep height O(log n) by rotating during insert and delete. The cost is slightly more work per operation. The payoff is that the worst case is the average case.
32.4 AVL trees
An AVL tree is a BST with the invariant that for every node, the heights of its left and right subtrees differ by at most 1.
int balanceFactor(Node* node) {
return height(node->left) - height(node->right);
}A balance factor in {-1, 0, +1} is fine. Outside that range, the tree is unbalanced and must be rotated.
32.4.1 The four rotation cases
When an imbalance occurs after insert at node X:
| Case | Description | Fix |
|---|---|---|
| LL | Inserted in left subtree of left child | Right rotation at X |
| RR | Inserted in right subtree of right child | Left rotation at X |
| LR | Inserted in right subtree of left child | Left rotation at left child, then right rotation at X |
| RL | Inserted in left subtree of right child | Right rotation at right child, then left rotation at X |
32.4.2 Right rotation (LL case)
30 20
/ / \
20 → 10 30
/
10
Node* rotateRight(Node* y) {
Node* x = y->left;
Node* T2 = x->right;
x->right = y;
y->left = T2;
updateHeight(y);
updateHeight(x);
return x; // new subtree root
}32.4.3 Left rotation (RR case)
The mirror image.
Node* rotateLeft(Node* x) {
Node* y = x->right;
Node* T2 = y->left;
y->left = x;
x->right = T2;
updateHeight(x);
updateHeight(y);
return y;
}32.4.4 Double rotations (LR, RL)
LR: rotate left at the left child to convert to LL, then right rotate at the imbalanced node.
30 30 20
/ / / \
10 → 20 → 10 30
\ /
20 10
RL: mirror.
32.4.5 AVL insert
Node* insert(Node* node, int value) {
if (node == nullptr) return new Node(value);
if (value < node->data) node->left = insert(node->left, value);
else if (value > node->data) node->right = insert(node->right, value);
else return node; // no duplicates
updateHeight(node);
int bf = balanceFactor(node);
// LL
if (bf > 1 && value < node->left->data) return rotateRight(node);
// RR
if (bf < -1 && value > node->right->data) return rotateLeft(node);
// LR
if (bf > 1 && value > node->left->data) {
node->left = rotateLeft(node->left);
return rotateRight(node);
}
// RL
if (bf < -1 && value < node->right->data) {
node->right = rotateRight(node->right);
return rotateLeft(node);
}
return node;
}The rotations happen on the way up the recursion. By the time you return to the root, the entire tree is balanced.
AVL trees were the first self-balancing BST, invented in 1962. They are stricter than red-black trees — balance factor must be in {-1, 0, +1}. Stricter balance means faster lookups but more rotations on insert.
32.5 Red-Black trees
A red-black tree is a BST where each node is colored red or black, satisfying:
- The root is black.
- Every leaf (
nullptr) is black. - A red node’s children are both black. (No two reds in a row.)
- Every path from a node to its descendant leaves contains the same number of black nodes.
These properties together guarantee that the longest path is at most twice the shortest, so height is O(log n).
32.5.1 Insert and rebalancing
After a normal BST insert, color the new node red. If its parent is also red, you have a violation. Fix it by:
- Recoloring if the uncle is red.
- Rotating if the uncle is black.
The case analysis is gnarly. For 343 you should know the properties and that O(log n) is guaranteed. The full implementation is a CLRS-grade exercise.
32.6 AVL vs. Red-Black: which to use?
| AVL | Red-Black | |
|---|---|---|
| Balance | Strict (≤1 height diff) | Loose (≤2× shortest) |
| Lookup | Slightly faster | Slightly slower |
| Insert / delete rotations | More | Fewer |
| Best for | Read-heavy workloads | Write-heavy workloads |
| In the wild | Some database indexes | std::map, Linux completely fair scheduler, Java TreeMap |
std::map and std::set are red-black trees. You have been using a red-black tree the entire course.
32.7 STL map / set patterns
map<int, int> m;
m[5] = 100;
m[2] = 50;
m[8] = 200;
for (auto& [k, v] : m) cout << k << " "; // 2 5 8 sorted
auto it = m.lower_bound(4); // first key >= 4 → 5
auto it2 = m.upper_bound(8); // first key > 8 → end
// Find first key in range [a, b]:
auto r = m.lower_bound(a);
if (r != m.end() && r->first <= b) { /* found */ }lower_bound and upper_bound are the killer features. They are O(log n) and they are the reason you sometimes want map over unordered_map.
32.8 Try it
Insert {30, 20, 10} into an empty AVL tree. After inserting 30 and 20, the tree is fine (heights 1 and 0). After inserting 10, node 30 has balance factor 2 (left height 1, right height -1). LL case. Rotate right at 30.
Draw the before and after.
32.9 Self-check
std::map<int, int> is implemented as:{10, 30, 20} into an empty AVL tree, the case is:32.10 Challenges
- Implement AVL insert end-to-end. Test with sorted-insertion sequence to confirm balancing.
- Convert Sorted Array to Binary Search Tree (#108) — produces a balanced tree from sorted input.
- Balanced Binary Tree (#110) — verify AVL property.
32.11 Where this fits
You can now build a tree that never degenerates. The midterm comes next; afterward we leave data structures and enter algorithm design — divide-and-conquer, greedy, DP.
| You are here | Coming up |
|---|---|
| AVL rotations, Red-Black properties, STL map/set internals | Chapter 30: midterm review |