最接近的三数之和
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
速度
题目描述
给你一个长度为 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)。
模拟答题者思考
1. 我先写暴力:三重循环枚举三个不同下标,计算和与 target 的差,取最小——O(n³),能过但太慢。
2. 重复在哪里?固定第一个数 nums[i] 后,问题变成「在剩余数组里找两数,使三数之和尽量接近 target」。
3. 排序后,两数之和可以用双指针:和小了 l++,和大了 r--,每步都在缩小与 target 的差距方向移动。
4. 与 #15 三数之和不同:本题只要一个最接近的和,不需要收集全部三元组,也不必做 i/l/r 去重(题目保证唯一解)。
5. 若某次 s == target,已不可能更优,可直接返回 s;否则遍历完所有 i 后返回 best。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
i | int | 定义:固定三元组中第一个数的位置(排序后作为最小候选) 维护:外层枚举,每轮锁定 nums[i] 后在内层找最优的 (l,r)更新: for i in range(n-2),每轮结束后 i++ |
l | int | 定义:在 i 右侧区间内指向较小候选值的左指针 维护:当前三数和偏小则右移,以增大总和 更新:初始 l=i+1;当 s < target 时 l++ |
r | int | 定义:在 i 右侧区间内指向较大候选值的右指针 维护:当前三数和偏大则左移,以减小总和 更新:初始 r=n-1;当 s > target 时 r-- |
best | int | 定义:截至目前与 target 最接近的三数之和维护:每算出一组 s,若 |s-target| 更小则刷新更新:初始可用前三项之和;遍历中 if abs(s-target) < abs(best-target): best=s |
落码步骤
1. 对 nums 升序排序
2. 初始化 best = nums[0]+nums[1]+nums[2](或第一个合法三数和)
3. 外层 for i in range(n-2),设 l=i+1, r=n-1
4. 当 l<r:计算 s=nums[i]+nums[l]+nums[r],用 |s-target| 更新 best
5. s==target 直接返回;s<target 则 l++,否则 r--
6. 全部 i 枚举完毕,返回 best
代码实现
class Solution:
def threeSumClosest(self, nums: list[int], target: int) -> int:
nums.sort()
n = len(nums)
best = nums[0] + nums[1] + nums[2]
for i in range(n - 2):
l, r = i + 1, n - 1
while l < r:
s = nums[i] + nums[l] + nums[r]
if abs(s - target) < abs(best - target):
best = s
if s == target:
return s
elif s < target:
l += 1
else:
r -= 1
return best
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size();
int best = nums[0] + nums[1] + nums[2];
for (int i = 0; i < n - 2; i++) {
int l = i + 1, r = n - 1;
while (l < r) {
int s = nums[i] + nums[l] + nums[r];
if (abs(s - target) < abs(best - target))
best = s;
if (s == target) return s;
else if (s < target) l++;
else r--;
}
}
return best;
}
};
// 时间 O(n²),空间 O(1)(不计排序)
复杂度分析
时间复杂度
O(n²)
空间复杂度
O(1)(不计排序)
常见坑
忘记排序:不排序就无法保证双指针单调移动,可能漏掉最优解。
照搬三数之和的去重逻辑:本题只返回一个最接近的和,且保证唯一解,不需要跳过重复的 i/l/r。
s==target 时仍继续循环:已命中最优,应立刻返回,否则浪费时间。
必测边界 Case
Case 1:恰好命中 target
nums = [1,2,3], target = 6 → 6(1+2+3)
Case 2:全零
nums = [0,0,0], target = 1 → 0
Case 3:最小长度 n=3
nums = [-1,2,1], target = 1 → 2(只有一组三元组)