Home > Enterprise >  How do I return all possible triplets in the 3Sum problem?
How do I return all possible triplets in the 3Sum problem?

Time:06-04

I am doing the 3Sum problem: https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/776/

Question in brief: 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.

My problem: I have a solution which does return some but not all possible triplets, and I don't understand where I am going wrong. Secondly, my algorithm is O(N^2 log N), any recommendation on improving it is welcome.

Input:
[-1,0,1,2,-1,-4,-2,-3,3,0,4]
Output:
[[-3,-1,4],[-3,0,3],[-4,1,3],[-2,0,2],[-4,0,4]]
Expected:
[[-4,0,4],[-4,1,3],[-3,-1,4],[-3,0,3],[-3,1,2],[-2,-1,3],[-2,0,2],[-1,-1,2],[-1,0,1]]

Algorithm: I have included my algorithm in the code along with comments, but here is the gist- For each pair of numbers I store their sum as a key and the indices of the numbers that give me that sum as the value. Then in a loop I go through each element and check if the difference between target value and that number is present as a key in the map. If it is, and all indices are unequal, I add it to a vector that is ultimately returned.

Code:

#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

vector<vector<int>> threeSum(vector<int> &nums) {
    /*
     * If less than 3 elements are passed return empty vector
     */
    if (nums.size() < 3)
        return {};

    int target = 0; // all three numbers sum up to 0 as given
    vector<vector<int>> outer; // this is returned by the function
    unordered_map<int, vector<int>> umap; // unordered_map for keeping sum and indices

    /*
     * Calculate sum of each pair of numbers
     * Store the sum as key and the pair as value
     * i != j is guaranteed
     */
    for (int i = 0; i < nums.size(); i  )
        for (int j = i   1; j < nums.size(); j  )
            umap[nums[i]   nums[j]] = {i, j};

    /*
     * Go through each element and calculate the difference
     * between target and that element, this gives the sum
     * of the other two elements.
     * Look for the sum in unordered_map
     * If it is present check if all three indices are not equal to each other
     */
    for (int i = 0; i < nums.size(); i  ) {
        vector<int> inner;
        int diff = target - nums[i];
        auto it = umap.find(diff);
        inner = umap[diff];
        if (it != umap.end() && i != inner[0] && i != inner[1]) {
            inner.push_back(i);
            vector<int> tmp;
            for (auto &j: inner)
                tmp.push_back(nums[j]); // push actual numbers instead of indices
            sort(tmp.begin(), tmp.end()); // sort the inner three elements
            if (find(outer.begin(), outer.end(), tmp) == outer.end()) // for removing duplicates
                outer.push_back(tmp);
        }
    }
    return outer;
}

int main() {
    vector<int> v{-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4};
    vector<vector<int>> ret = threeSum(v);
    for (auto &i: ret) {
        for (auto j: i)
            cout << j << " ";
        cout << endl;
    }
}

CodePudding user response:

Your bug is demonstrated with the input [-5, 1, 2, 3, 4].

The problem is that 5 == 1 4 == 2 3. Your map will only store one of those pairs as the value for that key, and it therefore does not record the fact that BOTH pairs can lead to that key.

CodePudding user response:

Since you asked for improvements how about this algorithm:

create an std::unordered_map<int, int> mapping each input to the number of occurances. Then:

for (const auto & it = map.begin(); it != map.end();   it) {
    for (const auto & it2 = it; it2 != map.end();   it2) {
        auto it3 = map.find(-*it - *it2);
        if (it3 != map.end()) {
            it ((*it > *it3) || (*it2 > *it3)) continue;
            if (it == it2) {
                if (it == it3) {
                   if (it.second > 2) cout << *it << ", " *it2 << ", " << *it3 << std::endl;
                } else {
                   if (it.second > 1) cout << *it << ", " *it2 << ", " << *it3 << std::endl;
                }
            } else if (it2 == it3) {
                if (it2.second > 1) cout << *it << ", " *it2 << ", " << *it3 << std::endl;
            } else {
                cout << *it << ", " *it2 << ", " << *it3 << std::endl;
            }
        }             
    }
}

That should be O(n^2) without duplicates.

  • Related