版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hang404/article/details/84892993
题目描述:
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
题目解答:
方法1:排序+双指针
和三数之和有些类似,先对数组进行排序,外层遍历,内层利用双指针。内层循环中,如果大于target
则减小右指针,如果小于target
则增大右指针,并将最小差值与当前差值进行比较,判断是否要更新差值。运行时间4ms,代码如下。
int comp(const void* a, const void* b) {
return (*(int*)a > *(int*)b ? 1 : -1);
}
int threeSumClosest(int* nums, int numsSize, int target) {
int n = numsSize, i = 0;
qsort(nums, n, sizeof(int), comp);
int min = INT_MAX;
int left = 0, right = 0, temp = 0;
for(i = 0; i < n - 2; i++) {
left = i + 1;
right = n - 1;
while(left < right) {
temp = nums[i] + nums[left] + nums[right] - target;
if(temp > 0)
right--;
else if(temp == 0) {
return target;
}
else
left++;
if(abs(temp) < abs(min))
min = temp;
}
}
return target + min;
}
但是数组中可能存在重复元素,所以上述过程中可能多判断了一些,可以进行优化,减少重复情况。需要注意的是内层循环中跳过重复元素是写在判断情况里边的,为什么不将判断left和right重复的循环放在更新最小值的位置呢?因为内层可能会用到两个相同的数。跳过元素可以放在上边的原因是temp与0的关系,比如:比0大,说明应该减小right,与right等值的都不应该用到,而与left等值的可能会用到,所以此时不应该跳过与left等值的。运行时间4ms,代码如下。
int comp(const void* a, const void* b) {
return (*(int*)a > *(int*)b ? 1 : -1);
}
int threeSumClosest(int* nums, int numsSize, int target) {
int n = numsSize, i = 0;
qsort(nums, n, sizeof(int), comp);
int min = INT_MAX;
int left = 0, right = 0, temp = 0;
for(i = 0; i < n - 2; i++) {
left = i + 1;
right = n - 1;
while(left < right) {
temp = nums[i] + nums[left] + nums[right] - target;
if(temp > 0) {
while(nums[right] == nums[right - 1])
right--;
right--;
}
else if(temp == 0) {
return target;
}
else {
while(nums[left] == nums[left + 1])
left++;
left++;
}
if(abs(temp) < abs(min))
min = temp;
}
while(nums[i] == nums[i + 1])
i++;
}
return target + min;
}