skip to content

questions

4

Why is binary search tree lookup described as O(h) rather than O(log n)?

level: juniorimportance: must knowfreq 82%

answer

  1. Count what one lookup actually visits
  2. One node per level, top to bottom
  3. What decides how many levels exist
  4. Insertion order, not key count
  5. Ascending inserts build a chain

basics

~20 s

Lookup follows one root-to-leaf path, so its cost is proportional to the tree's height h. Height depends on insertion order: roughly log n when the tree is bushy, but as much as n when nodes form a chain.

solid answer

~50 s

A search in a binary search tree compares the target with the current node and descends left or right, so it visits exactly one node per level. The number of comparisons is therefore the depth reached, bounded by the height `h` — that is the honest statement of the cost. `h` is not fixed by `n`; it is decided by the shape the insertions produced. A perfectly bushy tree of `n` nodes has height about `log2 n`, so lookup is O(log n) there. A tree built by inserting keys in ascending order has every node as the right child of the previous one — a chain of height `n` — and lookup is O(n), no better than scanning a list. Saying "BST search is O(log n)" quietly assumes a shape nothing in a plain BST enforces.

code

pseudocode · 12 lines
pseudocode
search(root, target):
    node = root
    steps = 0
    while node != null
        steps = steps + 1
        if target == node.key
            return node          // found after `steps` comparisons
        if target < node.key
            node = node.left
        else
            node = node.right
    return null                  // steps equals the depth reached

go deeper

for a junior

Be ready to state the cost as O(h) and immediately say what h is: the longest root-to-leaf path. Know both ends of the range — about log2 n for a bushy tree, n for a chain — and that ascending inserts produce the chain.

for a middle

Explain why the cost is one node per level, and why a plain tree has an ordering invariant but no shape invariant. You should be able to walk through inserting five ascending keys and describe the resulting structure precisely.

for a senior

Show that degeneration is a performance failure with correct results, so tests stay green while latency changes shape. Interviewers expect you to name how you would detect it in a running service rather than only define it.

for a principal

Own the framing that O(h) means the input chooses your complexity. Be ready to argue when a structure must carry its own height guarantee versus when caller-side discipline over insertion order is acceptable risk.

