Algorithm

Two Sum

Arrays & Strings Pattern

Two Sum

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. You can return the answer in any order.

CONSTRAINTS
  • 2 <= nums.length <= 10⁴
  • -10⁹ <= nums[i] <= 10⁹
  • -10⁹ <= target <= 10⁹
  • Exactly one valid answer exists
EXAMPLE 1
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
2 + 7 = 9, and the two values sit at different positions. The answer is the pair of indices [0, 1], not the values themselves.
EXAMPLE 2
Input: nums = [3,2,4], target = 6
Output: [1,2]
2 + 4 = 6. Note that [0, 0] would be wrong even though 3 + 3 = 6 — that would use the element at index 0 twice.
EXAMPLE 3
Input: nums = [3,3], target = 6
Output: [0,1]
Two different elements happen to hold the same value 3. Using both is allowed, because they are different positions.
Can I use the same element twice?
No — the two indices must be different. A single 3 cannot pair with itself to reach 6, but two 3s at different indices (as in [3, 3]) are a perfectly valid answer.
Is the array sorted?
No, assume no ordering. Always worth confirming: on a sorted array a different approach that uses no extra memory becomes possible — that variation appears as the follow-up problem, Two Sum II.
Can the numbers be negative, or appear more than once?
Yes to both. Values go down to -10⁹, and the same value may appear at several indices.
What if there are multiple valid answers?
The problem guarantees exactly one solution exists, so you never have to choose between pairs — and never have to handle a no-answer case.

Two Sum hands us a list of numbers and a target, and asks which two different positions hold values that add up to the target. The word positions matters: we must return the two indices, not the values, and we may not use the same position twice. The problem also promises that exactly one valid pair exists — a guarantee that frees us from worrying about ties, or about what to return when nothing matches.

The honest starting point: try every pair

With no better idea yet, we can test every possible pair: hold the first number and try it against each number after it, then hold the second number and try it against each number after that one, and so on until some pair hits the target.

python
for i in range(len(nums)):
    for j in range(i + 1, len(nums)):    # j only looks at positions AFTER i
        if nums[i] + nums[j] == target:
            return [i, j]

One line deserves a pause: the inner loop starts at i + 1, not at 0. That single choice does two jobs. It guarantees j is always a different position than i, satisfying the "don't use the same element twice" rule. And it avoids re-testing pairs in reverse — the numbers at positions (1, 4) add up to the same total as (4, 1), so checking both orders would be pure waste.

Now the cost. The first number gets compared against up to N - 1 partners, the second against N - 2, and so on — roughly N²/2 pair-checks in total for an array of N numbers. Notice what that means: double the array, and the work grows four times, because both loops doubled. This scaling is written O(N²), "quadratic time". At this problem's maximum size, N = 10,000, that is on the order of fifty million checks for a single query. The approach is correct — but it repeats an enormous amount of searching. Where exactly is the waste?

The reframe: you are not searching for "a pair"

Stand on any number x and look at what you actually need. If the target is 9 and x is 2, the only number in the world that can complete the pair is 9 - 2 = 7. Not "some suitable partner" — exactly 7. That required partner, target - x, is called the complement. This reframe changes the problem completely: instead of vaguely searching among roughly N²/2 pairs, we walk the numbers one by one, and at each step search for a single value we can name in advance. The brute force's inner loop was doing precisely that search — by scanning up to N elements every time. Scanning is only necessary when you don't remember what you have already seen. So: what if we remembered?

A structure that remembers: the hash map

A hash map (Python calls it a dictionary) stores entries of the form key → value, and it has the one property this solution depends on: asking "do you contain this key, and what is stored under it?" takes roughly the same, tiny, constant amount of time whether the map holds five entries or five million. The lookup cost does not grow with the map's size. (The trick underneath: from the key itself, the map computes where that key's entry must live, and jumps straight there instead of scanning.) We will use the numbers we have already visited as keys, and the index where we saw each one as its value.

The plan, then: walk the array once. At each number x, first ask the map whether the complement target - x has been seen before. If yes, the stored index and the current index are the answer. If no, record x with its index and step forward.

python
seen = {}                      # value -> index where we saw it
for i, x in enumerate(nums):   # i is the position, x the value stored there
    partner = target - x
    if partner in seen:        # a constant-time question
        return [seen[partner], i]
    seen[x] = i                # record x only AFTER the check above
Crucial Notewe check for the partner before recording the current number, and that order is not an accident. Suppose the target is 6 and we are standing on a 3. If we recorded first, the map would now contain this very 3 — and the lookup for the complement (also 3!) would find it and pair the element with itself, which the rules forbid. Checking first means the map only ever holds earlier positions, so any match is automatically a legal, different index. Genuine duplicates still work: in [3, 3] with target 6, the first 3 is recorded at index 0, and when we reach the second 3 at index 1, the lookup finds index 0 — a different position, exactly as required.
Worked Example:nums = [2, 7, 11, 15], target = 9
- Index 0, x = 2: complement is 9 - 2 = 7. The map is empty — not seen. Record {2: 0}.
- Index 1, x = 7: complement is 9 - 7 = 2. The map has 2, stored at index 0. Answer: [0, 1].
- Done in two steps — 11 and 15 were never even visited.
What we paid, and what we bought

The single pass makes N stops, and each stop performs one constant-time lookup and at most one constant-time insert, so the total time falls from O(N²) to O(N). The price is memory: in the worst case the map ends up remembering nearly all N numbers, so we now spend O(N) extra space where the brute force needed none. This exchange — spending memory to avoid repeated scanning — is one of the most reusable ideas in all of algorithms. Train the reflex now: whenever you catch an inner loop whose only job is "find one specific value I could name before searching", ask whether a hash map could have remembered it. That one question dissolves a whole family of interview problems: pair sums, "have I seen this before?" checks, frequency counting. And one closing thought worth saying aloud in an interview: everything above assumed the array is unsorted. If it were sorted, a different technique that needs no extra memory becomes possible — that is exactly the follow-up problem, Two Sum II.

Interactive Strategy Visualization

One-Pass Hash Map

Checking for complement while iterating

ARRAY
CUR
2
0
5
1
9
2
11
3
3
4
6
5
HASH MAP (Val → Idx)
Empty
CURRENT
2
NEED
7
Current: 2. Target: 9. Need: 9 - 2 = 7. Check Map.

Key Insight

We can iterate through the array once. For each element, we calculate the complement (Target - Current). If this complement is already in our hash map, we found the pair!

Time & Space

Time: O(N) because we traverse once. Space: O(N) because the hash map stores up to N elements.

O(N²) Time · O(1) Space
O(N) Time · O(N) Space