ITC 120 › Midterm › Balanced trees
Balanced trees · Lesson 2 of 2
Red-Black Trees
This lesson teaches:Data structureAlgorithmAnother self-balancing BST. Instead of measuring heights, it paints each node red or black and follows a few color rules.
By the end, you can:
- state the five rules, and say whether a colored tree follows them (and which rule it breaks);
- explain why the rules keep the longest path at most twice as long as the shortest;
- fix a red-red problem after an insertion: check the uncle, then recolor or rotate (line or triangle);
- insert a list of values into a red-black tree by hand, showing every fix;
- match each part of the Java insertion code to a step of the procedure;
- write the
rotateRightmethod for the given code and check it with the built-in tests.
Scope: this lesson covers insertion only. Deleting from a red-black tree uses a different, longer fix with more cases, and it is not covered here.
You need: the AVL trees lesson (especially rotations and the LL, RR, LR, RL shapes), scratch paper, and a pen (a red and a black pen help a lot). For Part 8: a phone or computer with internet.
Part 1 · The idea
Balancing with colors
In the AVL lesson, the tree stayed balanced by checking heights after every insert. A red-black tree solves the same problem in a different way: each node stores just one extra piece of information, its color (red or black).
Red-black tree
A red-black tree is a binary search tree in which every node is colored red or black, and the colors follow five rules. When an insertion breaks a rule, the tree fixes it by recoloring nodes and, if needed, rotating.
A red-black tree. Still a BST: in-order gives 4, 8, 12, 16, 20, 24, 28.
Think of lining up
Imagine a school rule for lining up: the first student in every line wears black, no two "red-shirt" students may stand next to each other, and every line must have the same number of "black-shirt" students. With those rules, no line can get much longer than the others. The shortest a line can be is all black. The longest goes black, red, black, red..., with at most one red after each black, so it is at most twice as long.
Where you see this in IT
Java's built-in TreeMap class is a red-black tree, and TreeSet is built on a TreeMap. You will use them in the Sets and Maps lessons. Every time you add a key, Java does the recoloring and rotations from this lesson for you.
Part 2 · The rules
The five rules
Every red-black tree follows these five rules at all times. Rules 4 and 5 are the important ones; the others set things up.
- Color rule. Every node is either red or black.
- Root rule. The root is black.
- NIL rule. Every empty spot (a
nullchild, called a NIL leaf) counts as black. See "What is NIL?" below. - Red rule. A red node cannot have a red child. No two reds in a row.
- Black rule. From any node, every path down to a NIL leaf passes through the same number of black nodes.
What is NIL?
NIL means "nothing here." It is an empty child spot: a place where a child could be, but there is none. In Java, it is a null link, like node.left == null.
Every node has two child spots, so the Part 1 tree, with 7 nodes, has 14 spots. Real nodes fill 6 of them, so 8 are NIL. You will see all 8 drawn as small black boxes in the picture below.
Rule 3 treats every NIL spot as a tiny black leaf for two reasons. First, every path down then ends at the same kind of thing, so we can count black nodes the same way on every path (rule 5). Second, when a node's uncle is missing, the uncle counts as black (you will need this in Part 3).
Careful with the word "leaf": in earlier lessons, a leaf is a real node with no children, like 4, 20, and 28 here. A NIL leaf is one of the empty spots below a node. In the red-black rules, "leaf" always means NIL leaf.
Seeing rule 5
Here is the same tree with its NIL leaves drawn as small black boxes. Start at the root, 12, and walk down to any box. Count the black circles you pass (do not count the box):
| Path from 12 | Black nodes passed |
|---|---|
| 12 → 8 → 4 → NIL | 12, 8 = 2 |
| 12 → 8 → NIL | 12, 8 = 2 |
| 12 → 24 → 16 → 20 → NIL | 12, 16 = 2 |
| 12 → 24 → 28 → NIL | 12, 28 = 2 |
Every path has 2 black nodes. Red nodes are "free": they do not count. This shared number is called the black-height.
Why the rules keep it balanced
The shortest possible path is all black. The longest possible path alternates black, red, black, red (rule 4 stops two reds in a row). Both have the same number of blacks (rule 5). So the longest path is at most twice as long as the shortest. The tree can lean a little, but it can never become a chain.
Your turn: valid or not?
Check each tree against the rules. Tap your answer to see the reason.
Tree 1
Every rule holds: the root is black, no red node has a red child, and every path down has the same number of black nodes.
Tree 2
The root (20) is red. Rule 2: the root must be black.
Tree 3
30 and its child 25 are both red. Rule 4: a red node cannot have a red child.
Tree 4
Every rule holds: the root is black, no red node has a red child, and every path down has the same number of black nodes.
Tree 5
Below 20, one path has 1 black node and another has 0 (not counting NIL). Rule 5: every path must have the same number of black nodes.
Tree 6
Below 10, one path has 1 black node and another has 0 (not counting NIL). Rule 5: every path must have the same number of black nodes.
Exam traps
- Colors do not have to alternate. Black under black is fine (see Tree 4).
- When counting black nodes for rule 5, count the same way on every path: either always include the starting node or never.
- A missing child is a black NIL leaf. This matters when you look at the uncle.
Part 3 · The fix
Inserting: recolor or rotate
Every insertion starts the same way: insert the value like a normal BST, and color it red.
Why red? A red node does not change any black count, so rule 5 stays safe. The only rule that can break is rule 4: if the new node's parent is also red, we have a red-red problem. (One exception: in an empty tree, the new node is the root, so just color it black for rule 2.)
Meet the family
To fix a red-red problem, we look at the new node's relatives. These are the names we will use:
| Name | Who it is |
|---|---|
| x | the node we are fixing (at first, the new node) |
| parent | x's parent |
| grandparent | the parent's parent |
| uncle | the parent's sibling (the grandparent's other child) |
New node 10 (x), parent 20, grandparent 30, uncle 40.
The decision
The fix depends on two questions. There are three types of situations:
| Situation | What to do |
|---|---|
| Parent is black | Nothing. No rule is broken. |
| Parent red, uncle red | Recolor: parent and uncle → black, grandparent → red. Then check the grandparent (it is red now, so its parent might also be red). If the grandparent is the root, just make it black. |
| Parent red, uncle black (or NIL) | Rotate, using the same shapes as AVL: Line (LL or RR): 1 rotation at the grandparent, then swap the colors of parent and grandparent. Triangle (LR or RL): first rotate at the parent to make a line, then do the line fix. |
How to remember
Red uncle → recolor. Black uncle → rotate.
After rotating, the node on top becomes black and its two children are red. In insertion, after a rotation, you are done.
Watch each situation
Pick one, then tap Next step. Watch the uncle (dashed ring) to decide: recolor or rotate?
Color key
Exam traps
- Look at the uncle, not the sibling of the new node.
- Recoloring can move the problem up the tree. Keep checking until the parent is black or you reach the root.
- Don't forget the last step: the root must end up black.
Part 4 · Putting it together
Inserting, step by step
This is the full procedure. Use it for every value, in order:
- Insert the value like a normal BST and color it red. (If the tree was empty, it is the root: color it black.)
- Check the parent. If it is black, you are done.
- If the parent is red, check the uncle.
- Red uncle: recolor, then repeat step 2 from the grandparent.
- Black uncle: name the shape (line or triangle), rotate, swap colors. Done.
- Finally, make sure the root is black.
Worked example
This list shows all three situations from the table, with both shapes (a triangle at 25 and a line at 60), including a recolor that moves the problem up the tree (watch 60). Turn on NIL leaves to see why an empty uncle counts as black. Then try your own numbers.
Color key
Doing it on paper
- Use two pens, or write R or B beside each node. Circle the red ones.
- Redraw the tree after every rotation. Recolors can be done by crossing out the letter.
- Before fixing, write what you see: for example, "60: parent 70 red, uncle 90 red → recolor".
- At the end, check rules 2, 4, and 5 on your final tree.
Part 5 · Your turn
Try it on paper
Do each problem on paper before opening the answer. Afterwards, type the same numbers into the lab in Part 4 to replay it step by step.
Problem 1
Insert these values, in order, into an empty red-black tree:
21, 9, 15, 5, 30, 26, 23
Show the colors in your final tree. Then write its pre-order traversal.
Show the answer
What happened at each insert (values not listed needed no fix):
- 21: becomes the root, colored black.
- 15: uncle is black, triangle (LR) → 2 rotations + swap colors.
- 5: uncle is red → recolor; then root turned red, so color it black.
- 26: uncle is black, triangle (RL) → 2 rotations + swap colors.
- 23: uncle is red → recolor.
These are the same numbers as Problem 1 in the AVL lesson. Compare the two final trees: same shape here, but now with colors.
| Pre-order | 15, 9, 5, 26, 21, 23, 30 |
|---|---|
| In-order | 5, 9, 15, 21, 23, 26, 30 |
Problem 2
Insert these values, in order, into an empty red-black tree:
40, 30, 50, 20, 25, 10, 5
Show the colors in your final tree. Then write its pre-order traversal.
Show the answer
What happened at each insert (values not listed needed no fix):
- 40: becomes the root, colored black.
- 20: uncle is red → recolor; then root turned red, so color it black.
- 25: uncle is black, triangle (LR) → 2 rotations + swap colors.
- 10: uncle is red → recolor.
- 5: uncle is black, line (LL) → 1 rotation + swap colors.
| Pre-order | 40, 25, 10, 5, 20, 30, 50 |
|---|---|
| In-order | 5, 10, 20, 25, 30, 40, 50 |
Problem 3
Insert these values, in order, into an empty red-black tree:
1, 2, 3, 4, 5, 6, 7, 8
Show the colors in your final tree. Then write its pre-order traversal.
Show the answer
What happened at each insert (values not listed needed no fix):
- 1: becomes the root, colored black.
- 3: uncle is black, line (RR) → 1 rotation + swap colors.
- 4: uncle is red → recolor; then root turned red, so color it black.
- 5: uncle is black, line (RR) → 1 rotation + swap colors.
- 6: uncle is red → recolor.
- 7: uncle is black, line (RR) → 1 rotation + swap colors.
- 8: uncle is red → recolor; then uncle is black, line (RR) → 1 rotation + swap colors.
Sorted input again. In a plain BST this would be a chain of height 7. Here the height is only 3.
| Pre-order | 4, 2, 1, 3, 6, 5, 7, 8 |
|---|---|
| In-order | 1, 2, 3, 4, 5, 6, 7, 8 |
Part 6 · Side by side
AVL vs. red-black
Both are self-balancing binary search trees, and both use the same rotations. They differ in how strict they are. Here are the main differences:
| AVL tree | Red-black tree | |
|---|---|---|
| Extra info per node | height (or balance factor) | one color: red or black |
| Balance promise | left and right heights differ by at most 1 | longest path is at most 2× the shortest |
| Shape | more tightly balanced (shorter) | a little looser (can be a bit taller) |
| Search, insert, delete | about log₂ n steps (see How fast?) | about log₂ n steps (the height can be up to twice as tall as AVL's) |
| How it fixes | rotations | recoloring first; rotations only when the uncle is black |
| Good for | lots of searching, fewer changes | lots of inserts and deletes |
AVL: 1 to 8
Red-black: 1 to 8
Same input, both balanced. They happen to have the same shape here, but that is not always true: for other inputs the red-black tree can be a little taller.
Part 7 · In code
Red-black insertion in Java
The red-black code is longer than AVL's, because each node needs a link back to its parent (to find the grandparent and uncle). Read it alongside the procedure in Part 4 ; you will see each step.
1. The node and a color helper
static final boolean RED = true; // final: never changes
static final boolean BLACK = false; // (a named constant)
static class RBNode { // static: used by static methods
int data;
boolean color;
RBNode left, right;
RBNode parent; // NEW: we need to go back up
RBNode(int value) {
data = value;
color = RED; // new nodes are always red
}
}
static RBNode root = null;
// an empty spot (null) counts as black
static boolean isRed(RBNode node) {
return node != null && node.color == RED;
} All the pieces in this part go inside one class (in Part 8 it is called Main). RBNode is a static class so that the static methods can make nodes with new RBNode(value). Without static, Java stops with "non-static variable this cannot be referenced from a static context".
2. Insert like a BST, then fix
static void insert(int value) {
// Step 1: normal BST insert
RBNode parent = null;
RBNode current = root;
while (current != null) {
parent = current;
if (value < current.data) {
current = current.left;
} else if (value > current.data) {
current = current.right;
} else {
return; // duplicate: do nothing
}
}
RBNode x = new RBNode(value); // red
x.parent = parent;
if (parent == null) {
root = x;
} else if (value < parent.data) {
parent.left = x;
} else {
parent.right = x;
}
fixUp(x); // Step 2: fix the colors
} 3. The fix: recolor or rotate
static void fixUp(RBNode x) {
while (isRed(x.parent)) { // red-red problem
RBNode p = x.parent;
RBNode g = p.parent; // grandparent
if (p == g.left) {
RBNode u = g.right; // uncle
if (isRed(u)) { // uncle red: recolor
p.color = BLACK;
u.color = BLACK;
g.color = RED;
x = g; // check g next
} else { // uncle black: rotate
if (x == p.right) { // triangle (LR)
rotateLeft(p);
x = p;
p = x.parent;
}
rotateRight(g); // line (LL)
p.color = BLACK;
g.color = RED;
}
} else { // mirror image
RBNode u = g.left;
if (isRed(u)) {
p.color = BLACK;
u.color = BLACK;
g.color = RED;
x = g;
} else {
if (x == p.left) { // triangle (RL)
rotateRight(p);
x = p;
p = x.parent;
}
rotateLeft(g); // line (RR)
p.color = BLACK;
g.color = RED;
}
}
}
root.color = BLACK; // rule 2
} Compare with the decision table in Part 3 : while (isRed(x.parent)) is "red-red problem", isRed(u) is "uncle red", and the else part is "uncle black: rotate". The last line is rule 2.
4. Rotation, with parent links
static void rotateLeft(RBNode z) {
RBNode y = z.right; // y moves up
z.right = y.left; // moved child
if (y.left != null) {
y.left.parent = z;
}
y.parent = z.parent; // y takes z's spot
if (z.parent == null) {
root = y;
} else if (z == z.parent.left) {
z.parent.left = y;
} else {
z.parent.right = y;
}
y.left = z; // z goes down, left
z.parent = y;
}
// rotateRight is the mirror image:
// swap every "left" and "right" above.
// Writing it is your coding task (Part 8). This is the same left rotation as in the AVL lesson. The extra lines keep every parent link correct, and update root if the rotation happens at the top.
Part 8 · Your coding task
Write rotateRight and test it
Part 7 showed the whole insertion code except one method: rotateRight. Your task is to write it. Everything else, including the tests, is given to you in one file.
Why this method? Every line fix (LL) and both triangle fixes (LR and RL) use it, so the tests can only pass when it is right. It is also the part that is new compared with AVL: a rotation must keep every parent link correct, and the tests check every link.
1. Prepare on paper
Do these three things on paper first. They make the code much easier to write.
- Insert 30, 20, 10. Before any fix, the tree is a line: 30, then 20 as its left child, then 10 as 20's left child. Draw it, then draw it again after rotate right at 30.
- Go through rotateLeft in Part 7 line by line, and write the mirror of each line: swap every
leftandright. - Trace your lines on your drawing. At the end, check every node: does its
parentpoint to the node just above it? Doesrootpoint to the top node?
2. Run the starter file
Next, run the file once without changing it:
- Open any free online Java compiler, for example Programiz or OneCompiler. It works in a phone browser.
- Tap Copy the whole file, paste it into the compiler (replace the code already there), and tap Run. The class is called
Main, as these sites expect. - You should see 2 of 8 tests passed. The other tests fail with "rotateRight is not written yet". That is expected.
Show the starter file (307 lines)
// ITC 120 · Red-black tree insertion · coding task
// 1. Copy this whole file into an online Java
// compiler. The class must be called Main.
// 2. Press Run once first. Some tests FAIL: that
// is expected, because rotateRight is missing.
// 3. Find "YOUR TASK", write rotateRight, and run
// again until every test says PASS.
public class Main {
static final boolean RED = true; // final: never changes
static final boolean BLACK = false; // (a named constant)
static class RBNode { // static: used by static methods
int data;
boolean color;
RBNode left, right;
RBNode parent; // NEW: we need to go back up
RBNode(int value) {
data = value;
color = RED; // new nodes are always red
}
}
static RBNode root = null;
// an empty spot (null) counts as black
static boolean isRed(RBNode node) {
return node != null && node.color == RED;
}
static void insert(int value) {
// Step 1: normal BST insert
RBNode parent = null;
RBNode current = root;
while (current != null) {
parent = current;
if (value < current.data) {
current = current.left;
} else if (value > current.data) {
current = current.right;
} else {
return; // duplicate: do nothing
}
}
RBNode x = new RBNode(value); // red
x.parent = parent;
if (parent == null) {
root = x;
} else if (value < parent.data) {
parent.left = x;
} else {
parent.right = x;
}
fixUp(x); // Step 2: fix the colors
}
static void fixUp(RBNode x) {
while (isRed(x.parent)) { // red-red problem
RBNode p = x.parent;
RBNode g = p.parent; // grandparent
if (p == g.left) {
RBNode u = g.right; // uncle
if (isRed(u)) { // uncle red: recolor
p.color = BLACK;
u.color = BLACK;
g.color = RED;
x = g; // check g next
} else { // uncle black: rotate
if (x == p.right) { // triangle (LR)
rotateLeft(p);
x = p;
p = x.parent;
}
rotateRight(g); // line (LL)
p.color = BLACK;
g.color = RED;
}
} else { // mirror image
RBNode u = g.left;
if (isRed(u)) {
p.color = BLACK;
u.color = BLACK;
g.color = RED;
x = g;
} else {
if (x == p.left) { // triangle (RL)
rotateRight(p);
x = p;
p = x.parent;
}
rotateLeft(g); // line (RR)
p.color = BLACK;
g.color = RED;
}
}
}
root.color = BLACK; // rule 2
}
static void rotateLeft(RBNode z) {
RBNode y = z.right; // y moves up
z.right = y.left; // moved child
if (y.left != null) {
y.left.parent = z;
}
y.parent = z.parent; // y takes z's spot
if (z.parent == null) {
root = y;
} else if (z == z.parent.left) {
z.parent.left = y;
} else {
z.parent.right = y;
}
y.left = z; // z goes down, left
z.parent = y;
}
// rotateRight is the mirror image:
// swap every "left" and "right" above.
// Writing it is your coding task (Part 8).
// ===== YOUR TASK: rotateRight =====
// The mirror image of rotateLeft above.
// Delete the "throw" line, then write the code.
static void rotateRight(RBNode z) {
throw new UnsupportedOperationException(
"rotateRight is not written yet");
}
// ===== Tests: no need to change below =====
static int passed = 0, total = 0;
static boolean stopped = false;
// Tree as text: 20B(10R,30R) means 20 is black,
// its left child is 10 (red), its right is 30 (red).
// "-" is an empty spot.
static String text(RBNode n) {
if (n == null) return "-";
String s = n.data + (isRed(n) ? "R" : "B");
if (n.left == null && n.right == null) return s;
return s + "(" + text(n.left) + ","
+ text(n.right) + ")";
}
// Checks rules 2, 4, 5 and every parent link.
// Returns null if all is well, or what is wrong.
static String why;
static String problems() {
why = null;
if (isRed(root)) return "the root is red";
blacks(root, null);
return why;
}
static int blacks(RBNode n, RBNode parent) {
if (n == null) return 1; // NIL is black
if (n.parent != parent && why == null)
why = "wrong parent link at " + n.data;
if (isRed(n) && isRed(parent) && why == null)
why = "red-red at " + parent.data
+ " and " + n.data;
int a = blacks(n.left, n);
int b = blacks(n.right, n);
if (a != b && why == null)
why = "black counts differ below " + n.data;
return a + (isRed(n) ? 0 : 1);
}
// Inserts the values one by one and checks the
// tree after each one. Stops after 2 seconds, in
// case of an endless loop. Returns null if all
// went well, or what went wrong.
static String tryInsert(int[] values) {
root = null;
String[] error = { null };
Thread t = new Thread(() -> {
try {
for (int v : values) {
insert(v);
String p = problems();
if (p != null) {
error[0] = p + " (right after inserting "
+ v + ")";
return;
}
}
} catch (StackOverflowError e) {
error[0] = "the links form a loop (a node"
+ " points back up to its ancestor)";
} catch (Throwable e) {
error[0] = e.toString();
}
});
t.setDaemon(true);
t.start();
try {
t.join(2000);
} catch (InterruptedException e) { }
if (t.isAlive()) {
stopped = true;
return "still running after 2 seconds"
+ " (endless loop?). Tests stopped.";
}
return error[0];
}
static void test(String name, int[] values,
String expected) {
if (stopped) return;
total++;
String error = tryInsert(values);
String got = error == null ? text(root) : "";
String bad = error != null ? error
: !got.equals(expected) ? "wrong tree" : null;
if (bad == null) {
passed++;
System.out.println("PASS " + name);
} else {
System.out.println("FAIL " + name + ": " + bad);
if (error == null) {
System.out.println(" expected " + expected);
System.out.println(" got " + got);
}
}
}
// 300 random lists, checked against the rules
// and against Java's own TreeSet.
static void randomTest() {
if (stopped) return;
total++;
java.util.Random r = new java.util.Random(120);
for (int t = 0; t < 300; t++) {
int[] values = new int[r.nextInt(30)];
java.util.TreeSet<Integer> set =
new java.util.TreeSet<>();
for (int i = 0; i < values.length; i++) {
values[i] = r.nextInt(60);
set.add(values[i]);
}
String bad = tryInsert(values);
if (bad == null) {
StringBuilder got = new StringBuilder();
inOrder(root, got);
StringBuilder want = new StringBuilder();
for (int v : set) want.append(v).append(',');
if (!got.toString().equals(want.toString()))
bad = "in-order is not sorted, or values"
+ " are missing";
}
if (bad != null) {
System.out.println("FAIL 300 random lists: "
+ bad);
System.out.println(" list: "
+ java.util.Arrays.toString(values));
return;
}
}
passed++;
System.out.println("PASS 300 random lists");
}
static void inOrder(RBNode n, StringBuilder sb) {
if (n == null) return;
inOrder(n.left, sb);
sb.append(n.data).append(',');
inOrder(n.right, sb);
}
// Prints the tree for your own numbers.
static void show(int[] values) {
String error = tryInsert(values);
System.out.println();
System.out.println("Your numbers: "
+ java.util.Arrays.toString(values));
if (error != null) {
System.out.println("Error: " + error);
return;
}
System.out.println("Tree: " + text(root));
System.out.println("All rules hold.");
}
public static void main(String[] args) {
test("Uncle red: recolor (20, 10, 30, 5)",
new int[] {20, 10, 30, 5}, "20B(10B(5R,-),30B)");
test("Line RR (10, 20, 30)",
new int[] {10, 20, 30}, "20B(10R,30R)");
test("Line LL (30, 20, 10)",
new int[] {30, 20, 10}, "20B(10R,30R)");
test("Triangle LR (30, 10, 20)",
new int[] {30, 10, 20}, "20B(10R,30R)");
test("Triangle RL (10, 30, 20)",
new int[] {10, 30, 20}, "20B(10R,30R)");
test("Worked example (Part 4)",
new int[] {5, 45, 25, 10, 35, 75, 70, 90, 60},
"45B(25R(5B(-,10R),35B),75R(70B(60R,-),90B))");
test("Problem 2 (Part 5)",
new int[] {40, 30, 50, 20, 25, 10, 5},
"40B(25R(10B(5R,20R),30B),50B)");
randomTest();
System.out.println();
System.out.println(passed + " of " + total
+ " tests passed.");
// Try your own numbers: change the list below.
show(new int[] {50, 40, 30, 20, 10});
}
}
3. Write rotateRight and test it
Now write the method:
- Find
YOUR TASKin the file. Delete thethrowstatement (both of its lines), then type your method. - Tap Run. Fix and run again until you see 8 of 8 tests passed.
- Then change the numbers in the last line of
mainto one of the Part 5 problems , and compare the printed tree with your paper answer.
The tests print a tree as text: 20B(10R,30R) means 20 is black, with 10 (red) on its left and 30 (red) on its right; a - is an empty spot. This table explains each kind of message:
| Message | What it means |
|---|---|
PASS | The tree is exactly right, and every rule and every parent link was correct after each insert. |
wrong tree | The shape or the colors are wrong. Compare the expected and got lines. |
wrong parent link at N | N's parent points to the wrong node. Check every line that sets a .parent. |
NullPointerException | The code followed an empty (null) link. Often root or a parent's child link was not updated. |
links form a loop or still running | Some node points back up to a node above it. Check which child (left or right) each line sets. |
Don't stop at 7 of 8
The first seven tests use small trees. Some mistakes only show up in bigger trees, when the node that moves up has a child that changes parent. That is what the 300 random lists are for.
No phone or data right now?
Do step 1, write your method on paper, and trace it on this tree: rotate right at 50. Here 30 moves up, and 40 changes parent.
This is good preparation, but it is only a temporary fallback: a paper trace shows you understand the steps; it does not show that your code compiles and passes the tests. Run it as soon as you can, for example in a campus computer lab. Your teacher will tell you how to submit this task.
What this task covers, and what it doesn't
You write one important method and test it inside a full, working insertion. You do not write the whole insertion (insert and fixUp) from scratch, and deletion is not part of this lesson.
Part 9 · Check yourself
Self-check
Not graded. Tap an answer to see if you got it, and read why.
-
What color is every newly inserted node (before any fixing)?
New nodes are always red. Adding a red node never changes the black counts (rule 5), so the only possible problem is red-red (rule 4), or a red root if the tree was empty.
-
Which of these is not a red-black tree rule?
Colors do not have to alternate. Two black nodes in a row are fine; even an all-black tree is valid if its black counts match. Only two reds in a row are forbidden.
-
You insert a node. Its parent is red and its uncle is red. What do you do?
Red uncle → recolor only, no rotation. Then check the grandparent, because it just turned red and may now clash with its own parent.
-
Parent is red, uncle is black (or NIL), and the new node forms a straight line with its parent and grandparent (left-left). What is the fix?
A line (LL) gets one right rotation at the grandparent, just like AVL. Then the parent (now on top) becomes black and the old grandparent becomes red.
-
Insert 10, 30, 20 into an empty red-black tree. Which shape appears when 20 is inserted?
From 10 the path goes right (30), then left (20): a triangle, RL. The uncle is NIL (black), so: right rotation at 30, then left rotation at 10, then swap colors. Result: 20 black on top, 10 and 30 red.
-
An empty child (NIL) counts as what color?
Every NIL leaf is black (rule 3). That is why an empty uncle means "uncle is black → rotate".
-
After recoloring, the root has become red. What do you do?
Rule 2: the root is always black. Turning the root black never breaks any other rule (it adds one black to every path equally).
-
Compared with an AVL tree, a red-black tree is:
AVL keeps heights within 1, so it is usually shorter. Red-black only promises the longest path is at most twice the shortest, so it rotates less often. Both are BSTs.
Score: 0 / 8
Key takeaways
- A red-black tree is a BST where every node is red or black.
- Root is black. NIL leaves are black. No two reds in a row. Same number of blacks on every path down.
- New nodes are red. The only possible problem is red-red (or a red root in an empty tree).
- Red uncle → recolor (and check higher). Black uncle → rotate (line: 1, triangle: 2) and swap colors.
- The root always ends up black.
- In code, a rotation must also fix every
parentlink. - Red-black trees are a little less balanced than AVL trees but rotate less often. Java's
TreeMapuses one.
You've finished the midterm topics. Next: Graphs, and how to store them, the start of the final topics.
References
- T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed. MIT Press, 2022, Chapter 13 (Red-Black Trees).
- L. J. Guibas and R. Sedgewick, "A dichromatic framework for balanced trees," in Proc. 19th Annual Symposium on Foundations of Computer Science, 1978, pp. 8–21.
- Programiz, "Red-Black Tree" and "Insertion in a Red-Black Tree." programiz.com/dsa/red-black-tree
- Oracle, "TreeMap (Java SE 21 & JDK 21)," Java API documentation. docs.oracle.com