优化三和

时间:2017-09-25 17:23:03

标签: python algorithm

我正在尝试解决所说的3 Sum问题:

  

给定n个整数的数组S,S中是否有元素a,b,c,a + b + c = 0?找到数组中所有唯一的三元组,它们总和为零。

     

注意:解决方案集不得包含重复的三元组。

以下是我对此问题的解决方案:

def threeSum(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    nums.sort()
    n = len(nums)
    solutions = []
    for i, num in enumerate(nums):
        if i > n - 3:
            break

        left, right = i+1, n-1
        while left < right:
            s = num + nums[left] + nums[right] # check if current sum is 0
            if s == 0:
                new_solution = [num, nums[left], nums[right]]
                # add to the solution set only if this triplet is unique

                if new_solution not in solutions: 
                    solutions.append(new_solution)
                right -= 1
                left += 1
            elif s > 0:
                right -= 1
            else:
                left += 1

    return solutions

此解决方案工作正常,时间复杂度为O(n**2 + k),空间复杂度为O(k),其中n是输入数组的大小,k是解决方案的数量。

在LeetCode上运行此代码时,我发现大型数组的TimeOut错误。我想知道如何进一步优化我的代码以通过判断。

P.S:我已经阅读了this related question中的讨论。这无助于我解决问题。

2 个答案:

答案 0 :(得分:3)

您可以对算法进行一些改进:

1)使用sets代替您的解决方案列表。使用套件将确保您没有任何重复,并且您不必进行if new_solution not in solutions:检查。

2)为全零列表添加边缘案例检查。没有太多开销,但在某些情况下节省了大量时间。

3)将枚举更改为秒。它快一点。奇怪的是,我在测试中使用while循环获得了更好的性能,然后n_max = n -2; for i in range(0, n_max):阅读this问题和xrange或范围的答案应该更快。

注意:如果我运行测试5次,我将无法获得相同的时间。我所有的测试都是+ -100毫秒。因此,需要进行一些小的优化。对于所有python程序,它们可能不会更快。对于运行测试的确切硬件/软件配置,它们可能更快。

另外:如果从代码中删除所有注释,那么速度快300毫安的HAHAHAH就好了。只是一个有趣的副作用,然而测试正在运行。

我已经在代码的所有部分中添加了O()表示法,这需要花费很多时间。

def threeSum(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    # timsort: O(nlogn)
    nums.sort()

    # Stored val: Really fast
    n = len(nums)

    # Memory alloc: Fast
    solutions = []

    # O(n) for enumerate
    for i, num in enumerate(nums):
        if i > n - 3:
            break

        left, right = i+1, n-1

        # O(1/2k) where k is n-i? Not 100% sure about this one
        while left < right:
            s = num + nums[left] + nums[right]  # check if current sum is 0
            if s == 0:
                new_solution = [num, nums[left], nums[right]]
                # add to the solution set only if this triplet is unique

                # O(n) for not in
                if new_solution not in solutions:
                    solutions.append(new_solution)
                right -= 1
                left += 1
            elif s > 0:
                right -= 1
            else:
                left += 1

    return solutions

这是一些不会超时且速度很快的代码(ish)。它还暗示了一种使算法更快的方法(使用更多设置;))

class Solution(object):
def threeSum(self, nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    # timsort: O(nlogn)
    nums.sort()

    # Stored val: Really fast
    n = len(nums)

    # Hash table
    solutions = set()

    # O(n): hash tables are really fast :)
    unique_set = set(nums)
    # covers a lot of edge cases with 2 memory lookups and 1 hash so it's worth the time
    if len(unique_set) == 1 and 0 in unique_set and len(nums) > 2:
        return [[0, 0, 0]]

    # O(n) but a little faster than enumerate.
    i = 0
    while i < n - 2:
        num = nums[i]

        left = i + 1
        right = n - 1

        # O(1/2k) where k is n-i? Not 100% sure about this one
        while left < right:
            # I think its worth the memory alloc for the vars to not have to hit the list index twice. Not sure
            # how much faster it really is. Might save two lookups per cycle. 
            left_num = nums[left]
            right_num = nums[right]
            s = num + left_num + right_num  # check if current sum is 0
            if s == 0:
                # add to the solution set only if this triplet is unique
                # Hash lookup
                solutions.add(tuple([right_num, num, left_num]))
                right -= 1
                left += 1
            elif s > 0:
                right -= 1
            else:
                left += 1
        i += 1

    return list(solutions)

答案 1 :(得分:0)

我整理了PeterH提供的更快的代码,但是我找到了一个更快的解决方案,而且代码也更简单。

class Solution(object):
def threeSum(self, nums):
    res = []
    nums.sort()
    length = len(nums)
    for i in xrange(length-2): #[8]
        if nums[i]>0: break #[7]
        if i>0 and nums[i]==nums[i-1]: continue #[1]

        l, r = i+1, length-1 #[2]
        while l<r:
            total = nums[i]+nums[l]+nums[r]

            if total<0: #[3]
                l+=1
            elif total>0: #[4]
                r-=1
            else: #[5]
                res.append([nums[i], nums[l], nums[r]])
                while l<r and nums[l]==nums[l+1]: #[6]
                    l+=1
                while l<r and nums[r]==nums[r-1]: #[6]
                    r-=1
                l+=1
                r-=1
    return res

https://leetcode.com/problems/3sum/discuss/232712/Best-Python-Solution-(Explained)