## What the search actually does A binary search tree stores keys so that everything in a node's left subtree is smaller than the node and everything in its right subtree is larger. Searching exploits exactly that: compare the target with the current node's key, and if it is smaller descend left, if larger descend right, otherwise stop. Each comparison eliminates one subtree and moves you down exactly one level. So the work done by a lookup is *the number of levels it descends*. In the worst case for a given tree that is the tree's **height** `h` — the number of edges (or nodes, depending on convention; the asymptotics are the same) on the longest root-to-leaf path. Hence the cost statement: **search, insert and delete are all O(h)**, because all three are built on the same downward walk. ## Why h is not the same thing as log n `log n` is what the height *would* be if every level were full — a bushy tree, where each level roughly doubles the node count, so `n` nodes fit into about `log2 n` levels. That is the best case, and it is where the familiar "halve the search space each step" intuition comes from. But a plain binary search tree has no mechanism that produces that shape. It has an *ordering* invariant, not a *shape* invariant. Insertion walks down to the first empty slot and hangs the new node there; nothing measures the height, nothing moves existing nodes. The resulting shape is a pure function of the order the keys arrived in. The two extremes: | Insertion order | Resulting height | Lookup cost | |---|---|---| | Ideal / bushy | about log2 n | O(log n) | | Uniformly random | Θ(log n), with a larger constant | O(log n) | | Strictly ascending or descending | n | O(n) | The last row is the one that matters in practice. Insert 1, 2, 3, 4, 5 in that order: 1 becomes the root; 2 is greater, so it becomes 1's right child; 3 is greater than both, so it becomes 2's right child; and so on. Every node has exactly one child, all on the same side. Structurally this is a linked list that also happens to pay for two child pointers per node. A million ascending inserts give you a chain a million nodes long, and a lookup for the largest key touches every one of them. Notice what has *not* broken: the ordering invariant still holds, an inorder traversal still yields the keys in sorted order, and every operation still returns the right answer. Degeneration is a **performance** failure, not a correctness one, which is precisely why it survives code review and unit tests and shows up later as a latency problem. ## Reading the bound correctly Three directions people get backwards: 1. **O(h) is an upper bound on one operation, not a promise about a shape.** It says a lookup never costs more than the height; it says nothing about what the height is. 2. **h ranges over `[about log2 n, n]`.** Both ends are reachable with ordinary data, so quoting either end alone as "the" complexity is wrong. 3. **The tree does not repair itself.** A plain BST that has degenerated stays degenerated; subsequent well-mixed inserts hang off the chain rather than fixing it. The balanced-tree families — AVL, red-black, and the B-tree family — exist exactly to close this gap. They add bookkeeping so that writes maintain a *height bound* as well as the ordering, converting O(h) into a genuine O(log n) worst case. You pay for it with extra per-node state and extra work on every insert and delete. When someone asks why balanced trees exist at all, the answer is this leaf: because in a plain BST, `h` is chosen by your input, not by you. ## How to say it in an interview "Lookup is O(h), one node per level. Height is between about log2 n and n, and which one you get depends entirely on insertion order — sorted input gives you a chain and O(n). If I need a worst-case bound rather than a hope, I use a self-balancing tree." That sentence contains the whole idea, and it is the answer the O(h) phrasing is fishing for.

  • You insert 1,000,000 keys in strictly increasing order. What does a lookup for the largest key cost?
    Every key is larger than all its predecessors, so each one becomes the right child of the previous node: a single chain a million nodes deep. Finding the largest key walks the entire chain — about 1,000,000 comparisons, versus roughly 20 in a bushy tree of the same size. The structure is a linked list with extra pointer overhead.
  • Does a plain binary search tree ever fix a bad shape on its own?
    No. Insertion only attaches a new leaf at the first empty slot, and deletion only splices out one node; neither inspects the height or moves unrelated nodes. Once the tree is a chain, later inserts hang off that chain. Recovering a good shape requires either an explicit rebuild or a structure that maintains a height bound on every write.
  • If the tree has degenerated, is anything about it still correct?
    Yes — everything except the cost. The ordering invariant still holds, searches still find the right node, and an inorder traversal still emits keys in sorted order. That is what makes degeneration hard to catch: no test fails, no assertion trips, and the only symptom is that operations that were supposed to be logarithmic are now linear.

Height is how many doors you open to reach a room. A bushy tree is a building with wide floors and short stairwells; a degenerate tree is a corridor where each room's only exit leads to the next one.

saying these in an interview costs you the question

  • Says BST search is O(log n), full stop
  • Believes the tree rebalances itself as it grows
  • Thinks height is determined by the number of keys
  • Claims a degenerate tree returns wrong results
  • Confuses the ordering invariant with a shape guarantee

context

open as a page

A binary search tree's expected height is Θ(log n) under random insertion — why not rely on that?

level: middleimportance: must knowfreq 64%

basics

~10 s

Expected height assumes a uniformly random insertion order, and real key streams — timestamps, sequential identifiers, sorted exports — are not random. Self-balancing trees replace that probabilistic hope with a worst-case height guarantee.

open as a page

A nightly import inserts sorted SKU IDs into a plain binary search tree — what do you flag in review?

level: seniorimportance: should knowfreq 47%

basics

~20 s

Flag that the arrival order is sorted: every key lands to the right of the previous one, so the index becomes a chain of length n. Lookups degrade from O(log n) to O(n), and the import loop itself costs Θ(n²).

open as a page

A shared plain binary search tree index serves several services, some feeding it monotone keys — what is your call?

level: principalimportance: nice to knowfreq 33%

basics

~10 s

Put the guarantee in the structure, not in caller discipline: a shared index whose inputs you do not control should maintain its own height bound. Caller-side fixes fit only small, bounded, cold indexes.

open as a page