[LeetCode]16. 最接近的三数之和

题目

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

解题思路

我们可以考虑先对整个数组进行升序排序,首先考虑枚举第一个元素 a,对于剩下的两个元素 b 和 c,希望它们的和最接近 target−a。假设数组的长度为 n,我们先枚举 a,它在数组中的位置为 i。为了防止重复枚举,我们在位置 [i+1,n) 的范围内枚举 b 和 c。我们用 p_b 和 p_c 分别表示指向 b 和 c 的指针,初始时 p_b 指向位置 i+1,即左边界;p_c 指向位置 n-1,即右边界。在每一步枚举的过程中,我们用 a+b+c 来更新答案:
1)如果 a+b+c = target,直接返回 target 作为答案,因为不会有再比这个更接近 target 的值了;
2)如果 a+b+c > target,那么就将 p_c 向左移动一个位置;
3)如果 a+b+c < target,那么就将 p_b 向右移动一个位置。
本题也有一些可以减少运行时间(但不会减少时间复杂度)的小优化,当我们枚举 a, b, c 中任意元素时,如果出现和上一个相同的,可以直接将其移动到下一个与这次枚举到的不相同的元素,减少枚举的次数。

复杂度分析:
时间复杂度:O(N^2)。我们首先需要 O(NlogN) 的时间对数组进行排序,随后在枚举的过程中,使用一重循环 O(N) 枚举 a,双指针 O(N) 枚举 b 和 c,故一共是 O(N^2)。
空间复杂度:O(logN)。排序需要使用 O(logN) 的空间。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,此时空间复杂度为 O(N)。

代码

原始版

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
        int best = nums[0] + nums[1] + nums[2];
        for(int a=0; a<n-2; a++){
            int b = a+1;
            int c = n-1;
            while(b<c){
                int cursum = nums[a] + nums[b] + nums[c];
                if(cursum == target){
                    // 肯定是最接近的,直接返回。
                    return target;
                }
                if(Math.abs(target-cursum) < Math.abs(target-best)){
                    best = cursum;
                }
                if(cursum > target){
                    c--;
                }else{
                    b++;
                }
            }
        }
        return best;
    }
}

优化版

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
        int best = nums[0] + nums[1] + nums[2];
        for(int a=0; a<n-2; a++){
            if(a>0 && nums[a]==nums[a-1]){
                continue;
            }
            int b = a+1;
            int c = n-1;
            while(b<c){
                int cursum = nums[a] + nums[b] + nums[c];
                if(cursum == target){
                    // 肯定是最接近的,直接返回。
                    return target;
                }
                if(Math.abs(target-cursum) < Math.abs(target-best)){
                    best = cursum;
                }
                if(cursum > target){
                    int c0 = c-1;
                    while(c0>b && nums[c0]==nums[c]){
                        c0--;
                    }
                    c = c0;
                }else{
                    int b0 = b+1;
                    while(b0<c && nums[b0]==nums[b]){
                        b0++;
                    }
                    b = b0;
                }
            }
        }
        return best;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章