本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
- v0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
输入: nums = [1,0,-1,0,-2,2], target = 0
输出: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
解题思路
思路一
我们先进行排序,这样便于我们使用双指针的时候可以少一层循环,然后再将 前两个值需要用for循环遍历,后两个值可以直接使用双指针,在固定住第一个值,再固定第二个值,在第二个值后续的数组中,进行双指针遍历,我们这里需要注意去重,其去重思路也与三数之和一样,固定的前两个值,将其与其上一个值作比,相同则说明本次迭代结果如果存在解会与上次趋同,可跳过。双指针内部,则与三数之和一样,我们还需要注意提前退出的条件,如果已排序,所以在第一层循环固定了firstVal时,如果其 >
(1/4)*target说明本次迭代后续的整个结果集必然是都 > target 的,可以结束本层for的所有循环了,还有对于secondVal也是一样的,当firstVal + secondVal >(1/2)*target说明结果集必然 > target,可以结束本层循环,回到第一层
var fourSum = function(nums, target) {
const len = nums.length,
res = [];
if (len < 4) {
return res;
}
nums = nums.sort((a, b) => a - b);
for (let firstIndex = 0; firstIndex < len; firstIndex++) {
const first = nums[firstIndex];
// 第一个数就超过了目标值的 1/4,说明其与后面的数的组合必定 > target 可以退出循环了
if (first > (target / 4)) {
break;
}
// 去除重复值
if (firstIndex > 0 && nums[firstIndex] === nums[firstIndex - 1]) {
continue;
}
// 如当前循环值,与最末尾三数之和还是 < target,说明本次循环不可能找到 = target的情况,直接跳过
if (nums[len - 1] + nums[len - 2] + nums[len - 3] + first < target) {
continue;
}
for (let secondIndex = firstIndex + 1; secondIndex < len; secondIndex++) {
const second = nums[secondIndex];
// 去重,second与前一位比较(去除first是前一位的情况),如果相同则可跳过本次
if ((secondIndex > firstIndex + 1) && (nums[secondIndex] === nums[secondIndex - 1])) {
continue;
}
// 与上面循环同理,后二者数之和必然大于 first + second,故此必然超过target
if (first + second > target / 2) {
break;
}
// 与上面循环同理
if (first + second + nums[len - 2] + nums[len - 1] < target) {
continue;
}
let left = secondIndex + 1,
right = len - 1;
while (left < right) {
const sum = first + second + nums[left] + nums[right];
if (sum === target) {
res.push([first, second, nums[left], nums[right]]);
// 去重,当下一位left与当前left值相同,说明该组合结果已被记录过了,可直接跳过
while (nums[left] === nums[left + 1]) {
left++;
}
while (nums[right] === nums[right - 1]) {
right--;
}
// 当前组合已收集,将左右指针向中间移动
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return res;
};