Hashing interview patterns
The single most useful move in algorithm interviews is this: turn two nested loops (O(n²)) into one loop (O(n)) with a hash structure.
The logic is always the same. A nested loop is really asking: "is there another element matching this one?" Instead of answering that by rescanning the array every time, you keep what you have already seen in a Map or Set — and the question becomes an O(1) lookup.
The price is O(n) extra memory. This is the classic time–space trade-off: you spend memory to buy time.
Saying that transition out loud in an interview is a very strong signal: "The naive solution is O(n²) with nested loops. But if I keep the values I have already seen in a hash map, each lookup becomes O(1) and the whole solution drops to O(n) time and O(n) memory."
| Pattern | Structure | How you recognise it | Complexity |
|---|---|---|---|
| Frequency counter | Map: value → count | "how many times", "most frequent", "is it an anagram" | O(n) time / O(k) memory |
| Complement lookup | Map: value → index | "two numbers summing to target" | O(n) / O(n) |
| Bucketing / grouping | Map: normalised key → list | "group by", "collect the ones sharing ..." | O(n·k) / O(n) |
| Seen-set | Set: elements already seen | "any duplicate", "is there a cycle", "unique" | O(n) / O(n) |
| Prefix sum + map | Map: prefix sum → count/index | "number of subarrays summing to k" | O(n) / O(n) |
Two patterns deserve extra explanation, because otherwise their "click" gets memorised without ever being felt.
Complement lookup (two-sum). The naive solution checks every pair. Instead, think of it this way: for nums[i] the required partner is exactly known — target - nums[i]. So the question is not "walk the pairs" but "have I seen this specific value before?" You keep every value seen so far in a map with its index and find the answer in one pass. A key subtlety: get first, then set — otherwise an element can pair with itself.
Prefix sum + map. In "how many subarrays sum to k?", sum(i..j) = prefix[j] - prefix[i-1]. Rearranged: prefix[i-1] = prefix[j] - k. So standing at j you ask: "how many times have I seen the value prefix - k so far?" Map: prefix sum → how many times it occurred. The seed is mandatory: counts.set(0, 1), to count subarrays starting at the very beginning of the array. Forgetting that one line is the most common mistake on this task.
Interview tip. What interviewers look for here is not just correct code — it is the sequence of reasoning. A strong candidate's script sounds like this:
1. "The naive solution is nested loops — O(n²)." 2. "But what I am really asking is 'have I seen this value before?', and that is a hash lookup." 3. "So I can do it in one pass: O(n) time, O(n) extra memory." 4. "Edge cases: empty array, repeated values, an element pairing with itself."
The three most commonly missed points: (1) not stating the memory cost — always say the O(n) extra memory out loud; (2) skipping edge cases; (3) not clarifying that hashing is O(1) on average, not worst case.
And a note on honesty: if n is genuinely small (say up to 100), the O(n²) solution is perfectly acceptable, and saying so reads as engineering maturity, not weakness.