四数之和
1.问题描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
2.求解
双指针法
- 为了去除所有重复,我们第一步需要做的是先对数组进行排序
- 两层for循环嵌套固定a,b,令b的初始值为a + 1
- 固定两个指针x,y,令x的初始值为b+1,y的初始值为
nums.length - 1 - 比较target与a,b,c,d的和sum,若sum > target,则令d指针前移,若sum < target,则令c指针后移
- 特别注意的一点是对相同元素的处理,对于a,b,c,d的值变化时,每次都需要判断是否等于上一个值,若相等,则跳过
代码如下
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
if (nums == null || nums.length < 4) {
return list;
}
int len = nums.length;
Arrays.sort(nums);
for (int i = 0; i < len - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < len - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int x = j + 1;
int y = len - 1;
while (x < y) {
int a = nums[i];
int b = nums[j];
int c = nums[x];
int d = nums[y];
int sum = a + b + c + d;
if (sum > target) {
//跳过c重复的值
while (x < y && nums[y] == nums[--y]) ;
} else if (sum < target) {
//跳过d重复的值
while (x < y && nums[x] == nums[++x]) ;
} else {
list.add(Arrays.asList(a, b, c, d));
while (x < y && nums[y] == nums[--y]) ;
while (x < y && nums[x] == nums[++x]) ;
}
}
}
}
return list;
}
- 时间复杂度O(n³),双指针枚举剩下的两个数的时间复杂度是 O(n),加上双重循环的O(n²)
- 空间复杂度,O(log n),其中 n是数组的长度。空间复杂度主要取决于排序额外使用的空间。
一开始写的时候犯了个错误,判断空直接判断长度了,应该写判断对象引用是否为null,再判断长度,因为null值是没有length属性的。
ps:写完后对比大佬们的解法,发现还可以对一些情况进行剪枝操作
- 在确定第一个数后,如果第一个数加后续三个数的值大于target,则跳过
- 在确定第一个数后,如果第一个数加倒数三个数的值小于target,则跳过
- 在确定第二个数后,如果第一、二个数加后续两个数的值大于target,则跳过
- 在确定第二个数后,如果第一、二个数加倒数两个数的值小于于target,则跳过
回溯解法
看到解答区有位大佬写了个回溯解法,学习了一下,回溯不太好理解,感觉难以理解可以一步步推下去,相比而言剪枝操作就很好理解了。
- 剩余元素少于不确定的数字个数,直接返回,当前深入终止
- 前后两个元素相等,继续下一步循环
- 顺序几个数相加大于target(已经排序完成),直接返回,当前深入无解
- 当前数加倒数数小于target,继续下一个循环
public class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> list = new ArrayList<>();
int cur = 0;
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
dfs(nums, target, 0);
return ans;
}
void dfs(int[] nums, int target, int begin) {
if (list.size() == 4) {
if (cur == target) {
ans.add(new ArrayList<>(list));
}
return;
}
for (int i = begin; i < nums.length; i++) {
if (nums.length - i < 4 - list.size()) {
return;
}
if (i > begin && nums[i] == nums[i++]) {
continue;
}
if (i < nums.length - 1 && cur + nums[i] + (3 - list.size()) * nums[i + 1] > target) {
return;
}
if (i < nums.length - 1 && cur + nums[i] + (3 - list.size()) * nums[nums.length - 1] < target) {
continue;
}
cur += nums[i];
list.add(nums[i]);
dfs(nums, target, i + 1);
list.remove(list.size() - 1);
cur -= nums[i];
}
}
}

定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?
浙公网安备 33010602011771号