来自leetcode的4sum Java实现

时间:2012-06-26 22:01:44

标签: java algorithm

来自leetcode的问题声明说:

  

给定n个整数的数组S,S中是否有元素a,b,c和d,使得a + b + c + d =目标?找到数组中所有唯一的四元组,它们给出了目标的总和。

注意:

Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
The solution set must not contain duplicate quadruplets.

关于这四个问题,我有两个问题:

  1. 哪里出错了?编译后的代码无法通过所有测试,但我认为代码应该是正确的,因为它只是使用蛮力来解决问题。
  2. 有没有有效的方法来解决四和问题而不是这个O(n ^ 4)运行时算法?

  3. import java.util.List;
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class FourSumSolution{
        public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target){
            ArrayList<ArrayList<Integer>> aList = new ArrayList<ArrayList<Integer>>();
            int N = num.length;
    
            for(int i=0; i<N; i++)
                for(int j=i+1; j<N; j++)
                    for(int k=j+1; k<N; k++)
                        for(int l=k+1; l<N; l++){
                            int sum = num[i] + num[j] + num[k] + num[l];                        
                            if(sum == target){
                                ArrayList<Integer> tempArray = new ArrayList<Integer>();
                                Collections.addAll(tempArray, num[i], num[j], num[k], num[l]);
                                aList.add(tempArray);
                            }
                        }
            return aList;   
        }
    }
    

6 个答案:

答案 0 :(得分:3)

这是一个O(n ^ 3)解决方案

import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
    // Start typing your Java solution below
    // DO NOT write main() function

    Arrays.sort(num);
    HashSet<ArrayList<Integer>> hSet = new HashSet<ArrayList<Integer>>();
    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    for (int i = 0; i < num.length; i++) {
        for (int j = i + 1; j < num.length; j++) {
            for (int k = j + 1, l = num.length - 1; k < l;) {
                int sum = num[i] + num[j] + num[k] + num[l];
                if (sum > target) {
                    l--;
                }
                else if (sum < target) {
                    k++;
                }
                else if (sum == target) {
                    ArrayList<Integer> found = new ArrayList<Integer>();
                    found.add(num[i]);
                    found.add(num[j]);
                    found.add(num[k]);
                    found.add(num[l]);
                    if (!hSet.contains(found)) {
                        hSet.add(found);
                        result.add(found);
                    }

                    k++;
                    l--;

                }
            }
        }
    }
    return result;
}

}

答案 1 :(得分:1)

这是一个不使用哈希的解决方案。它应该更快,但仍然超过时间限制。

import java.util.ArrayList;

public class Solution {
    public static void main(String[] args) {

      int[] S = {1, 0, -1, 0, -2, 2};
      int target = 0;
      test(S, target);
    }

    public static void test(int[] num, int target) {
      System.out.print("a:");
      for (int i : num) {
        System.out.print(" " + i);
      }
      System.out.println(": " + target);
      ArrayList<ArrayList<Integer>> res = fourSum(num, target);
      for (ArrayList<Integer> list : res) {
        System.out.println(list);
      }
      System.out.println();
    }

    // a+b+c+d = target
    public static ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
        // Start typing your Java solution below
        // DO NOT write main() function

       // Sort
       {
        for (int i = 0; i < num.length; i++) {
            for (int j = i + 1; j < num.length; j++) {
                if (num[j] < num[i]) {
                    int tmp = num[i];
                    num[i] = num[j];
                    num[j] = tmp;
                }
            }
        }
       }
      ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
      int i = 0;
      while (i < num.length - 3) {
        int j = i + 1;
        while (j < num.length - 2) {
            int k = j + 1, l = num.length - 1;
            while (k < l) {
                int sum = num[i] + num[j] + num[k] + num[l];
                if (sum > target) {
                    l--;
                    while (k < l && num[l] == num[l+1]) l--;
                } else if (sum < target) {
                    k++;
                    while (k < l && num[k] == num[k-1]) k++;
                } else {
                    ArrayList<Integer> list =
                        new ArrayList<Integer>(4);
                    list.add(num[i]); list.add(num[j]);
                    list.add(num[k]); list.add(num[l]);
                    res.add(list);
                    k++;
                    while (k < l && num[k] == num[k-1]) k++;
                    l--;
                    while (k < l && num[l] == num[l+1]) l--;
                }
            }
            j++;
            while (j < num.length && num[j] == num[j-1]) j++;
        }
        i++;
        while (i < num.length && num[i] == num[i-1]) {
            i++;
        }
      }

