16. 3Sum Closest [Medium]

这篇博客探讨了如何优化三数之和问题的解决方案,以找到数组中三个元素之和最接近给定目标值的组合。文章提供了三种不同的实现方式,包括使用排序和双指针技术,以及二分查找法。优化后的算法减少了计算量,提高了效率,特别是在处理大量数据时。
摘要由CSDN通过智能技术生成
/**
 * 自己的答案
 * 和普通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;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值