16.题目描述:
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
测试示例:
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
提示:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
解法一:暴力三重循环(会超时)
从头开始遍历数组,确定第一个数 nums[i] ,从 i 的下一位开始遍历 j ,从 j 的下一位开始遍历 k,对于每个 nums[i]+nums[j]+nums[k] 的组合,与 target 比较,计算出他们在数轴上的距离,并与最小值 minn 比较,如果更小就更新 minn 并记录当前组合的和。
三层循环,每层循环遍历N,时间复杂度为O()。
代码:
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int ans;//记录三个数的和
int length = nums.size();
int x,y,z;
int t=0;
int minn=500000;
for(int i=0;i<=length-3;i++)
{
x=nums[i];
for(int j=i+1;j<=length-2;j++)
{
y=nums[j];
for(int k=j+1;k<=length-1;k++)
{
z=nums[k];
if(x+y+z>target) //计算数轴上的距离
t=x+y+z-target;
else
t=target-x-y-z;
if(t<minn) //更新最优解
{
minn=t;
ans = x+y+z;
}
}
}
}
return ans;
}
};
解法二:排序+双指针
对于一个有序的数组,当我们确定第 i 位(0<=i<length-2)时,在(i,length-1]的范围内讨论剩余的两个数的位置。
初始时令左指针 left = i+1 ,右指针 right = n-1 。
对于当前的和 now = nums[i]+nums[left]+nums[right] ,比较它到 target 的差的绝对值与已记录的最优解 ans 到 target 的差的绝对值的大小,并根据结果决定是否更新最优解。
当 left < right 时,不断移动左右指针,当前和 now 与 target 的关系有以下三种情况:
- now == target
此时不会有跟接近 target 的解了,返回答案 target 。 - now < target
此时再减小 now 的大小一定不会得到更优解,所以右指针不动,左指针右移。 - now > target
此时再增大 now 的大小一定不会得到更优解,所以左指针不动,右指针左移。
Other tips:
每当我们增加 i 时,可以判断 nums[i] 是否与上一次的值相同,如果相同,可以直接再增加 i 直到不同,因为相同的 i 值最优解相同。
同理,在移动左右指针时,我们也可以跳过相同的值。
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int length = nums.size();
int ans = 999999999; //记录最优解
int now = 0; //当前的和
for(int i=0;i<length-2;i++)
{
if( i>0 && nums[i-1]==nums[i]) continue; //跳过相同的i值
int left = i+1;
int right = length-1;
while(left < right)
{
now = nums[i]+nums[left]+nums[right];
if(now == target) return target;
if(abs(target-now)<abs(target-ans)) //判断当前的解是否更优
{
ans = now;
}
if(now > target)
{
int right1 = right-1;
while( left < right1 && nums[right] == nums[right1-1]) right1--;
right = right1;
}
else
{
int left1 = left+1;
while( left1 < right && nums[left] == nums[left1+1]) left1++;
left = left1;
}
}
}
return ans;
}
};
一些坑:
if( i>0 && nums[i-1]==nums[i]) continue; //跳过相同的i值
这里不能使用while循环,否则会陷入死循环。
18.题目描述:
给你一个由 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
解法:排序+双指针
与前题不同的是,此题要确定四个数的和,那么我们要先循环遍历确定两个数的值,然后用双指针的方法来得到另外两个数相加之后的值。
代码:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
int a,b,c,d;
int length = nums.size();
sort(nums.begin(), nums.end());
for(int i=0;i<=length-4;i++)
{
a=nums[i];
if(a>ceil(target/4)) break;
for(int j=i+1;j<=length-3;j++)
{
b=nums[j];
int left = j+1;
int right = length-1;
while(left<right)
{
c=nums[left];
d=nums[right];
long long sum = 0ll+a+b+c+d;
if(sum == target)
{
ans.push_back({a,b,c,d});
int right1=right-1;
while(right1 > left && nums[right1]==nums[right]) right1--;
right = right1;
int left1=left+1;
while(left1 < right && nums[left1]==nums[left]) left1++;
left=left1;
}
else
{
if((long) a+b+c+d > target)
{
int right1=right-1;
while(right1 > left && nums[right1]==nums[right]) right1--;
right = right1;
}
if((long) a+b+c+d < target)
{
int left1=left+1;
while(left1 < right && nums[left1]==nums[left]) left1++;
left=left1;
}
}
}
while(j<=length-3 && nums[j+1]==nums[j]) j++;
}
while(i<=length-4 && nums[i+1]==nums[i]) i++;
}
return ans;
}
};