一、题目回顾
给定一个整数数组 nums 和一个目标值 target,
从数组中选出 三个整数,使它们的和 最接近 target。
要求返回这三个数的和。
示例:
nums = [-1, 2, 1, -4]
target = 1
输出:2
解释:
(-1) + 2 + 1 = 2
二、第一反应:暴力枚举能做吗?
最直接的想法是三重循环:
for i
for j
for k
时间复杂度:O(n³)
问题在于:
- n 稍微大一点就直接超时
- 而且每次只是“更接近一点”,却要遍历所有组合
所以关键问题变成:
能不能在枚举三元组时,有方向地靠近 target?
三、核心突破口:排序
Arrays.sort(nums);
排序的目的只有一个:
让“移动指针”这件事,具有明确的方向感
排序后,数组满足:
nums[left] 向右 → 变大
nums[right] 向左 → 变小
这为双指针提供了成立的基础。
四、整体思路拆解
整体策略可以拆成三步:
- 排序数组
- 固定一个数
nums[i] - 在剩余区间内,用双指针寻找最接近的两数之和
一句话总结:
三数问题 → 固定一个数 → 转化为“有序数组上的两数逼近问题”
五、完整代码
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length;
int closestSum = nums[0] + nums[1] + nums[2];
for (int i = 0; i < n - 2; i++) {
int left = i + 1;
int right = n - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (Math.abs(sum - target) < Math.abs(closestSum - target)) {
closestSum = sum;
}
if (sum < target) {
left++;
} else if (sum > target) {
right--;
} else {
return target;
}
}
}
return closestSum;
}
}
下面逐段拆解这段代码在“想什么”。
六、为什么 closestSum 要这样初始化?
int closestSum = nums[0] + nums[1] + nums[2];
原因很简单:
- 题目保证一定存在答案
- 我们需要一个 合法的初始对照值
- 排序后取前三个数,是最自然、最安全的选择
这样后续只需要比较:
当前 sum 是否比 closestSum 更接近 target
七、固定一个数,本质在干嘛?
for (int i = 0; i < n - 2; i++) {
int left = i + 1;
int right = n - 1;
这一步的含义是:
- 固定
nums[i] - 在区间
[i+1, n-1]中 - 找两个数,使三数之和最接近 target
也就是说:
三数问题 → 多次“两数最接近”问题
八、为什么每一步都要更新最优解?
if (Math.abs(sum - target) < Math.abs(closestSum - target)) {
closestSum = sum;
}
注意一点:
这题不是找“等于 target”的解,
而是找“距离最小”的解。
所以策略是:
只要当前结果更近,就立刻更新
哪怕后面还能继续移动指针,也不影响当前最优值的记录。
九、双指针为什么这样移动?
if (sum < target) {
left++;
} else if (sum > target) {
right--;
}
这是整个算法最核心的“直觉”。
sum < target
→ 当前和偏小
→ 想变大,只能让nums[left]变大
→left++sum > target
→ 当前和偏大
→ 想变小,只能让nums[right]变小
→right--
排序保证了这一步 一定是朝着目标逼近的。
十、为什么遇到刚好等于可以直接返回?
else {
return target;
}
因为:
|sum - target| = 0
这是理论上的最优解,不可能再被超越。
十一、为什么这题不需要去重?
这是很多人从「三数之和」转过来时最容易困惑的点。
原因是:
- 我们只关心 最接近的值
- 不关心具体是哪一组数
- 重复组合不会影响“距离最小值”的判断
而在「三数之和 = 0」中:
- 要返回所有不同组合
- 去重是必须的
十二、复杂度分析
-
时间复杂度:
O(n²)- 外层固定一个数
- 内层双指针线性扫描
-
空间复杂度:
O(1)- 除排序外只使用常量空间
十三、总结
这道题真正想教会我们的不是“三个数怎么加”,而是:
在有序数组中,如何用双指针进行“单调逼近”