Other balanced trees: red-black, 2-3, B-tree, splay
The five rules of a red-black tree and its insertion cases, splitting in 2-3 trees, the role of B-trees on disk and the splay idea. Drawing a colored tree and tracing splits are the exam questions from here.
AVL is not the only balanced tree. The one used most in practice is the red-black tree: Java’s TreeMap and TreeSet classes keep a red-black tree inside. Databases and file systems use B-trees. This week we see four structures. In the exam, red-black questions are “insert in order, draw with colors”, and 2-3 / B-tree questions are “show the splits”. The splay tree usually comes up as a concept question.
They all have the same goal: keeping the height of the tree at O(log n). Their methods differ:
| Structure | How balance is kept | Height |
|---|---|---|
| AVL | height difference at most 1, rotation | about 1.44 log2 n |
| Red-black | color rules, rotation + recoloring | at most 2 log2(n+1) |
| 2-3 / B-tree | many keys in a node, splitting | all leaves at the same depth |
| Splay | move the accessed node to the root | no guarantee, amortized O(log n) |
The rules of a red-black tree
Every node is red or black. The tree is a BST and obeys these rules:
- The root is black.
- Null links (empty leaves) count as black.
- The children of a red node cannot be red (no two reds in a row).
- Every path from a node down to any null has the same number of black nodes. This number is called the black height (black-height).
Why does it work? By rule 4, every path has the same number of blacks. By rule 3, on the longest path the reds can only be scattered one by one between the blacks. So the longest path is at most twice the shortest path. From this we get .
20B
/ \
10B 30B 2 blacks on every path
\ (excluding nulls)
15R
Insertion: the cases
Insertion is first a normal BST insertion. The new node is always inserted red. Why red? If you inserted black, the number of blacks on that path would increase and rule 4 would break everywhere. Inserting red breaks at most rule 3, and fixing that is easy locally.
Call the new node x, its parent p, its grandparent g, and the other child of the grandparent the uncle u.
| Case | What to do |
|---|---|
| p is black | Nothing, done. |
| p is red, u is red | Recolor: p and u become black, g becomes red. Then treat g as the new x and check again above. |
| p is red, u is black, x is “inside” (LR or RL) | Rotate at p, and the case becomes straight (LL or RR). Go to the next row. |
| p is red, u is black, x is “outside” (LL or RR) | Rotate at g (right for LL, left for RR). p becomes black, g becomes red. Done. |
| At the end | If the root stayed red, color it black. |
A null uncle counts as black. The case with a rotation happens at most once and insertion ends there. Recoloring can repeat up to the root, but each step is O(1), so the total is O(log n).
PITFALL · Look at the uncle, not the sibling. The decision is made by the color of the parent’s sibling. The new node’s own sibling decides nothing.
Worked example: 30, 20, 10, 25, 27, 5, 1
+30: the root, black. +20: the parent is black, done. +10: the parent 20 is red, the uncle (the right of 30) is null, so black. The path is left-left: a right rotation at 30, 20 becomes black, 30 becomes red.
30B 20B
/ / \
20R ==> 10R 30R
/
10R
+25: red, to the left of 30. The parent 30 is red, the uncle 10 is red: recolor. 10 and 30 become black, 20 becomes red, then black again because it is the root.
20B
/ \
10B 30B
/
25R
+27: to the right of 25. The parent 25 is red, the uncle (the right of 30) is null: black. The path from the grandparent (30) is left-right, so x is inside. First a left rotation at 25 (27 moves up), then a right rotation at 30. 27 becomes black, 30 becomes red.
20B
/ \
10B 27B
/ \
25R 30R
+5: to the left of 10, the parent is black: done. +1: to the left of 5. The parent 5 is red, the uncle (the right of 10) is null: black. The path is left-left: a right rotation at 10, 5 becomes black, 10 becomes red.
20B
/ \
5B 27B
/ \ / \
1R 10R 25R 30R
The trace printed by the program (the colored level order after each insertion):
+30 -> 30B
+20 -> 30B 20R
+10 -> 20B 10R 30R
+25 -> 20B 10B 30B 25R
+27 -> 20B 10B 27B 25R 30R
+5 -> 20B 10B 27B 5R 25R 30R
+1 -> 20B 5B 27B 1R 10R 25R 30R
On every root-to-null path there are 2 black nodes (excluding nulls), and there is no red-red.
TIP · The textbook version. The textbook (Weiss) describes top-down red-black insertion: while going down, it recolors in advance every node with two red children, so there is no need to go back up. The resulting tree may sometimes differ. In the exam, check which version is asked for; the traces on this page were made with the bottom-up version.
2-3 trees
In a 2-3 tree every internal node is either a 2-node (1 key, 2 children) or a 3-node (2 keys, 3 children). All leaves are at the same depth. There are no rotations; balance is kept by splitting.
The insertion rule:
- Find the leaf where the key belongs, and insert it there in order.
- If the leaf now has 3 keys, there is an overflow: the median key moves up to the parent, and the remaining two become two separate nodes.
- If the parent overflows too, the same operation repeats above. If the root overflows, a new root is created and the tree grows by one level.
The tree grows upward. That is why all leaves always stay at the same depth.
+10 +20 +30: [10 20 30] overflows
[10 20] [20]
/ \
[10] [30]
+40 +50: [30 40 50] overflows, 40 moves up
[20 40]
/ | \
[10] [30] [50]
2-3-4 trees and the red-black connection
A 2-3-4 tree also allows a 4-node (3 keys, 4 children). Insertion is usually done top-down: every 4-node met on the way down is split in advance, so there is guaranteed room when the leaf is reached.
The real point: every 2-3-4 tree corresponds one-to-one to a red-black tree. A 2-node is a single black node. A 3-node is a black node + one red child. A 4-node is a black node + two red children. The red-black case “uncle red, recolor” is the splitting of a 4-node in the 2-3-4 tree. The black height being the same everywhere corresponds to all leaves being at the same depth in a 2-3-4 tree.
B-trees
A B-tree is a generalization of the 2-3 idea. In a B-tree of order m:
- Every node holds at most m - 1 keys and m children.
- Every internal node except the root has at least children (that is, at least keys).
- All leaves are at the same depth.
A 2-3 tree is a B-tree with m = 3. In real systems m is in the hundreds, because a node is matched to a disk block (for example 4 KB). Reading a block from disk is much slower than comparing hundreds of keys in memory. Since the height is about , with m = 200 millions of records fit in 3-4 levels.
A trace of insertion with m = 5 (a node holds at most 4 keys; when the 5th key arrives, the median moves up):
+12 +4 +30 +8 -> [4, 8, 12, 30]
+22 -> overflow, 12 moves up
L0: [12]
L1: [4, 8] [22, 30]
+1 +17 +26 +3 ->
L0: [12]
L1: [1, 3, 4, 8] [17, 22, 26, 30]
+40 -> the right leaf overflows, 26 moves up
L0: [12, 26]
L1: [1, 3, 4, 8] [17, 22] [30, 40]
Databases generally use a B+ tree: all data stays in the leaves, the internal nodes only guide the way, and the leaves are linked to each other (range queries become fast).
Splay trees
A splay tree keeps no balance information (height, color) at all. Instead, after every access (search, insert), the accessed node is moved to the root with rotations (splaying). The idea: data used recently will be accessed again soon, so keeping it at the root pays off.
There are three kinds of steps (x is the accessed node, p the parent, g the grandparent):
| Step | When | What to do |
|---|---|---|
| zig | p is the root | a single rotation at x |
| zig-zig | x and p are in the same direction (left-left or right-right) | rotate p above g first, then x above p |
| zig-zag | different directions | rotate x twice (like the AVL double rotation) |
A single operation can take O(n) (accessing the bottom of a chain). But since a zig-zig step roughly halves the path, the total cost of m consecutive operations is O(m log n). This is called amortized O(log n). It is very fast in practice in cache-like usage.
access 3 in 10(5(3,7),15): zig-zig
10 3
/ \ \
5 15 ==> 5
/ \ \
3 7 10
/ \
7 15
Common mistakes
| Wrong idea | The truth |
|---|---|
| In a red-black tree the new node is inserted black | It is always inserted red; it is recolored later if needed |
| If the uncle is red, a rotation is done | If the uncle is red, only recoloring, then continue from the grandparent |
| A null uncle counts as “absent”, skip the case | Null is black: the rotation case |
| The root can stay red after recoloring | The root is always colored black at the very end |
| A red-black tree is as tightly balanced as AVL | It is looser (2 log n); in return it does fewer rotations |
| A 2-3 tree grows downward | The split propagates upward, a new level opens at the root |
| In a 2-3 overflow the largest key moves up | The median key moves up |
| In a B-tree 1 key per node is enough | Except for the root, at least ceil(m/2) - 1 keys |
| In a splay tree every operation is guaranteed O(log n) | A single operation can be O(n); the guarantee is amortized |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
Which of the following is NOT a red-black tree property?
- The root is black.
- A red node cannot have a red child.
- Every path from a node to its null descendants has the same number of black nodes.
- The heights of the two subtrees of every node differ by at most 1.
Show answer
This is the AVL rule. A red-black tree does not limit the height difference directly; with its color rules it makes sure the longest path is no more than twice the shortest path. The other three are red-black rules.
Insert 10, 20, 30 into an empty red-black tree (standard bottom-up insertion). Give the level-order traversal with colors (B = black, R = red).
Show answer
10 is the root and is colored black. 20 is inserted red, its parent is black: done.
30 is red, and its parent 20 is red. The uncle (the left child of 10) is null, so black. The path is right-right: a left rotation at 10, 20 becomes black and 10 becomes red.
Result: 20B with children 10R and 30R.
Continue the previous tree (20B, 10R, 30R) by inserting 15. Which case applies, and what is the resulting level-order with colors?
Show answer
15 goes in red as the right child of 10. The parent 10 is red and the uncle 30 is also red: the recolor case.
10 and 30 become black, and the grandparent 20 becomes red. Since 20 is the root, it is colored black again at the end. No rotation.
The black height increased by one: 20B 10B 30B 15R.
Insert 1, 2, 3, 4, 5, 6, 7, 8 into an empty red-black tree. Give the final level-order traversal with colors.
Show answer
+3: RR, a left rotation at 1: 2B(1R,3R). +4: uncle 1 is red, recolor: 2B(1B,3B(-,4R)).
+5: uncle is null, RR, left at 3: 2B(1B,4B(3R,5R)). +6: uncle 3 is red, recolor: 4 becomes red, 3 and 5 black.
+7: RR, left at 5: it becomes 6B(5R,7R), to the right of 4R. +8: uncle 5 is red, recolor: 6 becomes red, 5 and 7 black. Now the parent of 6R is 4R: the red-red problem has moved up. The uncle of 4 is 1, black, and the path (from 2) is right-right: a left rotation at 2, 4 becomes black at the root, 2 becomes red.
Result: 4B(2R(1B,3B), 6R(5B,7B(-,8R))).
A red-black tree contains n = 15 keys. Using the bound height <= 2 log2(n + 1), what is the largest height it can have?
Show answer
. A red-black tree can be a little taller than an AVL tree (the AVL bound is about 1.44 log2 n), but it is still O(log n).
Insert 10, 20, 30, 40, 50 into an empty 2-3 tree. Draw the final tree (list each level's nodes).
Show answer
10, 20 go in the same leaf: [10, 20]. When 30 arrives, [10, 20, 30] overflows and the median 20 moves up: root [20], leaves [10] [30].
40: [30, 40]. 50: [30, 40, 50] overflows, 40 moves up and joins the root: [20, 40]. Leaves [10] [30] [50]. The root did not overflow, so the height stays 1.
Insert 50, 30, 70, 10, 40, 60, 80, 20 into an empty 2-3 tree. List the nodes of each level of the final tree.
Show answer
50, 30 → [30, 50]. 70 → overflow, 50 moves up: [50] / [30] [70].
10 → [10, 30]. 40 → [10, 30, 40] overflows, 30 goes to the root: [30, 50] / [10] [40] [70].
60 → [60, 70]. 80 → [60, 70, 80] overflows, 70 moves up to the root: the root [30, 50, 70] overflows too, and 50 becomes the new root. The tree grows by one level: [50] / [30] [70] / [10] [40] [60] [80].
20 → [10, 20], no overflow.
In a B-tree of order m = 5, how many keys and how many children can a non-root internal node have?
Show answer
At most m - 1 = 4 keys and m = 5 children. At least children, that is, 3 - 1 = 2 keys. The root is exempt from this lower bound (at least 1 key).
A database index is stored on disk, where each disk access is very slow compared with a CPU operation. Which tree is the standard choice, and why?
Show answer
In a B-tree a node is the size of one disk block and holds hundreds of keys. Since the branching (m) is large, the height is about : millions of records fit in 3-4 levels. Each level is one disk read, so 3-4 reads. In a binary tree the height would be 20+.
Insert 20, 10, 30, 35, 33 into an empty red-black tree. Which case occurs when 33 is inserted, and what is the final level-order with colors?
Show answer
+35: uncle 10 is red, recolor: 20B(10B, 30B(-,35R)).
+33: red, to the left of 35. The parent 35 is red, the uncle (the left child of 30) is null, so black. The path from the grandparent (30) is right-left: RL.
First a right rotation at 35 (33 moves up), then a left rotation at 30. 33 becomes black, 30 becomes red: 33B(30R,35R).
Insert 8, 18, 5, 15, 17, 25, 40, 80 into an empty red-black tree. Give the final level-order traversal with colors.
Show answer
+15: uncle 5 is red, recolor: 8B(5B,18B(15R)).
+17: to the right of 15. The uncle is null, and the path from the grandparent (18) is left-right: LR. Left at 15, right at 18: 17B(15R,18R).
+25: uncle 15 is red, recolor: 17 becomes red, 15 and 18 black.
+40: parent 25 is red, uncle is null, right-right: a left rotation at 18, 25B(18R,40R).
+80: uncle 18 is red, recolor: 25 becomes red, 18 and 40 black. Now the parent of 25R is 17R. The uncle of 17 is 5, black, and the path from 8 is right-right: a left rotation at 8, 17 becomes black at the root, 8 becomes red.
Result: 17B(8R(5B,15B), 25R(18B, 40B(-,80R))). There are 3 blacks on every path (counting null).
Keys 5, 4, 3, 2, 1 are inserted into an ordinary BST (no balancing), giving a left chain. Then key 1 is accessed in a splay tree (bottom-up splaying). Give the level-order traversal of the tree after the splay.
Show answer
Start: 5 → 4 → 3 → 2 → 1, all left children. The parent of 1 is 2 and the grandparent is 3, in the same direction: zig-zig. First 2 (the parent) is rotated above the grandparent, then 1 above the parent: 1(-, 2(-, 3)).
The new parent of 1 is 4 and the grandparent is 5, again left-left: zig-zig. Result: 1(-, 4(2(-,3), 5)).
Level order 1 4 2 5 3. The chain length dropped from 4 to 3: splay roughly halves the accessed path.
Verification: All red-black insertion traces (colors, rotation and recoloring steps, black-height checks), 2-3 and B-tree (m=5) split traces and splay results were produced with Java (verify/SEN2212/W06.java, java W06.java).