题目出处
给定一个数组arr和一个目标值target,查找三个数a, b c, 使他们的各a+b+c与target的差距最小, 返回 a+b+c。
假设数组中满足要求的就只有一组数据。
如: 给定的数组为:[-1, 1, 2, -4], target = 1,
由于离target 1最小的三个数为[-1, 2, 1]其差距为1. 即(-1 + 1 + 2) - 1 = 1;
分析
很类似于3Sum的算法。 3sum算法地址:
http://blog.csdn.net/net_wolf_007/article/details/51788475
不同的点是3sum求的是各为0的3个数,这就使得3个数中要么都为0,要么必须有正有负。
而这道题目是要3个数的和与目标值最小,就没有正负之分。所以就不需要对此进行判断。
同时这道题目中要求只返回一个值,所以当判断为0时,就是要求的值,就可以停止查找。
算法(与3 sum比较改变的地方)
先对数组进行排序
min_gap = arr[0] + arr[1] + arr[2] - target;
定义三个指针: i, j, k
i: 从排序后的数组开始移动。
k: 从数组的最后往前移动。
j: 在[i, k]之间移动
如果i, j, k对应的数之和为sum;
计算gap = sum - target;
如果gap== 0, 得到一个解,直接返回 target。
如果gap 小于 min_gap , 更新min_gap
如果gap > 0: 移动k
如果gap < 0: 移动j
如果 j > k: 则移动i, 并重新定义 k, j.
同时对算法作两点优化来减少比较的次数
k :没必要每次都从最后元素开始移动,所以需要记录k的最后位置。
(去掉: 由于没有正负之分)i: 没必要移动最后, 要以i > 0 为终点。
代码
int threeSumClosest(vector<int>& nums, int target) {
if(nums.size() < 3) return 0;
sort(nums.begin(), nums.end());
// 初始化最小的差值
int min_gap = nums[0] + nums[1] + nums[2] - target;
int last = nums.size()-1;
for(int i = 0; i < nums.size()-2; i++){
if(i > 0 && nums[i] == nums[i-1]) continue;
int j = i + 1;
int k = last;
while(j < k){
if(j > i+1 && nums[j] == nums[j-1]) {j += 1;continue;}
int gap = nums[i] + nums[j] + nums[k]-target;
if(gap == 0) return target;//0为最小值。
if(abs(gap) < abs(min_gap)){
min_gap = gap;
}
if(gap < 0) j += 1;
else{
k -= 1;
if(j == i+1) last = k;
}
}
}
return target + min_gap;
}