题目:Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note: The solution set must not contain duplicate quadruplets. For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0. A solution set is:
[[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]]
本体是寻找和为target的四个数。联想TwoSum那道题很简单,所以我们就想要将本题转化为TwoSum进行求解。其实思路也比较简单,就是用ij两个游标分别从0和nums.length-1开始从两侧遍历,这样nums[i:j]就是一个要进行TwoSum的数组,在对其进行TwoSum求解即可。不过不是在找到一个解就返回,而是要找出所有解。除此之外还可以使用两个小tricks来加速求解过程:
- 如果nums[i]4>target,则直接返回结果。如果nums[j] 4 < target,则直接break内循环
- 对于连续相同的数字我们可以直接跳过不再进行判断。
实践表明,这两种方法很好地节省了运算时间,提高了执行效率、代码入下:
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i=0, L=nums.length; i<L-3; i++) {
if(nums[i]<<2 > target) return list; // return immediately
for(int j=L-1; j>i+2; j--) {
if(nums[j]<<2 < target) break; // break immediately
int rem = target-nums[i]-nums[j];
int lo = i+1, hi=j-1;
while(lo<hi) {
int sum = nums[lo] + nums[hi];
if(sum>rem) --hi;
else if(sum<rem) ++lo;
else {
list.add(Arrays.asList(nums[i],nums[lo],nums[hi],nums[j]));
while(++lo<=hi && nums[lo-1]==nums[lo]) continue; // avoid duplicate results
while(--hi>=lo && nums[hi]==nums[hi+1]) continue; // avoid duplicate results
}
}
while(j>=1 && nums[j]==nums[j-1]) --j; // skip inner loop
}
while(i<L-1 && nums[i]==nums[i+1]) ++i; // skip outer loop
}
return list;
}
此外,有人提出了kSUM的解决思路,虽然执行效率并不是最优的,但是通过这种递归的方法我们就可以轻易地解决所有kSum问题。其思路为,将kSum转化为k-1Sum,然后在解决2Sum。代码入下:
public class Solution {
int len = 0;
public List<List<Integer>> fourSum(int[] nums, int target) {
len = nums.length;
Arrays.sort(nums);
return kSum(nums, target, 4, 0);
}
private ArrayList<List<Integer>> kSum(int[] nums, int target, int k, int index) {
ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
if(index >= len) {
return res;
}
if(k == 2) {
int i = index, j = len - 1;
while(i < j) {
//find a pair
if(target - nums[i] == nums[j]) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(target-nums[i]);
res.add(temp);
//skip duplication
while(i<j && nums[i]==nums[i+1]) i++;
while(i<j && nums[j-1]==nums[j]) j--;
i++;
j--;
//move left bound
} else if (target - nums[i] > nums[j]) {
i++;
//move right bound
} else {
j--;
}
}
} else{
for (int i = index; i < len - k + 1; i++) {
//use current number to reduce ksum into k-1sum
ArrayList<List<Integer>> temp = kSum(nums, target - nums[i], k-1, i+1);
if(temp != null){
//add previous results
for (List<Integer> t : temp) {
t.add(0, nums[i]);
}
res.addAll(temp);
}
while (i < len-1 && nums[i] == nums[i+1]) {
//skip duplicated numbers
i++;
}
}
}
return res;
}
}