查找总和为零的所有唯一三元组

时间:2018-10-06 09:16:59

标签: algorithm

在采访中得到了这个。该问题与非常著名的问题Find a triplet that sum to a given value非常相似,但有细微差别。在这里,我们要打印 ALL 个三元组,而不仅仅是三个。

该数组可以包含重复项。

例如,考虑以下数组: [1, -1, 2, 0, -2, 4, -2, -2, 4]

输出应为:

[1, -1, 0]
[4, -2, -2]
[2, -2, 0]

三胞胎的顺序或三胞胎中的数字的顺序并不重要。

使用排序或使用集合,有n ^ 2个解决方案(如上面链接中的解决方案)。但是如何确保我们仅打印唯一的三元组?我能想到的一种解决方案是使用一组跟踪到目前为止我们看到的三胞胎。但是不确定这是否是最好的方法,或者是否存在其他使用排序等解决方案来生成唯一三胞胎的解决方案。

8 个答案:

答案 0 :(得分:2)

没有太大区别。仅当我们有重复项时,我们才需要在每个级别上跳过它们,因此std::set不一定是必需的。

#include <vector>
#include <algorithm>
#include <iostream>
#include <array>
int main()
{
    const int kDesired = 0;
    std::vector<int> a = { 1, -1, 2, 0, -2, 4, -2, -2, 4 };
    std::sort(a.begin(), a.end());
    std::vector<std::array<int, 3>> triples;
    for (int i = 0; i < (int)a.size(); ++i) {
        const int others = kDesired - a[i];
        for (int j = i + 1; j < (int)a.size(); ++j) {
            for (int k = (int)a.size() - 1; k > j; --k) {
                if (a[j] + a[k] == others) {
                    triples.push_back({ { a[i], a[j], a[k] } });
                }
                else if (a[j] + a[k] < others) {
                    break;
                }
                // we don't want a[k] to be the same next time
                while (j + 1 < k && k < (int)a.size() && a[k] == a[k - 1]) --k;
            }
            // we don't want a[j] to be the same next time
            while (i + 1 <= j && j < (int)a.size() - 1 && a[j] == a[j + 1]) ++j;
        }
        // we don't want a[i] to be the same next time
        while (0 <= i && i < (int)a.size() - 1 && a[i] == a[i + 1]) ++i;             }
    for (const auto& t : triples) {
        std::cout << t[0] << " " << t[1] << " " << t[2] << std::endl;
    }
    return 0;
}
  

-2 -2 4
  -2 0 2
  -1 0 1

online

答案 1 :(得分:2)

Python解决方案: 时间复杂度:O(n ^ 2) 空间复杂度:O(1)

class Solution:
def findSum(self , nums , rest_sum , start_from , soln ):
    i = start_from +1  
    j = len(nums)-1
    while(i < j ):
        if(nums[i] + nums[j] < rest_sum):
            i += 1
        elif(nums[i] + nums[j] > rest_sum):
            j -= 1
        else:    
            soln.append( [ nums[start_from] , nums[i] , nums[j] ])
            i += 1
            j -= 1
            while(i < j and nums[i] == nums[i-1]):#Loop to avoid duplicate
                i+=1
                continue
            while(j > i and nums[j] == nums[j+1] ):#Loop to avoid duplicate
                j-=1
                continue                             
    return 


def threeSum(self, nums: List[int]) -> List[List[int]]:
    if(len(nums) < 3 ):
        return []
    soln = []
    nums.sort()

    for i in range(0 , len(nums)):
        if(i > 0 and nums[i-1] == nums[i]):#Loop to avoid duplicate
            continue
        self.findSum(nums , (0 - nums[i]) ,i , soln ) #Use Two Sum Algo to find solution

    return soln

答案 2 :(得分:1)

  • 对数组进行排序后,您可以跳过以前访问过的相同整数。
  • 时间复杂度为O(N 2 log(N))。下面是一个例子(我更喜欢Java)。

代码:

