Pattern #38
Binary Search Trees
Important interview questions, thinking patterns, Inorder = Sorted rule, BST Search & Pruning, 3 cases of Deletion, and Go templates.
Must Solve
16 core questions — solve these first.
- 1.Search in a Binary Search TreeIterative loop: target < val -> go Left, target > val -> go Right in O(H) timeeasy
- 2.Validate Binary Search TreeCarry ancestor bounds: minVal < node.Val < maxVal down the recursionmedium
- 3.Kth Smallest Element in a BSTInorder traversal yields sorted values: return Kth visited nodemedium
- 4.Lowest Common Ancestor of a BSTIf p & q < node -> go Left; if both > node -> go Right; else current node is split point LCA!medium
- 5.Insert into a Binary Search TreeSearch for target position until nil, then attach new nodemedium
- 6.Delete Node in a BST3 cases: Leaf (remove), 1 child (replace), 2 children (replace with Inorder Successor = min of right subtree)medium
- 7.Convert Sorted Array to Binary Search TreeChoose middle element as root recursively: nums[mid] is rooteasy
- 8.Minimum Absolute Difference in BSTInorder traversal yields sorted values: track min diff between adjacent elementseasy
- 9.Two Sum IV — Input is a BSTInorder to sorted array + Two Pointers, or HashSet during DFSeasy
- 10.Range Sum of BSTPrune search: skip Left if node.Val < low, skip Right if node.Val > higheasy
- 11.BST IteratorControlled Inorder DFS using stack: push all left nodes initiallymedium
- 12.Recover Binary Search TreeInorder DFS detecting swapped adjacent / non-adjacent nodesmedium
- 13.Trim a Binary Search TreeIf node.Val < low return trim(Right); if node.Val > high return trim(Left)medium
- 14.Balance a Binary Search TreeInorder to sorted array + construct balanced BST from middlemedium
- 15.Convert BST to Greater TreeReverse Inorder (Right -> Root -> Left) maintaining running summedium
- 16.Construct BST from Preorder TraversalPass upper bound down recursion or use monotonic stackmedium
Also Important
9 more questions worth practicing.
- 17.Find Mode in Binary Search TreeInorder traversal tracking current count & max count without extra memoryeasy
- 18.Increasing Order Search TreeInorder DFS rewiring right pointers (Left = nil)easy
- 19.Closest Binary Search Tree ValueBinary search loop tracking min abs difference to targeteasy
- 20.Inorder Successor in BSTIf right subtree exists: min of right subtree. Else: last left-turn ancestormedium
- 21.Inorder Predecessor in BSTIf left subtree exists: max of left subtree. Else: last right-turn ancestormedium
- 22.Unique Binary Search TreesCatalan number DP: G(n) = sum(G(i-1) * G(n-i))medium
- 23.Unique Binary Search Trees IIDivide & conquer generating all valid BST combinationsmedium
- 24.Serialize and Deserialize BSTPreorder traversal string (no nil markers needed if upper/lower bounds used)medium
- 25.Split BSTRecursive split returning [<= val subtree, > val subtree]medium
How to Think
- Tree has ordering property?Binary Search Tree (BST)
- Need search for value?val < node -> go Left, val > node -> go Right
- Need sorted values from BST?Inorder Traversal (Left-Root-Right)
- Need Kth smallest?Inorder + Count to K
- Need validate BST?Lower Bound < Node < Upper Bound
- Need LCA in BST?Order split point (both < node -> left, both > node -> right)
Go BST Code Templates
Iterative BST Search & LCA in Go
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
// Iterative BST Search: O(H) time, O(1) space
func searchBST(root *TreeNode, target int) *TreeNode {
for root != nil {
if root.Val == target {
return root
}
if target < root.Val {
root = root.Left
} else {
root = root.Right
}
}
return nil
}
// Lowest Common Ancestor in a BST using Ordering
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
for root != nil {
if p.Val < root.Val && q.Val < root.Val {
root = root.Left
} else if p.Val > root.Val && q.Val > root.Val {
root = root.Right
} else {
return root // Split point is the LCA!
}
}
return nil
}👉 Total Time: O(H) | Space: O(1)
The Golden Rule: BST + Inorder = Sorted Array
Inorder Traversal (Left -> Root -> Right) of any valid Binary Search Tree visits nodes in strictly ascending sorted order!
// Find Kth Smallest Element in a BST
func kthSmallest(root *TreeNode, k int) int {
count := 0
result := 0
var inorder func(node *TreeNode)
inorder = func(node *TreeNode) {
if node == nil || count >= k {
return
}
inorder(node.Left)
count++
if count == k {
result = node.Val
return
}
inorder(node.Right)
}
inorder(root)
return result
}👉 Time: O(H + K) | Space: O(H) recursion stack (no need to store full array!)
Delete Node in a BST (3 Cases)
1. Leaf Node: Simply return nil to remove it.
2. One Child: Replace node with its non-nil child (node.Left or node.Right).
3. Two Children: Find the Inorder Successor (smallest value in right subtree: go Right once, then Left as far as possible). Copy successor value into node, then recursively delete successor in right subtree!
func deleteNode(root *TreeNode, key int) *TreeNode {
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 {
// Found node to delete!
if root.Left == nil {
return root.Right
}
if root.Right == nil {
return root.Left
}
// 2 Children case: find Inorder Successor (min in Right subtree)
curr := root.Right
for curr.Left != nil {
curr = curr.Left
}
root.Val = curr.Val
root.Right = deleteNode(root.Right, curr.Val)
}
return root
}Smaller -> Go Left | Larger -> Go Right
BST + Inorder = Strictly Sorted Array
Validate BST = Lower Bound < Node.Val < Upper Bound
LCA in BST = First node where targets p and q split
Delete Node (2 Children) -> Replace with Inorder Successor (Min of Right Subtree)💡 Golden Rule: "Use the BST ordering to eliminate the part of the tree that cannot contain your answer."
Common Interview Mistakes
Only checking left < node && right > node fails! Must carry ancestor bounds [minVal, maxVal] down the subtree.
Skewed BST (e.g. inserting 1,2,3,4,5) has height O(N)! Say time complexity is O(H) where H = O(log N) balanced.
In a valid BST, do NOT search both subtrees recursively! Use the ordering to prune one entire side.
When deleting a node with 2 children, copy the Inorder Successor value, then delete that successor node recursively from the right subtree.
Interview Rules
- 1. Ordered binary tree? → BST
- 2. Smaller target? → Go Left (
target < node.Val) - 3. Larger target? → Go Right (
target > node.Val) - 4. Sorted elements needed? → Inorder Traversal (
Left - Root - Right) - 5. Validate BST? → Pass
lower < node.Val < upperbounds downward - 6. LCA in BST? → First split point node where
pandqdiverge - 7. Delete node with 2 children? → Replace with Inorder Successor (minimum of right subtree)
- 8. Overall Complexity → Search/Insert/Delete:
O(H)(O(log N)balanced,O(N)skewed)
Small Rules
- Rule 1: BST ordering applies to the entire left/right subtree, not just immediate children.
- Rule 2: Inorder traversal of a BST always yields strictly sorted values.
- Rule 3: Search, Insert, and Delete run in O(H) time.
- Rule 4: Inorder Successor is the smallest value in the right subtree.
- Rule 5:Range queries prune impossible subtrees (skip Left if node.Val < low, skip Right if node.Val > high).
Production Thinking
Ordered In-Memory Maps & Sets → Self-balancing Red-Black trees powering C++ std::map & Java TreeMap
Range Queries (Timestamps/Prices) → Filtering score/price intervals efficiently without full scans
Database Indexing (B-Tree / B+Tree) → Ordered disk page search trees pruning search space in Postgres & MySQL
Priority / Order Maintenance → Maintaining dynamic ordered lists during frequent inserts & deletions
Remember This
BST
Left → Smaller
Right → Larger
Search → Choose one side
Inorder → Sorted array
Validate → Lower < Node < Upper
Kth Smallest → Inorder + Count
LCA → Find split point
Delete 2 Ch. → Successor / Predecessor
Balanced → O(log N)
Skewed → O(N)💡 Golden Rule: "Use the BST ordering to eliminate the part of the tree that cannot contain your answer."