BeeTool.Dev - Essential Tools for developers

Stories.Tool Tabs VaultPersonal Documents
Super Debugging Context
BeeTool.Dev

Essential tools, knowledge, and playgrounds for developers.

Explore

  • Stories
  • Personal Documents
  • Features
  • Algorithms Playground

Tool Tabs

  • JSON Compare
  • SQL Formatter
  • Text Diff
  • All tools →

Learn

  • Sorting algorithms
  • Searching algorithms
  • Algorithm Race Mode
© 2024 – 2026 BeeTool.DevPrivacy policy
← Algorithms Playground
Searching · Intermediate

Binary Search Tree Search

Binary search made dynamic — one ordering comparison per level steers you left or right, and the structure absorbs inserts and deletes that would cripple a sorted array.

AverageO(log n)StructureBinary search treeNeeds sortedNoComparisonYes
5738853352485675626073848096
Must remember

Compare at each node, go left if smaller, right if bigger — binary search over a linked structure that also supports fast inserts.

Best O(1) · Avg O(log n) · Worst O(n) · Space O(1) iterative

Hands on Interactive visualization

Target
91
68301682545375389847085939199
Queue
empty
visited 0 · depth 0
0/0Ready — press play
Speed
Step 0 / 0
Comparisons
0
Swaps
0
Reads
0
Writes
0
Elapsed
0.0s
Frames
0
Depth
0 / 0
Aux memory
0 B

Implementation Code

interface Node {
  key: number;
  left: Node | null;
  right: Node | null;
}

// Iterative descent — O(height), O(1) space.
export function bstSearch(root: Node | null, key: number): Node | null {
  let cur = root;
  while (cur !== null) {
    if (key === cur.key) return cur; // found
    // one comparison discards an entire subtree
    cur = key < cur.key ? cur.left : cur.right;
  }
  return null; // fell off a leaf: not present
}

Big-O Complexity

Best
O(1)
Average
O(log n)
Worst
O(n)
Space
O(1)

O(log n) holds only while the tree stays balanced; a degenerate tree (e.g. built from sorted inserts) is a linked list with O(n) search. Self-balancing variants — red-black (std::map, Java TreeMap), AVL, B-trees — guarantee O(log n) worst case. Iterative descent is O(1) space; recursion costs the tree height.

In production When to use

Ordered maps & sets

C++ std::map/std::set (red-black trees) and Java's TreeMap/TreeSet keep keys sorted while supporting O(log n) insert, delete, and search — the go-to when you need a mutable dictionary that stays ordered.

Range queries

Find all keys in [a, b] falls out naturally: descend to the boundary, then walk in-order — O(log n + k) for k results. A hash table cannot answer this at all without scanning everything.

Floor / ceiling lookups

Largest key ≤ x and smallest key ≥ x are one root-to-leaf descent, recording the best candidate at each turn — the backbone of rate limiters, calendar slot finders, and TreeMap.floorKey/ceilingKey.

In-order iteration

In-order traversal yields all keys in sorted order in O(n) with no separate sort step — leaderboards, ordered dumps, and merge-style operations get sorted output for free.

Database & filesystem indexes

B-trees and B+ trees — the wide, disk-friendly cousins of the BST — power nearly every relational database index and many filesystems, using the same ordered-descent search per node.

Priority-with-lookup workloads

Order books, schedulers, and interval trackers that need min/max, arbitrary delete, AND membership in log time use balanced BSTs where a heap (no arbitrary lookup) falls short.

Trade-offs Strengths & weaknesses

  • O(log n) search, insert, AND delete when balanced — the sorted array only matches the search.
  • Keys stay in sorted order: range queries, floor/ceiling, min/max, and ordered iteration come built in.
  • Self-balancing variants (red-black, AVL) make O(log n) a worst-case guarantee, not a hope.
  • In-order traversal produces sorted output in O(n).
  • Predecessor/successor navigation in O(log n) — the operations hash tables fundamentally can't do.
  • No resizing or rehashing pauses; cost is smooth per operation.
  • A plain BST degrades to O(n) on sorted or adversarial insert orders — balancing is not optional in production.
  • Slower than hash tables for pure equality lookup: O(log n) with a comparison per level vs expected O(1).
  • Per-node pointer overhead and scattered allocations are cache-hostile next to a flat sorted array.
  • Deletion (the two-children case) and rebalancing rotations are fiddly to implement correctly.
  • Requires a total order on keys — and the comparator must be consistent, or the structure silently corrupts.

