7

Original Problem Statement:

Given an array S of n integers, are there elements a, b, C in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note: The solution set must not contain duplicate triplets.

For example, given array S = [-1, 0, 1, 2, -1, -4],

A solution set is: [[-1, 0, 1], [-1, -1, 2]]

I solved the Two Sum problem on LeetCode some time back and I thought of using it to solve the three sum as well. My idea is that for every element find two elements in the remaining list which sum up to element * -1 to get 0. However, this code doesn't pass all the test, for example

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

I don't really know what's wrong. Can someone be kind enough to explain the problem to me.

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def twoSum(self, nums, target):
            targ = target
            for index, i in enumerate(nums):
                targ -= i
                if targ in nums[index+1:]:
                    return [nums[index], nums[nums[index+1:].index(targ)+index+1]]
                else:
                    targ = target
            return None
        res = []
        for index, i in enumerate(nums):
            target = i * -1
            num = nums[:index] + nums [index+1:]
            ans = twoSum(self, num, target)
            if ans != None:
                temp = ans + [i]
                temp.sort()
                res.append(temp)
        print(res)
        import itertools
        res.sort()
        res = list(res for res,_ in itertools.groupby(res))
        return res

Original Question: https://leetcode.com/problems/3sum/description/

The one I'm using to solve this: https://leetcode.com/problems/two-sum/description/

someRandomGuy
  • 137
  • 1
  • 11
  • Please include the description of the problem in your question, not as a link to another site. If that site goes down, or changes URL patterns, your question will not be understood in the future. – Phrogz Sep 06 '17 at 04:11
  • @Phrogz Noted and changes made, thanks for the input – someRandomGuy Sep 06 '17 at 04:15
  • I figured out the problem is that the iteration misses out on any combination that might occur with the same element twice. I'll try to figure a way around it and post the answer here. – someRandomGuy Sep 06 '17 at 04:51

4 Answers4

8

using itertools.

import itertools
stuff = [-1, 0, 1, 2, -1, -4]
stuff.sort()
ls = []
for subset in itertools.combinations(stuff, 3):
    if sum(list(subset))==0:
        # first I have sorted the list because of grouping
        # Ex: [-1, 0, 1] and [0, 1, -1] are build with the same element
        # so here is avoiding this.
        if list(subset) not in ls:
            ls.append(list(subset))
print(ls)

input/output

input : [-1, 0, 1, 2, -1, -4]
output : [[-1, -1, 2], [-1, 0, 1]]
input : [-4,-2,-2,-2,0,1,2,2,2,3,3,4,4,6,6] 
output: [[-4, -2, 6], [-4, 0, 4], [-4, 1, 3], [-4, 2, 2], [-2, -2, 4], [-2, 0, 2]]
R.A.Munna
  • 1,699
  • 1
  • 15
  • 29
  • Perfect, I figured a couple of ways to do it. However, I wanted to do it by using the two sum method. Your method works for the test case provided although I get a time limit exceeded when I submit it on LeetCode for this input [-7,-10,-1,3,0,-7,-9,-1,10,8,-6,4,14,-8,9,-15,0,-4,-5,9,11,3,-5,-8,2,-6,-14,7,-14,10,5,-6,7,11,4,-7,11,11,7,7,-4,-14,-12,-13,-14,4,-13,1,-15,-2,-12,11,-14,-2,10,3,-1,11,-5,1,-2,7,2,-10,-5,-8,-10,14,10,13,-2,-9,6,-7,-7,7,12,-5,-14,4,0,-11,-8,2,-6,-13,12,0,5,-15,8,-12,-1,-4,-15,2,-5,-9,-7,12,11,6,10,-6,14,-12,9,3,-10,10,-8,-2,6,-9,7,7,-7,4,-8,5,-4,8,0,3,11,0,-10,-9] – someRandomGuy Sep 06 '17 at 06:04
  • Ran out of space, this is not a problem with the code but with LeetCode so I'll accept your answer. Thank you for the solution – someRandomGuy Sep 06 '17 at 06:06
  • @someRandomGuy, Thanks for appreciate and happy codding +1. – R.A.Munna Sep 06 '17 at 06:09
  • 1
    As pointed out by [Alex Hall](https://stackoverflow.com/users/2482744/alex-hall) in https://stackoverflow.com/questions/50900899/time-limit-exceeded-on-leetcodes-three-sum-problm, `itertools.combinations(stuff, 3)` contains O(n^3) elements, so this solution has cubic time complexity. (Was the reason LeetCode didn't accept this solution by any chance "Time Limit Exceeded"?) – Kurt Peek Jun 18 '18 at 06:11
6

Here's another way of solving it which has O(n^2) time complexity and passes the LeetCode test. It counts the occurrences and then sorts (number, count) tuples so [-1, 0, 1, 2, -1, -4] becomes [(-4, 1), (-1, 2), (0, 1), (1, 1), (2, 1)]. Then it iterates from beginning picking first trying to pick each number twice and third greater if possible and add this to result. Then it picks number once and tries to find two greater numbers which sum to 0.

from collections import Counter

class Solution(object):
    def threeSum(self, nums):
        res = []
        counts = Counter(nums)
        num_counts = sorted(counts.items())

        # Handle the only case where we pick three same nums
        if counts[0] >= 3:
            res.append([0] * 3)

        for i, (first, first_count) in enumerate(num_counts):
            # Pick two of these and one greater
            if first_count >= 2 and first < 0 and -(first * 2) in counts:
                res.append([first, first, -(first * 2)])

            # Pick one and two greater
            for j in range(i + 1, len(num_counts)):
                second, second_count = num_counts[j]
                # Pick two of these as second and third num
                if second_count >= 2 and -first == 2 * second:
                    res.append([first, second, second])

                # Pick this as second num and third which is greater
                third = -(first + second)
                if third > second and third in counts:
                    res.append([first, second, third])

        return res
niemmi
  • 17,113
  • 7
  • 35
  • 42
3

One of the approach is using the HashSet, What I have try here:

public List<List<Integer>> threeSum(int[] nums) {
        Set<List<Integer>> set = new HashSet<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length - 1; i++) {
            int j = i + 1;
            int k = nums.length - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if (sum == 0) {
                    set.add(Arrays.asList(nums[i], nums[j++], nums[k--]));
                } else if (sum > 0) {
                    k--;
                } else if (sum < 0) {
                    j++;
                }
            }
        }
        return new ArrayList<>(set);
    }
}
1

I'd like to add the answer you claimed(in the comment) to post:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def twoSum(self, nums, target):
            targ = target
            for index, i in enumerate(nums):
                targ -= i

                # if targ in nums[index+1:]:
                #    return [nums[index], nums[nums[index+1:].index(targ)+index+1]]
                _num = nums[:index] + nums[index+1:]
                if targ in _num: 
                    return [i, targ]
                else:
                    targ = target
            return None
        res = []
        for index, i in enumerate(nums):
            target = i * -1
            num = nums[:index] + nums [index+1:]
            ans = twoSum(self, num, target)
            if ans != None:
                temp = ans + [i]
                temp.sort()
                res.append(temp)
        print(res)
        import itertools
        res.sort()
        res = list(res for res,_ in itertools.groupby(res))
        return res

I have only run it my brain and hope it is right.

Lerner Zhang
  • 6,184
  • 2
  • 49
  • 66