[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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章