3SUM(查找列表中等于0的所有唯一三元组)

时间:2017-01-07 18:23:15

标签: python list

我正在研究3SUM问题(取自leetcode),它将列表作为输入并在列表中找到所有唯一的三元组,使得a + b + c = 0。我不确定我的代码出错了什么,但它当前返回此列表的空列表[-1,0,1,2,-1,-4],因此它无法识别总和为0的任何三元组我很感激任何建议或改进的代码。

这是我的代码:

result = []
nums.sort()
l = 0
r=len(nums)-1
for i in range(len(nums)-2):
    while (l < r):
        sum = nums[i] + nums[l] + nums[r]
        if (sum < 0):
            l = l + 1
        if (sum > 0):
            r = r - 1
        if (sum == 0): 
            result.append([nums[i],nums[l],nums[r]])
print(result)

5 个答案:

答案 0 :(得分:5)

有几点需要注意。

  1. 不要将sum用作变量名,因为这是一个内置函数。
  2. 您的索引编制有点问题,因为您初始化l = 0并且i也开始0
  3. 不要满足于已有的成就:当您找到成功的组合时,请增加l的值。忘记这一步真的很容易!
  4. 以下代码的编辑版本。

    nums = [-1, 0, 1, 2, -1, -4]
    result = []
    nums.sort()
    r=len(nums)-1
    for i in range(len(nums)-2):
        l = i + 1  # we don't want l and i to be the same value.
                   # for each value of i, l starts one greater
                   # and increments from there.
        while (l < r):
            sum_ = nums[i] + nums[l] + nums[r]
            if (sum_ < 0):
                l = l + 1
            if (sum_ > 0):
                r = r - 1
            if not sum_:  # 0 is False in a boolean context
                result.append([nums[i],nums[l],nums[r]])
                l = l + 1  # increment l when we find a combination that works
    
    >>> result
    [[-1, -1, 2], [-1, 0, 1], [-1, 0, 1]]
    

    如果您愿意,可以省略列表中的重复项。

    unique_lst = []
    [unique_lst.append(sublst) for sublst in result if not unique_lst.count(sublst)]
    
    >>> unique_lst
    [[-1, -1, 2], [-1, 0, 1]]
    

    另一种方法使用itertools.combinations。这不需要排序列表。

    from itertools import combinations
    
    result = []
    for lst in itertools.combinations(nums, 3):
        if sum(lst) == 0:
            result.append(lst)
    

    嵌套for循环版本。不是这种方法的忠实粉丝,但它基本上是itertools.combinations解决方案的暴力版本。由于它与上述方法相同,因此不需要排序。

    result = []
    for i in range(0, len(nums)-2):
        for j in range(i + 1, len(nums)-1):
            for k in range(j + 1, len(nums)):
                if not sum([nums[i], nums[j], nums[k]]):  # 0 is False
                    result.append([nums[i], nums[j], nums[k]])
    

答案 1 :(得分:0)

我做了类似于3novak的方法,但我添加了数字列表少于三个整数返回空列表的情况。

