这个问题开始的思路还是对的,先求出任意两个数的和,保存这个值以及对应的加数的位置信息。复杂度为O(N^2)。
然后对这些和排序,复杂度估计就到了O(logN*(N^2))。
最后遍历这个序列,找出所有可能的值,暂不讨论这个复杂度。
尽管如此,还是经常超时,经过了一些细节的优化,才勉强通过。
不过通过测试,发现LeetCode对输出的结果的顺序可以匹配,另外这个题的超时时间大概是1s(Java)。
下面是第一个小板凳:
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
if (num.length < 4) {
return results;
}
int length = num.length;
Set<Integer> sumsOfTwoNumber = new HashSet<Integer>();
HashMap<Integer, ArrayList<ArrayList<Integer>>> mapForSums = new HashMap<Integer, ArrayList<ArrayList<Integer>>>();
for (int i = 0; i < length; ++i) {
int num1 = num[i];
for (int j = i + 1; j < length; ++j) {
Integer sum = num1 + num[j];
sumsOfTwoNumber.add(sum);
ArrayList<Integer> index = new ArrayList<Integer>(2);
index.add(i);
index.add(j);
ArrayList<ArrayList<Integer>> indexList = mapForSums.get(sum);
if (indexList != null) {
indexList.add(index);
} else {
indexList = new ArrayList<ArrayList<Integer>>();
indexList.add(index);
mapForSums.put(sum, indexList);
}
}
}
ArrayList<Integer> sumList = new ArrayList<Integer>(sumsOfTwoNumber.size());
sumList.addAll(sumsOfTwoNumber);
Collections.sort(sumList);
HashSet<ArrayList<Integer>> marks = new HashSet<ArrayList<Integer>>();
int i = 0, j = sumList.size() - 1;
while (i <= j) {
int sumA = sumList.get(i);
int sumB = sumList.get(j);
int sum4 = sumA + sumB;
int binary = 2;
if (sum4 > target) {
while (true) {
if (sumA + sumList.get(j - j / binary) > target) {
j = j - j / (binary) - 1;
break;
} else {
binary *= 2;
if (binary < 0) {
--j;
break;
}
}
}
} else if (sum4 < target) {
while (true) {
if (sumList.get(i + (j - i) / binary) + sumB < target) {
i = i + (j - i) / (binary) + 1;
break;
} else {
binary *= 2;
if (binary < 0) {
++i;
break;
}
}
}
} else {
ArrayList<ArrayList<Integer>> indexes1 = mapForSums.get(sumList
.get(i));
ArrayList<ArrayList<Integer>> indexes2 = mapForSums.get(sumList
.get(j));
for (int m = 0; m < indexes1.size(); ++m) {
for (int n = 0; n < indexes2.size(); ++n) {
TreeSet<Integer> allIndex = new TreeSet<Integer>();
allIndex.addAll(indexes1.get(m));
allIndex.addAll(indexes2.get(n));
if (allIndex.size() == 4) {
ArrayList<Integer> result = new ArrayList<Integer>();
Iterator<Integer> iter = allIndex.iterator();
for (int x = 0; x < 4; ++x) {
result.add(num[iter.next()]);
}
Collections.sort(result);
marks.add(result);
}
}
}
if (sumList.get(j) * 2 > target) {
--j;
} else {
++i;
}
}
}
results.addAll(marks);
return results;
}
}
后来发现已经用了hash,不用排序也可以,省了排序的时间,把二分查找去掉,从O(logN*(N^2))降到了O(lN^2),但是由于后面的复杂度比较复杂,整体的复杂度还是比较高。不知道是否把输入中出现4次以上的数可以去掉,但是那样代码就比较长了。
这是新的小板凳,虽然时间只提高了几十ms,但是代码简洁了很多。
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
if (num.length < 4) {
return results;
}
int length = num.length;
Set<Integer> sumsOfTwoNumber = new HashSet<Integer>();
HashMap<Integer, ArrayList<ArrayList<Integer>>> mapForSums = new HashMap<Integer, ArrayList<ArrayList<Integer>>>();
for (int i = 0; i < length; ++i) {
int num1 = num[i];
for (int j = i + 1; j < length; ++j) {
Integer sum = num1 + num[j];
sumsOfTwoNumber.add(sum);
ArrayList<Integer> index = new ArrayList<Integer>(2);
index.add(i);
index.add(j);
ArrayList<ArrayList<Integer>> indexList = mapForSums.get(sum);
if (indexList != null) {
indexList.add(index);
} else {
indexList = new ArrayList<ArrayList<Integer>>();
indexList.add(index);
mapForSums.put(sum, indexList);
}
}
}
ArrayList<Integer> sumList = new ArrayList<Integer>();
sumList.addAll(sumsOfTwoNumber);
HashSet<ArrayList<Integer>> marks = new HashSet<ArrayList<Integer>>();
for (int i = 0; i < sumList.size(); ++i) {
int sumA = sumList.get(i);
int sumB = target - sumA;
ArrayList<ArrayList<Integer>> indexB = mapForSums.get(sumB);
if (indexB != null) {
ArrayList<ArrayList<Integer>> indexA = mapForSums.get(sumA);
for (int m = 0; m < indexA.size(); ++m) {
for (int n = 0; n < indexB.size(); ++n) {
TreeSet<Integer> allIndex = new TreeSet<Integer>();
allIndex.addAll(indexA.get(m));
allIndex.addAll(indexB.get(n));
if (allIndex.size() == 4) {
ArrayList<Integer> result = new ArrayList<Integer>();
Iterator<Integer> iter = allIndex.iterator();
for (int x = 0; x < 4; ++x) {
result.add(num[iter.next()]);
}
Collections.sort(result);
marks.add(result);
}
}
}
}
}
results.addAll(marks);
return results;
}
}
看了别人的小板凳后,重写了一下,运行时间又缩减了100ms左右。目前是(716ms)
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
if (num.length < 4) {
return results;
}
int length = num.length;
HashMap<Integer, ArrayList<ArrayList<Integer>>> mapForSums = new HashMap<Integer, ArrayList<ArrayList<Integer>>>();
HashSet<ArrayList<Integer>> marks = new HashSet<ArrayList<Integer>>();
for (int i = 0; i < length; ++i) {
int num1 = num[i];
for (int j = i + 1; j < length; ++j) {
int num2 = num[j];
int sumA = num1 + num2;
int sumB = target - sumA;
ArrayList<ArrayList<Integer>> indexB = mapForSums.get(sumB);
if (indexB != null) {
for (int n = 0; n < indexB.size(); ++n) {
int k = indexB.get(n).get(0);
int l = indexB.get(n).get(1);
if (k != i && k != j && l != i && l != j) {
ArrayList<Integer> result = new ArrayList<Integer>();
result.add(num1);
result.add(num2);
result.add(num[k]);
result.add(num[l]);
Collections.sort(result);
marks.add(result);
}
}
}
ArrayList<Integer> index = new ArrayList<Integer>();
index.add(i);
index.add(j);
ArrayList<ArrayList<Integer>> indexList = mapForSums.get(sumA);
if (indexList != null) {
indexList.add(index);
} else {
indexList = new ArrayList<ArrayList<Integer>>();
indexList.add(index);
mapForSums.put(sumA, indexList);
}
}
}
results.addAll(marks);
return results;
}
}