Problem
Given an array of integer write an algorithm to find 3 elements that sum to a zero. In short a+b+c = 0.
OR
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.
Example
Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output:[[-1,-1,2],[-1,0,1]]
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
The distinct triplets are [-1,0,1] and [-1,-1,2].
Notice that the order of the output and the order of the triplets does not matter.
Example 2:
Input: nums = [0,1,1]
Output: []
Explanation: The only possible triplet does not sum up to 0.
Example 3:
Input: nums = [0,0,0]
Output:[[0,0,0]]
Explanation: The only possible triplet sums up to 0.
Solution
Video explanation
Here is the video explaining below methods in detail. Please check it out:
Method 1 - Brute Force
Use 3 nested loops and find the 3 elements which sum to 0.
In the naive approach, we can:
- run three nested loops, and generate all possible combinations of three indices
(i, j, k)
in the array wherei != j
,i != k
, andj != k
. - For each combination:
- Check if
nums[i] + nums[j] + nums[k] == 0
. - If true, add the triplet
[nums[i], nums[j], nums[k]]
to the result list.
- Check if
- To handle duplicate triplets, use a set to ensure you store only unique triplets.
Code
Java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
List<List<Integer>> ans = new ArrayList<>();
Set<String> seenKeys = new HashSet<>(); // HashSet of strings to track unique triplets
// Three nested loops to find all possible triplets
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
if (nums[i] + nums[j] + nums[k] == 0) {
List<Integer> triplet = Arrays.asList(nums[i], nums[j], nums[k]);
Collections.sort(triplet); // Sort the triplet
String key = triplet.get(0) + "-" + triplet.get(1) + "-" + triplet.get(2);
// Check if the key is unique
if (!seenKeys.contains(key)) {
seenKeys.add(key);
ans.add(triplet);
}
}
}
}
}
return ans;
}
}
Python
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
seen_keys = set() # HashSet of strings to track unique triplets
# Three nested loops to find all possible triplets
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
if nums[i] + nums[j] + nums[k] == 0:
triplet = [nums[i], nums[j], nums[k]]
triplet_sorted = sorted(triplet)
key = f"{triplet_sorted[0]}-{triplet_sorted[1]}-{triplet_sorted[2]}"
# Check if the key is unique
if key not in seen_keys:
seen_keys.add(key)
ans.append(triplet_sorted)
return ans
Complexity
- ⏰ Time complexity:
O(n^3)
wheren
is the size of the input array as we have 3 nested loops. - 🧺 Space complexity:
O(1)
Method 2 - Use Hashing
We need 3 numbers (a, b, c), such that their sum is equal to 0
(write a + b + c = 0
). So, if fix the first element a
in the array, then we have to find two numbers (b, c)
, such that their sum is equal to -a
. Now, this problem is very close to Two Sum problem.
So, this is what we can do:
- First, we fix one element at a time from the array. Let’s call this fixed element
a
. For example, let’s considera = -1
. - Now, our task is to find two numbers (
b
andc
) which should sum up to-a
. For example, here the required sum should be+1
.- This problem now reduces to a Two Sum problem, where we have to find two numbers with a target equal to
-a
.
- This problem now reduces to a Two Sum problem, where we have to find two numbers with a target equal to
- Next, we create a HashSet to help track which numbers we’ve seen so far as we iterate through the remaining part of the array. This allows us to efficiently find pairs that complete the triplet.
- For each element in this remaining array (let’s call it
b
), we calculate the remaining value (c
) needed to reach therequired_sum
. Specifically:c = required_sum - b
.
- Finally, check if
c
is already in the HashSet:- If yes, we’ve found a valid triplet
[a, b, c]
. - If not, add
b
to the HashSet and continue iterating.
- If yes, we’ve found a valid triplet
Code
Java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> result = new HashSet<>();
int n = nums.length;
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
int a = nums[i];
int requiredSum = -a;
Set<Integer> seen = new HashSet<>();
for (int j = i + 1; j < n; j++) {
int b = nums[j];
int c = requiredSum - b;
if (seen.contains(c)) {
List<Integer> triplet = Arrays.asList(a, b, c);
Collections.sort(triplet);
result.add(triplet);
} else {
seen.add(b);
}
}
}
return new ArrayList<>(result);
}
}
Python
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
result = set()
for i in range(n):
if i > 0 and nums[i] == nums[i - 1]:
continue
a = nums[i]
required_sum = -a
seen = set()
for j in range(i + 1, n):
b = nums[j]
c = required_sum - b
if c in seen:
triplet = tuple(sorted([a, b, c]))
result.add(triplet)
else:
seen.add(b)
return [list(t) for t in result]
Complexity
- ⏰ Time complexity:
O(n^2)
due to nested loops. - 🧺 Space complexity:
O(n)
for storing the numbers in hashset.
Method 3 - Using Sorting and Two Pointer Approach
Remember the Two Sum problem where we used sorting and the two-pointer approach? There, the time complexity was O(n log n)
(for sorting) and the space complexity was O(1)
by avoiding extra data structures. Here, even if we use a hashmap for solving the 3Sum problem, we can’t do better than O(n^2)
in terms of time complexity.
So, why not just sort the array and use the two-pointer approach to save on space? Another bonus of sorting is that it groups all duplicates together, making it easy to skip repeated numbers without using extra data structures. This simplifies the implementation while maintaining efficiency.
Here is the approach:
- Sort the array to enable easy identification of duplicates and efficient traversal using pointers.
- Fix one element in the array (let’s call it
firstNum
ora
) as the first number of the triplet. - Use two pointers (
left
andright
) in the remaining part of the array to identify the other two values (b
andc
) that sum up witha
to zero. - Skip duplicates for the fixed element (
a
) and for the two-pointer traversal (b
andc
) to ensure only unique triplets are added to the result.
Code
Java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> triplets = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length-2; i++) {
if(i > 0 && nums[i] == nums[i-1]) {
continue;
}
int firstNum = nums[i];
int left = i+1, right = nums.length-1;
while(left < right) {
int sum = firstNum + nums[left] + nums[right];
if(sum == 0) {
triplets.add(Arrays.asList(firstNum, nums[left], nums[right]));
left++;
right--;
while(left < right && nums[left] == nums[left-1]) {
left++;
}
while(left < right && nums[right] == nums[right+1]) {
right--;
}
} else if(sum < 0) {
left++;
} else {
right--;
}
}
}
return triplets;
}
}
Python
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
triplets = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
first_num = nums[i]
left, right = i + 1, len(nums) - 1
while left < right:
total_sum = first_num + nums[left] + nums[right]
if total_sum == 0:
triplets.append([first_num, nums[left], nums[right]])
left += 1
right -= 1
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
elif total_sum < 0:
left += 1
else:
right -= 1
Complexity
- ⏰ Time complexity:
O(n^2)
- 🧺 Space complexity:
O(1)