Welcome to our next lesson in the "Arrays & Hashing" module. In our previous session, we established the fundamental power of HashMap and HashSet for achieving near-instantaneous O(1) lookups. You learned how these data structures work internally and how to recognize common problem-solving patterns like "frequency counting" and the "seen set."
Today, we'll put that theory into immediate practice by tackling one of the most iconic coding interview questions: Two Sum. This problem is a cornerstone of technical interviews because it perfectly tests your ability to analyze a problem and optimize a solution by choosing the right data structure. As you requested, we will dissect this problem by starting with the intuitive brute-force method, then exploring a better alternative, and finally arriving at the elegant and optimal one-pass HashMap solution.
Let's begin by formally defining the problem.
The Two Sum Problem
The problem is straightforward: given an array of integers and a target value, you need to find two numbers in the array that add up to the target. Most commonly, you are asked to return the indices of these two numbers.
Two Sum : Check if a pair with given sum exists in Array
This article from the Striver A2Z DSA course clearly defines the Two Sum problem and its two common variants.
Please read the problem statement and the accompanying examples to fully grasp the input and expected output. Notice the two variants: one asks for a simple "YES/NO", and the other asks for the indices. We will focus on the second, more common variant.
Now that we understand the goal, let's start with the most direct way to solve it.
Approach 1: The Brute-Force Solution
The most intuitive approach is to simply try every possible pair of numbers in the array and see if their sum equals the target. This is the brute-force method. How would we implement this? We can use two nested loops. The outer loop selects the first number, and the inner loop iterates through the rest of the array to find a second number that forms a valid pair.
The "take U forward" video below explains this logic clearly.
2 Sum Problem | 2 types of the same problem for Interviews | Brute-Better-Optimal
Striver explains the brute-force approach using nested loops and analyzes its time complexity.
Watch the segment from this timestamp. Pay attention to how the inner loop can be slightly optimized by starting from i + 1 to avoid checking the same pair twice.
Here is a typical Java implementation for this approach:
public class Solution {
public int[] twoSum(int[] nums, int target) {
// Outer loop iterates from the first element to the second-to-last
for (int i = 0; i < nums.length; i++) {
// Inner loop iterates from the element after 'i' to the end
for (int j = i + 1; j < nums.length; j++) {
// Check if the pair sums to the target
if (nums[i] + nums[j] == target) {
// If so, return their indices
return new int[]{i, j};
}
}
}
// If no solution is found (though the problem guarantees one exists)
return new int[]{-1, -1};
}
}
Complexity Analysis:
- Time Complexity: . For each element in the array, we iterate through the rest of the array. If the array has elements, this results in approximately comparisons, which is on the order of .
- Space Complexity: . We are not using any extra space that scales with the size of the input array.
For a small array, this solution works fine. However, in an interview context where arrays can contain thousands or millions of elements, an solution is often too slow. The interviewer will almost certainly ask you to optimize it.
Approach 2: A Better, but Not Optimal, Solution (Two Pointers)
Before we jump to the optimal hash-based solution, it's useful to know another common pattern: the two-pointer technique. This approach works on a sorted array.
The algorithm is as follows:
- Sort the input array.
- Initialize two pointers:
leftat the beginning of the array (index 0) andrightat the end (n-1). - While
leftis less thanright:- Calculate the sum:
currentSum = nums[left] + nums[right]. - If
currentSum == target, you've found the pair. - If
currentSum < target, you need a larger sum, so you increment theleftpointer to include a larger number. - If
currentSum > target, you need a smaller sum, so you decrement therightpointer to include a smaller number.
- Calculate the sum:
Complexity Analysis:
- Time Complexity: . The dominant operation is sorting the array. The two-pointer scan itself is only .
- Space Complexity: or depending on the sorting algorithm's implementation space, assuming we can modify the input array.
This is a significant improvement over the brute-force method. However, it has a major drawback for the specific problem of returning original indices: sorting the array shuffles the elements, losing their original positions. You would need to use extra space (e.g., an array of pairs [value, original_index]) to keep track of the original indices, which complicates the solution and increases space complexity to .
Because of this complication, for the specific task of returning original indices from an unsorted array, there is a cleaner and more efficient way.
Approach 3: The Optimal One-Pass HashMap Solution
This is where the concepts from our last lesson come into play. The bottleneck in the brute-force approach is the inner loop, which performs a linear search for the second number. The core question we are trying to answer is: "For the current number nums[i], does the required complement, target - nums[i], exist elsewhere in the array?"
A linear search takes time. But as you know, a HashMap can answer the "does it exist?" question in average time.
This insight allows us to redesign the algorithm completely. We can iterate through the array just once, and for each element, we can instantly check if its complement has already been seen.
Here is the one-pass algorithm:
- Create an empty
HashMap<Integer, Integer>to store numbers we've seen and their corresponding indices. The map will store(value -> index). - Iterate through the
numsarray fromi = 0ton-1. - For each element
nums[i], calculate thecomplementwe need:complement = target - nums[i]. - Check if this
complementexists as a key in ourHashMap.- If it does, we have found our solution! The two numbers are
nums[i](the current number) andcomplement(the number we saw earlier). We can immediately return the current indexiand the index of the complement, which we retrieve from the map:map.get(complement). - If it does not exist, it means we haven't found a pair yet. We add the current number and its index to the map:
map.put(nums[i], i). This is so that future elements can use it as their complement.
- If it does, we have found our solution! The two numbers are
- Repeat until the solution is found.
The "Coding with John" video provides an exceptionally clear explanation and visualization of this one-pass approach.
LeetCode Exercise in Java Tutorial - Two Sum FAST Solution
This video brilliantly explains the intuition behind using a HashMap and walks through the one-pass algorithm.
First, watch the section explaining the core idea. The walkthrough with the example array is key to understanding why this works. Then, follow along with the Java implementation to see how this logic translates directly into code using HashMap.
To see this in action, consider the example nums = [3, 2, 4] and target = 6.
- i = 0, num = 3:
complement = 6 - 3 = 3.- Is
3in the map? No, the map is empty. - Add
(3, 0)to the map. Map is now{3: 0}.
- i = 1, num = 2:
complement = 6 - 2 = 4.- Is
4in the map? No. - Add
(2, 1)to the map. Map is now{3: 0, 2: 1}.
- i = 2, num = 4:
complement = 6 - 4 = 2.- Is
2in the map? Yes! Its index is1. - Solution found! Return the complement's index from the map (
1) and the current index (2). Result:[1, 2].
The image below from algo.monster depicts the state of the hash table just before finding the solution in a similar example. Notice how it stores the numbers encountered so far and their indices, ready for a quick lookup.

