Binary search trees
In one line
A binary search tree keeps every left subtree smaller and every right subtree larger than its node, so search, insert and delete follow one path and cost O(height).
What it is
A binary search tree (BST) is a binary tree with an ordering rule: for every node, all values in its left subtree are smaller than the node's value and all values in its right subtree are larger. The rule applies to whole subtrees, not just to direct children.
Because of the rule, finding a value is like binary search: compare with the node and go left or right, discarding a whole subtree each step. The cost is the height h of the tree. If the tree is balanced, h is about log2(n), so a million values need about 20 comparisons. If values are inserted in sorted order, the tree becomes a chain and h is n.
Self-balancing BSTs such as red-black trees and AVL trees rotate nodes after inserts and deletes to keep the height O(log n) in the worst case. Interviews usually ask you to work with a plain BST but expect you to know this.
Why interviewers ask
BST questions test whether you can use an invariant instead of brute force: validate a BST, find the k-th smallest, find a successor, convert a sorted array to a balanced tree. Interviewers also probe whether you know that O(log n) depends on balance, and how ordered maps in real languages are built.
How it works
- 1Search: start at the root. If the target equals the node, stop. If smaller, go left; if larger, go right. Reaching an empty child means the value is not present.
- 2Insert: search for the value; the empty spot where the search falls off the tree is exactly where the new node goes, as a leaf.
- 3Delete a leaf: remove it. Delete a node with one child: replace it with that child.
- 4Delete a node with two children: find its inorder successor (the smallest value in its right subtree), copy that value into the node, then delete the successor, which has at most one child.
- 5Validate: carry an allowed range (low, high) down the tree. The root may be anything; a left child narrows high to the parent's value, a right child narrows low. Any value outside its range breaks the BST.
Worked example · Building, searching and validating
- Insert 8, 3, 10, 1, 6, 14, 4, 7 in that order. 8 is the root. 3 goes left of 8. 10 goes right of 8. 1 goes left of 3. 6 goes right of 3. 14 goes right of 10. 4: less than 8, more than 3, less than 6, so left of 6. 7: right of 6.
- Search 7: compare with 8 (smaller, go left), 3 (larger, go right), 6 (larger, go right), 7 (found). Four comparisons instead of scanning eight values.
- Search 5: 8 left, 3 right, 6 left, 4 right, empty. Not found after four steps.
- Delete 3 (two children): its successor is the smallest value in its right subtree, which is 4. Copy 4 into the node, then remove the old leaf 4. Inorder is now 1 4 6 7 8 10 14, still sorted.
- Validation trap: root 10, right child 15, and 15 has left child 6. Every parent-child pair looks fine, but 6 sits in 10's right subtree, so its allowed range is (10, 15). 6 is outside, so the tree is not a valid BST.
Picture it
A BST is like a game of higher-or-lower with a smart host: each answer lets you ignore everything on one side. A balanced tree is a host who always picks the middle; a degenerate tree is a host who guesses 1, 2, 3, 4 in order.
Reference code
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val, self.left, self.right = val, left, right
def search(node, target):
while node and node.val != target:
node = node.left if target < node.val else node.right
return node # None if not found
def insert(node, val):
if node is None:
return TreeNode(val) # the empty spot is where it belongs
if val < node.val:
node.left = insert(node.left, val)
elif val > node.val:
node.right = insert(node.right, val)
return node
def is_valid_bst(node, low=float('-inf'), high=float('inf')):
if node is None:
return True
if not (low < node.val < high): # must fit the range set by all ancestors
return False
return (is_valid_bst(node.left, low, node.val) and
is_valid_bst(node.right, node.val, high))Search walks a single path, so it costs O(h). Insert reuses that path and attaches a leaf, ignoring duplicates. Validation passes the allowed range down instead of only comparing with the direct parent, which catches violations deep in a subtree. The TreeNode class (val, left, right) is the node shape used on every tree page.
Key patterns
Validate with bounds
When: Is this a valid BST?
How: Recurse with (low, high); left child gets (low, node.val), right child gets (node.val, high). Alternatively check that an inorder walk is strictly increasing.
Inorder with early stop
When: k-th smallest, or two-sum in a BST
How: Iterative inorder and count visits; stop at the k-th. For two-sum, run one forward and one reverse inorder iterator as two pointers.
Use the ordering to prune
When: Range sum, trim a BST, LCA in a BST, closest value
How: If the node is below the range, only the right subtree can help; if above, only the left. Skip the other side entirely.
Build balanced from sorted
When: Convert a sorted array or list to a height-balanced BST
How: Pick the middle element as the root and recurse on the left and right halves; height stays about log2(n).
Real-world use
Ordered maps in standard libraries
Java's TreeMap and TreeSet and C++'s std::map and std::set are red-black trees, giving O(log n) insert, delete, lookup and ordered iteration, plus floor and ceiling queries that hash maps cannot do.
Linux CPU scheduler
The Linux kernel keeps runnable tasks in a red-black tree ordered by how much CPU time they deserve, so picking the next task to run is a quick lookup of the leftmost node.
Database indexes
PostgreSQL and MySQL indexes are B-trees: a generalisation of BSTs where each node holds hundreds of keys, so a lookup over millions of rows touches only 3 or 4 disk pages.
In-memory sorted structures
Redis sorted sets use a skip list for the same ordered operations; it is a common alternative to a balanced BST with similar O(log n) expected cost.
In a project
You build a booking service for meeting rooms that must answer: what is the next free slot after 2 pm, and does a new booking overlap an existing one? Bookings change all day.
- Store each room's bookings in an ordered map keyed by start time (a balanced BST such as TreeMap, or SortedDict in Python's sortedcontainers).
- To check overlap for a new booking, look up the floor entry (latest start at or before the new start) and the ceiling entry (earliest start after it) and compare their end times.
- To find the next free slot, start at the ceiling of the requested time and walk forward in order until a gap is large enough.
- Insert and delete bookings in O(log n) as people book and cancel, without re-sorting.
Impact: Each overlap check costs O(log n) instead of scanning every booking, so a room with thousands of bookings still answers in microseconds, and results always come back in time order.
Trade-offs
Use it when
- You need ordered operations: sorted iteration, min and max, floor and ceiling, range queries
- Data changes often and must stay sorted without full re-sorts
- You can use a self-balancing implementation from a library
Avoid it when
- You only need exact lookups: a hash map gives O(1) average instead of O(log n)
- You insert mostly sorted data into a plain, unbalanced BST: it degrades to a linked list with O(n) operations
- Data is static: a sorted array with binary search is simpler and more cache-friendly
Cost: Search, insert and delete are O(h): O(log n) balanced, O(n) worst case for a plain BST. Self-balancing trees guarantee O(log n). Inorder iteration is O(n).
Common mistakes
- Validating by comparing each node only with its children; the rule covers entire subtrees, so pass bounds down.
- Claiming BST operations are always O(log n): that needs balance. A plain BST built from sorted input has height n.
- Using int limits like 0 or -1 as initial bounds, which breaks when the tree contains those values; use infinity or None.
- Forgetting how duplicates are handled: decide (ignore, count, or always go right) and say so.
- Deleting a node with two children by just removing it, instead of replacing it with its inorder successor or predecessor.
Interview questions
Quick revision
- Left subtree < node < right subtree, for the whole subtree.
- Search, insert, delete: O(h). Balanced h is about log2(n); sorted inserts make h = n.
- Inorder of a BST is sorted.
- Validate with (low, high) bounds, not parent-child checks.
- Delete with two children: swap in the inorder successor (min of right subtree).
- Red-black and AVL trees self-balance to guarantee O(log n).
- Hash map for exact lookups; BST when you need order or ranges.
Check yourself
1. Values 1, 2, 3, 4, 5 are inserted in that order into an empty plain BST. What is its height in nodes?
2. Root 10 has right child 15, and 15 has left child 6. Is this a valid BST?
3. When deleting a node with two children, what value usually replaces it?
4. Which operation can a balanced BST do efficiently that a hash map cannot?
Related
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.