/**
* 自己的答案
* 和普通3sum一样:先排序数组,然后从头开始遍历,固定一个nums[i],在其后通过两头向中间逼近的方式找两个和与target-nums[i]最接近的数
* 和普通3sum不同:需要不断更新3sum与target的最小差距,并需要记录正负
*/
class Solution {
public int threeSumClosest(int[] nums, int target) {
int closest = Integer.MAX_VALUE, flag = 0, length = nums.length; // closest为最小差距,flag标识当前最小差距是比target大还是小
if (length < 3)
return 0;
Arrays.sort(nums);
for (int i = 0; i < length - 2; i++) {
int res = target - nums[i];
int start = i + 1, end = length - 1;
while (start < end) {
int sum = nums[start] + nums[end];
if (sum == res)
return target;
if (sum > res) { // 3sum大于target
int dis = sum + nums[i] - target;
if (dis < closest) {
closest = dis;
flag = 1;
}
end--;
while (start < end && nums[end] == nums[end + 1])
end--;
} else { // 3sum小于target
int dis = target - sum - nums[i];
if (dis < closest) {
closest = dis;
flag = -1;
}
start++;
while (start < end && nums[start] == nums[start - 1])
start++;
}
}
}
return flag > 0 ? target + closest : target - closest;
}
}
/**
* 根据标准答案改良
* 1. 不用单独保存closest的符号,保存带正负的closest,比大小的时候用Math.abs()取绝对值,这样不管在while中或者return的时候,都简洁很多
* 2. 和3sum一样不用单独计算res,三个数加起来和target比大小就行
*/
class Solution {
public int threeSumClosest(int[] nums, int target) {
int closest = Integer.MAX_VALUE, length = nums.length;
if (length < 3)
return 0;
Arrays.sort(nums);
for (int i = 0; i < length - 2; i++) {
int start = i + 1, end = length - 1;
while (start < end) {
int sum = nums[i] + nums[start] + nums[end];
if (sum == target)
return target;
if (Math.abs(target - sum) < Math.abs(closest))
closest = target - sum;
if (sum > target) {
end--;
while (start < end && nums[end] == nums[end + 1])
end--;
} else {
start++;
while (start < end && nums[start] == nums[start - 1])
start++;
}
}
}
return target - closest;
}
}
/**
* 另一种答案,没有固定一个双指针查找剩余两个的方法效率高,权当看看
* 逐个固定第一、二、三位数字,最外围循环固定第一位,内围循环固定第二位,target - nums[i] - nums[j]算出第三位需要最接近多少
* 然后在数组中二分查找用Arrays.binarySearch()找到数组中一个位置,该位置或该位置的前一位就是使已固定的两数加上它最接近target的
*/
class Solution {
public int threeSumClosest(int[] nums, int target) {
int diff = Integer.MAX_VALUE, sz = nums.length;
Arrays.sort(nums);
for (int i = 0; i < sz - 2 && diff != 0; ++i) {
for (int j = i + 1; j < sz - 1; ++j) {
int complement = target - nums[i] - nums[j];
int idx = Arrays.binarySearch(nums, j + 1, sz - 1, complement);
int hi = idx >= 0 ? idx : -idx - 1, lo = hi - 1; // 这句在答案中用了位运算非:int hi = idx >= 0 ? idx : ~idx, lo = hi - 1;
if (hi < sz && Math.abs(complement - nums[hi]) < Math.abs(diff))
diff = complement - nums[hi];
if (lo > j && Math.abs(complement - nums[lo]) < Math.abs(diff))
diff = complement - nums[lo];
}
}
return target - diff;
}
}