题目
给你一个由 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
-109 <= nums[i] <= 109
-109 <= target <= 109
提交代码
//四数之和
//三次双指针
//先确定数1可能的位置,再确定数2可能的位置,最后确定数3和数4
//数组无序
//答案不唯一,且不能重复
//四个数之和是目标值
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<long long> nums;//整数数组
int target;//目标值
vector<vector<int>> result;//存储结果
int n;//数组长度
//三次双指针算法
void fs(){
int i = 0;//数1指针
int last1 = (int)1e9 + 1;//存储上一个数1的值,防止重复
while(i < n - 3){
if(last1 == nums[i]){
//防止重复
i++;
}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] == target){
//找到一个答案
vector<int> res;
res.push_back(nums[i]);
res.push_back(nums[n - 3]);
res.push_back(nums[n - 2]);
res.push_back(nums[n - 1]);
result.push_back(res);
//数1在位置i不可能还有答案,找下一个可能的数1
last1 = nums[i];
i++;
}else if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] == target){
//找到一个答案
vector<int> res;
res.push_back(nums[i]);
res.push_back(nums[i + 1]);
res.push_back(nums[i + 2]);
res.push_back(nums[i + 3]);
result.push_back(res);
//不可能再有下一个答案了
break;
}else if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){
//不可能再有答案了
break;
}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target){
//数1在位置i不可能有答案
//找下一个可能的数1
last1 = nums[i];
i++;
}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] > target){
//找可能的数2的位置
int j = i + 1;//数2指针
int last2 = (int)1e9 + 1;//上一个数2的值,防重复
while(j < n - 2){
if(last2 == nums[j]){
//防止重复
j++;
}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] == target){
//找到一个答案
vector<int> res;
res.push_back(nums[i]);
res.push_back(nums[j]);
res.push_back(nums[n - 2]);
res.push_back(nums[n - 1]);
result.push_back(res);
//数2在j位置不可能还有答案,找下一个可能的数2
last2 = nums[j];
j++;
}else if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] == target){
//找到一个答案
vector<int> res;
res.push_back(nums[i]);
res.push_back(nums[j]);
res.push_back(nums[j + 1]);
res.push_back(nums[j + 2]);
result.push_back(res);
//该数1的情况下不可能再有答案了
break;
}else if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target){
//该数1的情况下不可能再有答案了
break;
}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target){
//该数2不可能再有答案了
last2 = nums[j];
j++;
}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] > target){
//找可能的数3和数4
int k = j + 1,m = n - 1;//数3指针和数4指针
int last3 = (int)1e9 + 1;//上一个数3的值,防重复
while(k < m){
if(last3 == nums[k]){
//防止重复
k++;
}else if(nums[i] + nums[j] + nums[k] + nums[m] == target){
//找到一个答案
vector<int> res;
res.push_back(nums[i]);
res.push_back(nums[j]);
res.push_back(nums[k]);
res.push_back(nums[m]);
result.push_back(res);
//找下一个答案
last3 = nums[k];
k++;
m--;
}else if(nums[i] + nums[j] + nums[k] + nums[m] < target){
last3 = nums[k];
k++;
}else if(nums[i] + nums[j] + nums[k] + nums[m] > target){
m--;
}
}
//找下一个可能的数2
last2 = nums[j];
j++;
}
}
//找下一个可能的数1
last1 = nums[i];
i++;
}
}
}
int main(){
//输入数组
int t;//存储整数
while(cin.peek() != '\n'){
scanf("%d",&t);
nums.push_back(t);
}
//输入目标值
scanf("%d",&target);
//-------------------------------
n = nums.size();//数组长度
//数组排序
sort(nums.begin(),nums.end());
//三次双指针算法
fs();
//输出结果
for(int i = 0;i < n;i++){
for(int j = 0;j < 4;j++){
printf("%d ",result[i][j]);
}
printf("\n");
}
return 0;
}
总结
解题思路:该题目不难看出暴力解法,可以使用双指针优化,四个元素用三次双指针,第一次双指针判断数1可能的位置,第二次双指针判断数2可能的位置,第三次双指针判断数3和数4可能的位置,时间复杂度为O(n^3)。使用last1、last2、last3记录数1、数2、数3上次的数值,防止重复。
注意:防止整数溢出的情况,虽然每个整数都未超过int的范围,但是相加后可能会超过int的范围,注意类型转换。在C++中,整数相加后的结果类型由操作数的类型来确定。如果你将两个整数相加,它们的类型决定了结果的类型。如果两个整数的类型相同,那么它们相加的结果将是相同类型的整数。如果两个整数的类型不同,C++ 将执行整数提升(Integer Promotion),将较小类型转换为较大类型,然后进行相加。如果有符号整数和无符号整数相加,有符号整数会被转换为无符号整数。