解析:(双指针,不使用哈希表)
题目要求:一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
大体思路:这道题比上一题三数之和,多了一个数,是四数之和。其实就是在三数之和上再套一层for循环。对数组进行遍历,每次遍历下计算三数之和。同样的道理五数之和就是再加一层for循环。
具体做法,先对数组进行升序排序
第一层for循环,nums[i]是每次for循环的元素,在第一个数确认是num[i]下,找剩下三个数的所有可能的答案。(这里注意如果nums[i]>0并且nums[i]>target,就直接返回res,因为num[i]>0说明是num[i]往后正数,num[i]>target说明四个数而且是正数加起来一定大于target)
第二层for循环,j从num[i]后面一位也就是i+1开始迭代,在第二个数确认是num[j]下,找剩下两个数所有的可能的答案。
上面两个数,都要注意去重。就是去除已经变遍历过的而且值一样的。注意必须是确定这个值已经遍历过了,而且值相同,所以是nums[i]和nums[i-1]比较,nums[j] 和 nums[j-1]比较。nums[i-1]和nums[j-1]肯定是已经遍历过的。
剩下两个数所有的可能的答案,就是使用双指针去找。和三数之和那题一样。
定义left指向j的后一位,定义right指向最后一位
对遍历到的四个数求和:sum = nums[i] + nums[j] + nums[left] + nums[right];
如果sum < target; 说明偏小了,需要调大一点,left后移一位,left++
如果sum > target; 说明偏大了,需要调小一点,right前移一位,right–
如果sum == target;说明找到了一个结果,添加入res。但是要注意去重,因为答案不可以重复,所以这里需要去重,判断left+1位置的值和left位置的值是否相等,相等则left后移一位,并且继续判断(所以使用while),同样对right判断right-1位置的值和right位置的值是否相等,相等则right前移一位并且继续判断。
去重的核心思路:已经将该四元组答案添加进res中后,一直按照上述移动到不重复为止。
同样在去重过程中,需要关注左右指针不可以越界,使用left<right作为每次去重前提条件即可。
注意点:有一个边界条件和三数之和不同
三数之和是和0比较的,三数之和只要第一个数大于0,就可以直接结束程序。因为后面越加越大,肯定>0.
和三数之和不同,四数之和和target比较,仅仅满足num[i] > target还不足以结束程序,因为如果num[i]是负数,num[i]后面的也是负数,负数越加越小。所以还要满足num[i] > 0 ,即num[i]以及num[i]往后的是正数才行
代码:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0 && nums[i] > target) return res;
if (i >= 1 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < nums.length; j++) {
if (j >= i + 2 && nums[j] == nums[j - 1]) continue;
int left = j + 1, right = nums.length - 1;
while (left < right) {
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum < target) left++;
else if (sum > target) right--;
else {
res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
}
return res;
}
}