Find Unique Triplets in an Integer Array That Sum to Zero

Nitish Kumar Singh
Mar 1, 2025Given an array of integers nums
, find all unique triplets in nums
that sum up to zero, where all elements in a triplet are different elements from the array. This is the 3Sum problem on LeetCode.
For example, given nums = [-1, 1, 0, 2, -1, -4]
, the unique triplets are [[ -1, -1, 2 ], [ -1, 0, 1 ]]
, because:
- nums[0] + nums[1] + nums[2] = -1 + 1 + 0 = 0
- nums[0] + nums[3] + nums[4] = -1 + 2 - 1 = 0
- nums[1] + nums[2] + nums[4] = 1 + 0 - 1 = 0
There are three triplets that sum up to zero, but [-1,1,0]
and [1,0,-1]
are duplicates. Therefore, there are only two unique triplets: [[ -1, -1, 2 ], [ -1, 0, 1 ]]
.
Constraints:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
We will solve this problem using the two-pointer approach. First, we sort the array, run an outer for loop from 0
to n-1
, and an inner while loop until left < right
with the initial values of left = i + 1
and right = n - 1
.
The complete code for solving this problem is below:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i=0; i<nums.length-2<=0;i++){
if(i>0 && nums[i] == nums[i-1]) continue;
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) left++;
else right--;
}
}
return result;
}
Explanation:
In the above code, we consider elements at indices i
, left
, and right
as the three items of a possible triplet. Since these three indices are never the same, and the statement nums[i] + nums[left] + nums[right] == 0
ensures a valid triplet, the approach works correctly.
We have added the following three lines to ensure the uniqueness of each triplet:
// This line avoid adding same 1st item of triplet
if(i>0 && nums[i] == nums[i-1]) continue;
// These two lines avoid adding same 2nd and 3rd item of triplet
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
However, we do not need to check for a duplicate third item because if a triplet contains two unique elements, it is already unique, even if the third item repeats.
If i = 3
for the sorted nums = [-4, -1, -1, 0, 1, 2, 4, 7, 9]
, all numbers after i = 3
are positive, meaning three positive numbers can never sum up to zero.
So, we can stop the outer loop early by adding this condition:
for(int i=0; i<nums.length-2 && nums[i]<=0;i++){
When sum < 0
, we increase left
because we need a larger value to increase the sum. Decreasing right
would give us a smaller value, which is incorrect since the array is sorted. Similarly, when sum > 0
, we decrease right
to get a smaller value.
This approach has a time complexity of O(n²) + O(n log n). Since O(n log n) (sorting) is negligible compared to O(n²), the overall complexity is O(n²).
Alternative Approach Using Lazy Evaluation
We can further optimize runtime by using a different way to find triplets using an anonymous subclass of AbstractList
. This improves performance and memory efficiency using lazy evaluation (deferring computation until necessary).
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
return new AbstractList<>() {
List<List<Integer>> results;
@Override
public int size() {
if (results == null)
results = getResults(nums);
return results.size();
}
@Override
public List<Integer> get(int index) {
if (results == null)
results = getResults(nums);
return results.get(index);
}
};
}
private List<List<Integer>> getResults(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
int len = nums.length;
Arrays.sort(nums);
for (int i = 0; i < len - 2 && nums[i] <= 0; ++i) {
if (i > 0 && nums[i] == nums[i - 1])
continue;
twoSum(results, nums, i + 1, len - 1, -nums[i]);
}
return results;
}
private void twoSum(List<List<Integer>> results, int[] nums, int left, int right, int target) {
while (left < right) {
if (nums[left] + nums[right] > target) {
--right;
continue;
}
if (nums[left] + nums[right] < target) {
++left;
continue;
}
results.add(Arrays.asList(-target, nums[left++], nums[right--]));
while (left <= right && nums[left] == nums[left - 1])
++left;
}
}
}
How This Works:
- This code works similar to the first one but implements lazy evaluation, which improves performance by deferring computation until absolutely necessary and optimizing memory usage.
- It splits the outer and inner loops of the standard two-pointer method into two separate methods: getResults() (outer loop) and twoSum() (inner loop).
This is the standard and optimized two-pointer approach for solving the 3Sum problem. I hope you understand each line of code and the logic behind this approach.
Happy Coding!