DSA DSA In Depth
0%
lesson bsthibbard-deletiontrees

Binary Search Trees: Search, Insertion, Successor & Hibbard Deletion Asymmetry

7 min read Module 3.1: Binary Trees, Traversals, BSTs & Priority Heaps Updated Oct 23, 2026
📄 Cheat Sheet
Delete a node from Binary Search Tree (Hibbard Deletion)
mycodeschool Delete a node from Binary Search Tree (Hibbard Deletion) Watch on YouTube ↗

Clear breakdown of the 0-child, 1-child, and 2-child Hibbard replacement cases and why the in-order successor has no left child.

0:00 / --:--

The Global BST Invariant, Successor Geometry & Hibbard's Three Deletion Cases

Notice a critical distinction between the BST property and the Heap property: the BST property is a global subtree invariant, not merely a local parent-child condition. A node $y$ inside $x$'s left-right grandchild slot must satisfy $x.\text{left}.\text{key} < y.\text{key} < x.\text{key}$; merely checking parent vs. child locally fails to validate a BST.

Finding the In-Order Successor of a node $x$ splits into two structural cases in $O(h)$ time:

  1. $x.\text{right} \ne \text{null}$: The successor is $\text{minimum}(x.\text{right})$, found by walking left pointers from $x.\text{right}$ until left == null.Crucially, this successor node $y$ is guaranteed to have $y.\text{left} == \text{null}$.
  2. $x.\text{right} == \text{null}$: The successor is the lowest ancestor of $x$ whose left subtree contains $x$ (found by walking up parent pointers while coming from a right child, or tracking the last left-turn ancestor from the root).

Hibbard Deletion (treeDelete(T, z)): To delete node $z$, CLRS uses a helper transplant(T, u, v) that replaces subtree $u$ with subtree $v$ in $O(1)$ pointer updates:

  • Case 1 ($z.\text{left} == \text{null}$): Replace $z$ directly with $z.\text{right}$ (covers both 0-child leaf and right-only child).
  • Case 2 ($z.\text{right} == \text{null}$): Replace $z$ directly with $z.\text{left}$.
  • Case 3 ($z$ has two children): Find $z$'s successor $y = \text{minimum}(z.\text{right})$. Because $y$ is the minimum of $z.\text{right}$, $y$ has no left child ($y.\text{left} == \text{null}$). If $y$ is not $z$'s immediate right child ($y.\text{parent} \ne z$), first transplant $y$ with $y.\text{right}$ and attach $z.\text{right}$ to $y.\text{right}$. Finally, transplant $z$ with $y$ and attach $z.\text{left}$ to $y.\text{left}$.
BST Operation Balanced Tree ($h = \Theta(\log N)$) After $O(N^2)$ Asymmetric Hibbard Deletes Adversarial Sorted Input
search(k) / insert(k) O(log N) Θ(√N) Expected O(N)
successor(x) / predecessor(x) O(log N) Worst / O(1) Amortized Θ(√N) Expected O(N)
delete(z) (Hibbard / Symmetric) O(log N) Θ(√N) (Hibbard) O(N)

Eppinger / Culberson–Munro Theorem: Why Asymmetric Hibbard Deletion Degrades to $\Theta(\sqrt{N})$

Hibbard (1962) proved that deleting a random element from a randomly built BST yields a tree whose key permutations look random, leading computer scientists to assume for 13 years that expected depth remained $O(\log N)$. In 1975, Jonassen and Knuth proved via exact Markov chain analysis on $N=3$ that alternating random insertions and Hibbard deletions breaks the random BST distribution.

Why? In Case 3, unconditionally pulling the replacement node $y$ from the right subtree ($y = \min(z.\text{right})$) systematically empties right subtrees and lengthens left subtrees. Culberson and Munro (1989) proved that after $\Omega(N^2)$ random insert/delete pairs, the expected internal path length degrades from $1.386 N \log_2 N$ to $\Theta(N^{3/2})$, meaning expected search depth becomes $\Theta(\sqrt{N})$. Randomly alternating between the in-order successor and in-order predecessor restores empirical $O(\log N)$ behavior, though a formal proof of symmetric randomized Hibbard deletion remains one of the longest-standing open problems in theoretical computer science!

Visualizing Case 3 Hibbard Transplant ($y.\text{parent} \ne z$)

1. Before Delete(z=12): y = min(z.right) = 15 (note y.left == null) z:12 l:6 r:20 y:15 x:17 2. After Transplant(y, x) & Transplant(z, y): y:15 l:6 r:20 x:17
flowchart TD Del["delete(T, z)"] --> C1{"z.left == null ?"} C1 -- "Yes" --> T1["transplant(T, z, z.right)"] C1 -- "No" --> C2{"z.right == null ?"} C2 -- "Yes" --> T2["transplant(T, z, z.left)"] C2 -- "No (2 Children)" --> FindY["y = minimum(z.right) (Invariant: y.left == null)"] FindY --> CheckParent{"y.parent != z ?"} CheckParent -- "Yes" --> DetachY["transplant(T, y, y.right) y.right = z.right y.right.parent = y"] CheckParent -- "No" --> ReplaceZ["transplant(T, z, y) y.left = z.left y.left.parent = y"] DetachY --> ReplaceZ
BST Hibbard Successor Deletion, Tree Diameter & QuickSort Duality
Expected Height ≈ 4.311 ln N · O(H) Delete

