Two Sum
EasyPrompt
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?Approaches
3 approaches with complexity analysis and trade-offs.
The most straightforward approach is to check every possible pair of numbers in the array. We can use two nested loops to iterate through all pairs and see if their sum equals the target.
Algorithm
- Iterate through the array with an index
ifrom 0 ton-2. - For each
i, iterate through the rest of the array with an indexjfromi+1ton-1. - Check if
nums[i] + nums[j] == target. - If the condition is true, return
[i, j].
Walkthrough
We use two nested loops to find the pair of numbers. The outer loop, with index i, iterates from the beginning of the array to the end. The inner loop, with index j, starts from i + 1 to avoid using the same element twice and to avoid duplicate pairs. Inside the inner loop, we check if nums[i] + nums[j] is equal to the target. If the sum is equal to the target, we have found our solution and can return the indices [i, j]. Since the problem guarantees exactly one solution, we don't need to worry about finding multiple pairs or no pairs at all.
class Solution { public int[] twoSum(int[] nums, int target) { int n = nums.length; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (nums[i] + nums[j] == target) { return new int[]{i, j}; } } } return new int[]{}; // Should not be reached as per problem statement }}Complexity
Time
O(n²)
Space
O(1)
Trade-offs
Pros
Simple to understand and implement.
Uses constant extra space.
Cons
Inefficient for large input arrays due to its quadratic time complexity.
To improve the time complexity, we can use a hash table (or HashMap in Java). A hash table allows us to check for the existence of an element in constant time on average. This approach involves two separate passes over the array.
Algorithm
- Create a hash map to store elements and their indices.
- Iterate through the array and populate the hash map with
(number, index)pairs. - Iterate through the array a second time.
- For each element
nums[i], calculatecomplement = target - nums[i]. - Check if
complementexists in the hash map and its index is noti. - If both conditions are met, return
[i, map.get(complement)].
Walkthrough
In the first pass, we iterate through the array and populate a hash map. The keys of the map are the numbers from the array, and the values are their corresponding indices. In the second pass, we iterate through the array again. For each element nums[i], we calculate its required complement: complement = target - nums[i]. We then check if this complement exists as a key in our hash map. An important edge case is that the complement found in the map must not be nums[i] itself. We verify this by checking if the index stored for the complement (map.get(complement)) is different from the current index i. If the complement exists and its index is different, we have found the solution and return [i, map.get(complement)].
import java.util.HashMap;import java.util.Map; class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> numMap = new HashMap<>(); // First pass: build the hash map for (int i = 0; i < nums.length; i++) { numMap.put(nums[i], i); } // Second pass: find the complement for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (numMap.containsKey(complement) && numMap.get(complement) != i) { return new int[]{i, numMap.get(complement)}; } } return new int[]{}; // Should not be reached }}Complexity
Time
O(n)
Space
O(n)
Trade-offs
Pros
Significantly faster than the brute-force approach with linear time complexity.
Cons
Requires extra space proportional to the number of elements in the array.
Requires two passes over the input array.
We can optimize the two-pass hash table approach into a single pass. While iterating and inserting elements into the hash table, we can also look back to check if the current element's complement already exists in the table. This combines the building and checking phases into one.
Algorithm
- Create an empty hash map.
- Iterate through the array with an index
ifrom 0 ton-1. - For each element
nums[i], calculatecomplement = target - nums[i]. - Check if
complementexists as a key in the hash map. - If it exists, return
[map.get(complement), i]. - If it does not exist, add the current element and its index to the map:
map.put(nums[i], i).
Walkthrough
We create an empty hash map and iterate through the nums array just once. For each element nums[i], we first calculate the complement = target - nums[i]. Then, we check if this complement is already a key in our hash map. If it is, we have found our pair. The complement must have been inserted at an earlier iteration, so we can immediately return its index from the map along with the current index i: [map.get(complement), i]. If the complement is not in the map, we insert the current number nums[i] and its index i into the map. This is done so that subsequent elements can find it if it's their complement. This approach works because by the time we find a complement in the map, we are guaranteed to have two different indices, as we check for the complement before adding the current element to the map.
import java.util.HashMap;import java.util.Map; class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> numMap = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (numMap.containsKey(complement)) { return new int[]{numMap.get(complement), i}; } numMap.put(nums[i], i); } return new int[]{}; // Should not be reached }}Complexity
Time
O(n)
Space
O(n)
Trade-offs
Pros
Most optimal solution with linear time complexity.
Slightly more efficient than the two-pass approach as it combines two loops into one.
Cons
Requires extra space proportional to the number of elements in the array.
Solutions
Solution
public class Solution { public int[] TwoSum(int[] nums, int target) { var m = new Dictionary < int, int > (); for (int i = 0, j;; ++i) { int x = nums[i]; int y = target - x; if (m.TryGetValue(y, out j)) { return new [] { j, i }; } if (!m.ContainsKey(x)) { m.Add(x, i); } } }}class Solution { public int [] twoSum ( int [] nums , int target ) { Map < Integer , Integer > m = new HashMap <>(); for ( int i = 0 ;; ++ i ) { int x = nums [ i ]; int y = target - x ; if ( m . containsKey ( y )) { return new int [] { m . get ( y ), i }; } m . put ( x , i ); } } }/** * @param {number[]} nums * @param {number} target * @return {number[]} */ var twoSum = function (nums, target) { const m = new Map(); for (let i = 0; ; ++i) { const x = nums[i]; const y = target - x; if (m.has(y)) { return [m.get(y), i]; } m.set(x, i); } };class Solution {public: vector<int> twoSum(vector<int> &nums, int target) { unordered_map<int, int> m; for (int i = 0;; ++i) { int x = nums[i]; int y = target - x; if (m.count(y)) { return {m[y], i}; } m[x] = i; } }};class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: m = {} for i, x in enumerate(nums): y = target - x if y in m: return [m[y], i] m[x] = iVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.