力扣.16 最接近的三數之和

老马啸西风發表於2024-11-13

陣列系列

力扣資料結構之陣列-00-概覽

力扣.53 最大子陣列和 maximum-subarray

力扣.128 最長連續序列 longest-consecutive-sequence

力扣.1 兩數之和 N 種解法 two-sum

力扣.167 兩數之和 II two-sum-ii

力扣.170 兩數之和 III two-sum-iii

力扣.653 兩數之和 IV two-sum-IV

力扣.015 三數之和 three-sum

力扣.016 最接近的三數之和 three-sum-closest

力扣.259 較小的三數之和 three-sum-smaller

題目

給你一個長度為 n 的整數陣列 nums 和 一個目標值 target。請你從 nums 中選出三個整數,使它們的和與 target 最接近。

返回這三個數的和。

假定每組輸入只存在恰好一個解。

示例 1:

輸入:nums = [-1,2,1,-4], target = 1
輸出:2
解釋:與 target 最接近的和是 2 (-1 + 2 + 1 = 2)。
示例 2:

輸入:nums = [0,0,0], target = 1
輸出:0
解釋:與 target 最接近的和是 0(0 + 0 + 0 = 0)。

提示:

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

前言

這道題作為 leetcode 的第 15 道題,看起來似曾相識。

大概思路可以有下面幾種:

  1. 暴力解法

  2. 陣列排序+二分

  3. Hash 最佳化

  4. 雙指標

v1-暴力解法

思路

直接 3 次迴圈,找到符合結果的資料返回。

這種最容易想到,一般工作中也是我們用到最多的。

大機率會超時。

實現

public int threeSumClosest(int[] nums, int target) {
    final int n = nums.length;
    int result = Integer.MAX_VALUE;
    int minDis = Integer.MAX_VALUE;
    for(int i = 0; i < n; i++) {
        for(int j = i+1; j < n; j++) {
            for(int k = j+1; k < n; k++) {
                int sum = nums[i]+nums[j]+nums[k];
                int dis = Math.abs(sum - target);
                if(minDis > dis) {
                    minDis = dis;
                    result = sum;
                }
            }
        }
    }
    return result;
}

效果

574ms 8.04%

竟然透過了,還挺意外的

小結

這裡慢在三層迴圈,可以考慮排序後利用雙指標最佳化。

可以參考 T015 的思路。

v2-排序+雙指標

思路

首先排序

固定第一個元素,然後後面兩個元素透過雙指標尋找,類似於 T015

這裡需要用一個變數記錄最小的距離,另一個記錄 result 和。

實現

public int threeSumClosest(int[] nums, int target) {
    Arrays.sort(nums);
    // 處理雙指標
    final int n = nums.length;
    int result = Integer.MAX_VALUE;
    int minDis = Integer.MAX_VALUE;
    for(int i = 0; i < n-2; i++) {
        int left = i+1;
        int right = n;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            // 判斷是否為最小距離
            if(sum == target) {
                return target;
            }
            // 更新最小距離
            int abs = Math.abs(sum - target);
            if(abs < minDis) {
                minDis = abs;
                // 最小的 sum
                result = sum;
            }

            if(sum > target) {
                right--;
            }
            if(sum < target) {
                left++;
            }
        }
    }
    return result;
}

效果

12ms 77.79%

效果還行。看了下基本實現就是這個。

小結

這裡對雙指標的理解要求比較高。

在理解了 T015 的基礎上實現這一題並不算特別難。

相關文章