3.4.4 · D2Trees

Visual walkthrough — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

2,217 words10 min readBack to topic

Step 0 — The one rule everything hangs on

Before we delete anything, let us lock in the picture we will keep pointing back to.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Look at the picture. The blue node is . Every number in the orange (left) blob is below ; every number in the green (right) blob is above . That is the invariant we must never break, no matter what we delete. Deleting is only "hard" when it threatens this rule.

Let us build our running tree once and reuse it:

Read this as: is the root; sits left of it, right of it; then hang under and hangs under . In-order walk (left → node → right) gives — sorted, so it is a valid BST.


Step 1 — Finding the victim (every delete starts here)

WHAT. To delete key , first walk down to it using the exact same rule as search: if go left, if go right, if you found it.

WHY. We cannot fix a node we have not located. And the walk is free information: it also tells us who the parent is, which we will need to re-wire pointers.

PICTURE. Below, the red path is the walk for deleting : . Each step throws away one whole subtree (the greyed-out blobs) because the rule promises cannot be there.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Once we arrive at the victim node, how many children it has decides everything. There are exactly three possibilities: 0, 1, or 2. That is where the "three cases" come from — nothing more mysterious than counting arrows.


Step 2 — Case 1: the leaf (0 children)

WHAT. The victim has no children (both arrows point at None). Just cut it off: set its parent's arrow to None.

WHY. A leaf holds no subtree, so removing it orphans nobody. Nothing below it needs re-parenting, so the invariant cannot break.

PICTURE. We delete (a leaf under ). The red removes it; 's left arrow simply becomes None.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Step 3 — Case 2: one child (splice it out)

WHAT. The victim has exactly one child (one arrow to a real node, the other to None). Delete the victim by hooking its single child directly to its parent — the child "moves up" one level.

WHY. Here is the key insight: that lone child subtree is already on the correct side of every ancestor. The victim was just a pass-through box. Removing the box and joining the pipe keeps every ancestor's left/right relationship intact.

PICTURE. Suppose has only a left child (imagine 's right arrow is None). Delete : the orange arrow that used to be "" now becomes "". The child was already , so 's right side stays valid.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Step 4 — Case 3, the problem: two children

WHAT. The victim has both a left and a right child (two real subtrees). We cannot splice — there are two pipes, not one, and the parent has only one arrow to give.

WHY it's hard. Look at the picture: if we naively delete , both the orange blob (all ) and the green blob (all ) are orphaned. Whoever takes 's spot must be a single key that keeps both blobs valid at once.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Step 5 — Case 3, the fix: the in-order successor

WHAT. Copy the successor's key up into the victim's box. Then delete the successor from the right subtree.

WHY it's the perfect replacement. The successor is bigger than every left-blob key (it lives in the right subtree, which is all ). And it is the smallest right-blob key, so it is smaller than everything else remaining on the right. It fits the razor-thin slot exactly — both subtrees stay legal.

PICTURE. For deleting : go right to , then left to (leftmost). is the successor. Copy into the root box; the root now reads . Then delete the old node — which is a leaf, so we are back to Case 1.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

Step 6 — The degenerate & edge cases (never leave a gap)

Every real implementation must survive the weird inputs. Here they are.

Deleting a key that isn't there. The walk hits None and returns None up the chain — the tree is unchanged. No crash, no-op.

Deleting the root when it's the only node. Root is a leaf → Case 1 → the whole tree becomes None (empty tree). Callers must accept "empty" as a valid result.

The two "no left child" shortcut. In code, if root.left is None: return root.right cleanly handles both the leaf case (right is also None, so it returns None) and the one-right-child case — one line, two cases. Symmetrically if root.right is None: return root.left.

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

The one-picture summary

Figure — Binary Search Tree (BST) — BST property, insert, search, delete (3 cases)

This single diagram is the whole delete algorithm: locate the victim, count its children, and branch into 0 / 1 / 2. The two-child branch reduces to a smaller delete via the successor, so ultimately every delete bottoms out at a leaf or one-child splice.

zero

one

two

Find node with key v

How many children

Case 1 remove leaf

Case 2 splice child to parent

Case 3 copy successor key up

Delete successor in right subtree

Recall Feynman retelling — say it in plain words

To delete a number from a BST, first walk down to it the same way you'd search, comparing at each step and dropping half the tree. When you arrive, count its children. No children? Snip it — it was a dead end, nothing below it to save. One child? That child was already sitting on the correct side of everyone above, so just pull it up into the gap; the pipe reconnects cleanly. Two children? You can't pick a side, so instead of moving nodes you swap a key in: grab the smallest number in the right subtree (walk right once, then left until you can't) — it's the one number that's bigger than the whole left half and smaller than the rest of the right half, so it fits perfectly. Paste that number into the spot, then delete its original node — which, being the leftmost of a subtree, has no left child, so it's an easy Case 1 or 2. Weird inputs: deleting a missing key does nothing; deleting the last node leaves an empty tree. Every path ends at a simple snip.

Recall Predict before peeking

Why are there exactly three cases, and not four? ::: Because a binary node has at most 0, 1, or 2 children — you're just counting arrows. Two children is the only case that can't splice. Why must the replacement in Case 3 be the smallest key on the right (not any right key)? ::: It must sit just above the whole left subtree and below the rest of the right subtree; only the minimum of the right subtree squeezes into that gap. Why can deleting the successor never trigger another two-child case? ::: The successor is the leftmost node of the right subtree, so it has no left child — at most one child, i.e. Case 1 or 2. What does return root.right accomplish when root.left is None? ::: It handles leaf (returns None) and single-right-child (returns that child) in one line.


See also: Binary Tree — terminology & traversals, Recursion on Trees, Binary Search on Sorted Array, Heap vs BST.