3Sum

Problem

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] where nums[i] + nums[j] + nums[k] == 0, and the indices ij and k are all distinct.

The output should not contain any duplicate triplets. You may return the output and the triplets in any order.

Examples

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].

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.

Constraints

  • 3 <= nums.length <= 1000
  • -10^5 <= nums[i] <= 10^5

You should aim for a solution with O(n^2) time and O(1) space, where n is the size of the input array.

Solution

A triple sum equal of i + j + k = 0 is the same as -i = j + k. We first sort the input. We use an outer loop to to pick the element i. If this element is positive, then everything to the right is also positive and the sum will never match, so we can skip. We can also skip duplicates. We now have a subarray to search through. We set j to be right of i, and k to point to the end. If the sum is too small, we walk j forward. If the sum is too large, we walk k backward. If we have a valid sum, store, then move j and k forward/backward ensuring no duplicates.

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // Sort the array
        std::sort(nums.begin(), nums.end());
        // nums[i] + nums[j] + nums[k] == 0 for i != j != k
        // -nums[i] == nums[j] + nums[k]
 
        std::vector<std::vector<int>> triplets;
        // Outer loop picks the i element
        for (std::size_t i = 0; i < nums.size(); ++i) {
            const auto el_i = nums[i];
            // If first element is positive, all other elements are positive
            // and thus we cannot get a triplet
            if (el_i > 0) {
                break;
            }
 
            // Skip duplicate starting elements
            if (i > 0 && nums[i-1] == el_i) {
                continue;
            }
 
            // j points to right of i, k points to end
            std::size_t j = i + 1; 
            std::size_t k = nums.size() - 1;
            // Walk j forward if sum too small, k backward if sum too large
            // If we get 0, then we found a triplet
            while (j < k) {
                const auto sum = el_i + nums[j] + nums[k];
                if (sum > 0) {
                    --k;
                } else if (sum < 0) {
                    ++j;
                } else {
                    // sum = 0, store
                    std::vector<int> res = {el_i, nums[j], nums[k]};
                    triplets.push_back(std::move(res));
                    // Move pointers ensuring no duplicates
                    ++j;
                    --k;
                    while (j < k && nums[j-1] == nums[j]) {
                        ++j;
                    }
                    while (j < k && nums[k+1] == nums[k]) {
                        --k;
                    }
                }
            }
        }
        return triplets;
    }
};