题目描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + 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 = [], target = 0
输出:[]
提示:
0 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
解题思路
看到这题,第一眼想法就是双指针加排序,诶,不就是和求三数之和差不多的解法吗?其实主要思想还是双指针加排序,不过去重的话可以用哈希表,这里的话就说一下简单的解法。
Leetcode15 史上最详细题解之三数之和 | 刷题打卡
- 题目给的数组是无序的,那么我们首先就来排个序,让数组变成有序的,这里用的是升序;那为什么要先排序呢?因为排序之后能够避免枚举到重复的;
- 既然是求四数之和,那么我们首先就要取得四个数;
- 有了求三数之和的经验之后,第一想到的就是在求三数之和的基础上再加上一层循环,即固定前面两个数,在同一次循环中,分别对前面两个数做判断,如果当前元素和上一个元素相同,就跳过此次循环进入下一次循环;这么做的目的就是为了避免同一次循环中枚举到重复的元素会形成重复的四元组;
- 再用双指针的方法给定两个指针,最后四数相加判断是否满足条件,满足条件的话就将这四个数以数组的方式放进 数组 arr 中,再对左右指针进行一个去重的判断:,如果指针本次的值和上一次的值相同,那么就跳过本次循环,这样就避免结果出现重复数组;这些都和求三数之和是一样的。
- 到这里好像觉得没啥问题了,于是就去测试了一下,果然没问题。
AC代码
var fourSum = function (nums, target) {
let arr = []
if ( nums.length < 4) return arr
let sum = 0
let len = nums.length
nums.sort((a, b) => a - b)
for (let i = 0; i < nums.length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue
for (let j = i + 1; j < nums.length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) continue
let left = j + 1
let right = nums.length - 1
while (left < right) {
sum = nums[i] + nums[j] + nums[left] + nums[right]
if (sum == target) {
arr.push([nums[i], nums[j], nums[left], nums[right]])
while (left < right && nums[left] == nums[left + 1])
left++
left++
while (left < right && nums[right] == nums[right - 1])
right--
right--
} else if (sum > target) {
right--
} else if (sum < target) {
left++
}
}
}
}
return arr
};
不过我们还可以在上面的基础上做一些剪枝(就是通过某种判断,避免一些不必要的遍历过程)的操作:(let len = nums.length)
- 在确定第一个数的时候,如果 nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,说明此时剩下的三个数无论取什么值,四数之和一定大于 target,因此退出第一重循环;
- 在确定第一个数之后,如果nums[i] + nums[len - 1] + nums[len - 2] + nums [len - 3] < target,那么就退出当前循环,进入下一次循环;
- 同理,在确定第二个数的时候,跟上述相同;
var fourSum = function (nums, target) {
let arr = []
if ( nums.length < 4) return arr
let sum = 0
let len = nums.length
nums.sort((a, b) => a - b)
for (let i = 0; i < nums.length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue
if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) // 剪枝
break
if (nums[i] + nums[len - 1] + nums[len - 2] + nums[len - 3] < target) // 剪枝
continue
for (let j = i + 1; j < nums.length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) continue
if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) // 剪枝
break
if (nums[i] + nums[j] + nums[len - 1] + nums[len - 2] < target) // 剪枝
continue
let left = j + 1
let right = nums.length - 1
while (left < right) {
sum = nums[i] + nums[j] + nums[left] + nums[right]
if (sum == target) {
arr.push([nums[i], nums[j], nums[left], nums[right]])
while (left < right && nums[left] == nums[left + 1])
left++
left++
while (left < right && nums[right] == nums[right - 1])
right--
right--
} else if (sum > target) {
right--
} else if (sum < target) {
left++
}
}
}
}
return arr
};
相关题目
Leetcode15 史上最详细题解之三数之和 | 刷题打卡
总结
题目还是要多刷,题解也要保持写,可能刷着刷着看见某一个题目或者某一类型的题目的时候,就会联想到之前写过的相关的题目和解法等;就好比如三数之和跟四数之和,刷了三数之和之后第一联想到的就是排序加双指针,而不是用四重循环的暴力破解。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情