3.3.3 · D2Hashing

Visual walkthrough — Chaining — linked lists in buckets, load factor, resizing

2,498 words11 min readBack to topic

Step 1 — Draw the table and name every symbol

WHAT. A hash table is just an array of slots, numbered . Into it we have already dropped keys. Each key was sent to a slot by a rule called the hash function : feed it a key , it hands back a slot number .

WHY start here. Before we can say "the search is fast," we must fix what we are counting. We are counting how many keys sit in the slot we land on — because a chained search walks exactly that slot's list, one node at a time.

PICTURE. Below: slots (the boxes), keys (the dots) already dropped in. The slot we will search — call it slot — is marked in red. The little tower of dots above it is the linked list we must walk.

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 2 — The one assumption that makes counting possible

WHAT. We assume every key, when dropped, is equally likely to land in any of the boxes, and the keys don't influence each other. This is called Simple Uniform Hashing.

WHY this assumption and not another. Without some rule about where keys land, an enemy could throw all keys into one box and there'd be nothing to prove. "Equally likely, independent" is the cleanest statement of "a good hash spreads keys evenly" — it is exactly what Hash Functions tries to achieve in practice. It lets us treat each drop like rolling a fair -sided die.

PICTURE. One key facing boxes, each door the same width, each with the same chance written on it. The red door is slot — but it is no more likely than any other.

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 3 — Turn "is it in the box?" into a number you can add

WHAT. For each key we invent a tiny counter that is if landed in box , and otherwise. Call it . This is an indicator variable — a switch that reports "yes/no" as "/".

WHY do this. We want the total pile height in box . If every key waves a flag showing (I'm here) or (I'm not), then adding all the flags gives the pile height. Turning a yes/no question into a number is the trick that lets us use plain addition.

PICTURE. The eight keys, each carrying a flag: red flag (landed in box ), black flag (landed elsewhere). Summing the red flags = height of the red tower.

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 4 — Add the switches to get the expected pile height

WHAT. Let = the length of box 's chain = the sum of all the switches. Take its average.

WHY. The pile height changes from run to run (randomness), so we ask for the average height. A beautiful rule — the average of a sum is the sum of the averages — lets us add the tiny averages instead of wrestling with the whole pile at once. This rule is called linearity of expectation, and its magic is that it does not require the switches to be independent: even if knowing one flag told you something about another, the average of their sum still equals the sum of their averages. So we may add them freely.

PICTURE. flags of value being poured into one bucket; the bucket's reading settles at . That reading gets its own name: .

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 5 — The unsuccessful search: walk the whole pile, plus one

WHAT. We search for a key that is not in the table. We compute (one step), jump to that box, and scan the entire chain because is never found.

WHY "whole pile plus one". The "" is unavoidable no matter what: you must compute the hash and touch one box even if it's empty. The "" is the average number of nodes you then flip through. Because is absent, you cannot stop early — you walk to the end.

PICTURE. A red pointer computing (cost ), landing on a box, then stepping through every node of the chain (cost = its length, averaging ).

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 6 — The successful search: count the nodes you pass before stopping

WHAT. Now is in the table. You do not scan the whole chain — you stop the instant you reach . The cost is (the hash) plus the number of nodes you touch up to and including . We now derive that number by counting, exactly as in Steps 3–4.

WHY count "who came before whom". Insertion prepends at the head, so a node sits in front of in its chain exactly when it was inserted after and hashed to the same box. So the nodes you must pass before reaching are precisely the keys added later that collided with . Counting those, key by key, is the whole job.

PICTURE. Chain drawn head-to-tail. glows red. Every node inserted after (the ones nearer the head) is shaded — those are the ones you step over. Nodes inserted before sit behind it and are never touched.

Figure — Chaining — linked lists in buckets, load factor, resizing

Step 7 — The edge cases: empty box, giant pile, and the adversary

WHAT. We must not leave a scenario undrawn. Three extremes:

  1. Empty box (): cost is just the — compute hash, find nothing, done.
  2. Balanced table ( constant, e.g. ): is a constant → expected . This is the whole reason chaining resizes to hold down.
  3. Adversarial collapse (all keys in one box): the uniform assumption of Step 2 is violated; the pile is height , so a search is . This is the true worst case.

WHY show all three. The formula is an average under Step 2's assumption. When that assumption holds you get ; when it is broken you get . A reader must see both ends.

PICTURE. Three tables side by side: an empty red box (cost ), a short-and-even table (), and one red skyscraper chain holding all keys ().

Figure — Chaining — linked lists in buckets, load factor, resizing

The one-picture summary

Figure — Chaining — linked lists in buckets, load factor, resizing

Read it left to right: a key faces equal doors () → keys pour in, so a box averages dots → a search pays to reach the box plus to walk it → total . Keep small and that whole chain of pictures reads "".

Recall Feynman retelling — the whole walk in plain words

Picture a wall of mailboxes and letters. A fair rule scatters letters so each box has the same chance, , of catching any given letter. Give every letter a flag: raise it if the letter landed in your box. Add the raised flags to get how tall the pile in your box is — since each letter raises its flag of the time and there are letters, the average pile is , which we call the load factor . To find a letter you walk to its box (that costs ) and flip through the pile. If the letter isn't there you flip the whole pile (). If it is there you stop when you reach it — on average after flipping only half the pile (), because only the letters dropped in after it sit in front of it. If the box is empty you still paid . Only if a cheating rule dumps every letter in one box does the pile become tall and the search slow. That single "fair scatter" assumption is what turns a wall of mailboxes into a machine that answers in constant time.

Recall Predict then verify

Where does the "" in come from? ::: Computing the hash and touching one box — paid even if the box is empty. Where does the "" come from? ::: Summing indicator switches, each averaging , gives expected nodes to walk. In a successful search, why does the appear? ::: You only pass keys inserted after the target; averaging over targets turns the triangular sum into a half. Why is successful search also ? ::: Big-O ignores the constant , so sits in the same class. Which step breaks in the worst case? ::: Step 2 (uniform hashing); an adversary makes one pile of height , giving .


Connections

  • Chaining — linked lists in buckets, load factor, resizing — the topic this derivation supports.
  • Hash Functions — supplies the "fair scatter" of Step 2; break it and the proof collapses.
  • Linked Lists — the pile we walk in Steps 5–6.
  • Pigeonhole Principle — why collisions (piles taller than one) must exist at all.
  • Open Addressing — the rival scheme, which instead dies at .
  • Amortized Analysis — defines big-O rigorously and keeps small via resizing.
  • Dynamic Arrays — same doubling logic that bounds .