Skip to content
beginner

Two Sum

A strong Two Sum solution replaces repeated pair scanning with one sharper question: has the array already shown us the value this number needs?

Published 2026-08-28Updated 2026-09-127 min read
A dark-themed chat interface displaying an AI assistant conversation starter on a screen.
A dark-themed chat interface displaying an AI assistant conversation starter on a screen. Photo by Matheus Bertelli on Pexels.
Problem

Two Sum

Difficulty: EasyAcceptance rate: 58.1%

Given an integer array nums and an integer target, return the indices of the two distinct elements whose values add up to target.

ArrayHash Table

Constraints

  • 2 <= nums.length <= 10^4
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9

Important details

  • Each input has exactly one valid pair of indices.
  • The same array element cannot be used twice.
  • The returned indices may be in any order.

A strong Two Sum solution replaces repeated pair scanning with one sharper question: has the array already shown us the value this number needs?

The Core Idea: Look Up the Complement

Given an integer array nums and an integer target, return the indices of two different elements whose values add up to target. The problem guarantees exactly one valid answer, and the indices may be returned in either order.

The elements must be different positions, but they do not need to have different values. For example, [3, 3] with target 6 is valid because the two 3s occur at indices 0 and 1.

For a current value nums[i], the missing partner is deterministic:

complement = target - nums[i]

That turns pair search into a lookup problem. Instead of repeatedly scanning the rest of the array, remember values already seen and the indices where they occurred. The hash map becomes a memory indexed by value; the current number asks it for one specific key.

Recognition rule: If a problem asks whether a value needed by the current element has appeared earlier, consider a hash map that stores the required lookup key and its useful payload.

Two Sum does not depend on sorted input, so sorting and two pointers are not the natural first move. The leverage comes from keyed memory: trade O(n) extra space for a one-pass, O(n) average-time search.

Start with the Brute-Force Baseline

The direct solution checks every pair of distinct indices:

def two_sum_brute(nums, target):
    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            if nums[i] + nums[j] == target:
                return [i, j]

This is a useful baseline, not wasted work. Starting j at i + 1 examines every unordered pair once and prevents an element from pairing with itself. The algorithm is correct because any valid answer must appear among those pairs.

Its cost is the problem. For n values, the nested loops perform up to n(n - 1) / 2 comparisons, which is O(n²) time. The extra space is O(1), excluding the input.

The optimization should preserve the same obligation—find two indices whose values sum to the target—while removing the repeated scan.

Derive the One-Pass Algorithm

Scan the array from left to right. Maintain a map called seen with this invariant:

Before processing index i, seen maps values from earlier indices to one of their earlier indices.

For the current num, there are two cases:

  1. target - num is already in seen. The stored index and i form the answer.
  2. The complement is absent. No earlier value can pair with num, so record num and i for a later element.

The order matters: check first, then insert.

Suppose num == 3 and target == 6. At the first 3, the complement is also 3. If you inserted before checking, the value could match its own index and produce an invalid pair such as [i, i]. Checking only the processed prefix makes every successful match use two distinct positions.

This gives each piece of state a clear job:

  • complement identifies the exact value required to complete the sum.
  • seen stores the earlier values that may satisfy that requirement.
  • The map’s value is an index because the problem asks for positions, not just numbers.

The code is short because the invariant does the heavy lifting.

Python Implementation

def two_sum(nums, target):
    seen = {}

    for i, num in enumerate(nums):
        complement = target - num

        if complement in seen:
            return [seen[complement], i]

        seen[num] = i

Read each iteration as three verbs:

  1. Compute the missing partner.
  2. Search the earlier prefix for that partner.
  3. If it is absent, remember the current value and index.

The function returns as soon as it finds the pair. The supplied contract guarantees one solution, so continuing would only do unnecessary work. If you reuse this pattern for a different problem that allows no solution, define the fallback behavior explicitly; it is outside this contract.

Dry Run: Watch the Map Become Useful

A two-step trace for nums [2, 7, 11, 15] with target 9: index 0 stores value 2 at index 0, then index 1 computes complement 2, finds it in seen, and returns indices [0, 1].
The map becomes useful when the current value finds its complement in the processed prefix.

Take nums = [2, 7, 11, 15] and target = 9:

IndexValueComplementseen before lookupAction
027{}Store 2: 0
172{2: 0}Return [0, 1]

At index 1, the current value is 7, so it needs 2. The map already remembers that 2 appeared at index 0. The answer is assembled from the old index and the current index.

Now use the duplicate case:

nums = [3, 3]
target = 6

The first 3 finds no earlier complement and is stored as {3: 0}. The second 3 finds that entry and returns [0, 1]. Equal values are allowed; reusing one position is not.

The map is not a second copy of the array. It is a purpose-built record of the past, shaped around the question the current element needs to ask.

Correctness: Why the Invariant Holds

At the beginning of an iteration, seen contains only values from indices before i.

  • If the complement is present, its stored index j satisfies j < i. By definition, nums[j] == target - nums[i], so nums[j] + nums[i] == target. The indices are distinct and the returned pair is valid.
  • If the complement is absent, no earlier index can form the target with nums[i]. Storing seen[num] = i extends the remembered prefix for the next iteration.

The invariant therefore remains true after every iteration. Because the problem guarantees a valid pair, the scan eventually reaches an iteration where the complement is present.

This is the difference between a plausible implementation and a proved one: the map is not merely convenient storage. It represents exactly the earlier state required by the next decision.

Complexity and Edge Cases

Each element is processed once. Python dictionary membership and assignment take O(1) time on average, so the algorithm uses:

  • Time: O(n) average case
  • Extra space: O(n) worst case

The space trade is deliberate. We spend memory to eliminate repeated scanning. If the matching pair appears near the end, the map may hold nearly every previous value.

Important cases to verify:

  • Two elements: The first is stored; the second can find its complement.
  • Duplicate values: [3, 3] with target 6 returns two different indices.
  • Zeroes: [0, 0] with target 0 works because the second zero finds the first.
  • Negative values: In [-3, 4, 3, 90] with target 0, the 3 at index 2 finds -3 at index 0.
  • Negative target: target - num still directly computes the required value; no special branch is needed.
  • No solution: The supplied problem contract excludes this case, so the function does not need to invent a sentinel return value.

When debugging, inspect the state rather than staring at the return line. For a small input, trace i, num, complement, and seen. Read the error, trace the state, fix the assumption. That habit transfers well beyond this coding interview Two Sum problem.

The Transferable Pattern

Two Sum teaches a durable move for arrays and hashing: identify what the current state needs, then ask whether earlier work has already recorded it.

Here, the key is a number and the payload is its index. In another problem, the payload might be a count, a first position, a running total, or a grouped collection. The reusable derivation is:

  1. Name the missing partner or required state.
  2. Decide what earlier information must be remembered.
  3. Check that memory before updating it.
  4. State the invariant that makes a hit correct.

My interview rule is simple: derive the invariant before polishing the syntax. Once you can explain what the map remembers, why lookup precedes insertion, and why the cost changes from quadratic to linear average time, the implementation is mostly transcription. The real skill is recognizing when memory can replace a scan.

References

  1. Two Sum - LeetCodeleetcode.com
8sources checked
8source domains
5searches run

Research updated Sep 5, 2026

Related sites

Strengthen the language foundations behind the solution

Use LearnPyFast and LearnJSFast when you want to reinforce the language mechanics that support interview implementations.

Python tutorialstutorial

LearnPyFast

Beginner-friendly Python tutorials, examples, and learning paths for practical programming foundations.

PythonProgrammingBeginners
Visit LearnPyFast
JavaScript tutorialstutorial

LearnJSFast

Beginner-friendly JavaScript tutorials for practical web development and self-taught developers.

JavaScriptFrontendWeb development
Visit LearnJSFast

Keep grinding

Related coding interview problems

Continue with nearby problems that reuse the same data-structure, invariant, or optimization pattern.

Person interacts with robot images on a screen in a dark room, highlighting technology use.
intermediate
9 min read

Group Anagrams

A useful Group Anagrams solution does not compare every string with every existing group. It assigns each string a stable identity based on its character…

View solution
A modern open laptop with a black screen placed on lush green grass, symbolizing technology and nature.
intermediate
8 min read

Valid Sudoku

A Sudoku validator does not solve the puzzle. It tracks whether the digits already placed violate any row, column, or 3×3 box constraint.

View solution