Intuition Mental model

Twenty questions on ordered data

Playing 20 questions with is it bigger or smaller?: each answer eliminates one entire subtree from consideration. A well-built tree halves the candidates per question — but if the questions were arranged badly (a degenerate tree), you're reduced to asking about candidates one at a time.

  1. Start at the root question = compare the target with the root key.
  2. Smaller sends you down the left branch, bigger down the right = one comparison discards a whole subtree.
  3. Each level is another question; a balanced tree of a million keys needs about 20 = O(log n).
  4. Reaching a question with no follow-up (a null child) = the key is not in the tree.
  5. A quiz where every answer leads to just one more question in a long chain = the degenerate tree: 20 questions becomes n questions.

Prep Interview questions

Validate that a binary tree is a BST. What's the classic wrong answer?
Checking only node.left < node < node.right is wrong — a grandchild can violate an ancestor's bound while satisfying its parent. Pass down (min, max) bounds that tighten at each level, or verify the in-order traversal is strictly increasing.
Find the k-th smallest element in a BST.
In-order traversal visits keys in sorted order — stop at the k-th visit, O(h + k). For repeated queries, augment each node with its subtree size and descend numerically: left subtree size tells you which side k falls on.
Find the floor of x (largest key ≤ x) in one descent.
Walk from the root: at a node ≤ x, record it as the best-so-far and go right (something bigger but still ≤ x may exist); at a node > x, go left. The last recorded node is the floor — no backtracking needed.
Why does inserting sorted data destroy a plain BST, and what fixes it?
Each new key is larger than every existing one, so it becomes the right child of the rightmost node — a linked list, O(n) everything. Fixes: self-balancing trees (red-black/AVL rotations), shuffling input when possible, or building from sorted data by recursive midpoint (yielding a perfectly balanced tree).
Delete a node with two children from a BST.
Replace its key with the in-order successor (leftmost node of the right subtree) or predecessor, then delete that node instead — it has at most one child, reducing to the easy case. Off-by-one errors in re-linking the successor's parent are the classic slip.

Watch out Common bugs

Sorted inserts → linked-list degeneration

Feeding ascending keys into a plain BST chains every node down the right spine: height n, search O(n). It passes small tests and collapses on real data. Use a self-balancing tree, or detect that your input is presorted and build by midpoint instead.

Comparing with equals instead of the comparator

The tree is organized by its comparator; if equals() disagrees with compare() == 0 (Java's TreeMap docs warn exactly this), lookups descend to the wrong subtree and 'contained' keys go unfound. All navigation — search, insert, delete — must use the one comparator.

No duplicate policy

Deciding ad hoc whether duplicates go left, right, or get rejected leads to inconsistent trees where count queries and deletes misbehave. Pick one policy up front: reject, keep a count field per node, or store a list of values per key.

Losing the parent link during delete

The two-children delete swaps in the in-order successor; forgetting to reattach the successor's own right child to the successor's parent silently drops a whole subtree. Draw the three cases (leaf, one child, two children) before coding.

One screen Cheat sheet

Compare at each node, go left if smaller, right if bigger — binary search over a linked structure that also supports fast inserts.

Best O(1) · Avg O(log n) · Worst O(n) · Space O(1) iterative
  • O(log n) only while balanced; sorted inserts degrade a plain BST to a linked list.
  • Self-balancing variants guarantee log n: red-black (std::map, TreeMap), AVL, B-trees on disk.
  • In-order traversal = sorted order; validates the tree and powers ordered iteration.
  • Floor/ceiling: record best-so-far during a single descent, no backtracking.
  • All navigation must use one comparator — equals() disagreeing with compare() corrupts lookups.
  • Decide the duplicate policy up front: reject, count field, or bucket per key.
Pick whenYou need a mutable collection with ordered operations — range queries, floor/ceiling, sorted iteration — alongside log-time search.
Avoid whenYou only need equality lookup (hash table wins) or the data is static (a sorted array is smaller and more cache-friendly).

Hash Lookup

Trie Search