Facebook Pixel

16. 3Sum Closest

Problem Description

You are given an integer array nums of length n and an integer value called target. Your task is to find three integers from the nums array such that their sum is as close as possible to the target value.

The problem asks you to return the actual sum of these three integers (not the difference from the target).

Key constraints and guarantees:

  • You need to select exactly three integers from the array
  • The three integers should produce a sum that has the minimum absolute difference from target
  • The problem guarantees that each input will have exactly one solution (meaning there's a unique triplet that produces the closest sum)

For example, if nums = [-1, 2, 1, -4] and target = 1, you would need to find three numbers whose sum is closest to 1. The triplet [-1, 2, 1] gives a sum of 2, which is the closest possible sum to the target of 1, so you would return 2.

The solution approach uses sorting combined with a two-pointer technique. After sorting the array, for each element nums[i], two pointers are used to scan the remaining elements to find the best combination. The left pointer j starts at i+1 and the right pointer k starts at the end of the array. Based on whether the current sum is greater or less than the target, the pointers are adjusted to explore different combinations while tracking the closest sum found so far.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that once we fix one number, we're essentially looking for two other numbers whose sum gets us as close as possible to target - first_number. This reduces our problem from finding three numbers to finding two numbers, which is a simpler problem to solve efficiently.

Why sorting helps: When the array is sorted, we gain the ability to make intelligent decisions about which direction to search. If our current sum is too large, we know we need smaller numbers, so we can move the right pointer left. If our sum is too small, we need larger numbers, so we move the left pointer right. This property only works in a sorted array.

Consider what happens without sorting: we'd need to check every possible combination of three numbers, which would take O(nยณ) time. With sorting, we can eliminate many possibilities systematically.

The two-pointer technique works because of the sorted order. When we calculate sum = nums[i] + nums[j] + nums[k]:

  • If sum > target, we know that keeping nums[i] and nums[j] fixed, any number to the right of nums[k] would make the sum even larger (since the array is sorted). So the only way to get closer to target is to try a smaller value by moving k leftward.
  • If sum < target, similarly, any number to the left of nums[j] combined with nums[i] and nums[k] would make the sum even smaller. So we move j rightward to increase the sum.

This way, for each fixed nums[i], we explore all viable pairs (nums[j], nums[k]) in just O(n) time instead of O(nยฒ) time, making the overall complexity O(nยฒ) instead of O(nยณ).

The beauty of this approach is that we never miss the optimal solution because the pointer movements are guided by the mathematical relationship between our current sum and the target.

Learn more about Two Pointers and Sorting patterns.

Solution Approach

The implementation follows a sorting + two pointers pattern to efficiently find the three numbers with sum closest to target.

Step 1: Sort the array

nums.sort()

This preprocessing step enables us to use the two-pointer technique effectively.

Step 2: Initialize variables

  • n = len(nums) to store the array length
  • ans = inf to track the closest sum found so far (initialized to infinity)

Step 3: Fix the first element and use two pointers for the remaining two

For each element at index i, we treat nums[i] as the first number of our triplet:

for i, v in enumerate(nums):
    j, k = i + 1, n - 1
  • j starts right after i (at position i + 1)
  • k starts at the end of the array (at position n - 1)

Step 4: Two-pointer search

While j < k, we calculate the current sum of three numbers:

t = v + nums[j] + nums[k]

Three key decisions are made based on this sum:

  1. If sum equals target: We've found the exact match, immediately return t

    if t == target:
        return t
  2. Update the closest sum: If the current sum t is closer to target than our previous best ans, update it

    if abs(t - target) < abs(ans - target):
        ans = t
  3. Adjust pointers based on comparison with target:

    • If t > target: The sum is too large, so we need smaller numbers. Move k leftward (k -= 1)
    • If t < target: The sum is too small, so we need larger numbers. Move j rightward (j += 1)
    if t > target:
        k -= 1
    else:
        j += 1

Step 5: Return the result

After checking all possible triplets, return the closest sum found:

return ans

Time Complexity: O(nยฒ) - We have O(n log n) for sorting, and O(nยฒ) for the nested loops (outer loop runs n times, inner two-pointer loop runs at most n times for each iteration).

Space Complexity: O(1) or O(log n) depending on the sorting algorithm's space requirements. We only use a constant amount of extra variables.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with a concrete example:

Input: nums = [-1, 2, 1, -4], target = 1

Step 1: Sort the array After sorting: nums = [-4, -1, 1, 2]

Step 2: Initialize variables

  • n = 4
  • ans = inf (closest sum found so far)

Step 3-4: Iterate with first element fixed

Iteration 1: i = 0, v = -4

  • Set pointers: j = 1 (pointing to -1), k = 3 (pointing to 2)
  • Calculate sum: t = -4 + (-1) + 2 = -3
  • Check if closer to target: |(-3) - 1| = 4 < |inf - 1| = inf โ†’ Update ans = -3
  • Since t = -3 < target = 1, move j right: j = 2
  • Now j = 2, k = 3: Calculate sum: t = -4 + 1 + 2 = -1
  • Check if closer: |(-1) - 1| = 2 < |-3 - 1| = 4 โ†’ Update ans = -1
  • Since t = -1 < target = 1, move j right: j = 3
  • Now j = 3 = k, so this iteration ends

Iteration 2: i = 1, v = -1

  • Set pointers: j = 2 (pointing to 1), k = 3 (pointing to 2)
  • Calculate sum: t = -1 + 1 + 2 = 2
  • Check if closer: |2 - 1| = 1 < |(-1) - 1| = 2 โ†’ Update ans = 2
  • Since t = 2 > target = 1, move k left: k = 2
  • Now j = 2 = k, so this iteration ends

Iteration 3: i = 2, v = 1

  • Set pointers: j = 3 (pointing to 2), k = 3 (also pointing to 2)
  • Since j = k, no valid triplet can be formed, skip

Result: Return ans = 2

The triplet [-1, 1, 2] gives sum = 2, which is the closest possible sum to target = 1.

Solution Implementation

1class Solution:
2    def threeSumClosest(self, nums: List[int], target: int) -> int:
3        # Sort the array to enable two-pointer technique
4        nums.sort()
5        n = len(nums)
6      
7        # Initialize closest sum with infinity
8        closest_sum = float('inf')
9      
10        # Iterate through each element as the first number of the triplet
11        for i in range(n):
12            # Set two pointers: left pointer after current element, right at the end
13            left = i + 1
14            right = n - 1
15          
16            # Use two-pointer technique to find the best pair for current element
17            while left < right:
18                # Calculate current sum of three numbers
19                current_sum = nums[i] + nums[left] + nums[right]
20              
21                # If we found exact target, return immediately
22                if current_sum == target:
23                    return current_sum
24              
25                # Update closest sum if current sum is closer to target
26                if abs(current_sum - target) < abs(closest_sum - target):
27                    closest_sum = current_sum
28              
29                # Move pointers based on comparison with target
30                if current_sum > target:
31                    # Sum is too large, move right pointer left to decrease sum
32                    right -= 1
33                else:
34                    # Sum is too small, move left pointer right to increase sum
35                    left += 1
36      
37        return closest_sum
38
1class Solution {
2    /**
3     * Finds three numbers in the array whose sum is closest to the target value.
4     * 
5     * @param nums   The input array of integers
6     * @param target The target sum to get closest to
7     * @return The sum of three numbers that is closest to the target
8     */
9    public int threeSumClosest(int[] nums, int target) {
10        // Sort the array to enable two-pointer approach
11        Arrays.sort(nums);
12      
13        // Initialize closest sum with a large value (2^30)
14        int closestSum = 1 << 30;
15        int arrayLength = nums.length;
16      
17        // Fix the first number and find the other two using two pointers
18        for (int i = 0; i < arrayLength; ++i) {
19            // Initialize two pointers: left pointer starts after i, right pointer at the end
20            int left = i + 1;
21            int right = arrayLength - 1;
22          
23            // Use two-pointer technique to find the closest sum
24            while (left < right) {
25                // Calculate the current sum of three numbers
26                int currentSum = nums[i] + nums[left] + nums[right];
27              
28                // If we found the exact target, return immediately
29                if (currentSum == target) {
30                    return currentSum;
31                }
32              
33                // Update closest sum if current sum is closer to target
34                if (Math.abs(currentSum - target) < Math.abs(closestSum - target)) {
35                    closestSum = currentSum;
36                }
37              
38                // Move pointers based on comparison with target
39                if (currentSum > target) {
40                    // Sum is too large, move right pointer left to decrease sum
41                    --right;
42                } else {
43                    // Sum is too small, move left pointer right to increase sum
44                    ++left;
45                }
46            }
47        }
48      
49        return closestSum;
50    }
51}
52
1class Solution {
2public:
3    int threeSumClosest(vector<int>& nums, int target) {
4        // Sort the array to enable two-pointer technique
5        sort(nums.begin(), nums.end());
6      
7        // Initialize closest sum with a large value (2^30)
8        int closestSum = 1 << 30;
9        int n = nums.size();
10      
11        // Fix the first element and use two pointers for the remaining elements
12        for (int i = 0; i < n; ++i) {
13            // Initialize two pointers: left pointer starts after i, right pointer at the end
14            int left = i + 1;
15            int right = n - 1;
16          
17            // Use two-pointer technique to find the closest sum
18            while (left < right) {
19                // Calculate current sum of three elements
20                int currentSum = nums[i] + nums[left] + nums[right];
21              
22                // If we found exact target, return immediately
23                if (currentSum == target) {
24                    return currentSum;
25                }
26              
27                // Update closest sum if current sum is closer to target
28                if (abs(currentSum - target) < abs(closestSum - target)) {
29                    closestSum = currentSum;
30                }
31              
32                // Move pointers based on comparison with target
33                if (currentSum > target) {
34                    // If sum is too large, move right pointer left to decrease sum
35                    --right;
36                } else {
37                    // If sum is too small, move left pointer right to increase sum
38                    ++left;
39                }
40            }
41        }
42      
43        return closestSum;
44    }
45};
46
1/**
2 * Finds three numbers in the array whose sum is closest to the target value
3 * @param nums - The input array of numbers
4 * @param target - The target sum to get closest to
5 * @returns The sum of three numbers that is closest to the target
6 */
7function threeSumClosest(nums: number[], target: number): number {
8    // Sort the array in ascending order for two-pointer technique
9    nums.sort((a: number, b: number) => a - b);
10  
11    // Initialize the result with infinity to track the closest sum
12    let closestSum: number = Infinity;
13    const arrayLength: number = nums.length;
14  
15    // Iterate through each element as the first number of the triplet
16    for (let firstIndex: number = 0; firstIndex < arrayLength; ++firstIndex) {
17        // Set up two pointers for the remaining elements
18        let leftPointer: number = firstIndex + 1;
19        let rightPointer: number = arrayLength - 1;
20      
21        // Use two-pointer technique to find the best pair for current first number
22        while (leftPointer < rightPointer) {
23            // Calculate the sum of the current triplet
24            const currentSum: number = nums[firstIndex] + nums[leftPointer] + nums[rightPointer];
25          
26            // If we found exact match, return immediately
27            if (currentSum === target) {
28                return currentSum;
29            }
30          
31            // Update closest sum if current sum is closer to target
32            if (Math.abs(currentSum - target) < Math.abs(closestSum - target)) {
33                closestSum = currentSum;
34            }
35          
36            // Move pointers based on comparison with target
37            if (currentSum > target) {
38                // Sum is too large, decrease it by moving right pointer left
39                --rightPointer;
40            } else {
41                // Sum is too small, increase it by moving left pointer right
42                ++leftPointer;
43            }
44        }
45    }
46  
47    return closestSum;
48}
49

Time and Space Complexity

Time Complexity: O(n^2)

The algorithm first sorts the array, which takes O(n log n) time. Then it uses a fixed pointer i to iterate through each element in the array (O(n)), and for each fixed element, it uses two pointers j and k to scan the remaining elements in a single pass (O(n)). Therefore, the nested loop structure results in O(n) ร— O(n) = O(n^2) for the main logic. Since O(n^2) + O(n log n) = O(n^2), the overall time complexity is O(n^2).

Space Complexity: O(log n)

The space complexity comes from the sorting algorithm. Python's sort() method uses Timsort, which requires O(log n) space for the recursion stack in the worst case. The algorithm itself only uses a constant amount of extra space for variables like i, j, k, ans, and t, which is O(1). Therefore, the total space complexity is O(log n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Sort the Array

One of the most common mistakes is forgetting to sort the array before applying the two-pointer technique. The two-pointer approach relies on the sorted order to make intelligent decisions about pointer movement.

Incorrect Implementation:

def threeSumClosest(self, nums: List[int], target: int) -> int:
    # Missing nums.sort() - WRONG!
    n = len(nums)
    closest_sum = float('inf')
  
    for i in range(n):
        left = i + 1
        right = n - 1
        # Rest of the logic won't work correctly without sorting

Why it fails: Without sorting, moving pointers based on sum comparison doesn't guarantee we're exploring the solution space correctly. We might miss the optimal triplet entirely.

2. Incorrect Pointer Movement Logic

A subtle but critical mistake is moving the wrong pointer or using incorrect comparison logic.

Incorrect Implementation:

# WRONG - reversed pointer movement
if current_sum > target:
    left += 1  # Should be right -= 1
else:
    right -= 1  # Should be left += 1

Correct Logic:

  • When sum > target: We need a smaller sum, so decrease the right pointer
  • When sum < target: We need a larger sum, so increase the left pointer

3. Not Handling the Exact Match Case

Failing to immediately return when finding an exact match (sum equals target) can lead to unnecessary computation.

Inefficient Implementation:

# Missing early return for exact match
while left < right:
    current_sum = nums[i] + nums[left] + nums[right]
  
    # Only updating closest_sum without checking for exact match
    if abs(current_sum - target) < abs(closest_sum - target):
        closest_sum = current_sum
  
    # Continues searching even if current_sum == target

Optimal Solution: Always check if current_sum == target: return current_sum first, as no sum can be closer to the target than the target itself.

4. Integer Overflow in Large Inputs

While Python handles large integers automatically, in other languages like Java or C++, calculating the sum of three large integers might cause overflow.

Potential Issue in Other Languages:

// In C++ or Java
int current_sum = nums[i] + nums[left] + nums[right]; // May overflow

Solution for Other Languages:

  • Use long/long long data type for sum calculations
  • Or calculate differences instead of absolute sums when possible

5. Off-by-One Errors in Pointer Initialization

Setting incorrect initial positions for pointers is another common mistake.

Incorrect Implementation:

for i in range(n):
    left = i  # WRONG - should be i + 1 to avoid using same element twice
    right = n  # WRONG - should be n - 1 (last valid index)

Why it matters:

  • left = i would reuse the same element
  • right = n would cause index out of bounds error

6. Not Updating the Answer Correctly

Using wrong comparison when updating the closest sum.

Incorrect Implementation:

# WRONG - comparing sums directly instead of distances to target
if current_sum < closest_sum:  
    closest_sum = current_sum

Correct Approach:

# Compare distances to target, not the sums themselves
if abs(current_sum - target) < abs(closest_sum - target):
    closest_sum = current_sum

The key is to compare how close each sum is to the target (the absolute difference), not the sums themselves.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More