      return res;
    }
}

答案 2 :(得分:1)

您的解决方案可能已超过时间限制,因此可能无法通过测试用例。以下是具有运行时O(n^3)的代码。最初,我们对数组进行排序,这将有助于我们根据要查找的数字向前或向后移动来查找元素。然后,我们从数组中获取第一个元素,并对其余部分执行三和,从而找到两个和。仅使用ArrayList。不需要HashMap

public static List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> result = new ArrayList<List<Integer>>();

    if(nums==null|| nums.length<4)
        return result;

    Arrays.sort(nums);

    for(int i=0; i<nums.length-3; i++)
    {
        if(i!=0 && nums[i]==nums[i-1]) continue; //Avoid duplicate elements

        for(int j=i+1; j<nums.length-2; j++)
        {
            if(j!=i+1 && nums[j]==nums[j-1]) continue; //Avoid duplicate elements

            int k=j+1;
            int l=nums.length-1;

            while(k<l)
            {
               if(nums[i]+nums[j]+nums[k]+nums[l]<target)
               {
                  k++;
               }
               else if(nums[i]+nums[j]+nums[k]+nums[l]>target)
               {
                  l--;
               }
               else
               {
                  List<Integer> t = new ArrayList<Integer>();
                  t.add(nums[i]);
                  t.add(nums[j]);
                  t.add(nums[k]);
                  t.add(nums[l]);
                  result.add(t);

                  k++;
                  l--;

                  while( k<l &&nums[l]==nums[l-1] ) l--;
                  while( k<l &&nums[k]==nums[k+1] ) k++;
                }
              }
           }
       }
    return result;
}

答案 3 :(得分:0)

你的解决方案显然正确计算。但是如果输入数组中出现多次值,则会得到“相同”的结果。也许这已经过测试。

目标= 4的num = {1,3,1,3,4}的“twoSum”将是:

  • {1,3}
  • {1,3}
  • {3,1}
  • {1,3}

这就是测试失败的原因,解决方案可能是一组有序列表

如果还必须给出子集,则缺少{4}。

所以问题是,为什么测试失败,有什么要求。

优化可能涉及特殊的数据结构:至少一个(反向)排序的数字。 已经给出了许多链接。递归可以帮助找到具有前置和后置条件的可证明的且可理解的最佳解决方案。 (当你分裂问题时。)

一种解决方案是创建对,保持对和的映射到导致该和的所有索引对。然后找到两对分离指数。


由于没有人给出满意的答案:

一个简单的递归解决方案(不是很好或最优)。然而,它表明数据结构是相关的。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;

public class FourSumSolution {

    private static class SortedTuple implements Comparable<SortedTuple> {
        int[] values;

        public SortedTuple(int... values) {
            this.values = Arrays.copyOf(values, values.length);
            Arrays.sort(this.values);
        }

        public int size() {
            return values.length;
        }

        public int get(int index) {
            return values[index];
        }

        public ArrayList<Integer> asList() {
            // Result type is ArrayList and not the better List,
            // because of the external API of the outer class.
            ArrayList<Integer> list = new ArrayList<Integer>(values.length);
            for (int value: values)
                list.add(value);
            return list;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + Arrays.hashCode(values);
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            SortedTuple other = (SortedTuple) obj;
            if (!Arrays.equals(values, other.values))
                return false;
            return true;
        }

        @Override
        public int compareTo(SortedTuple other) {
            int c = cmp(values.length, other.values.length);
            if (c == 0) {
                for (int i = 0; i < values.length; ++i) {
                    c = cmp(values[i], other.values[i]);
                    if (c != 0)
                        break;
                }
            }
            return c;
        }

        @Override
        public String toString() {
            return Arrays.toString(values);
        }

        private static int cmp(int lhs, int rhs) {
            return lhs < rhs ? -1 : lhs > rhs ? 1 : 0; // Cannot overflow like lhs - rhs.
        }
    }

