2021.5.11
题目:
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
难度:中等
力扣地址:https://leetcode-cn.com/problems/3sum-closest/
解题思路:
错误的想法:有个细节点没注意
最开始我看到题目的时候就联想到昨天的三数之和,所以我是想着先把三数变两数之和,然后再用双指针去求它。考虑到题目是求最接近的三数和,那么求出【三数和】和【target】的差值的绝对值最小即可。当然我们还可以优化一下,如果【三数和】和【target】相等,那么直接返回target作为答案即可。
class Solution {
public int threeSumClosest(int[] nums, int target) {
//1、排序
Arrays.sort(nums);
int best=1000000;
//2、枚举
for(int i=0;i<nums.length;i++){
//2.1去重,增加运算速度
if(i>0 && nums[i]==nums[i-1])
continue;
int twoSumTarget=target-nums[i];
int left=i+1,right=nums.length-1;
while(left<right){
int twoSum=nums[left]+nums[right];
if(twoSum-twoSumTarget==0)
return target;
if(Math.abs(twoSum-twoSumTarget)<Math.abs(best-twoSumTarget))
best=twoSum+nums[i];
if(twoSum-twoSumTarget>0)
right--; //左移,使数字变小
else
left++;//右移,使数字变大
}
}
return best;
}
}
但是,很遗憾,没过没过!!
仔细看了下解题错误,原因就是因为这道题其实和三数之和还是有丢丢区别的,三数之和的target确定的,而这道题他不仅包含三数之和等于target,还包含接近值。而我把三数转化为两数时,就相当于把第一个数a固定了,然后再去求剩下两数和target的接近程度,然后我们再判断接近值的时候是判断两个数的和与两个数目标值的差的绝对值,那么就会出现题解错误这种情况,首先排序后数组为[0,1,1,1],最开始a=0,后面两个数b和c都是1。那么求出的最接近值就是2,然后继续循环之后,第一个数a=1,后面两个数b和c还是1,那么这时候得到的最接近值还是2,但是实际应该是1+1+1=3是最接近的值。因为我们再判断的时候忽略了第一个数的答案从而导致只要b和c的和不变,不管a怎么变化,Math.abs(sum-target)<Math.abs(best-target)这部分都不会执行。但是如果这道题不是求接近值,那么这种解法就是没问题的。
总结:对于求接近值的问题,我们在判断的时候不能忽略任何一个数的值而去缩小范围解题。
正确思想:
整体解法思路和上一道题是差不多的,唯一区别就是没有三数转化为两数去求。
- 先排序数组,然后枚举第一个元素a,剩下两个元素b和c用双指针表示,左指针表示b,右指针表示c。
- 如果三数之和等于target,那么肯定是最接近的,直接返回target即可
- 根据差值的绝对值来判断最接近的值。
- 如果三数之和比target大,想要接近他,那么就需要减小和,所以需要左指针右移使得和变小。
- 如果三数之和比target小,想要接近他,那么就需要增大和,所以需要右指针左移使得和变小。
class Solution {
public int threeSumClosest(int[] nums, int target) {
//1、排序
Arrays.sort(nums);
int best=1000000;
//2、枚举
for(int i=0;i<nums.length;i++){
//2.1去重,增加运算速度
if(i>0 && nums[i]==nums[i-1])
continue;
int left=i+1,right=nums.length-1;
while(left<right){
int sum=nums[left]+nums[right]+nums[i]; //区别点**********
if(sum-target==0)
return target;
if(Math.abs(sum-target)<Math.abs(best-target)) //区别点,判断的时候是三个数的和去判断******
best=sum;
if(sum-target>0)
right--; //左移,使数字变小
else
left++;//右移,使数字变大
}
}
return best;
}
}