力扣第十六题——最接近的三数之和

内容介绍

给你一个长度为 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

提示:

  • 3 <= nums.length <= 1000
  • -1000 <= nums[i] <= 1000
  • -104 <= target <= 104

完整代码

 class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
        int best = 10000000;
        for (int i = 0; i < n; ++i) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int j = i + 1, k = n - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if (sum == target) {
                    return target;
                }
                if (Math.abs(sum - target) < Math.abs(best - target)) {
                    best = sum;
                }
                if (sum > target) {
                    int k0 = k - 1;
                    while (j < k0 && nums[k0] == nums[k]) {
                        --k0;
                    }
                    k = k0;
                } else {
                    int j0 = j + 1;
                    while (j0 < k && nums[j0] == nums[j]) {
                        ++j0;
                    }
                    j = j0;
                }
            }
        }
        return best;
    }
}

思路详解

首先搭建一个框架

  1. 排序:首先对数组进行排序,这样可以方便地使用双指针方法来寻找合适的组合。

  2. 遍历:遍历数组,对于每个元素 nums[i],使用双指针 j 和 k 在其右侧寻找另外两个数,使得这三个数的和最接近 target

  3. 双指针:在遍历过程中,j 指向 i+1k 指向数组末尾。计算 nums[i] + nums[j] + nums[k] 的和,并根据和与 target 的关系来移动指针。

  4. 去重:为了避免重复的组合,需要跳过那些重复的元素。

  5. 更新答案:在每一步中,如果找到了一个和更接近 target 的组合,就更新最佳答案 best

有了框架后我们继续一步一步解决完善代码。

1. 数组排序
Arrays.sort(nums);

首先,使用Arrays.sort方法对数组进行排序。排序是必要的,因为它使我们能够使用双指针技术来有效地找到最接近目标值的和。

2. 初始化变量
int n = nums.length;
int best = 10000000;
  • n存储数组的长度。
  • best初始化为一个很大的数,用来在后续过程中记录最接近目标值的和。
3. 遍历数组
for (int i = 0; i < n; ++i) {

通过一个for循环遍历数组中的每个元素,每个元素都将作为可能的三元组中的一个元素。

4. 跳过重复元素
if (i > 0 && nums[i] == nums[i - 1]) {
    continue;
}

如果当前元素与前一个元素相同,则跳过当前循环。这是为了避免重复的三元组,因为我们已经考虑了相同的第一个元素的所有可能组合。

5. 初始化双指针
int j = i + 1, k = n - 1;

对于每个i,初始化两个指针jk,分别指向i之后的元素和数组的最后一个元素。

6. 双指针搜索
while (j < k) {

while循环中,使用双指针来寻找和最接近目标值的三个数的组合。

7. 计算当前组合的和
int sum = nums[i] + nums[j] + nums[k];

计算当前三个数的和。

8. 检查是否找到了目标值
if (sum == target) {
    return target;
}

如果当前组合的和正好等于目标值,直接返回这个和,因为它是最接近的。

9. 更新最接近的和
if (Math.abs(sum - target) < Math.abs(best - target)) {
    best = sum;
}

如果当前组合的和比之前记录的最接近和更接近目标值,则更新best

10. 移动指针
if (sum > target) {
    int k0 = k - 1;
    while (j < k0 && nums[k0] == nums[k]) {
        --k0;
    }
    k = k0;
} else {
    int j0 = j + 1;
    while (j0 < k && nums[j0] == nums[j]) {
        ++j0;
    }
    j = j0;
}

根据当前和与目标值的关系,移动指针jk

  • 如果当前和大于目标值,则将k向左移动以尝试找到一个更小的和。
  • 如果当前和小于目标值,则将j向右移动以尝试找到一个更大的和。
  • 在移动指针时,跳过重复的元素以避免重复的三元组。
11. 返回结果
return best;

在遍历完所有可能的组合后,返回记录的最接近目标值的和。

知识点精炼

  1. 数组排序

    • 使用Arrays.sort()方法对整数数组进行排序,为后续的双指针操作提供有序数组。
  2. 遍历数组

    • 使用for循环遍历数组元素,每个元素都作为可能的三元组中的第一个元素。
  3. 去重

    • 通过条件判断if (i > 0 && nums[i] == nums[i - 1])跳过重复元素,避免生成重复的三元组。
  4. 双指针技术

    • 在固定一个元素后,使用两个指针分别指向剩余元素的头和尾,通过比较和调整指针位置来寻找合适的组合。
  5. 求和与比较

    • 计算当前指针所指三个元素的和,并与目标值进行比较,以确定是否需要更新最接近的和。
  6. 绝对值比较

    • 使用Math.abs()函数计算与目标值的差的绝对值,以比较不同和与目标值的接近程度。
  7. 指针移动与去重

    • 根据当前和与目标值的关系移动指针,并跳过重复元素,确保每个可能的组合只被考虑一次。
  8. 边界条件处理

    • 当找到和等于目标值的组合时,立即返回目标值,因为这是最接近的情况。
  9. 返回结果

    • 在遍历完所有可能的组合后,返回记录的最接近目标值的和。
  10. 时间复杂度分析

    • 整体时间复杂度为O(n^2),其中n是数组的长度,因为外层循环是O(n),内层双指针操作是O(n)。
  11. 空间复杂度分析

    • 空间复杂度为O(log n),主要由排序算法决定,因为双指针操作是原地进行的,没有使用额外的空间。

 

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值