Binary Search Trees · 2 of 22
Deletion of a node in a BST
Take one number out of a sorted tree and patch the hole so every spot still has smaller numbers on the left and bigger ones on the right.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(h)
room O(h)
The problem, simply
You get a binary search tree (BST): at every spot, the left side holds smaller numbers and the right side holds bigger numbers. You also get a key.
Remove the spot that holds the key. The tree must still follow the rule afterwards. Give back the top of the tree, because the top itself might be the one that got removed. If the key is not in the tree, give the tree back unchanged.
A story to remember it
Picture it
A BST with 10 numbers. 45 is a leaf (no children). 60 has one child, 65. 30 has two children, 20 and 40. Those are the three cases.
- checking
Examples
Remove a leaf
- In
tree above, key = 45- Out
same tree without 45
45 holds nobody up. Cut the branch from 40 to 45 and you are done.
Remove a spot with one child
- In
tree above, key = 60- Out
65 takes the place of 60
60 has only a right child, 65. Hook 65 straight to 70. Everything under 65 is still between 50 and 70, so the rule holds.
Remove a spot with two children
- In
[50, 30, 70, 20, 40, 65, 80, null, null, 35], key = 30- Out
[50, 35, 70, 20, 40, 65, 80]
The lightest number on the right side of 30 is 35. Write 35 in place of 30, then remove the old 35, which is a leaf.
Remove the top
- In
[5, 3, 6], key = 5- Out
[6, 3]
The top has two children. Its right side's smallest number is 6. 6 moves up and becomes the new top. This is why we return the top.
Key not in the tree
- In
[5, 3, 6], key = 4- Out
[5, 3, 6]
We walk 5, then 3, then fall off. Nothing to remove.
Way 1: the simple way
Pour every number out in sorted order, skip the key, and build a brand new tree from the sorted list.
- Walk the tree left, then me, then right. This gives the numbers in sorted order. Skip the key.
- Build a new tree from the sorted list: the middle number is the top, the left half builds the left side, the right half builds the right side.
import java.util.*;
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
List<Integer> sorted = new ArrayList<>();
collect(root, key, sorted);
return build(sorted, 0, sorted.size() - 1);
}
// left, me, right gives sorted order; leave out the key
private void collect(TreeNode node, int key, List<Integer> out) {
if (node == null) return;
collect(node.left, key, out);
if (node.val != key) out.add(node.val);
collect(node.right, key, out);
}
// the middle number becomes the top
private TreeNode build(List<Integer> a, int lo, int hi) {
if (lo > hi) return null;
int mid = (lo + hi) / 2;
TreeNode node = new TreeNode(a.get(mid));
node.left = build(a, lo, mid - 1);
node.right = build(a, mid + 1, hi);
return node;
}
}How long it takes: we touch every number twice, O(n). Extra room: the list of all numbers, O(n). It also gives back a different shape than the original tree, which many interviewers do not accept.
Way 2: the clever way
Find the key the BST way (smaller goes left, bigger goes right). When you find it, there are three cases.
- No children: replace the spot with
null. - One child: replace the spot with that child.
- Two children: find the smallest number on the right side (go right once, then left as far as you can). That number is called the inorder successor. Copy it into the spot, then delete it from the right side. It has no left child, so that second delete is case 1 or case 2.
The function returns the new top of whatever part of the tree it was given, and each parent hooks that result back in. That is how a removed child gets replaced without any special parent tracking.
Three removals: a leaf, a spot with one child, a spot with two
01 / 10
Remove 45. 45 is smaller than 50, so go left.
- key
- 45
- looking now
- checking
- seen
- done
- answer
- rejected
- path
- just added
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null; // key not found
if (key < root.val) {
root.left = deleteNode(root.left, key); // fix the left side, hook it back
} else if (key > root.val) {
root.right = deleteNode(root.right, key); // fix the right side, hook it back
} else {
if (root.left == null) return root.right; // no child, or only a right child
if (root.right == null) return root.left; // only a left child
TreeNode succ = root.right; // two children:
while (succ.left != null) succ = succ.left; // smallest on the right side
root.val = succ.val; // copy it up
root.right = deleteNode(root.right, succ.val); // remove the old copy
}
return root;
}
}How long it takes: one walk down to the key, then one walk down to the successor, both no longer than the height, O(h). Extra room: the recursion is O(h) deep. It is O(log n) for a balanced tree and O(n) for a stick.
Watch out for
- The leaf case is already covered by
if (root.left == null) return root.right;, sinceroot.rightisnulltoo. - Removing the top. The function returns the new top, so the caller must use the return value.
- Copying the successor value and then deleting
keyagain from the right side. You must deletesucc.val, notkey. - Some interviewers want real nodes moved, not values copied (for example when a node carries other data). Then you unhook the successor and relink its children instead of copying
val.
What interviewers ask next
- Can you use the predecessor instead? Yes. Take the biggest number on the left side (go left once, then right as far as you can). Both keep the rule. Alternating between them keeps the tree a little more even.
- Do it without recursion. Walk down with
parentandcurr. When found, compute the replacement subtree and hook it toparent.leftorparent.right, or make it the new root ifparentisnull. That usesO(1)extra room. - What if many deletes make the tree lopsided? Use a self balancing tree (AVL or red black) that rotates after each delete to keep the height near
log n. - Delete every node whose value is outside a range
[lo, hi]. If a node is too small, return the trimmed right side. If too big, return the trimmed left side. Otherwise trim both children.O(n).
Remember this
Grown-up words: the pyramid is a BST, a kid with nobody on top is a leaf node, sliding the only child down is splicing, the lightest kid on the right is the inorder successor, and returning the new top so the parent can hook it back is the "return the new subtree root" recursion pattern.