PYTHON & CODING · QUESTION 22
How do you find two numbers that add up to a target?
On this page
Scan the numbers once while storing values already seen and their indices in a dictionary. For each value, compute the complement needed to reach the target and check whether it is in the dictionary. If so, return the earlier and current indices; otherwise store the current value. This takes O(n) expected time and O(n) extra space, assuming dictionary operations are expected O(1).
LET'S MAKE IT SIMPLE
First, the idea in plain English
A slow approach checks every possible pair, which repeats work as the list grows. Instead, while looking at a number, ask what other number would complete the target and whether you have already seen it. A dictionary makes that lookup fast on average. You store indices so the answer can refer to the original positions, and check before adding the current value so one element is not used twice.
A USEFUL COMPARISON
Picture it this way
Imagine looking for two price tags that total a budget while walking through a store. For each price, you calculate the amount still needed and check a list of prices already passed. The dictionary is that quick lookup list. The analogy explains the complement idea; a program must also handle repeated values and define what to return when no pair exists.
FOLLOW THE JOURNEY
Remember seen values to avoid checking every pair
Trace the complement lookup that reduces pair search from quadratic to linear expected time.
Checking before storing prevents the current array position from pairing with itself. A dictionary assignment can overwrite duplicate values, so consider the contract if all pairs are required.
The algorithm scans values, calculates target minus current value, checks a dictionary of previous values, and either returns both indices or stores the current value.
How to read this visual
Checking before storing prevents the current array position from pairing with itself. A dictionary assignment can overwrite duplicate values, so consider the contract if all pairs are required.
UNDER THE HOOD
How it works, step by step
Clarify the result
Confirm whether the caller wants indices or values and what to do if no pair exists.
Why it matters: The contract determines edge-case behavior.
Compute the complement
For each number x, calculate target - x.
Why it matters: Any matching earlier number must equal that complement.
Look up before storing
Check the dictionary for the complement, then store the current value and index if absent.
Why it matters: This avoids pairing an element with itself.
State complexity
The scan is O(n) expected time and the dictionary can hold O(n) entries.
Why it matters: This explains the time-space trade-off against checking every pair.
OUTSIDE THE TEXTBOOK
A practical example
For values [4, 7, 1, 9] and target 8, the algorithm first stores 4. At 7, it needs 1, which has not appeared, so it stores 7. At 1, it needs 7, finds the earlier index, and returns the positions of 7 and 1.
GO A LITTLE DEEPER
Technical details that matter
- A brute-force solution checks all pairs in O(n²) time and O(1) extra space; the dictionary trades memory for expected linear time.
- If the input is sorted and only values are required, two pointers can use O(1) extra space after sorting, though sorting costs O(n log n) and changes index handling.
- For duplicate values, checking before insertion allows a pair such as target 6 from [3, 3].
- Python dictionaries use hashing; O(1) lookup is expected, not a strict worst-case guarantee for every possible key distribution.
def two_sum(numbers: list[int], target: int) -> tuple[int, int] | None:
seen: dict[int, int] = {}
for index, value in enumerate(numbers):
complement = target - value
if complement in seen:
return seen[complement], index
seen[value] = index
return NoneThe function returns the first matching pair of indices, or None when no pair exists. Looking up the complement before inserting the current value prevents reusing the same element.
MAKE A GOOD CHOICE
When is this useful?
- Finding pairs that meet a target in a single-pass data stream.
- Recognizing when a hash map can replace repeated nested-loop searches.
- Practicing how to explain a brute-force baseline and a time-space optimization.
If only values are needed and the list can be sorted, a two-pointer approach may reduce auxiliary space. If all matching pairs are required, define duplicate and output-size behavior before choosing the algorithm.
WATCH OUT FOR
Common misunderstandings
- Returning values when the problem specifically asks for indices.
- Adding the current value before checking and accidentally using one element twice.
- Claiming worst-case O(1) hash lookup without qualification.
REAL-WORLD TRADE-OFFS
Limitations and failure cases
- The dictionary requires O(n) additional memory.
- The first pair found may not be the only pair; output requirements can change the solution.
- If input values are not hashable or arrive under a different constraint, the approach must change.
WHAT TO REMEMBER
Key takeaways
- 1Use target minus current value to find the required complement.
- 2Store previously seen values and their indices in a dictionary.
- 3Check before storing to avoid reusing one array position.
- 4Explain expected O(n) time and O(n) space.
PRACTICE THE FOLLOW-UP
What might an interviewer ask next?
Why check the dictionary before inserting the current value?
It prevents the current element from matching itself, such as using a single 4 twice to reach 8.
What if the array is sorted?
Two pointers can scan inward with constant extra space when values are required. If original indices are required, preserve their positions through sorting.
How would you return all pairs?
The contract must define whether pairs are distinct by value or by index. Track frequencies or indices and ensure duplicates do not create repeated or invalid results.
SAY IT OUT LOUD
How to explain this in an interview
“I would use a dictionary from previously seen values to their indices. For each number, I calculate the complement, check whether it is already present, and return the two indices if it is. Otherwise, I store the current value and continue. Checking before insertion prevents using one element twice. The expected time is O(n), with O(n) extra space.”
Connect this question to the roadmap topic it supports.