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

Algorithm 查找所有和为零的唯一三元组,algorithm,Algorithm,这是在采访中得到的。这个问题与非常著名的问题非常相似,只是略有不同。这里我们要打印所有的三胞胎,而不仅仅是一个 数组可以包含重复项 例如考虑以下数组: [1,-1,2,0,-2,4,-2,-2,4] 输出应为: [1, -1, 0] [4, -2, -2] [2, -2, 0] 三胞胎的顺序或三胞胎中的数字顺序并不重要 使用排序或使用集合有n^2个解决方案(如上面链接中的解决方案)。但如何确保我们只打印唯一的三胞胎?我能想到的一个解决方案是使用一个集合来跟踪到目前为止我们看到的三胞胎。但不确定

这是在采访中得到的。这个问题与非常著名的问题非常相似,只是略有不同。这里我们要打印所有的三胞胎,而不仅仅是一个

数组可以包含重复项

例如考虑以下数组:

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

输出应为:

[1, -1, 0]
[4, -2, -2]
[2, -2, 0]
三胞胎的顺序或三胞胎中的数字顺序并不重要


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

没什么不同。只有当我们有重复项时,我们才需要在每个级别上跳过它们,所以
std::set
不一定

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);
    }
}
#包括
#包括

  • 对数组排序后,可以跳过以前访问过的相同整数
  • 时间复杂度为O(N2log(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]]

多亏了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);
    }
}
如果您正在寻找javascript解决方案,下面是一个用简单英语解释逻辑的解决方案: 输出 解释
  • 遍历数组>如果不在对象中,则添加到对象
  • 迭代对象>如果不在对象的对象中,则将其值添加为null | else log
  • Python解决方案: 时间复杂度:O(n^2) 空间复杂度:O(1)

    类解决方案:
    def findSum(self、nums、rest\u sum、start\u from、soln):
    i=从+1开始
    j=len(nums)-1
    而(i剩余金额):
    j-=1
    其他:
    soln.append([nums[start_from]、nums[i]、nums[j]]
    i+=1
    j-=1
    而(ii和nums[j]==nums[j+1]):#循环以避免重复
    j-=1
    继续
    返回
    def threeSum(self,nums:List[int])->List[List[int]:
    如果(len(nums)<3):
    返回[]
    soln=[]
    nums.sort()
    对于范围(0,len(nums))内的i:
    如果(i>0且nums[i-1]==nums[i]):#循环以避免重复
    持续
    self.findSum(nums,(0-nums[i]),i,soln)#使用两个和算法来寻找解
    返回soln
    
    导入java.util.*;
    零级
    {
    公共静态void main(字符串abc[])
    {
    int arr[]=新int[6];
    int i,j,x;
    扫描仪c=新扫描仪(System.in);
    System.out.println(“输入要排序的数组”);
    
    对于(i=0;i请参见复杂度为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);
        }
    
    公共静态列表findtriplet(int nums[]{
    布尔值=false;
    列表三元组=空;
    HashSet=null;
    HashSet-tripleSet=新HashSet();
    对于(int i=0;i
    仅当当前数字与您已经看到的数字相同时,才会出现重复。哈希集可用于此目的

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

    这里有一个C#中的解决方案

    公共类解决方案
    {
    公共IList三和(int[]nums)
    {
    var result=新列表();
    如果(nums.Length目标)
    {
    ecurr=nums[end];
    结束--;
    }
    否则如果(总和<目标值)
    {
    scurr=nums[开始];
    启动++;
    }
    其他的
    {
    scurr=nums[开始];
    ecurr=nums[end];
    Add(新列表(){nums[start],nums[end]});
    启动++;
    结束--;
    }
    }
    返回结果;
    }
    }
    
    解释得很好,因此可以直观地调整边界情况以避免重复。
    console.log(threeSum([-1, 0, 1, 2, -1, -4], 0));
    
    [ [ -1, 0, 1 ], [ 0, 1, -1 ], [ -1, 2, -1 ] ]
    
    {
        -1: {0:1, 1:null, 2:-1, -1:null, -4:null},
        0: {1:-1, 2: null, -1:null, -4:null},
        1: {2: null, -1:null, -4: null},
        2: {-1:null, -4:null},
        -4: {}
    }
    
    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
    
    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;
        }
     }
    }
    
    }
    }
    
    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);
        }
    
    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;
        
      }
    }