持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
解题思路
三数之和,直接用三个下标取值,排序后可以根据与目标值的大小判断下标的移动方向,从而找出最接近的和。
一、排序并初始化
对数组进行排序,以便决定下标的移动方向。
取出数组长度并减一,存放到 length 常量中,以便减少运算次数。
由于三数控制变化较多,则先固定一个数,根据情况移动另外两个下标:
用 i 代表固定值下标,也是三个数中最小的数,剩下的数中 start 从最小开始遍历,end 从最大开始遍历,并且 start 小于 end。即:0 <= i < start < end <= length,start 的取值范围为 [i + 1, end],end 的取值范围为 [start, length]。 当 start 与 end 重合,则固定值 i 右移,start 和 end 再次从初始位置开始遍历,直到数组不再有三个元素提供。
二、遍历数组
由于数组至少需要三个元素提供运算,所以当 i === length - 1 的时候就可以跳出循环了。
循环中先取出三个下标取值之和 sum,通过和已有结果 result 的比较确定是否要替换结果:
计算 sum 和 target 的差值,计算 result 和 target 的差值,比较两个差值; 当且仅当 sum 和 target 的差值更小的时候,用 sum 替换 result 的值。 无论有没有替换 result,均用 sum 和 目标值 target 进行比较:
- 若 sum < target,即三数之和小于目标值,需要尝试增大三数因子,i 指向固定值,end 指向最大值,则需要 start 右移;
- 若 sum > target,即三数之和大于目标值,需要尝试减小三数因子,i 指向固定值,start 指向最小值,则需要 end 左移;
- 若 start === end,即 start 下标与 end 下标重合,则需要固定值 i 右移,并重置 start 和 end。
代码
function threeSumClosest(nums: number[], target: number): number {
nums = nums.sort((a, b) => a - b); // 数组排序
const length: number = nums.length - 1; // 记录数组长度减一
let [i, start, end] = [0, 1, length]; // 数组遍历下标,i 代表固定值下标,start 代表开始下标,end 代表结束下标。则 start ∈ [i + 1, end],end ∈ [start, length]。
let result: number = nums[0] + nums[1] + nums[2]; // 初始化结果为数组前三个元素之和
while(i < length - 1) { // 由于 i 小于 start 和 end,且 start 和 end 不能重合,所以至少留出最后两个元素,否则就结束遍历
const sum: number = nums[i] + nums[start] + nums[end]; // 获取三个下标的元素之和
result = Math.abs(target - sum) < Math.abs(target - result) ? sum : result; // 根据与目标值的差值判断是否替换结果
sum < target ? start ++ : end --; // 三数之和小于目标值,start 下标右移;三数之和大于目标值,end 下标左移。
if (start === end) { // 当 start 下标与 end 下标重合
i ++; // 固定值下标右移
[start, end] = [i + 1, length]; // 重置 start 下标和 end 下标
}
}
return result; // 返回结果
};