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.
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 keepingnums[i]
andnums[j]
fixed, any number to the right ofnums[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 movingk
leftward. - If
sum < target
, similarly, any number to the left ofnums[j]
combined withnums[i]
andnums[k]
would make the sum even smaller. So we movej
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 lengthans = 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 afteri
(at positioni + 1
)k
starts at the end of the array (at positionn - 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:
-
If sum equals target: We've found the exact match, immediately return
t
if t == target: return t
-
Update the closest sum: If the current sum
t
is closer to target than our previous bestans
, update itif abs(t - target) < abs(ans - target): ans = t
-
Adjust pointers based on comparison with target:
- If
t > target
: The sum is too large, so we need smaller numbers. Movek
leftward (k -= 1
) - If
t < target
: The sum is too small, so we need larger numbers. Movej
rightward (j += 1
)
if t > target: k -= 1 else: j += 1
- If
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 EvaluatorExample 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
โ Updateans = -3
- Since
t = -3 < target = 1
, movej
right:j = 2
- Now
j = 2
,k = 3
: Calculate sum:t = -4 + 1 + 2 = -1
- Check if closer:
|(-1) - 1| = 2
<|-3 - 1| = 4
โ Updateans = -1
- Since
t = -1 < target = 1
, movej
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
โ Updateans = 2
- Since
t = 2 > target = 1
, movek
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 elementright = 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.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donโt Miss This!