twoSum查找所有可能的唯一对

时间:2019-03-24 11:22:38

标签: python python-3.x algorithm

我有这样的问题

  

给出一个由 n 个整数组成的数组numsnums中是否存在元素 a b 使得 a + b = 10?在数组中找到给出目标总和的所有唯一对。

     

注意:

     

解决方案集不能包含重复的对。

     

示例:

     
Given nums = [4, 7, 6, 3, 5], target = 10

because   4+ 6= 7+ 3   = 10
return  [[4, 6], [7,3]]

我的解决方案:

class SolutionAll: #Single Pass Approach 
    def twoSum(self, nums, target) -> List[List[int]]:
        """
        :type nums: List[int]
        :type target: int
        """
        nums.sort()
        nums_d:dict = {}
        couples = []

        if len(nums) < 2:
            return []

        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i-1]: continue #skip the duplicates

            complement = target - nums[i]

            if nums_d.get(complement) != None:
                couples.append([nums[i], complement])          
            nums_d[nums[i]] = i                            
        return couples 

TestCase结果:

target: 9 
nums: [4, 7, 6, 3, 5]
DEBUG complement: 6
DEBUG nums_d: {3: 0}
DEBUG couples: []
DEBUG complement: 5
DEBUG nums_d: {3: 0, 4: 1}
DEBUG couples: []
DEBUG complement: 4
DEBUG nums_d: {3: 0, 4: 1, 5: 2}
DEBUG couples: [[5, 4]]
DEBUG complement: 3
DEBUG nums_d: {3: 0, 4: 1, 5: 2, 6: 3}
DEBUG couples: [[5, 4], [6, 3]]
DEBUG complement: 2
DEBUG nums_d: {3: 0, 4: 1, 5: 2, 6: 3, 7: 4}
DEBUG couples: [[5, 4], [6, 3]]
result: [[5, 4], [6, 3]]
.
target: 2 
nums: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
DEBUG complement: 2
DEBUG nums_d: {0: 0}
DEBUG couples: []
DEBUG complement: 1
DEBUG nums_d: {0: 0, 1: 9}
DEBUG couples: []
result: []

该解决方案适用于[4、7、6、3、5],但失败于[0,0,0,0,0,0,0,0,0,1,1,1,1,1, 1]

我试图删除重复项,但是得到了意外的结果。

如何通过这种“一站式”解决方案解决问题?

4 个答案:

答案 0 :(得分:2)

您的代码存在的问题是它会跳过重复的数字,而不是重复的 pairs 。因为

if i > 0 and nums[i] == nums[i-1]: continue #skip the duplicates

您的代码从不尝试对1 + 1 = 2求和。


这是一个O(n)复杂性的可行解决方案:

from collections import Counter

def two_sum(nums, target):
    nums = Counter(nums)  # count how many times each number occurs

    for num in list(nums):  # iterate over a copy because we'll delete elements
        complement = target - num

        if complement not in nums:
            continue

        # if the number is its own complement, check if it
        # occurred at least twice in the input
        if num == complement and nums[num] < 2:
            continue

        yield (num, complement)

        # delete the number from the dict so that we won't
        # output any duplicate pairs
        del nums[num]
>>> list(two_sum([4, 7, 6, 3, 5], 10))
[(4, 6), (7, 3)]
>>> list(two_sum([0, 0, 0, 1, 1, 1], 2))
[(1, 1)]

另请参阅:

答案 1 :(得分:1)

不确定解决方案出了什么问题(也不知道解决方案有什么问题),但是您可以通过“漂亮的Python方式”轻松实现这一目标:

def func(nums,target):
    return [(a,b) for a in nums for b in nums if a+b == target]

假定两个仅在元素顺序上不同的元组是唯一的,并且一个元素可以在同一元组中使用两次。如果问题的定义不正确,则可以从返回的值中过滤出这些元组。

答案 2 :(得分:0)

编辑:请参见下面的讨论。

from itertools import combinations

list(set([(a,b) for a,b in combinations(sorted(nums),2) if a+b == target]))

这也会删除重复项。

答案 3 :(得分:0)

其他版本:

>>> nums = [4, 7, 6, 3, 5]
>>> target = 9 
>>> set((a, target-a) for a in nums if target-a in set(nums))
{(4, 5), (5, 4), (3, 6), (6, 3)}

对于a中的每个元素nums,如果target-a中也有nums,则我们有:

  • a + target-a = target(显而易见);
  • atarget-a都在nums中。

由于我们遍历了每个a,因此我们获得了所有解决方案。

要消除重复的(x, y)(y, x)

>>> set((a, target-a) for a in nums if 2*a<=target and target-a in set(nums))
{(4, 5), (3, 6)}

因为2*a <= target等效于a <= target-a。当a > target-a和要求的条件都满足时,我们有一个先前的b = target-a,因此(b, target-b)是一个解决方案。