Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情。
一、题目描述:
- 四数之和-难度中等
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] 示例 2:
输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-10^9 <= nums[i] <= 10^9
-10^9 <= target <= 10^9
二、题目和思路分析:
返回等于目标值的数组,其实和三数之和差不多,这么想起来,循环加双指针即可。
但实际写起来才发现,和三数还是有区别的,三数的时候,以for循环当前下标为i的数字为第一个数,然后下标为i之后的数组,最前一个值下标为left和最后一个值下标为right,加起来一共三个值,结果大于目标值就减最后一个值的下标,结果小于目标值就加最前一个值的下标。保证了每个组合都会被考虑进去。
那么四数之和的第四个数从哪来呢?
我原本想着第四个数是第一个数的下一个数,即下标为i+1的数字,但实际写的时候发现第二个数字并不应该永远挨着第一个数字,比如[1,2,3,4,5,6,7],如果想要的结果是[1,3,5,7],那么按下标i,i+1,left,right去取,永远不会有正确答案,所以四个数字可能存在的位置应该没有任何的关系。
那么该怎么考虑呢?
我们可以把第四个数字下标以j表示,j取值范围为 left 和 right 之间,即left < j < right。
逻辑想好之后,剩下的就是写了好几遍才通过的代码了。
三、代码:
代码实现如下:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var fourSum = function(nums, target) {
const length = nums.length
if(nums == null || length < 4){
return []
}
let arr = [] // 声明空数组
nums.sort((a, b) => a - b) // 数组从小到大进行排序
// console.log(nums)
for (let i = 0; i < length ; i++) { // 确定第一个数字,初始值为nums[0]
if(nums[i] > target && !(nums[i]<0 && target<0)){ // 如果最小数字大于target,那么和也一定大于target, 负数应该除外
break
}
if(i > 0 && nums[i] == nums[i-1]){ // 当前数字与上个数字相同,说明已经循环过了,跳出本次循环
continue
}
let left = i+1 // 确定第二个数字,初始值为nums[1]
let right = length-1 // 确定第四个数字,初始值为最后一个数字
while(left < right-1){
let j = left+1 // 确定第三个数字,初始值为第二个数字和第四个数字中间的第一个数字
while(j<right){
// console.log(i, left, j, right)
const sum = nums[i] + nums[left] + nums[j] + nums[right]
// console.log(sum, target)
if(sum == target){
// console.log(i, left, j, right)
arr.push([nums[i], nums[left], nums[j], nums[right]])
j++
while(j<right && nums[j] == nums[j-1]){
// console.log('跳过',j)
j++
}
}else if (sum > target){
right--
// }else if (sum < target){
// j++
}else{
j++
}
}
while (left<right && nums[left] == nums[left+1]){
left++
}
left++
right = length-1
}
}
return arr
};
复制代码
四、总结:
本来我想,解题逻辑已经没问题了,代码写起来应该很快。但是写代码时总是考虑不周到,就像逻辑只是一个大纲,一条路,而写代码这个实现的过程并不比想逻辑要简单,也是充满了坎坷陷阱。
加油吧!