原题网址:https://leetcode.com/problems/4sum/
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:
- 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.
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)
方法一:应用两数之和的方法将时间复杂度降低一维。
public class Solution {
private List<int[]> twoSum(int base, int[] nums, int target) {
int i=0, j=nums.length-1;
List<int[]> result = new ArrayList<>();
while (i < j) {
if (base + nums[i] + nums[j] < target) {
i ++;
} else if (base + nums[i] + nums[j] > target) {
j --;
} else {
int[] pair = new int[2];
pair[0] = nums[i];
pair[1] = nums[j];
result.add(pair);
i ++;
}
}
return result;
}
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
Set<String> founded = new HashSet<>();
List<List<Integer>> result = new ArrayList<>();
for(int i = 0; i < nums.length - 3; i ++) {
while (0 < i && i < nums.length - 3 && nums[i] == nums[i-1]) i++;
if (i >= nums.length - 3) break;
for(int j = i + 1; j < nums.length - 2; j ++) {
int[] twos = new int[nums.length - j - 1];
for(int k = j + 1; k < nums.length; k ++ ) twos[k-j-1] = nums[k];
List<int[]> pairs = twoSum(nums[i] + nums[j], twos, target);
for(int p = 0; p < pairs.size(); p ++) {
List<Integer> fours = new ArrayList<>(4);
fours.add(nums[i]);
fours.add(nums[j]);
fours.add(pairs.get(p)[0]);
fours.add(pairs.get(p)[1]);
String s = fours.toString();
if (founded.contains(s)) continue;
result.add(fours);
founded.add(s);
}
}
}
return result;
}
}
方法二:与方法一相似,深度优先搜索。
public class Solution {
private void find(int[] nums, int from, int[] quad, int step, int target, List<List<Integer>> results) {
if (step == 2) {
int i=from, j=nums.length-1;
while (i<j) {
if (nums[i] + nums[j] < target) {
i ++;
} else if (nums[i] + nums[j] > target) {
j --;
} else if (i>from && nums[i]==nums[i-1]) {
i ++;
} else if (j<nums.length-1 && nums[j]==nums[j+1]) {
j --;
} else {
quad[2] = nums[i];
quad[3] = nums[j];
List<Integer> result = new ArrayList<>(4);
for(int k=0; k<4; k++) result.add(quad[k]);
results.add(result);
i ++;
j --;
}
}
return;
}
for(int i=from; i<=nums.length-(4-step); i++) {
if (i>from && nums[i]==nums[i-1]) continue;
quad[step] = nums[i];
find(nums, i+1, quad, step+1, target - nums[i], results);
}
}
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> results = new ArrayList<>();
find(nums, 0, new int[4], 0, target, results);
return results;
}
}
方法三:采用哈希映射保存一半数字之和。
public class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> results = new ArrayList<>();
Arrays.sort(nums);
Map<Integer, List<Pair>> sums = new HashMap<>();
Set<String> set = new HashSet<>();
for(int i=1; i<nums.length; i++) {
for(int j=i+1; j<nums.length; j++) {
if (j>i+1 && nums[j]==nums[j-1]) continue;
int sum = nums[i]+nums[j];
List<Pair> pairs = sums.get(target-sum);
if (pairs == null) continue;
for(int p=0; p<pairs.size(); p++) {
Pair pair = pairs.get(p);
String str = nums[pair.i] + "," + nums[pair.j] + "," + nums[i] + "," + nums[j];
if (set.contains(str)) continue;
set.add(str);
List<Integer> result = new ArrayList<>(4);
result.add(nums[pair.i]);
result.add(nums[pair.j]);
result.add(nums[i]);
result.add(nums[j]);
results.add(result);
}
}
for(int k=0; k<i; k++) {
if (k>0 && nums[k]==nums[k-1]) continue;
int sum = nums[k] + nums[i];
List<Pair> pairs = sums.get(sum);
if (pairs == null) {
pairs = new ArrayList<>();
sums.put(sum, pairs);
}
pairs.add(new Pair(k, i));
}
}
return results;
}
}
class Pair {
int i, j;
Pair(int i, int j) {
this.i = i;
this.j = j;
}
}