1

leetcode 的问题陈述说:

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

笔记:

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) 运行时算法?

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;   
    }
}
4

6 回答 6

4

这是一个 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;
}

}

于 2012-08-09T12:54:32.393 回答
2

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

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;
    }
}
于 2012-12-08T03:10:40.670 回答
1

您的解决方案显然计算正确。但是,如果一个值在输入数组中出现多次,则会给出“相同”的结果。也许那是经过测试的。

num = { 1, 3, 1, 3, 4 } 与 target = 4 的“twoSum”将是:

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

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

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

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

优化可能涉及特殊的数据结构:至少一个(反向)排序的 num。许多链接已经给出。递归可以帮助找到一个可证明和可理解的最优解,包括前置条件和后置条件。(当您拆分问题时。)

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


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

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

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 < nTerms 则终止
于 2012-06-26T22:53:55.927 回答
1
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;
}
于 2014-01-08T22:37:21.403 回答
1

公共类 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));
}

}

于 2018-04-04T12:34:25.220 回答
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;
}
于 2019-12-05T17:51:59.777 回答