Given an integer array nums, return all the triplets that sum up to 0. Notice that the solution set must not contain duplicate triplets.
LeetCode-15: Given an integer array nums
, return all the triplets $[nums[i], nums[j], nums[k]]$ such that i != j, i != k, and j != k
, and $nums[i] + nums[j] + nums[k] == 0$.
Notice that the solution set must not contain duplicate triplets.
The problem is an extension of the 2-sum problem. Another thing to notice is that the solution set must not contain duplicate triplets.
Let’s try to extend the 2-sum approach to solve this problem. We can fix one element and then find the other two elements such that their sum is equal to the negative of the fixed element.
This way, we can reduce the problem to a 2-sum problem.
Also, to ensure that the solution set does not contain duplicate triplets, we can sort the array and skip the duplicate elements while iterating over the array.
Sorting the array gives us another advantage. We can use two pointers to find the other two elements such that their sum is equal to the negative of the fixed element. Here is the approach:
Let’s see a working example to understand the approach better.
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int index = 0;
List<List<Integer>> result = new ArrayList<>();
while(index < nums.length){
if(index == 0 || nums[index] != nums[index-1]){
int one = nums[index];
int start = index+1;
int end = nums.length -1;
while(start < end){
int two = nums[start];
int three = nums[end];
if(one + two + three == 0){
result.add(List.of(one, two, three));
start++;
end--;
while(start < end && nums[start] == nums[start-1]){
start++;
}
}else if(one + two + three < 0){
start++;
}else{
end--;
}
}
}
index++;
}
return result;
}
Due to the array sorting and nested loops, the overall time complexity for this approach is $O(n \cdot \log n + n^2) \approx O(n^2)$, where $n$ is the number of elements in the array.
The space complexity is $O(1)$, as we are using only a constant amount of extra space.
Working with a similar method, instead of using two-pointer approach, we can use a HashSet
to store the elements and then find the other two elements such that their sum is equal to the negative of the fixed element.
Here is the approach:
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int index = 0;
List<List<Integer>> result = new ArrayList<>();
while(index < nums.length){
if(index == 0 || nums[index] != nums[index-1]){
Set<Integer> uniq = new HashSet<>();
for(int i=index+1; i < nums.length; i++){
int one = nums[index];
int two = nums[i];
int compliment = -one-two;
if(uniq.contains(compliment)){
result.add(List.of(one, two, compliment));
while(i < nums.length-1 && nums[i] == nums[i+1]){
i++;
}
}
uniq.add(two);
}
}
index++;
}
return result;
}
As it is with the previous approach, the overall time complexity for this approach is still $O(n \cdot \log n + n^2) \approx O(n^2)$, where $n$ is the number of elements in the array. Additionally, the space complexity is $O(n)$, as we are using a HashSet
to store the elements.
But, is there a way to solve this problem in just $O(n^2)$ time that avoids the additional complexity of sorting the array?
Here is the approach:
public List<List<Integer>> threeSum(int[] nums) {
Set<Integer> uniq = new HashSet<>();
Set<List<Integer>> result = new HashSet<>();
int index = 0;
while(index < nums.length){
int one = nums[index];
// re-create hashmap or clear if created outside
// to ensure the map is reset in every iteration
// this ensures that compliment's presence is not
// checked across the iterations
Map<Integer, Integer> mapping = new HashMap<>();
// if not seen before
// add to the set
if(uniq.add(one)){
for(int i = index+1; i < nums.length; i++){
int two = nums[i];
int compliment = -one-two;
// if the map is NOT cleared
// and is created outside of while loop
// the add a check on index as well
// mapping.containsKey(compliment) && mapping.get(compliment) == index
if(mapping.containsKey(compliment)){
List<Integer> temp = Arrays.asList(one, two, compliment);
Collections.sort(temp);
result.add(temp);
}
mapping.put(two, index);
}
}
index++;
}
return new ArrayList<>(result);
}
The above approach uses a HashSet
to store the unique elements and a HashMap
to store the elements and their indices. This way, we can avoid the additional complexity of sorting the array.
The overall time complexity for this approach is $O(n^2)$, where $n$ is the number of elements in the array. The space complexity is $O(n)$, as we are using a HashSet
and a HashMap
to store the elements.
Be notified of new posts. Subscribe to the RSS feed.