题目:
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; } };