Understanding the problem
The scan you write without thinking
Almost every array question starts the same way. You need to know something about a value relative to the others — has it appeared before, how often, which index — and the first solution is to go and look:
for i in range(n):
for j in range(n):
if i != j and a[i] == a[j]:
return True
It is correct. On the eight-element example in the question it returns immediately. Then the array is a hundred thousand elements and the inner scan runs a hundred thousand times for each of them: ten billion comparisons, several minutes, for a question whose answer takes a fraction of a second.
The waste is specific and worth naming. You are re-deriving the same fact — which values are present — once per element, and throwing it away each time.
What hashing actually replaces
A hash map turns "is this value present?" from a walk over the array into an arithmetic operation. The key is run through a hash function to get a bucket number, and the answer is wherever that bucket points. The array's length stops mattering.
So the inner loop disappears:
seen = set()
for x in a:
if x in seen:
return True
seen.add(x)
One pass, one O(1) question per element, O(n) overall. The saving is not a constant factor; it is a change of shape, from n² to n.
Say "expected O(1)", not "O(1)", if you are asked precisely. Hashing is constant on average. Adversarial keys can collide into one bucket and degrade a lookup to O(n) — which is why hash functions in real languages are randomised per process.
The decision is what to key on
Once you know the tool, the problem is no longer "use a hash map". It is choosing the key so that the thing you want becomes a lookup. Three problems, three keys:
- Duplicate detection — key on the value itself. Present or not.
- Two numbers summing to a target — key on the value, look up
target − x. You are asking whether the partner has already gone past. - Subarrays summing to k — key on the running total, not on any element. Two equal prefix sums mean the stretch between them sums to zero, and the general version of that gives the whole answer in one pass.
That last one is the jump worth practising. Nothing in the question mentions prefix sums; you have to notice that the quantity worth remembering is not any value in the array but a fact about everything seen so far.
Look up before you insert
One ordering bug accounts for most wrong answers in this pattern. Inside the loop you both query the map and update it, and the order matters:
for i, x in enumerate(a):
if target - x in seen: # query first
return (seen[target - x], i)
seen[x] = i # then insert
Insert first and an element can match itself. On a = [3, 5] with target 6, inserting
3 before querying finds 6 − 3 = 3 already present and reports the pair
(0, 0). The array does not contain two 3s; you matched the element with its own
reflection.
The same bug appears as an off-by-one in counting problems, where it silently inflates the total instead of returning an impossible index. That version is harder to spot, which is why the habit is worth fixing now: query, then insert, always.
When not to reach for it
Hashing buys time with memory, and there are two cases where the trade is bad.
The input is already sorted. Sortedness is information you have already paid for. Two pointers exploit it in O(1) extra space, where a hash map spends O(n) to ignore it. If a question goes out of its way to tell you the array is sorted, that is the interviewer telling you which pattern they want.
The data does not fit. A map over a hundred million distinct keys is gigabytes. At that size an external sort followed by a linear scan wins, despite being asymptotically worse, because it streams. Knowing that O(n log n) can beat O(n) in practice is the kind of answer that separates candidates.
Every problem in this section is built on the same reflex — notice the repeated lookup, pick the key that collapses it — and they are ordered so the key gets less obvious as you go: the value itself, then a count, then a running total, then a running product, and finally no hash map at all, because the array has to become one. Do them in order.