题目出处
给定一个数组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; }