    public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
        final int nTerms = 4;
        SortedTuple values = new SortedTuple(num);
        SortedSet<SortedTuple> results = new TreeSet<SortedTuple>();
        int[] candidateTerms = new int[nTerms];
        int valuesCount = values.size();
        solveNSum(target, nTerms, values, results, candidateTerms, valuesCount);

        ArrayList<ArrayList<Integer>> aList = new ArrayList<ArrayList<Integer>>();
        for (SortedTuple solution: results) {
            aList.add(solution.asList());
        }
        return aList;   
    }

    public static void main(String[] args) {
        final int[] num = { 1, 3, 1, 3, 4 };
        final int requiredSum = 4;
        final int nTerms = 2;

        SortedTuple values = new SortedTuple(num);
        SortedSet<SortedTuple> results = new TreeSet<SortedTuple>();
        int[] candidateTerms = new int[nTerms];
        int valuesCount = values.size();
        solveNSum(requiredSum, nTerms, values, results, candidateTerms, valuesCount);

        System.out.println("Solutions:");
        for (SortedTuple solution: results) {
            System.out.println("Solution: " + solution);
        }
        System.out.println("End of solutions.");
    }

    private static void solveNSum(int requiredSum, int nTerms, SortedTuple values, SortedSet<SortedTuple> results, int[] candidateTerms, int valuesCount) {
        if (nTerms <= 0) {
            if (requiredSum == 0)
                results.add(new SortedTuple(candidateTerms));
            return;
        }
        if (valuesCount <= 0) {
            return;
        }

        --valuesCount;
        int candidateTerm = values.get(valuesCount);

        // Try with candidate term:
        candidateTerms[nTerms - 1] = candidateTerm;
        solveNSum(requiredSum - candidateTerm, nTerms - 1, values, results, candidateTerms, valuesCount);

        // Try without candidate term:
        solveNSum(requiredSum, nTerms, values, results, candidateTerms, valuesCount);
    }
}

可以进一步修剪:

  • 如果(nTerms - 1)最低加上候选词汇给出太大的总和,则跳过候选词;
  • 如果候选词加上下一个(nTerms -1)词太小,则终止。
  • 如果valuesCount&lt;终止nTerms

答案 4 :(得分:0)

public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
    Arrays.sort(num);
    ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
    int i=0;
    while(i<num.length-3){
        int j=i+1;
        while(j<num.length-2){
            int left=j+1, right=num.length-1;
            while(left<right){
                if(num[left]+num[right]==target-num[i]-num[j]){
                    ArrayList<Integer> t=new ArrayList<Integer>();
                    t.add(num[i]);
                    t.add(num[j]);
                    t.add(num[left]);
                    t.add(num[right]);
                    res.add(t);
                    left++;
                    right--;
                    while(left<right && num[left]==num[left-1])
                        left++;
                    while(left<right && num[right]==num[right+1])
                        right--;
                }else if(num[left]+num[right]>target-num[i]-num[j])
                    right--;
                else
                    left++;
            }
            j++;
            while(j<num.length-2 && num[j]==num[j-1])
                j++;
        }
        i++;
        while(i<num.length-3 && num[i]==num[i-1])
            i++;
    }
    return res;
}

答案 5 :(得分:0)

公共课FourSum {

private static int countFourSum(int[] numbers) {
    int count = 0;
    for (int j = 0; j < numbers.length; j++) {
        for (int i = j + 1; i < numbers.length; i++) {
            int front = i + 1, rear = numbers.length - 1;

            while (front < rear) {
                if (numbers[front] + numbers[rear] + numbers[i] + numbers[j] == 0) {
                    System.out.printf(String.format("{%d} : {%d} : {%d} : {%d}  \n", numbers[front], numbers[rear],
                            numbers[j], numbers[i]));
                    front++;
                    rear--;
                    count++;
                } else {
                    if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
                        front++;
                    } else {
                        rear--;
                    }
                }
            }
        }

    }
    return count;
}

public static void main(String[] args) {
    int[] numbers = { 1, 2, 3, 4, -4, -5, -6, 2, 4, -1 };
    Arrays.sort(numbers);
    System.out.println(countFourSum(numbers));
}

}