datarekha
Python Easy Asked at GoogleAsked at AmazonAsked at MetaAsked at MicrosoftAsked at Apple

Solve the Two Sum problem. Given a list of integers and a target, return the indices of the two numbers that add up to the target. What is the optimal complexity?

The short answer

The hashmap approach solves Two Sum in O(n) time and O(n) space by storing each number's index as you iterate and checking whether the complement has already been seen. A brute-force nested loop is O(n²) and unnecessary.

How to think about it

The best way to start this one is out loud, not in code: “I want to avoid the nested loop. For each number I just need to know whether its complementtarget − x — has already appeared, so I’ll keep a dict of what I’ve seen and check it as I go. One pass, O(n).” That sentence tells the interviewer you understand why the hashmap is the answer, not just that it is.

The complement is the whole idea. If x = 7 and target = 9, you need a 2 — and instead of rescanning the array, you ask the dict “have I seen a 2?” in O(1). The one subtlety is order: store each number after checking, so you never pair an element with itself.

A worked example

def two_sum(nums, target):
    seen = {}                       # value -> index
    for i, x in enumerate(nums):
        complement = target - x
        if complement in seen:
            return [seen[complement], i]
        seen[x] = i                 # store AFTER the check, never reuse an index
    return []                       # no pair found

print(two_sum([2, 7, 11, 15], 9))   # [0, 1]
print(two_sum([3, 2, 4], 6))         # [1, 2] — complement comes later
print(two_sum([3, 3], 6))            # [0, 1] — two separate equal elements
print(two_sum([1, 2, 3], 99))        # []     — no solution

# Trace the seen-dict to watch the single pass work
def two_sum_verbose(nums, target):
    seen = {}
    for i, x in enumerate(nums):
        complement = target - x
        print(f"  i={i}, x={x}, need={complement}, seen={seen}")
        if complement in seen:
            return [seen[complement], i]
        seen[x] = i
    return []

print("Trace for [2,7,11,15], target=9:")
two_sum_verbose([2, 7, 11, 15], 9)
[0, 1]
[1, 2]
[0, 1]
[]
Trace for [2,7,11,15], target=9:
  i=0, x=2, need=7, seen={}
  i=1, x=7, need=2, seen={2: 0}

Read the trace. At i=0 we look for 7, don’t find it, and remember 2→0. At i=1, x=7 needs 2 — already in seen at index 0 — so we return [0, 1] without ever scanning forward. That’s why a single pass suffices: if two numbers pair up, the earlier one is always already recorded by the time we reach the later one.

A common variant — return the values, not the indices:

def two_sum_values(nums, target):
    seen = set()
    for x in nums:
        if target - x in seen:
            return (target - x, x)
        seen.add(x)

This same “have I already seen what I need?” pattern is the seed of three-sum (fix one element, two-sum the rest) and a lot of sliding-window problems. Whenever a question asks “does a value satisfying some relation exist?”, reach for a dict or set.

Keep practising

All Python questions

Explore further

Skip to content