A binary tree with the BST property β left subtree values less than node, right subtree values greater β enabling O(log n) search, insert, and delete.
Trees
Binary Search Tree Fundamentals
A binary tree with one additional constraint β the BST property: for every node, all values in its left subtree are strictly less and all values in its right subtree are strictly greater. This ordering invariant transforms O(n) linear search into O(log n) binary search on a linked structure.
The ordering constraint that separates a binary search tree from a general binary tree is a single invariant applied recursively: every node's value is strictly greater than all values in its left subtree and strictly less than all values in its right subtree, making the entire structure a sorted collection navigable by comparison alone. That invariant buys logarithmic elimination β every comparison at a node discards an entire subtree, halving the remaining candidates at each step and reducing search from O(n) linear scan to O(log n) binary search without requiring the data to be stored in a contiguous array. Beyond search, the ordering produces a remarkable side effect: inorder traversal of a BST visits nodes in ascending sorted order, making it the only tree structure where a standard traversal yields sorted output as a natural byproduct. The hidden danger is that a BST is only O(log n) when balanced β inserting already-sorted data produces a degenerate tree of height nβ1 that degrades every operation to O(n), which is why self-balancing variants like AVL and Red-Black trees exist. In Java, TreeMap and TreeSet use red-black trees β a self-balancing BST β internally, giving O(log n) put, get, contains, floor, ceiling, and range operations that make sorted collections practical at scale.
Analogy: A dictionary where every page is a decision point β the word you seek is either on this page, in the left half (alphabetically before), or in the right half (alphabetically after). At each page you eliminate half the remaining book. That is a binary search tree: every node is a comparison that routes you left or right, and the ordering constraint guarantees you are always moving toward your target β never backward, never sideways. A sorted dictionary works because of its ordering invariant; a BST works for exactly the same reason.
02
Section Two Β· Mental Model
How It Thinks
Every comparison eliminates an entire subtree
βΆ
Search costs O(log n) on a balanced BST β the same binary search as on a sorted array, but on a linked structure
The BST property holds at every node, not just the root
βΆ
Validation cannot check only the root's children β it must propagate min/max bounds through the entire tree top-down
Inorder traversal visits left subtree before root before right
βΆ
Inorder traversal of a BST always yields sorted ascending output β the sorted array view of the BST is free and always up to date
Insertion always adds a new leaf β never restructures existing nodes
βΆ
Insert follows the search path until null and places the node there β no rotations, no rebalancing in a plain BST
Inserting already-sorted data appends to one side only
βΆ
Sorted insertion creates a degenerate right-skewed chain β height becomes O(n) and all operations degrade to O(n)
Deletion with two children cannot remove the node directly
βΆ
The node must be replaced with its inorder successor (leftmost node in the right subtree) to preserve the BST property
03
Section Three Β· BST Property
The BST Property β The Ordering Invariant
The BST property is the single rule that makes a binary tree searchable: for every node N, every value in the left subtree of N is strictly less than N's value, and every value in the right subtree of N is strictly greater β this invariant must hold not just for direct children but for every node in both subtrees. The most common interview mistake is checking only direct children (left.val < node.val < right.val) and missing violations where a node's value is correct relative to its parent but wrong relative to a distant ancestor β the classic "subtree violation" that requires propagating bounds through the entire tree.
Valid BST vs Invalid BST β ancestor violation
Validating the BST Property
Pseudocode
functionisValidBST(node, min, max):
if node isnull: returntrueif node.val <= min or node.val >= max:
returnfalse// value outside allowed rangereturnisValidBST(node.left, min, node.val) // tighten maxandisValidBST(node.right, node.val, max) // tighten min// O(n) β every node checked once// Initial call: isValidBST(root, -β, +β)
Validation β bound propagation on the invalid BST
Check min/max bounds, not parent comparison:
The naive check (node.left.val < node.val < node.right.val) fails on ancestor violations β a node can satisfy its parent's constraint while violating a grandparent's.
The correct approach passes inherited bounds [min, max] top-down and rejects any node whose value falls outside its allowed range.
This is the standard O(n) single-pass BST validation.
04
Section Four Β· Operations
Core Operations
Search
Compares the target value with the current node and recurses into the left subtree if smaller, the right subtree if larger, or returns the current node if equal β eliminating one subtree at every step.
Pseudocode
functionsearch(node, target):
if node isnull: returnnull// not foundif target == node.val: return node
if target < node.val:
returnsearch(node.left, target) // go leftelse:
returnsearch(node.right, target) // go right// O(h): O(log n) balanced, O(n) degenerate
Search β finding value 6 in the BST
Search on a BST is binary search β not linear scan:
Each comparison eliminates an entire subtree.
On a balanced tree of 1,000,000 nodes, search visits at most 20 nodes (logβ 1,000,000 β 20).
The iterative version (while loop, no recursion) is preferred in practice β it avoids call stack overhead and is easier to reason about for very deep trees.
Insert
Follows the search path to find where the new value belongs β the position where search would return null β and places a new leaf node there, preserving the BST property without restructuring.
Pseudocode
functioninsert(node, val):
if node isnull:
returnnewTreeNode(val) // insert here as a new leafif val < node.val:
node.left = insert(node.left, val)
else if val > node.val:
node.right = insert(node.right, val)
// val == node.val: duplicate β ignore (or handle per requirements)return node // O(h): O(log n) balanced, O(n) degenerate
Insert β adding value 5 to the BST
Insert always creates a new leaf β no restructuring:
Unlike arrays (which require shifting) or balanced BSTs (which require rotations), a plain BST insert always adds a new leaf at the exact position where the search terminated.
The return-node pattern (returning node at each recursive call) cleanly handles the parent pointer update without a separate parent reference.
Min / Max / Floor / Ceiling
BST structure makes extremes and neighbors O(h) β min is the leftmost node, max is the rightmost node, floor is the largest value β€ target, ceiling is the smallest value β₯ target.
Pseudocode
functionfindMin(node):
while node.left != null: node = node.left
return node // leftmost nodefunctionfindMax(node):
while node.right != null: node = node.right
return node // rightmost nodefunctionfloor(node, target):
if node isnull: returnnullif node.val == target: return node
if node.val > target:
returnfloor(node.left, target) // must go left// node.val < target β this node is a candidate, but right might be closer
rightFloor = floor(node.right, target)
return rightFloor if rightFloor != nullelse node
// O(h)functionceiling(node, target):
if node isnull: returnnullif node.val == target: return node
if node.val < target:
returnceiling(node.right, target) // must go right
leftCeiling = ceiling(node.left, target)
return leftCeiling if leftCeiling != nullelse node
// O(h)
Floor and ceiling exploit BST ordering β O(h) not O(n):
On a sorted array, floor and ceiling require binary search β O(log n).
On a BST, the same logic works by following left or right pointers: any node smaller than the target is a floor candidate, but the right subtree might have a closer candidate.
Java's TreeMap exposes this via floorKey(key) and ceilingKey(key), both O(log n).
Complexity Reference
Operation
Balanced BST
Degenerate BST
Notes
Search
O(log n)
O(n)
Follows one path root-to-leaf
Insert
O(log n)
O(n)
Always adds new leaf
Delete
O(log n)
O(n)
3 cases; see Section 7
Find Min/Max
O(log n)
O(n)
Go left/right until null
Floor/Ceiling
O(log n)
O(n)
BST-ordering exploitation
Inorder
O(n)
O(n)
Must visit all nodes
Validate BST
O(n)
O(n)
Must check all nodes
05
Section Five Β· Traversals
Traversals on a BST β Why Inorder Is Special
All four traversal orders from the Binary Tree page work identically on a BST β the BST property does not change how traversal works, but it fundamentally changes what inorder traversal produces: unlike a general binary tree where inorder output is arbitrary, inorder traversal of a BST always yields elements in ascending sorted order. This sorted-output guarantee is not incidental β it is a direct consequence of the BST invariant (left < root < right at every node) and makes inorder traversal the solution to any problem requiring sorted extraction, kth smallest, or in-place BST sorting.
Inorder traversal β sorted output from a BST
BST Traversal Use Cases
Traversal
Produces
Common BST Use Case
Inorder
Sorted ascending output
Kth smallest, sorted iteration, BST sort
Preorder
Root-first serialisation
Reconstruct BST from array (O(n) with BST property)
Postorder
Children-first aggregation
Delete all nodes, size/height computation
Level-Order
Level-by-level
Minimum depth, BST level sums
Kth Smallest Element
Returns the kth smallest value in the BST β the kth node visited in inorder traversal.
Pseudocode
functionkthSmallest(node, k):
// Iterative inorder β stop at kth visit
stack = empty stack
count = 0
curr = node
while curr != nullor stack not empty:
while curr != null:
push(stack, curr)
curr = curr.left
curr = pop(stack)
count++
if count == k: return curr.val // found kth smallest
curr = curr.right
// O(h + k) β not O(n)
Kth smallest stops early β O(h + k) not O(n):
The iterative inorder approach stops as soon as the kth node is visited β it does not traverse the entire tree.
Cost is O(h) to reach the leftmost node plus O(k) to count up to k β significantly faster than full traversal when k is small.
The recursive version requires extra state (a counter passed by reference or a class field) to stop early β the iterative version is cleaner for this problem.
06
Section Six Β· Variants
BST Variants β When Balance Matters
A plain BST offers O(log n) operations only when balanced β the insertion order of the data determines the tree's shape, and inserting sorted data degrades every operation to O(n) by creating a degenerate chain; self-balancing BST variants solve this by performing rotations after insert and delete to maintain a height bound of O(log n) regardless of insertion order. For interviews, understanding that AVL trees and Red-Black trees are BSTs with automatic rebalancing is sufficient β you will not be asked to implement rotations, but you will be asked why Java's TreeMap is O(log n) for all inputs while a plain BST can degrade to O(n).
Balanced BST vs Degenerate BST β same values, different insertion order
BST Variants Comparison
Variant
Balance Guarantee
Height
Rotations
Java Class
Plain BST
None
O(n) worst
None
Custom TreeNode
AVL Tree
|h(L)βh(R)| β€ 1
O(log n) strict
Yes (LL,RR,LR,RL)
None built-in
Red-Black Tree β Java
Longest path β€ 2Γ shortest
O(log n)
Yes (color flip + rotation)
TreeMap / TreeSet
Splay Tree
Amortised O(log n)
O(n) worst
Yes (splay)
None built-in
Treap
Expected O(log n)
O(log n) expected
Randomised
None built-in
AVL vs Red-Black β know the trade-off conceptually:
AVL trees are more strictly balanced (|h(L) β h(R)| β€ 1 at every node) which makes lookups slightly faster β at the cost of more rotations on insert and delete.
Red-Black trees are less strictly balanced but require fewer rotations, making insert and delete faster in practice.
Java chose Red-Black for TreeMap and TreeSet because real-world workloads involve more writes than reads in sorted collections.
07
Section Seven Β· Delete
The Delete Problem β Three Cases
Deletion is the hardest BST operation because removing a node with two children cannot eliminate it directly β the tree must be restructured to maintain the BST property, and the restructuring strategy is non-obvious until you understand why the inorder successor is the correct replacement. The three cases β deleting a leaf, deleting a node with one child, and deleting a node with two children β must be handled separately, and only the third case requires any structural reasoning. The inorder successor (the leftmost node in the right subtree) is always the correct replacement for a two-child deletion because it is the smallest value larger than the deleted node, maintaining the BST property for all remaining nodes in both subtrees.
Delete β three cases side by side
Delete Implementation
Java
// BST Delete β handles all 3 cases O(h)TreeNodedelete(TreeNode node, int val) {
if (node == null) returnnull; // not foundif (val < node.val) {
node.left = delete(node.left, val); // go left
} else if (val > node.val) {
node.right = delete(node.right, val); // go right
} else {
// FOUND the node to delete β handle 3 cases:// Case 1: leaf nodeif (node.left == null && node.right == null) returnnull;
// Case 2: one child β return the non-null childif (node.left == null) return node.right;
if (node.right == null) return node.left;
// Case 3: two children β find inorder successorTreeNode successor = findMin(node.right); // leftmost in right subtree
node.val = successor.val; // copy successor's value
node.right = delete(node.right, successor.val); // delete successor
}
return node;
}
// Helper: find leftmost node (minimum value)TreeNodefindMin(TreeNode node) {
while (node.left != null) node = node.left;
return node;
}
Why the inorder successor β and not the predecessor?:
Both the inorder successor (smallest value in the right subtree) and the inorder predecessor (largest value in the left subtree) are valid replacements for a two-child deletion β both maintain the BST property.
The convention of using the inorder successor is a convention; some implementations use the predecessor.
What matters is understanding why either choice works: the replacement must be the value closest to the deleted node in sorted order.
Common Mistake: Confusing "delete the inorder successor node" with "restructure the tree." After copying the successor's value into the deleted node's position, the successor must be deleted from the right subtree β and that deletion is itself a recursive delete call. The successor has at most one child (no left child, by definition of leftmost), so this recursive call always reduces to Case 1 or Case 2 β it never recurses into Case 3 again.
08
Section Eight Β· Implementation
Build It Once
Build this from scratch once β it makes the mechanics concrete.
In any real project or interview, reach for the built-in (Section 09).
Java β BST core mechanics
// BST operations follow the search path β go left if smaller,// right if larger. All three operations here are O(h): O(log n)// balanced, O(n) degenerate.// Search β iterative (preferred over recursive) O(h)TreeNodesearch(TreeNode node, int target) {
while (node != null && node.val != target)
node = (target < node.val) ? node.left : node.right;
return node; // null if not found, node if found
}
// Insert β recursive with return-node pattern O(h)TreeNodeinsert(TreeNode node, int val) {
if (node == null) return newTreeNode(val);
if (val < node.val) node.left = insert(node.left, val);
else if (val > node.val) node.right = insert(node.right, val);
return node; // return-node pattern rewires parent pointers
}
// Validate BST β min/max bounds propagation O(n)booleanisValidBST(TreeNode node, long min, long max) {
if (node == null) return true;
if (node.val <= min || node.val >= max) return false;
returnisValidBST(node.left, min, node.val) // tighten max
&& isValidBST(node.right, node.val, max); // tighten min
}
// Initial call: isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE)
Binary Search Tree β Full Implementation
Java β Complete Binary Search Tree implementation
β Gotcha: TreeMap and TreeSet maintain sorted order and provide BST-like navigation, but they expose no node-level API β you cannot access parent pointers, left/right children, or perform rotations. For interview problems requiring structural tree manipulation (LCA, serialize/deserialize, validate BST), always build a custom TreeNode from scratch.
β Gotcha: Use long (not int) as bounds when validating a BST in Java β node values can be Integer.MIN_VALUE or Integer.MAX_VALUE, and passing int min/max bounds causes overflow. Initialize: isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE).
10
Section Ten Β· Practice
Problems To Solve
BST problems split into two categories: structural problems that require understanding the BST property itself (validate BST, insert, delete, lowest common ancestor), where the ordering invariant drives every decision, and sorted-output problems that exploit inorder traversal (kth smallest, convert sorted array to BST, recover BST). Before writing code on any BST problem, confirm whether the BST property actually helps β some problems on BST nodes are solved exactly like general binary tree problems (height, depth, symmetry), while others are O(log n) precisely because the ordering allows elimination of an entire subtree at every comparison.
Pass long min and max bounds top-down β not int, to avoid overflow with Integer.MIN_VALUE/MAX_VALUE inputs. A node is invalid if node.val β€ min or node.val β₯ max.
Prefer the iterative version β while node != null, go left if target < node.val, go right if larger, return node if equal. No recursion overhead, no call stack.
Recurse left or right to the null position, return a new TreeNode(val). The return-node pattern updates parent pointers without a separate parent reference.
Three cases in order: (1) leaf β return null, (2) one child β return that child, (3) two children β copy inorder successor value then recursively delete successor from right subtree.
Iterative inorder with an early exit β decrement a counter on each visit, return when counter reaches zero. Cost is O(h + k), not O(n). Recursive version needs a class-level or array counter to enable early exit.
Exploit BST ordering: if both p and q are less than node, go left; if both greater, go right; otherwise node is the LCA. O(log n) on balanced BST β faster than general binary tree LCA.
Interview Pattern:
For BST problems, always ask first: does this problem need to exploit the BST ordering property, or is it a binary tree problem that happens to be on a BST?
If the ordering matters (search, insert, delete, LCA, floor/ceiling), the BST property drives every decision.
If the ordering does not matter (height, balance check, level-order), solve it exactly as you would a general binary tree.
Conflating the two approaches leads to O(n) solutions for problems that should be O(log n).