【问题标题】:4sum implementation in Java from leetcode来自leetcode的Java中的4sum实现
【发布时间】:2012-06-26 22:01:44
【问题描述】:

来自 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;   
    }
}

【问题讨论】:

  • 你可能应该详细说明一下原来的问题是什么,有些人可能认为四和是指别的东西......
  • 您需要提供问题的链接。
  • 致 Gene 和 Dann.dev,非常感谢。我编辑了帖子,链接是leetcode.com/onlinejudge。问题是4sum。非常感谢。

标签: java algorithm


【解决方案1】:

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

}

【讨论】:

    【解决方案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;
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      目标 = 4 的 num = { 1, 3, 1, 3, 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

      【讨论】:

      • 给定一个包含 n 个整数的数组 S,S 中是否存在元素 a、b、c 和 d 使得 a + b + c + d = target?在给出目标总和的数组中找到所有唯一的四元组。注意:要求说“解决方案集不得包含重复的四联体”。
      • 努力将我对数据结构的“blabla”转换为可执行代码。
      【解决方案4】:
      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;
      }
      

      【讨论】:

      • 欢迎来到 SO!仅代码的答案通常在这里不受欢迎。请添加一些关于为什么这个答案是正确的描述以及你为使它工作所做的改变。
      【解决方案5】:

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

      }

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-04-04
          • 1970-01-01
          • 2012-07-22
          • 2020-12-08
          • 2016-11-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多