import java.util.*;
public class Solution{
    public static void main(String[] args) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        int[] arr = {1,-1,2,0,-2,4,-2,-2, 4,-2,-2,-2,4,4,4};
        int target = 0;
        Arrays.sort(arr);

        int low = 0,mid = 0,high = 0,seek = 0;
        for(int i=0;i<arr.length;++i){
            if(i > 0 && arr[i] == arr[i-1]) continue;// skip it to avoid getting same triplets
            for(int j=i+1;j<arr.length;++j){
                if(j > i+1 && arr[j] == arr[j-1]) continue; // skip it to avoid getting same triplets
                seek = target - arr[i] - arr[j];
                if(seek < arr[j]) break; // we break because seek cannot be found ahead if arr[j] is greater than it after sorting. 
                low = j+1;
                high = arr.length-1;        
                while(low <= high){
                    mid = low + (high - low) / 2;
                    if(arr[mid] == seek){
                        // add this triplet to results.
                        List<Integer> triplet = new ArrayList<>();
                        triplet.add(arr[i]);
                        triplet.add(arr[j]);
                        triplet.add(seek);
                        res.add(triplet);
                        break;
                    }else if(arr[mid] > seek){
                        high = mid - 1;   
                    }else{
                        low = mid + 1;   
                    }
                }
            }
        }

        System.out.println(res.toString());
    }
}

输出:

[[-2, -2, 4], [-2, 0, 2], [-1, 0, 1]]

答案 3 :(得分:1)

多亏了Yola的回答,我才能够真正理解如何避免重复,因此这里是O(n ^ 2)解决方案。

主要思想是,主循环遍历每个唯一数字,然后尝试查找另外两个数字,它们加起来等于0。

主要技巧是对数组进行排序,然后对i,j,k中的每一个进行排序,以免在其回合中访问任何重复的数字,并保证不产生任何重复的三元组。

import java.util.Arrays;

public class Find3TripletSum0 {

    public static void find(int a[]) {
        Arrays.sort(a);
        for (int i = 0; i < a.length; i++) {
            if (i > 0 && a[i] == a[i - 1]) // pass duplicates for i
                continue;  
            int j = i + 1; 
            int k = a.length - 1; 
            int target = -a[i];
            while (j < k) {
                if (j > i + 1 && a[j] == a[j - 1]) { // pass duplicates for j
                    j++;
                    continue; 
                }
                if (k < a.length - 1 && a[k] == a[k+1]) { // pass duplicates for k
                    k--;
                    continue; 
                }
                if (a[i] + a[j] + a[k] == 0)
                    System.out.printf("[%d, %d, %d]\n", a[i], a[j], a[k]);
                if (a[j] + a[k] < target)
                    j++; 
                else
                    k--; 
            }
        }
    }

    public static void main(String[] args) {
        int a[] = {1, -1, 2, 0, -2, 4, -2, -2, 4};
        find(a);
    }
}

输出:

[-2, -2, 4]
[-2, 0, 2]
[-1, 0, 1]

答案 4 :(得分:0)

如果您正在寻找使用javascript的解决方案,请使用简单的英语说明以下逻辑:

resources :users do
  resources :books, shallow: true 
end

resources :books do
  resources :posts, shallow: true
end

resources :posts do
  resources :comments, shallow: true
end

示例

const threeSum = (nums, target) => {
  const hash = {};
  const ans = [];
  for (let i = 0; i < nums.length; i++) {
    for (key in hash) {
      if(hash[key][target - (nums[i] + +key)] === undefined) {
        hash[key][nums[i]] = null;
      } else {
        hash[key][target - (nums[i] + +key)] = nums[i];
        ans.push([+key, target - (nums[i] + +key), nums[i]]);
      }
    }
    if (hash[nums[i]] === undefined) {
      hash[nums[i]] = {}
    }
  }
  return ans;
}

输出

console.log(threeSum([-1, 0, 1, 2, -1, -4], 0));

说明

