力扣第16题 中等难度 卡了一整天

卡了一天,气死了😤,最后还是没做出来
先看一眼题吧:
在这里插入图片描述

思路+代码:

//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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值