Beginner coding problem · reviewed 2026-08-17

Two Sum with Indices

Learn the two-sum pattern by predicting state, tracing a hash map, comparing brute force, and testing edge cases in Python and TypeScript.

arrayshash mapscomplementssingle-pass algorithmstime complexity

The problem

Given an array of integers and a target, return the indices of two different elements whose values add to the target. Assume exactly one valid pair exists, and do not use the same array element twice.

Constraints that change what “correct” means

  • The input array contains at least two integers.
  • Exactly one valid pair exists for the core exercise.
  • The same index cannot be used twice, even when target = 2 × value.
  • Return indices, not the values themselves.

Examples: predict before reading the explanation

nums = [2, 7, 11, 15], target = 9

Expected: [0, 1]

At index 1 the value is 7, so its needed complement is 2. Index 0 already stored 2.

nums = [3, 2, 4], target = 6

Expected: [1, 2]

The pair is 2 + 4, not 3 + 3, because there is only one 3 and an index cannot be reused.

Build the mental model first

Think first

Before coding, scan [2, 7, 11, 15] from left to right. When you stand on 7, what single value would prove that a pair exists? If you could remember every value already seen together with its index, could you answer that question immediately?

Need a hint?

For each value x, compute target - x. Check whether that complement was seen earlier. Only after checking should you store x, which naturally prevents using the current index twice.

Trace the state before reading code

Trace input: nums = [4, 1, 6, 3], target = 9. Follow the map as it existed before the current value is stored. That detail is the core correctness invariant.

StepIndexValueNeededSeen before this stepDecision
Start045{}5 is absent, so remember 4 → index 0.
Continue118{4: 0}8 is absent, so remember 1 → index 1.
Continue263{4: 0, 1: 1}3 is absent, so remember 6 → index 2.
Match336{4: 0, 1: 1, 6: 2}6 is present at index 2, so return [2, 3].

Quick reasoning checks

Answer before expanding the explanation. These checks focus on the invariant behind the algorithm rather than syntax recall.

Why does the single-pass solution check for the complement before storing the current value?
  1. It makes the array sorted automatically.
  2. It prevents the current element from matching itself.
  3. It reduces the hash map to constant space.
  4. It guarantees there are no duplicate values.

Answer: B. Checking first means the map contains only earlier indices. A value can therefore match another occurrence, but never the same array position.

In TypeScript, why is `earlierIndex !== undefined` safer than `if (earlierIndex)`?
  1. Map.get always returns strings.
  2. Index 0 is falsy even though it is a valid index.
  3. undefined means the target is negative.
  4. Truthiness checks are slower than comparisons.

Answer: B. A valid complement may have been stored at index 0. Testing truthiness would incorrectly treat that valid index as missing.

Primary solutions

Python

def two_sum(nums, target):
    seen = {}
    for index, value in enumerate(nums):
        needed = target - value
        if needed in seen:
            return [seen[needed], index]
        seen[value] = index
    raise ValueError("no valid pair")
  1. seen maps a value to an index where that value appeared earlier.
  2. At each element we ask the problem in reverse: which value would complete the target?
  3. We check before inserting the current value, so a single element cannot match itself.
  4. Once the complement exists, the earlier index and current index form the required pair.

Complexity: O(n) expected time and O(n) additional space because each element is processed once and the hash map can store up to n values.

TypeScript

function twoSum(nums: number[], target: number): [number, number] {
  const seen = new Map<number, number>();
  for (let index = 0; index < nums.length; index += 1) {
    const value = nums[index];
    const needed = target - value;
    const earlierIndex = seen.get(needed);
    if (earlierIndex !== undefined) return [earlierIndex, index];
    seen.set(value, index);
  }
  throw new Error('no valid pair');
}
  1. Map stores values already observed and the index that produced each value.
  2. The lookup asks whether the complement was seen before the current position.
  3. Using get() carefully matters because index 0 is valid; testing the returned index by truthiness would incorrectly treat 0 as missing.

Complexity: O(n) expected time and O(n) additional space under normal hash-map behavior.

Compare with a simpler baseline

Brute-force pair checking

Use one loop to choose the first index and a second loop to inspect every later index.

This is a good correctness baseline because it mirrors the requirement directly and uses constant extra space.

Its weakness is repeated comparison: as the array grows, the number of candidate pairs grows quadratically.

Complexity: O(n²) time and O(1) extra space.

Common mistakes

Store the current value before checking its complement

For nums = [3, 2, 4] and target = 6, the first 3 could immediately find itself and return the same index twice.

Return the two values instead of their indices

The contract asks for positions. Correct arithmetic can still produce the wrong API result.

In TypeScript, test the found index with if (earlierIndex)

Index 0 is falsy in JavaScript. Compare against undefined instead so index 0 remains valid.

Test the algorithm, not just the happy path

CaseInputExpectedWhy it matters
Pair at the beginning[2, 7, 11, 15], target 9[0, 1]Confirms the normal complement path.
Duplicate values[3, 3], target 6[0, 1]Confirms two distinct indices can hold the same value.
Negative number[-4, 8, 5, 12], target 1[0, 2]Confirms the complement model is not limited to positive numbers.

Independent practice

Change the contract so the function returns every unique value pair that reaches the target instead of one index pair. Decide how duplicates should behave before coding, then explain how that contract changes your data structures.

Continue learning