class Solution:
def threeSum(self, nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """        
    # if less than three numbers, don't bother searching
    if len(nums) < 3:
        return []

    # sort nums and use current sum to see if should have larger number or smaller number
    nums = sorted(nums)
    triplets = []
    for i in range(len(nums)-2):    # i point to first number to sum in list
        j = i + 1                   # j points to middle number to sum in list
        k = len(nums) - 1           # k points to last number to sum in list
        while j < k:
            currSum = nums[i] + nums[j] + nums[k]
            if currSum == 0:
                tmpList = sorted([nums[i], nums[j], nums[k]])
                if tmpList not in triplets:
                    triplets.append(tmpList)
                j += 1 # go to next number to avoid infinite loop
            # sum too large, so move k to smaller number
            elif currSum > 0:
                k -= 1
            # sum too small so move j to larger number
            elif currSum < 0:
                j += 1
    return triplets

我在leetcode上做同样的问题,但仍然有运行时错误。这可以通过使用二分搜索树类算法来找到第三个结果。

答案 2 :(得分:0)

我的解决方案中取消注释打印语句:

class Solution:


    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        # print('Input: {}'.format(nums))
        nums.sort() # inplace sorting, using only indexes
        N, result = len(nums), []
        # print('Sorted Input: {}'.format(nums))
        for i in range(N):

            if i > 0 and nums[i] == nums[i-1]:
                # print("Duplicate found(when 'i' iterate ) at index: {}, current: {}, prev: {}, so JUMP this iteration------".format(i,nums[i], nums[i-1]))
                continue

            target = nums[i]*-1
            s,e = i+1, N-1
            # print('~'*50)
            # print("Target: {} at index: {} & s: {}  e: {} {}".format(target,i, s, e, '----'*2))

            while s<e: # for each target squeeze in s & e
                if nums[s]+nums[e] == target:
                    result.append([nums[i], nums[s], nums[e]])
                    # print(' {} + {} == {}, with  s: {} < e: {}, Triplet: {},  MOVING --> R'.format(nums[s], nums[e], target,s, e,result))
                    s = s+1

                    while s<e and nums[s] == nums[s-1]: # duplicate
                        # print("Duplicate found(when 's' iterates) at s: {} < e: {}, WILL KEEP MOVING ---> R  (s: {}) == (s-1: {})".format(s, e, nums[s], nums[s - 1]))
                        s = s+1
                elif nums[s] + nums[e] < target:
                    # print(' {} + {} < {}, with s: {} e: {}, MOVING ---> R'.format(nums[s], nums[e], target,s, e))
                    s = s+1

                else:
                    # print(' {} + {} > {}, with s: {} e: {}, MOVING <--- L'.format(nums[s], nums[e], target,s, e))
                    e = e-1

        return result

它将帮助您更好地理解算法。而且,该算法比上述可用选项快3倍。与上述替代方案相比,它需要约892.18 ms的时间,运行时间约为4216.98 ms。开销是因为额外删除了重复逻辑。

答案 3 :(得分:0)

使用两个指针方法:

  1. 首先对列表进行排序。
  2. 从左到右迭代。假设当前位置为i,将左侧位置设置为i+1,并将右端设置为列表N-1的末尾。
    • 如果总和大于0,则将右端减少1。
    • 否则,如果总和小于0,则将左端增加1,
    • 否则,检查新条目的唯一性,如果唯一,则将其添加到答案列表。继续使用leftEnd++, rightEnd--搜索更多条目。

Java代码:

public ArrayList<ArrayList<Integer>> threeSum(ArrayList<Integer> A) {
    ArrayList<ArrayList<Integer>> ans =  new ArrayList<ArrayList<Integer>>();
    Collections.sort(A); // takes O(nlogn)
    if (A.size() < 3) return ans;
    ArrayList<Integer> triplet = new ArrayList<>();
    for(int i = 0; i < A.size()-3; i++){ // takes O(n^2)
        if (i > 0 && A.get(i) == A.get(i-1)) continue; // to maintain unique entries
        int r = A.size()-1;
        int l = i+1;
        while (l < r){
            int s = sumOfThree(A, i, l, r);
            if (s == 0){
                if (ans.size() == 0 || !bTripletExists(A, i, l, r, triplet)){
                    triplet = getNewTriplet(A, i, l, r); // to be matched against next triplet
                    ans.add(triplet);
                }
                l++;
                r--;
            }else if (s > 0){
                r--;
            }else {
                l++;
            }
        }
    }
    return ans;
}
public int sumOfThree(ArrayList<Integer> A, int i, int j, int k){
    return A.get(i)+A.get(j)+A.get(k);
}
public ArrayList<Integer> getNewTriplet(ArrayList<Integer> A, int i, int j, int k){
    ArrayList<Integer> newTriplet = new ArrayList<>();
    newTriplet.add(A.get(i));
    newTriplet.add(A.get(j));
    newTriplet.add(A.get(k));
    return newTriplet;
}
public boolean bTripletExists(ArrayList<Integer> A, int i, int j, int k, ArrayList<Integer> triplet){
    if (A.get(i).equals(triplet.get(0)) && 
        A.get(j).equals(triplet.get(1)) &&
        A.get(k).equals(triplet.get(2)))
        return true;
    return false;
}

答案 4 :(得分:0)

上面给出的大多数答案都不错,但是在leetcode上有些不完善的情况。 我添加了一些检查以通过所有测试用例

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:

    # if the list has less than 3 elements
    if len(nums)<3:
        return []

    # if nums is just zeroes return just one zeroes pair
    elif sum([i**2 for i in nums]) == 0:
        return [[0,0,0]]

    nums.sort()

    result = []


    for i in range(len(nums)):

        #duplicate skip it
        if i > 0 and nums[i]== nums[i-1]:
            continue

        # left pointer starts next to current i item
        l = i+1
        r = len(nums)-1

        while l< r:
            summ = nums[l] + nums[r]

            # if we find 2 numbers that sums up to -item
            if summ == -nums[i]:
                result.append([nums[i],nums[l],nums[r]])
                l +=1

                # duplicate skip it
                while l<r and nums[l] == nums[l-1]:
                    l +=1


            # if the sum is smaller than 0 we move left pointer forward
            elif summ + nums[i] < 0:
                l +=1
            # if the sum is bigger than 0 move the right pointer backward   
            else:
                r -=1

    return result