The problem asks us to find all unique triplets in an array that sum up to zero. Pattern: Sorting + Two Pointers.
A brute-force approach would check all possible triplets using three nested loops. This would take time, which is too slow.
To optimize, we can sort the array first. Sorting takes but allows us to use the Two Pointers technique, bringing the time down to .
By iterating through the array and fixing one number nums[i], the problem reduces to finding two numbers in the remaining sorted array that sum up to -nums[i]. This is exactly the “Two Sum II” problem, which can be solved efficiently using two pointers (one at the beginning of the remaining array, one at the end).
A critical constraint is that the solution set must not contain duplicate triplets. Sorting inherently helps with this because duplicates are placed next to each other, allowing us to easily skip them.
Pseudocode:
- Initialize an empty set
resto store unique triplets. - Loop
ifrom 0 to n-1. - Loop
jfromi+1to n-1. - Loop
kfromj+1to n-1. - If
nums[i] + nums[j] + nums[k] == 0, sort the triplet and add tores. - Return
resas a list.
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> res;
sort(nums.begin(), nums.end()); // Sorting helps to easily add identical tuples to the set
int n = nums.size();
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) {
res.insert({nums[i], nums[j], nums[k]});
}
}
}
}
return vector<vector<int>>(res.begin(), res.end());
}
};Pseudocode:
- Sort the input array
nums. - Initialize an empty list
res. - Loop
ifrom 0 to n-1:- If
i > 0andnums[i] == nums[i-1],continue(skip duplicates for the first element). - Set
left = i + 1,right = n - 1. - While
left < right:- Calculate
total = nums[i] + nums[left] + nums[right]. - If
total > 0, decrementright. - If
total < 0, incrementleft. - If
total == 0, append[nums[i], nums[left], nums[right]]tores.- Increment
leftand decrementright. - Skip duplicates for
leftandrightby advancing them past duplicate values.
- Increment
- Calculate
- If
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
int n = nums.size();
for (int i = 0; i < n; ++i) {
// If the current value is greater than zero, we can't ever sum to zero
// since the array is sorted.
if (nums[i] > 0) {
break;
}
// Skip positive duplicates to avoid identical triplets
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = n - 1;
while (left < right) {
int total = nums[i] + nums[left] + nums[right];
if (total > 0) {
right--;
} else if (total < 0) {
left++;
} else {
res.push_back({nums[i], nums[left], nums[right]});
left++;
right--;
// Skip internal duplicates
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
return res;
}
};- Brute Force:
- Time: due to the three nested loops.
- Space: for the set to store unique triplets.
- Optimal (Sorting + Two Pointers):
- Time: . The outer loop runs times. For each iteration, the two pointers traverse the rest of the array in time. Sorting takes . Overall time is dominated by .
- Space: or depending on the sorting algorithm implementation in C++. We don’t use extra space for sets or hash maps.
- All Zeros:
[0, 0, 0, 0]. The algorithm successfully records[0, 0, 0]once and uses the inner duplicate skipping logic to bypass the rest. - No valid triplets:
[1, 2, 3]. Thetotal > 0condition breaks early since it’s sorted, returning[]efficiently. - Array size less than 3: A simple check or the range logic inherently handles this by never entering the loops.
Thought Process & Recognition:
Whenever a problem asks for combinations of items (pairs, triplets, quadruplets) that meet a target sum and specifically demands unique combinations, Sorting should be your immediate instinct.
Hash maps are great for Two Sum because they find exact indices fast, but when uniqueness is required, hash maps get messy with deduplication. Sorting places identical elements adjacent to each other, making duplicate skipping trivial (nums[i] == nums[i-1]).
Mental breakdown:
- Sort the array.
- Fix one element.
- Solve the rest using classic two pointers.