18. 四数之和
题目描述
给定一个包含 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]
]
题解:
排序+双指针。
与 三数之和 太像了,无非就是固定两个数,剩下的两个元素使用双指针处理就行。
首先对 nums
排序,这个可以方便我们后面的操作。
对于四元组(i, j, k, l)
,我们固定 i
和 j
, k
和 l
分别从 j + 1
和 n - 1
开始执行以下操作:
-
设
sum = nums[i] + nums[j] + nums[k] + nums[l]
-
若
sum > target
,说明nums[l]
值太大了,需要变小,于是--l
; -
若
sum < target
,说明nums[k]
值太小了,需要变大,于是++k
; -
若
sum == target
,此时我们找到了一组解,保存到结果中,指针同时移动++k, --l
,继续查找结果(需要判重)
判重:
- 若
i > 0 && nums[i] == nums[i - 1]
跳过 - 若
j != i + 1 && nums[j] == nums[j - 1]
跳过 - 若
k != j + 1 && nums[k] == nums[k - 1]
跳过 - 若
j != n - 1 && nums[j] == nums[j + 1]
跳过
同时,这题还有一些优化小技巧:
nums[0] + nums[1] + nums[2] + nums[3] > target
无解nums[n - 1] + nums[n - 2] + nums[n - 3] + nums[n - 4] < target
无解nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target
无解nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target
无解nums[i] + nums[j] + nums[k] + nums[k + 1] > target
无解nums[l] + nums[l - 1] + nums[l - 2] + nums[l - 3] < target
无解
后面两个好像优化效果不明显。
时间复杂度: O ( n 3 ) O(n^3) O(n3)
额外空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
if ( n < 4 ) return {};
sort( nums.begin(), nums.end() );
int t = nums[0] + nums[1] + nums[2] + nums[3];
if ( t > target ) return {};
t = nums[n - 1] + nums[n - 2] + nums[n - 3] + nums[n - 4];
if ( t < target ) return {};
vector<vector<int>> ret;
for ( int i = 0; i < n - 3; ++i ) {
if ( nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target ) break;
if ( i && nums[i] == nums[i - 1] ) continue;
for ( int j = i + 1; j < n - 2; ++j ) {
if ( nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target ) break;
for ( int k = j + 1, l = n - 1; k < l; ) {
//if ( nums[i] + nums[j] + nums[k] + nums[k + 1] > target ) break;
//if ( nums[l] + nums[l - 1] + nums[l - 2] + nums[l - 3] < target ) break;
t = nums[i] + nums[j] + nums[k] + nums[l];
if ( t > target ) --l;
else if ( t < target ) ++k;
else {
ret.push_back( {nums[i], nums[j], nums[k++], nums[l--]} );
while ( k < l && nums[k] == nums[k - 1] ) ++k;
while ( k < l && nums[l] == nums[l + 1] ) --l;
}
}
while ( j + 1 < n - 2 && nums[j + 1] == nums[j] ) ++j;
}
}
return ret;
}
};
/*
时间:12ms,击败:97.09%
内存:12.5MB,击败:99.98%
*/