题目:
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
题目大意:
给定一个数组S和一个目标值target,在S中寻找三个数,使它们的和最接近target。返回三个数的和。
思路:
一、暴力求解法:
大体思路是用一个变量记录最小差值和要返回的值,然后枚举出所有可能的组合,判断最小差值是不是小于记录值,如果小于最小值则更新记录的最小值和最终结果。暴力求解法的时间复杂度是O(n^3),但是可以通过一定的判断使得程序的循环次数降低,从而通过测试。判断如下:
(1)、如果刚好获得了target,则直接返回。
(2)、三重循环中如果碰到与上一次判断的数字相同则直接跳过(因为已经在上一次判断过)
二、3Sums解法的改进版:
大体思路是先排序,用一个变量dis保存距离,用tmp保存候选数,然后每次选择一个数字,先让这个数字和最大的两个数字相加,如果小于目标值说明和目标值相等的概率为零(即:差值为0的概率为0),所以保存三个数字的和进tmp,保存差值进dis;如果大于目标值,说明还有可能使得结果和目标值相等(即:差值为0的概率不为0),所以问题就变成了3Sums中固定一个值然后求另外两个值的问题了(也看一看作是2Sums问题)只不过在寻找能否和目标值相等的路上保存下来不相等时的最小差值进dis。在这个撒u你法中也有一些边界调节可以直接得到答案:
(1)、等于target,直接返回target
(2)、如果最大的三个数加起来还小于目标值,则最接近的就是这三个数相加
(3)、如果最小的三个数加起来还大于目标值,则最接近的就是这三个数相加
(4)、如果当前处理过的数字上一次处理的数字相同,则不再处理
代码:
暴力法:
class Solution {
public:
int threeSumClosest(std::vector<int>& nums, int target) {
int result = target, dis = INT_MAX, dis_tmp, tmp;
if (nums.size() == 0) {
result = 0;
}
for (int i = 0; i < nums.size(); ++i) {
if (i != 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.size(); ++j) {
if (j != i + 1 && nums[j] == nums[j - 1]) {
continue;
}
for (int k = j + 1; k < nums.size(); ++k) {
if (k != j + 1 && nums[k] == nums[k - 1]) {
continue;
}
tmp = nums[i] + nums[j] + nums[k];
dis_tmp = abs(tmp - target);
if (dis_tmp < dis) {
dis = dis_tmp;
result = tmp;
if (result == target) {
return target;
}
}
}
}
}
return result;
}
};
3Sums改进法:
class Solution {
public:
int threeSumClosest(std::vector<int>& nums, int target) {
const int n = nums.size();
sort(nums.begin(),nums.end());
//如果最大的三个数加起来还小于目标值,则最接近的就是这三个数相加
if(nums[n-1] + nums[n-2] + nums[n-3] <= target) {
return nums[n-1] + nums[n-2] + nums[n-3];
}
//如果最小的三个数加起来还大于目标值,则最接近的就是这三个数相加
if(nums[0] + nums[1] + nums[2] >= target) {
return nums[0] + nums[1] + nums[2];
}
int tmp; //候选数
int dis = INT_MAX; //距离
//因为要选择三个数,所以i < n - 2
for (int i = 0; i < n-2; i++) {
//如果当前处理过的数字上一次处理过,则不再处理
if (i != 0 && nums[i] == nums[i-1]) {
continue;
}
//如果当前扫描的值加上最大的三个数字之后如果还小于目标值
//说明和目标值相等的概率为零(即:差值为0的概率为0)
if (nums[i] + nums[n-1] + nums[n-2] <= target) {
tmp = nums[i] + nums[n-1] + nums[n-2];
if (tmp == target) {
return target;
}
dis = tmp - target; //差值最小等于候选值减去目标值
continue;
}
//如果和最大的三个数字相加之后大于目标值,说明还有希望找到差值为0的值
//接下来就是求2Sums问题了(不同的一点是加了一个差值最小的判断)
int target2 = target - nums[i];
int j = i + 1;
int k = n - 1;
while (j < k) {
const int sum2 = nums[j] + nums[k];
if (abs(sum2 - target2) < abs(dis)){
dis = sum2 - target2;
}
if(sum2 > target2) {
k--;
}
else if(sum2 < target2) {
j++;
}
else {
return target;
}
while (nums[j] == nums[j-1]) {
j++;
}
while(nums[k] == nums[k+1]) {
k--;
}
}
}
return target + dis;
}
};