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. 思路分析:
① 根据题目可以知道我们可以取出数组的任意四个字母,将这四个字母加起来的和判断一下是否与目标值target相等,对于数组中的数字,我们可以取也可以不取,所以我们可以使用递归来求解,存在着两个平行状态,但是后面发现当求出结果之后去重很麻烦,很难判断当前满足条件的四个数字与之前的集合是否存在重复,所以没有采取这种方法
② 在领扣的题解中,发现有一个不错的思路,使用的是四个指针a, b, c, d的做法,分别指向四个元素,首先是要对数组中的元素进行排序,排序的目的是为了之后判断取出的元素是否存在重复,在开始的时候令a指向数组的第一个元素,b = a + 1为a下一个元素,c = b + 1为b下一个元素,d指向的是最末尾的元素,判断nums[a]+nums[b]+nums[c]+nums[d]==target是否成立,假如
nums[a]+nums[b]+nums[c]+nums[d] > target那么d指针往前移动,nums[a]+nums[b]+nums[c]+nums[d]< target了那么c指针往后移动
③ 因为求解的是不重复的组合,所以在循环的时候就应该判断一下是否存在之前使用过这个元素了,因为是从数组中选取四个元素,所以排序之后假如存在着重复那么相邻的元素应该是一样的,所以需要从下一个与之前不重复的元素开始,a,b位置判断去掉重复的如下面划线部分:
④ 对于取出的c,d位置的元素也是在判断四个数字是否满足条件的时候也是如此,需要使用循环来去重,并且判断出满足条件四个数字之和等于target的时候需要c++,d--同时操作这样以a,b为当前位置的四种情况才会被判断完,而且下一次d又是从nums.length - 1的位置开始的,并且c往下移动一位了所以会尝试所有的四种不重复的组合(同时移动是为了在一个范围内进行缩小组合因为下一次是从c的下一个位置,d为数组的最后一个位置开始进行组合和判断)
3. 代码如下:
public class Solution {
/*采用四个指针来进行操作*/
static List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
/*使用四个指针也是选择数组中的数字, 并且需要先进行排序*/
Arrays.sort(nums);
for (int a = 0; a <= nums.length - 4; ++a){
if (a > 0 && nums[a] == nums[a - 1]) continue;//确保选择的数字与之前是不一样的
for (int b = a + 1; b <= nums.length - 3; ++b){
if (b > a + 1 && nums[b] == nums[b - 1]) continue;
/*并且令c = b + 1, d = nums.length - 1这样来进行四个数字不重复性的选择*/
int c = b + 1, d = nums.length - 1;
while (c < d){
if (nums[a] + nums[b] + nums[c] + nums[d] < target){
++c;
}
else if (nums[a] + nums[b] + nums[c] + nums[d] > target) {
--d;
}
else {
List<Integer> list = new ArrayList<>();
list.add(nums[a]); list.add(nums[b]); list.add(nums[c]); list.add(nums[d]);
res.add(list);
while (c < d && nums[c + 1] == nums[c]) ++c;
while (c < d && nums[d - 1] == nums[d]) --d;
++c;
--d;
}
}
}
}
return res;
}
}