1

leetcodeの問題ステートメントは次のように述べています。

n個の整数の配列Sが与えられた場合、a + b + c + d =ターゲットとなるような要素a、b、c、およびdがSにありますか?ターゲットの合計を与える配列内のすべての一意の4つ組を検索します。

ノート:

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.

この4つの合計の問題について、2つの質問があります。

  1. どこが間違っていたのですか?コンパイルされたコードはすべてのテストに合格することはできませんが、問題を解決するためにブルートフォースのみを使用しているため、コードは正しいはずだと思いました。
  2. このO(n ^ 4)ランタイムアルゴリズムの代わりに、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

あなたのソリューションは明らかに正しく計算されています。ただし、値が入力配列に複数回出現する場合、「同じ」結果が得られます。多分それはテストされました。

target = 4 の num = { 1, 3, 1, 3, 4 } の "twoSum" は次のようになります。

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

これがテストで失敗した理由である場合、解決策はおそらく一連の順序付きリストになるでしょう。

サブセットも指定する必要がある場合は、{ 4 } がありません。

問題は、なぜテストが失敗したのか、要件は何かということです。

最適化には、おそらく特別なデータ構造が含まれます。少なくとも (逆に) ソートされた num です。多くのリンクがすでに与えられています。再帰は、事前条件と事後条件を使用して、証明可能で理解可能な最適解を見つけるのに役立ちます。(問題を分割しているため。)

1 つの解決策は、ペアの合計を、その合計につながるすべてのインデックスのペアにマッピングしたままペアを作成することです。次に、分離インデックスを持つ 2 つのペアを見つけます。


誰も満足のいく答えを出さなかったので:

単純な再帰的ソリューション (あまり良くない、または最適ではない)。ただし、データ構造が関連していることを示しています。

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) の最小値と候補用語の合計が大きすぎる場合は、候補用語をスキップします。
  • 候補用語と次の (nTerm -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 に答える