[ [ -1, 0, 1 ], [ 0, 1, -1 ], [ -1, 2, -1 ] ]
  1. 遍历数组>如果不在对象中,则添加到对象
  2. 遍历对象>如果不在对象中,则将其添加为null |其他日志

答案 5 :(得分:0)

import java.util.*;
class zero
{
public static void main(String abc[])
{
int arr[]=new int[6];
int i,j,x;


Scanner c=new Scanner(System.in);

System.out.println("Enter an array to be sorted ");
    for(i=0;i<6;i++)
     {
      arr[i]=c.nextInt();
      }

Arrays.sort(arr);

for(i=0;i<6;i++)
     {
      System.out.println(arr[i]);
    }

for(i=0;i<4;i++)
{
x=arr[i]*-1;
j=5;
 while(i<j)
 { 
  if(arr[i+1]+arr[j]>x)
   {
    j--;
   }else if(arr[i+1]+arr[j]<x){
    i++;
   }else{ 
   System.out.println("Found ");
   break;
    }
 }
}

}
}

答案 6 :(得分:0)

以O(n ^ 2)的复杂度查看此解决方案,我不知道其复杂度是否可以最小化为线性。

public static List<List<Integer>> findTriplets(int nums[]) {
        boolean found = false;
        List<Integer> triples = null;
        HashSet<Integer> set = null;
        HashSet<List<Integer>> tripleSet = new HashSet<List<Integer>>();
        for (int i = 0; i < nums.length - 1; i++) {         
            set = new HashSet<Integer>();
            for (int j = i + 1; j < nums.length; j++) {
                found = false;
                int x = -(nums[i] + nums[j]);
                if (set.contains(x)) {
                    Integer [] temp = {x,nums[i],nums[j]};
                    Arrays.sort(temp);
                    triples = new ArrayList<Integer>();
                    triples.add(temp[0]);
                    triples.add(temp[1]);
                    triples.add(temp[2]);
                    found = true;
                } else {
                    set.add(nums[j]);
                }
                
                if(found==true){
                    tripleSet.add(triples);

                }
                
            }
        }
        return new ArrayList<List<Integer>>(tripleSet);
    }

答案 7 :(得分:0)

只有当当前数字与您已经看到的数字相同时,才会出现重复。可以使用 HashSets。

但是,由于您正在对数组进行排序,因此您只需要跟踪前一个数字,因为排序可确保所有重复项都在同一位置。

这是 C# 中的解决方案。

public class Solution 
{
  public IList<IList<int>> ThreeSum(int[] nums)
  {
    var result = new List<IList<int>>();
    
    if(nums.Length<3)
        return result;
    
     Array.Sort(nums);
    int curr=nums[0]-1;
   
    
    for(int i=0; i<nums.Length;i++)
    {
        if(nums[i]!=curr)
        {
            var tsum= Get2Sum(-nums[i],nums, i+1);
            
            if(tsum.Count>0)
            {
                for(int j=0; j<tsum.Count;j++)
                {
                    tsum[j].Add(nums[i]);
                }
                
                result.AddRange(tsum);
            }
            
            
            curr=nums[i];
        }
    }
    return result;
    
}

public IList<IList<int>> Get2Sum(int target, int[] nums, int start)
{
    
    var result = new List<IList<int>>();
    int end = nums.Length-1;
    if(start>=end)
    {
        return result;
    }
    //current value for starting pointer
    int scurr=nums[start]-1;
    //current value for ending pointer
    int ecurr= nums[end]+1;
    
    while(start< end)
    {
        if(nums[start]==scurr)
        {
            start++;
            continue;
        }
        if(nums[end] == ecurr)
        {
            end--;
            continue;
        }
        
        int sum = nums[start]+nums[end];
        if(sum > target)
        {
            ecurr= nums[end];
            end--;
        }
        else if(sum < target)
        {
            scurr=nums[start];
            start++;
        }
        else
        {
            scurr=nums[start];
            ecurr=nums[end];
            result.Add(new List<int>(){nums[start], nums[end]});
            start++;
            end--;
        }
        
        
    }
    
    return result;
    
  }
}