Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Red-black trees: audit color and black-height invariants

Last updated: 7 Oct 20269 min read
tutorial
IntermediateBy AITrove Editorial

A red-black tree is an ordered binary search tree with a color bit on each node. The root is black; null leaves count as black; red nodes have black children; and every path from a node to a null leaf has the same number of black nodes. Those invariants bound height by O(log n), giving O(log n) worst-case search, insertion, and deletion when the algorithms maintain them. The executable case here is an invariant checker, not insertion or deletion. A checker can catch a broken state after a rotation or recoloring but cannot repair it. Duplicate-key policy must also be explicit.

Operational case

A scheduler indexes deadlines 47, 52, and 61. Deadline 52 is the black root; 47 and 61 are red children. Both root-to-null paths contain the same number of black nodes. Adding a red node 19 beneath red node 47 makes two red nodes adjacent and fails the audit even though numeric key order still looks correct. This shows why checking only in-order traversal is insufficient. A production insertion routine must handle red-uncle recoloring, inner and outer rotations, and root recoloring, then preserve the black-height rule across the whole changed path.

Working Python program

python
class DeadlineNode:
    def __init__(self, deadline, color, left=None, right=None):
        self.deadline = deadline
        self.color = color
        self.left = left
        self.right = right

def black_height(node, lower, upper):
    if node is None:
        return 1
    assert lower < node.deadline < upper
    assert node.color in ("red", "black")
    if node.color == "red":
        assert node.left is None or node.left.color == "black"
        assert node.right is None or node.right.color == "black"
    left_height = black_height(node.left, lower, node.deadline)
    right_height = black_height(node.right, node.deadline, upper)
    assert left_height == right_height
    return left_height + (node.color == "black")

root = DeadlineNode(52, "black", DeadlineNode(47, "red"), DeadlineNode(61, "red"))
assert root.color == "black"
print(black_height(root, float("-inf"), float("inf")))
root.left.left = DeadlineNode(19, "red")
try:
    black_height(root, float("-inf"), float("inf"))
except AssertionError:
    print("invalid")

Output

Output
2
invalid

Time, space, and tradeoff

The audit visits every node and runs in O(n) time, with O(h) recursion stack for tree height h. That is separate from O(log n) search and update bounds for a correctly maintained red-black tree. The sample uses assertions for a compact teaching check; production validation should raise explicit errors even when Python optimization disables assertions. A checker also needs a root-color test at its public boundary. Never infer that a tree is valid just because local parent-child ordering holds or a single sample insertion worked.

Common Mistakes

  • Do not equate sorted in-order output with valid color invariants.
  • Do not count null leaves inconsistently across paths.
  • Do not present an invariant checker as a complete update implementation.

Connected lessons

Apply it: Project: own a maintenance index and work queue and Index and queue invariants.

data structures
range-query-structures
Storage details