Step through Hibbard’s 3-case BST deletion (splicing out the in-order successor y = min(z.right) when deleting a 2-child node z), or inspect the exact bijection between Random BST depth and QuickSort pivot comparisons.

Step 1 / 8
Custom Input:
Timeline

Invariant & State

Delete z = 20 (Case 3: Two Children): Locate in-order successor y = min(z.right) = 25. By definition, y has NO left child (y.left == null)!
z=20 10 30 y=25 35
Hibbard Step
2 Children → Find min(z.right)
Successor Node y
y = 25 (y.left == null)
In-Order Sequence
[10, 20, 25, 30, 35]
Asymmetry Note
Repeated delete skews height → Θ(√N)
[Step 1] Delete z = 20 (Case 3: Two Children): Locate in-order successor y = min(z.right) = 25. By definition, y has NO left child (y.left == null)!

Hardware Reality: Pointer Transplant vs Key Copy

Why does CLRS implement transplant by rewiring pointers to physically move node $y$ into $z$'s position rather than simply copying z->key = y->key and deleting $y$? In production C++ containers like std::map and intrusive kernel trees, iterator and pointer stability is part of the API contract: external callers hold direct raw pointers or iterators to node $y$, and mutating z->key while freeing node $y$ would invalidate every external pointer to $y$!

Implementation: Pointer-Stable CLRS Transplant & Symmetric Hibbard Delete

// C++20: Pointer-Stable CLRS BST Transplant & Deletion (Zero Iterator Invalidation)
struct BSTNode {
    int key;
    BSTNode *left = nullptr, *right = nullptr, *parent = nullptr;
};

void transplant(BSTNode*& root, BSTNode* u, BSTNode* v) {
    if (u->parent == nullptr) {
        root = v;
    } else if (u == u->parent->left) {
        u->parent->left = v;
    } else {
        u->parent->right = v;
    }
    if (v != nullptr) {
        v->parent = u->parent;
    }
}

BSTNode* treeMinimum(BSTNode* x) {
    while (x != nullptr && x->left != nullptr) x = x->left;
    return x;
}

void treeDelete(BSTNode*& root, BSTNode* z) {
    if (z->left == nullptr) {
        transplant(root, z, z->right);
    } else if (z->right == nullptr) {
        transplant(root, z, z->left);
    } else {
        BSTNode* y = treeMinimum(z->right); // Guaranteed: y->left == nullptr
        if (y->parent != z) {
            transplant(root, y, y->right);
            y->right = z->right;
            y->right->parent = y;
        }
        transplant(root, z, y);
        y->left = z->left;
        y->left->parent = y;
    }
    delete z;
}

Failure Modes & Adversarial Pitfalls

  • Local-Only BST Validation Bug: Writing isValidBST(u) by checking only u->left->key < u->key && u->right->key > u->key is broken! A root 10 with left child 5 whose right child is 12 passes local parent-child checks everywhere while violating the global BST invariant ($12 > 10$ inside $10$'s left subtree). Always propagate open range bounds (minKey, maxKey) down the recursion.
  • Unbalanced BST Degeneration on Pre-Sorted Streams: Inserting chronological timestamps or auto-incrementing database IDs into an unbalanced BST produces a right-leaning linked list of height $h = N - 1$, turning $O(\log N)$ lookups into $O(N)$ scans.
Check yourself Question 1 of 4
Concept Check Structural Guarantee of the In-Order Successor in Two-Child Deletion recall

In Hibbard deletion of a BST node `z` that has two non-null children, we locate `z`'s in-order successor `y = minimum(z.right)`. Which structural property is guaranteed to hold for node `y`?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

What happens to the expected height and search cost of an initially randomly built N-node BST if it is subjected to Ω(N²) random insertions and standard asymmetric Hibbard deletions (always replacing a two-child node with its in-order successor)?

Key Takeaways

The BST invariant is global across entire subtrees; validating a BST requires propagating (minBound, maxBound) intervals rather than checking only immediate children.
When deleting a node z with two children, its in-order successor y = minimum(z.right) is guaranteed to have y.left == null, allowing y to be spliced out via a single right-child transplant.
Asymmetric Hibbard deletion (always choosing the successor) degrades expected tree height from O(log N) to Θ(√N) after Ω(N²) random insert/delete pairs (Culberson & Munro, 1989).
Calling successor(x) N - 1 times starting from minimum(T) runs in O(N) total time (O(1) amortized per step) because every tree edge is traversed at most twice.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue