LeetCode #1 — Easy

Two Sum

A complete visual walkthrough from brute force to the optimal hash map approach — the classic interview warm-up.

Solve on LeetCode
The Problem

Problem Statement

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.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

Constraints:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • Only one valid answer exists.
Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?

Roadmap

  1. The Brute Force Approach
  2. The Core Insight — Hash Map Complements
  3. Algorithm Walkthrough
  4. Edge Cases
  5. Full Annotated Code
  6. Interactive Demo
  7. Complexity Analysis
Step 01

The Brute Force Approach

The simplest solution: try every possible pair of elements and check if they sum to the target. Given nums = [2, 7, 11, 15] and target = 9:

Checking All Pairs

2
7
11
15
2 + 7 = 9
2 + 11 = 13
2 + 15 = 17
7 + 11 = 18
7 + 15 = 22
11 + 15 = 26

We check n(n-1)/2 pairs. For 4 elements that's 6 comparisons. For 10,000 elements? Nearly 50 million.

// Brute force: O(n^2) time, O(1) space
for (int i = 0; i < nums.length; i++) {
    for (int j = i + 1; j < nums.length; j++) {
        if (nums[i] + nums[j] == target)
            return new int[] { i, j };
    }
}

This works, but the nested loops give us O(n2) time. Can we do better?

Step 02

The Core Insight — Hash Map Complements

Instead of asking "which two numbers add up to target?" — rephrase: for each number, ask "have I already seen the number that completes this pair?"

The Complement Trick

For every element nums[i], its complement is target - nums[i]. If the complement already exists in our hash map, we found the pair.

complement = target - nums[i]
What number do I need to reach the target?
map.containsKey(complement)
Have I already seen that number?
💡

One-pass strategy: As we iterate, we store each number and its index in a hash map. Before storing, we check if the complement is already there. This means we only need a single pass through the array — O(n) time instead of O(n2).

Step 03

Algorithm Walkthrough

Let's trace through nums = [2, 7, 11, 15] with target = 9, building the hash map one element at a time.

Iteration 0 : nums[0] = 2

2
7
11
15

Complement = 9 - 2 = 7. Is 7 in the map? No. Store {2 : 0}.

Key (value)Value (index)
20

Iteration 1 : nums[1] = 7

2
7
11
15

Complement = 9 - 7 = 2. Is 2 in the map? Yes, at index 0!

2
7
11
15
return [0, 1]

Notice we found the answer on the second iteration. The brute force would have found it on the first pair too, but imagine the answer was at indices [0, 9999] in a 10,000-element array. The hash map approach still finds it in at most n lookups, while brute force might need nearly n2/2.

Step 04

Edge Cases

The hash map approach handles all these naturally, but it's good to verify your understanding.

Negative Numbers
nums = [-3, 4, 3, 90], target = 0
complement of -3 is 3. complement of 3 is -3. Works with signed arithmetic.
Duplicate Values
nums = [3, 3], target = 6
First 3 is stored at index 0. Second 3 finds complement 3 already in map. Returns [0, 1].
First & Last Elements
nums = [1, 5, 8, 3], target = 4
The pair [1, 3] is at indices 0 and 3. The map stores all prior elements, so this works at any distance.
Minimum Array (length 2)
nums = [5, 4], target = 9
i=0: store 5. i=1: complement = 5, found in map. Always exactly one answer guaranteed.
📝

Why "store after checking" matters: We store nums[i] in the map after checking for the complement. This ensures we never match an element with itself. For [3, 3] with target 6, index 0's "3" is stored, then index 1's complement lookup finds index 0 — two different indices.

Step 05

Full Annotated Code

class Solution {
    public int[] twoSum(int[] nums, int target) {

        // Map from value -> index for O(1) complement lookup
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < nums.length; i++) {

            // What number do we need to reach the target?
            int complement = target - nums[i];

            // Have we already seen that number?
            if (map.containsKey(complement)) {
                // Found it! Return both indices
                return new int[] { map.get(complement), i };
            }

            // Store current value and its index for future lookups
            map.put(nums[i], i);
        }

        // Problem guarantees exactly one solution exists
        throw new IllegalArgumentException("No two sum solution");
    }
}
Step 06

Interactive Demo

Enter your own array and target, then step through the hash map algorithm one iteration at a time.

⚙ Hash Map Visualizer



HashMap
(empty)
Press "Step →" or "Run All" to begin.
Step 07

Complexity Analysis

Time
O(n)
Space
O(n)

We iterate through the array once. Each hash map lookup (containsKey) and insertion (put) is O(1) amortized, so the total time is O(n). In the worst case, we store all n elements in the map before finding the answer, giving O(n) space.

Approach Breakdown

BRUTE FORCE
O(n2) time
O(1) space

Two nested loops check every pair. The outer loop picks one element, the inner scans the rest -- n*(n-1)/2 comparisons total, which is O(n2). Only two index variables are allocated.

SORT + TWO PTR
O(n log n)
O(n) space

Sorting dominates at O(n log n). After sorting, two pointers converge from both ends in O(n). A copy of the original array is needed to recover the original indices, costing O(n) space.

HASH MAP
O(n) time
O(n) space

Single pass through the array with O(1) amortized hash lookups. Each of n elements does one lookup and one insert. In the worst case all n elements are stored in the map before finding the pair.

🎯

The classic time-space tradeoff: We spend O(n) extra memory on the hash map to eliminate the inner loop entirely. This is one of the most fundamental patterns in algorithm design — trading space for time.

Coach Notes

Common Mistakes

Review these before coding to avoid predictable interview regressions.

Mutating counts without cleanup

Wrong move: Zero-count keys stay in map and break distinct/count constraints.

Usually fails on: Window/map size checks are consistently off by one.

Fix: Delete keys when count reaches zero.

Off-by-one on range boundaries

Wrong move: Loop endpoints miss first/last candidate.

Usually fails on: Fails on minimal arrays and exact-boundary answers.

Fix: Re-derive loops from inclusive/exclusive ranges before coding.