Theory

What the ordering costs

The same library, the same 10,000 questions, three structures:

$ algo tree -in s10k.jsonl
n = 10000 distinct titles

  tree height      30   (log2 n = 13.3)
  in order?        true

                      cmps/lookup     build cmps  lookup time
  tree                     16.703         157025      3.108ms
  hash (lesson 10)          1.310           7198      1.001ms
  linear (lesson 1)         5000.5              0        125ms

  build time: tree 4ms, hash 2.501ms

  keys in sorted order: tree yes (walk), hash no (must sort all 10000)

The tree loses to the hash table by 12.8× on the one question "is this title present". Building it costs 157,025 comparisons against 7,198 — twenty-two times more.

Say that plainly rather than stepping around it. If your program only ever needs exact-key lookup, the hash table is the right choice and a tree would be a mistake.

Where those 16.7 go

The height is 30 and log2(10000) is 13.3. The tree is more than twice as deep as an ideal one.

That is expected. No node is looking after the balance — the tree takes whatever shape insertion order gives it, and random order gives a lopsided one rather than a catastrophic one. Measured:

$ for n in 100 1000 10000 100000; do algo gen -n $n -seed 7; algo tree; done

        n   height   log2 n    cmps/lookup
      100       13      6.6          7.710
     1000       22     10.0         12.273
    10000       30     13.3         16.703
   100000       41     16.6         21.367

A hundredfold increase in n, from 1,000 to 100,000, moved the cost from 12.3 to 21.4 — less than double. That is what a logarithm is: it grows, and the growth dies away.

Divide the columns into each other and a steady multiple appears:

n height ÷ log2 n comparisons ÷ log2 n
100 1.97 1.17
1,000 2.20 1.23
10,000 2.26 1.26
100,000 2.47 1.29

Not 1.0, but not a growing quantity either. The tree is roughly twice as deep as ideal and stays that way — which is exactly what "O(log n) with a worse constant" means.

The hash table's column in the same table would have read 1.31 throughout, with no log n in it at all.

Memory: measure it, do not assume

A tree node carries two pointers where a chain entry carried one:

Item         56 bytes
entry (L10)  80 bytes  (key + record + 1 pointer)
node  (L11)  88 bytes  (key + record + 2 pointers)

Eight bytes per node — so the tree looks more expensive. Now add up the whole structure at n = 10,000:

hash: 10,000 × 80 B  +  16,384 buckets × 8 B  =  931,072 B
tree: 10,000 × 88 B                           =  880,000 B

In total the tree uses less. Because the hash table does not only pay for its entries — it pays for its bucket array too, and that array is precisely what buys it the short chains. At load factor 0.61 there are 8,925 empty buckets, and every one of them takes up room.

Per node the tree costs more; per structure it does not. Which way it lands depends on the load factor you chose in lesson 10.

The trade, in two lines

Hash table: 1.3 comparisons per question, and one question. Tree: 16.7 comparisons, and six questions.

The second column is not "the same thing, but slower". It is a different product.