卡了一天,气死了😤,最后还是没做出来
先看一眼题吧:
思路+代码:
//16. 最接近的三数之和
//思路:a、b、c分别对应三个数,首先固定a,然后b和c分别是已排序数组的两头。dis为三数之和与target的距离
//如果j往右,dis变大了,说明三个数的和应该越小才越接近,因此b不能往右移,要让c往左移
//如果k往左,dis变大了,说明三个数的和应该越大才越接近,因此c不能往右移,要让b往左移
//如果j不能往右,且k不能往左,则循环结束——找到了对于此a来说的最优b和c
//注意:最后再用本次的最优解与全局最优解进行比较!
//此算法的缺点:容易在有序数组的中间部分(负数与正数变化的那一段子数组)出现问题,因为dis使用绝对值的形式进行比较的,
// 在这一段子数组时有可能出现j往右或者k往左都使得dis变大从而终止本次对a的循环,错过了寻找最优解的情况
//补充:优化了同时减小的情况,通过了倒数第四个测试用例,但是倒数第三个测试用例又运行结果错误了。
//由于倒数第三个测试用例太长了,用debug的方式去寻找哪里出问题是在是太困难了,故放弃!
public class Solution16 {
public static int threeSumClosest(int[] nums, int target) {
if (nums.length == 3) {
return nums[0] + nums[1] + nums[2];
}
Arrays.sort(nums);
int f_a = nums[0], f_b = nums[1], f_c = nums[nums.length - 1];
int minDis = Math.abs(target - nums[0] - nums[1] - nums[nums.length - 1]);
for (int i = 0; i < nums.length - 2; i++) {
int a = nums[i],b=0,c=0,dis=0;//初始化b,c,dis
for (int j = i + 1, k = nums.length - 1; j < k; ) {
b = nums[j];
c = nums[k];
dis = Math.abs(target - a - b - c);
if (dis == 0) {
return target;
}
if (Math.abs(target - a - c - nums[j + 1]) > dis && Math.abs(target - a - b - nums[k - 1]) <= dis) {
k--;
} else if (Math.abs(target - a - b - nums[k - 1]) > dis && Math.abs(target - a - c - nums[j + 1]) <= dis) {
j++;
} else if(Math.abs(target - a - c - nums[j + 1]) <= dis && Math.abs(target - a - b - nums[k - 1]) <= dis){
// j往左或者k往右都可以的情况,选择二者中最优
if(Math.abs(target - a - c - nums[j + 1]) > Math.abs(target - a - b - nums[k - 1]))
k--;
else
j++;
} else if (Math.abs(target - a - nums[k-1] - nums[j + 1]) <= dis){
// 优化:可能j往右且k往左的同时,能使得dis变得更小
j++;k--;
} else break;
}
if (dis < minDis) {
minDis = dis;
f_a = a;
f_b = b;
f_c = c;
}
}
return f_a + f_b + f_c;
}
public static void main(String[] args) {
//test
System.out.println(threeSumClosest(new int[]{1, 1, -1, -1, 3}, -1));
System.out.println(threeSumClosest(new int[]{-1, 2, 1, -4}, 1));
System.out.println(threeSumClosest(new int[]{1, 1, 1, 0}, 100));
System.out.println(threeSumClosest(new int[]{1, -3, 3, 5, 4, 1}, 1));
System.out.println(threeSumClosest(new int[]{-55,-24,-18,-11,-7,-3,4,5,6,9,11,23,33}, 0));//倒数第四个测试用例
//预期结果应该是-58,我的运行结果是-57
System.out.println(threeSumClosest(new int[]{13,2,0,-14,-20,19,8,-5,-13,-3,20,15,20,5,13,14,-17,-7,12,-6,0,20,-19,-1,-15,-2,8,-2,-9,13,0,-3,-18,-9,-9,-19,17,-14,-19,-4,-16,2,0,9,5,-7,-4,20,18,9,0,12,-1,10,-17,-11,16,-13,-14,-3,0,2,-18,2,8,20,-15,3,-13,-12,-2,-19,11,11,-10,1,1,-10,-2,12,0,17,-19,-7,8,-19,-17,5,-5,-10,8,0,-12,4,19,2,0,12,14,-9,15,7,0,-16,-5,16,-12,0,2,-16,14,18,12,13,5,0,5,6}, -59));//倒数第三个测试用例
}
}
看了答案以后,改进思路:指针移动时不要引入绝对值直接进行比较,不要采取原来的移动指针的方法,避免部分数据永远没办法取到的情况。
//看了答案以后,改进思路:不要引入绝对值
public class Solution16_2 {
public static int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int minDis = Integer.MAX_VALUE;
int sum=0;
for(int i = 0 ; i < nums.length-2;i++){
int j = i+1;
int k = nums.length-1;
while (j<k){
//如果a+b+c>=target 则k--;如果a+b+c<target 则j++
int a = nums[i],b=nums[j],c=nums[k];
if(a+b+c==target)
return target;
//更新最小值
int dis = Math.abs(target-a-b-c);
if(dis<minDis){
minDis=dis;
sum=a+b+c;
}
if(a+b+c>=target)
k--;
else
j++;
}
}
return sum;
}
}