Here is the clean Java code for the optimal solution:
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(); // Stores <number, index>
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
// Found the solution
return new int[] { map.get(complement), i };
}
// If complement not found, put the current number and its index in the map
// for future elements to check against.
map.put(nums[i], i);
}
// According to the problem statement, a solution always exists,
// so this part should ideally not be reached.
throw new IllegalArgumentException("No two sum solution");
}
}
Complexity Analysis:
- Time Complexity: . We traverse the list containing elements only once. Each lookup and insertion in the
HashMaptakes, on average, time. - Space Complexity: . In the worst case, we might have to store all elements in the
HashMapbefore finding a pair at the very end.
This solution is both time-efficient and relatively straightforward to implement, making it the preferred answer in a FAANG-level interview.
You can find the full write-up, including all three approaches, in the Striver A2Z course material.
Two Sum : Check if a pair with given sum exists in Array
This article consolidates all the approaches we've discussed.
Review the Brute force approach first. Then, read the section on the HashMap solution, which corresponds to our optimal one-pass method. Finally, you can look at the Two-Pointer method as a review of that alternative pattern. Focus on the Java code provided for each.
Conclusion
In this lesson, we took a deep dive into solving a classic algorithm problem, Two Sum, by systematically improving our solution. This process mirrors the expectations in a real coding interview.
Key Takeaways:
- Problem-solving progression: We moved from a brute-force solution to an two-pointer solution, and finally to the optimal one-pass
HashMapsolution. - Choosing the right data structure is key: The
HashMap's average time complexity for lookups was the critical factor that enabled the optimal solution. - The "Complement Lookup" Pattern: The strategy of iterating through an array while using a hash map to look for a
target - current_elementcomplement is a fundamental and powerful pattern you will use again and again.
Now that you've mastered the Two Sum problem, you are ready to apply similar logic to other problems. In our next lesson, we will tackle the "Longest Consecutive Sequence" problem, where you'll use a HashSet to achieve a similarly elegant and efficient solution.