18. 四数之和(中等)
给定一个包含 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
输出:[]
思路:排序+双指针
与第十五题思路相同,通过i,j下标(j>i)遍历数组,确定前两个元素,然后通过双指针确定剩余两个元素的。
- 排序: 先将给定 nums 排序,复杂度为
O(NlogN)
。 - 双指针法思路: 固定 4个指针中前两个指针 i,j且
i<j
,然后设置双指针 l,r 分设在数组索引(j, len(nums))
两端,通过双指针交替向中间移动,记录对于每个固定指针 i、j 的所有满足nums[i]+nums[j] + nums[l] + nums[r] == target
的 l,r 组合:- 当
nums[i]+nums[i+1] + nums[i+2] + nums[i+3] >target
时直接break跳出:因为以nums[i]为开始的连续四个数的和是包含nums[i]的所有组合中最小的和,在此固定指针 i 之后不可能再找到结果了。 - 当 i> 0且
nums[i] == nums[i - 1]
时即跳过此元素nums[i]:因为已经将 nums[i - 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。 - 同理当
j>i+1 and nums[j]==nums[j-1]
时跳过此元素nums[j] - l,r分设在数组索引
(j, len(nums))
两端,当l< r时循环计算s = nums[i]+nums[j] + nums[l] + nums[r]
,并按照以下规则执行双指针移动:- 当s < target时,l += 1并跳过所有重复的nums[l];
- 当s > target时,r -= 1并跳过所有重复的nums[r];
- 当s == target时,记录组合[ i, j,l,r]至res,执行l += 1和r -= 1并跳过所有重复的nums[l]和nums[r],防止记录到重复组合。
- 当
注意:
- target和nums[i]范围为int范围,但当四个nums元素相加时会超过int型的范围,python不需要考虑溢出问题
- 当nums中存在复数,且target也为复数时,nums[i]+nums[i+1]会大于targer,但nums[i]+nums[i+1]+nums[i+2]+nums[i+3]可以等于target。如[-4,-3,-2,-1] target=-10。
(1)C++
注意:
当所给数组为[1000000000, 1000000000, 1000000000, 1000000000]时,其中三个数的和超出了int的范围,执行
nums[i]+nums[j]+nums[l]+nums[r]
时会报错。因此把判断条件改成
target-nums[i]-nums[i+1]<nums[i+2]+nums[i+3]
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
vector<int> temp(4);
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i=0; i<n-3;i++){
if((long)nums[i]+nums[i+1]+nums[i+2]+nums[i+3] > target) break;
if((long)nums[i]+nums[n-1]+nums[n-2]+nums[n-3] < target) continue;
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1;j<n-2;j++){
if(j>i+1 && nums[j]==nums[j-1]) continue;
int a = j+1, b=n-1;
while(a<b){
long s = (long) nums[i]+nums[j]+nums[a]+nums[b];
if (s==target){
temp[0]=nums[i];
temp[1]=nums[j];
temp[2]=nums[a];
temp[3]=nums[b];
res.emplace_back(temp);
a++;
b--;
}
else if(s>target){
b--;
}
else a++;
while(a<b && a>j+1 && nums[a]==nums[a-1]) a++;
while(a<b && b<n-1 && nums[b]==nums[b+1]) b--;
}
}
}
return res;
}
};
(2)python
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
n = len(nums)
nums.sort()
if(n<4):
return []
res = []
for i in range(n-3):
if(i>0 and nums[i]==nums[i-1]):
continue
if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target):
break
for j in range(i+1,n-2):
if(j>i+1 and nums[j]==nums[j-1]):
continue
l ,r = j+1 ,n-1
while(l<r):
s = nums[i]+nums[j]+nums[l]+nums[r]
if s==target:
res.append([nums[i],nums[j],nums[l],nums[r]])
l+=1
r-=1
while(l<r and nums[l]==nums[l-1]):
l+=1
while(l<r and nums[r]==nums[r+1]):
r-=1
elif s>target:
r-=1
while(l<r and nums[r]==nums[r+1]):
r-=1
else:
l+=1
while(l<r and nums[l]==nums[l-1]):
l+=1
return res