5

Leetcode - Three sums

https://leetcode.com/problems/3sum/

def threeNumberSum(array, targetSum):
    array = sorted(array)
    results = []
    for idx, elem in enumerate(array):
        i = idx + 1
        j = len(array) - 1
        target = targetSum - elem
        while i < j:
            currentSum = array[i] + array[j]
            if currentSum == target:
                result = [array[i], array[j], array[idx]]
                results.append(sorted(result))
                i += 1 
                j -= 1 
            elif currentSum < target:
                i += 1
            else:
                j -= 1

    return results  

So time is O(n^2), I am fine with that, but space is O(n), according to Algoexpert.io, and I am not sure of why. His explanation was:

"We might end up storing every single number in our array, if every single number is used in some triplet, we will store a lot of numbers and it is going to be bound by O(n) space. Even if some numbers are used multiple times, it will be bounded by O(n)"

But I can't make sense of his explanation yet. If the provided array has (nearly) all unique triplet permutations summing to that target number, isn't space complexity going to be n choose 3 instead? If its n choose k=3 simplifying it would yield O(n^3).

Note, however, that the Algoexpert problem has one additional assumption with the input array that every element will be distinct, whereas the Leetcode version doesn't have that assumption. How would I formally address that information in space complexity analysis?

zcahfg2
  • 861
  • 1
  • 12
  • 27
  • 1
    You are right, the problem is itself O(n^3), for example, every element is equal to `0` and target is also `0`. – Quang Hoang Feb 17 '20 at 11:41
  • Thank you Quang, I added additional information in the question, the Algoexpert problem had one assumption that every element is distinct from each other. How would that narrow the space complexity down to O(n)? – zcahfg2 Feb 17 '20 at 11:41
  • Well, the number of combinations is reduced. If you have 4 numbers, you only have 4C3 *different* combinations, 4. – samthegolden Feb 17 '20 at 11:44
  • 1
    Where is your n choose 3 coming from? At most you append once in every inner iteration, so with no extra calculation the space bound is at least bounded by the time bound. – kabanus Feb 17 '20 at 11:46
  • @samthegolden, I am not sure if your reasoning is any different to my original statement above. If n=4 yes, but n=10 it's 120 and n=100 then it's 161,700. Bounded by O(n^3). You haven't introduced any new information as far as I'm concerned – zcahfg2 Feb 17 '20 at 11:52
  • @kabanus, Thank you for your insight, that you are suggesting makes sense. n choose k was all possible unordered unique pairs (discrete maths). I am sure permutations summing to one particular number will be less than that, but I am not sure how to formally address that information mathematically. But I can see it has to be less than O(n^2) just like you have mentioned. – zcahfg2 Feb 17 '20 at 11:54
  • 1
    Actually that discrepancy is an hint. Indeed, for [1,1,1,1,1...] we have n choose 3. This algorithm fails on this case with less triplets. – kabanus Feb 17 '20 at 12:01
  • @kabanus Thank you, I can see this algorithm doesn't generate all triplets for arrays with equal numbers like [1,1,1,1,1]... The algorithm should have another loop/recursion when it finds a new number i.e. increment i OR j separately instead of both like above. I am not sure what you meant by 'less triplets' though? – zcahfg2 Feb 17 '20 at 12:10
  • 1
    Less then n choose 3 triplets as it should be exactly. I think it does assume unique elements. – kabanus Feb 17 '20 at 12:11
  • @kabanus, yes you are completely right, the solution above assumes unique triplets. Could you write a solution instead of comment and explain why the space complexity has to be O(n) (According to Algoexpert) I will accept that as an answer. I can see why it is O(n^2) thanks to you, but still not sure why it has to be O(n)... – zcahfg2 Feb 17 '20 at 12:13

1 Answers1

1

If your code is correct (and I have no reason to assume it isn't), then the space complexity for the list of matching triplets is in fact O(n2).

It's not O(n3) because the third member of any triplet is uniquely determined by the first two, so there is no freedom of choice for this value.

If all the numbers are unique, then the space requirement is definitely O(n2):

>>> [len(threeNumberSum(range(-i,i+1),0)) for i in range(1,10)]
[1, 2, 5, 8, 13, 18, 25, 32, 41]

You should be able to satisfy yourself that the terms in this series correspond to ceil(n2/2). (See https://oeis.org/A000982).

If there are repeated numbers in the list, then the overall space requirement should decrease (relative to n) due to the requirement for unique triplets in the returned array.

r3mainer
  • 23,981
  • 3
  • 51
  • 88
  • 1
    AHH so Algoexpert is wrong, and with your solution and kind comment from @kabanus, I can finally see why space complexity has to be O(n^2) and 'the third member of any triplet is uniquely determined by the first two' was really good, it made sense, thank you! From your link, I can see the numbers equal to ceiling function which is awesome, Is there a mathematical proof why? I am curious to know how I could analyse 'three sums' space complexity mathematically. – zcahfg2 Feb 17 '20 at 12:19
  • 1
    I'm not so good at rigorous proofs, unfortunately! I expect someone would be able to come up with one at math.stackexchange.com – r3mainer Feb 17 '20 at 12:40