BST Delete Operation in DSA Go - Time & Space Complexity
We want to understand how the time to delete a node in a Binary Search Tree (BST) changes as the tree grows.
Specifically, how does the number of steps needed to find and remove a node grow with the number of nodes?
Analyze the time complexity of the following BST delete function.
func deleteNode(root *Node, key int) *Node {
if root == nil {
return nil
}
if key < root.val {
root.left = deleteNode(root.left, key)
} else if key > root.val {
root.right = deleteNode(root.right, key)
} else {
if root.left == nil {
return root.right
} else if root.right == nil {
return root.left
}
minNode := findMin(root.right)
root.val = minNode.val
root.right = deleteNode(root.right, minNode.val)
}
return root
}
func findMin(node *Node) *Node {
for node.left != nil {
node = node.left
}
return node
}
This code deletes a node with a given key from a BST by searching for it, then handling three cases: no child, one child, or two children.
Look for loops and recursion that repeat work.
- Primary operation: Recursive search down the tree to find the node to delete.
- How many times: At most once per level of the tree, moving down either left or right child.
- Additional operation: Finding the minimum node in the right subtree (findMin) which loops down left children.
As the number of nodes (n) grows, the height of the tree affects how many steps we take.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to 4-5 steps (height of tree) |
| 100 | Up to 7-8 steps |
| 1000 | Up to 10-11 steps |
Pattern observation: The steps grow roughly with the height of the tree, which depends on how balanced it is.
Time Complexity: O(h) where h is the tree height
This means the time to delete depends on how tall the tree is, not just the number of nodes.
[X] Wrong: "Deleting a node always takes O(n) time because we might check every node."
[OK] Correct: We only follow one path down the tree, so we don't visit all nodes, just those along the height.
Understanding how tree height affects delete operation time helps you explain efficiency clearly and shows you know how data structure shape impacts performance.
"What if the BST is always perfectly balanced? How does that change the time complexity of delete?"
Practice
Solution
Step 1: Understand leaf node deletion
A leaf node has no children, so removing it does not affect other nodes.Step 2: Effect on BST structure
Since no children exist, simply remove the node and update its parent's pointer to null.Final Answer:
The node is simply removed without affecting other nodes. -> Option CQuick Check:
Leaf node deletion = simple removal [OK]
- Thinking children need rearrangement
- Assuming root always changes
- Believing tree rebuild is needed
Solution
Step 1: Check parameter order and types
The root node pointer should be first, key integer second, returning updated root pointer.Step 2: Validate pointer usage and key type
Root must be a pointer to allow tree modification; key must be int for integer keys.Final Answer:
func deleteNode(root *Node, key int) *Node -> Option BQuick Check:
Correct signature = root pointer first, int key [OK]
- Swapping parameter order
- Using value instead of pointer for root
- Using wrong key type
root = deleteNode(root, 10)
Solution
Step 1: Identify node to delete and its children
Node 10 has two children: 8 (left) and 12 (right).Step 2: Replace node 10 with its in-order successor
In-order successor is 12. Replace 10 with 12, then delete original 12 node.Step 3: Perform in-order traversal after deletion
Traversal order: 8, 12, 15, 20.Final Answer:
[8, 12, 15, 20] -> Option DQuick Check:
In-order after deleting 10 = [8, 12, 15, 20] [OK]
- Deleting successor node incorrectly
- Skipping replacement step
- Confusing in-order predecessor with successor
func deleteNode(root *Node, key int) *Node {
if root == nil {
return nil
}
if key < root.key {
root.left = deleteNode(root.left, key)
} else if key > root.key {
root.right = deleteNode(root.right, key)
} else {
if root.left == nil {
return root.right
} else if root.right == nil {
return root.left
}
temp := findMin(root.right)
root.key = temp.key
deleteNode(root.right, temp.key)
}
return root
}Solution
Step 1: Analyze deletion of node with two children
After replacing root.key with successor's key, the successor node must be deleted from right subtree.Step 2: Check recursive call effect on tree structure
The call deleteNode(root.right, temp.key) is made but its result is not assigned back to root.right, so the successor node remains.Final Answer:
Missing assignment of root.right after deleting successor node. -> Option AQuick Check:
Assign recursive delete result to root.right [OK]
- Ignoring returned subtree after deletion
- Not updating pointers after recursive calls
- Confusing base case with recursive case
Solution
Step 1: Identify node to delete and its children
Node 30 is root with two children: left subtree (20) and right subtree (40).Step 2: Find in-order successor of node 30
In-order successor is the smallest node in right subtree, which is 35.Step 3: Replace root with in-order successor
Replace 30 with 35, then delete original 35 node from right subtree.Final Answer:
Node 35 (in-order successor) -> Option AQuick Check:
Root replaced by in-order successor = 35 [OK]
- Using in-order predecessor instead of successor
- Choosing direct child without successor check
- Not deleting successor node after replacement
Ready to prove it?
Pass the test and this pattern moves unknown
