题目
给定一个包括 n 个整数的数组 nums
和 一个目标值 target
。找出 nums
中的三个整数,使得它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
一. 末尾二分法
将数组排序后,首先通过双层遍历得到前两个的值,然后使用二分法获取第三个数。遍历过程中取最近接的值即可。
js实现
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
var answer = Infinity
nums.sort((a, b) => a - b)
for (var i=0;i<nums.length-2;i++) {
for (var j=i+1;j<nums.length-1;j++) {
var left = j + 1
var right = nums.length - 1
while (left <= right) {
var mid = (left + right) / 2 | 0
var sum = nums[i] + nums[j] + nums[mid]
if (sum < target) {
left = mid + 1
} else if (sum > target) {
right = mid - 1
} else {
return target
}
if (Math.abs(target - sum) < Math.abs(target - answer)) {
answer = sum
}
}
}
}
return answer
}
复杂度分析
时间复杂度:O(n2*log(n))
空间复杂度:O(1)
测试结果
✔ Accepted
✔ 125/125 cases passed (84 ms)
✔ Your runtime beats 91.82 % of javascript submissions
✔ Your memory usage beats 47.96 % of javascript submissions (35 MB)
二. 双指针法
将数组排序后,首先遍历数组取第一个数,然后使用两个指针分别指向剩余数组的开头和结尾,求出他们的和,如果和小于目标值,那么移动左边的指针,如果和大于目标值,那么移动右边的指针,直到两指针相遇为止,移动过程中,如果出现和更接近的情况,则替换结果。
js实现
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
var answer = Infinity
nums.sort((a, b) => a - b)
for (var i=0;i<nums.length-2;i++) {
var left = i + 1
var right = nums.length - 1
while (left < right) {
var sum = nums[i] + nums[left] + nums[right]
if (sum < target) {
left++
} else if (sum > target) {
right--
} else {
return target
}
if (Math.abs(sum - target) < Math.abs(answer - target)) {
answer = sum
}
}
}
return answer
}
复杂度分析
时间复杂度:O(n2)
空间复杂度:O(1)
测试结果
✔ Accepted
✔ 125/125 cases passed (72 ms)
✔ Your runtime beats 99.17 % of javascript submissions
✔ Your memory usage beats 44.8 % of javascript submissions (35 MB)
三. 优化的双指针法
- 元素重复时,不必再进行遍历
- 如果目标比双指针的可能取到的最小值更小或比最大值更大,那么不必使用双指针
js实现
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
var answer = Infinity
nums.sort((a, b) => a - b)
var len = nums.length
for (var i=0;i<len-2;i++) {
while (nums[i] === nums[i-1]) {
i++
}
if (nums[i] + nums[i+1] + nums[i+2] > target) {
if (Math.abs(target - (nums[i] + nums[i+1] + nums[i+2])) < Math.abs(target - answer)) {
answer = nums[i] + nums[i+1] + nums[i+2]
continue
}
}
if (nums[i] + nums[len-1] + nums[len-2] < target) {
if (Math.abs(target - (nums[i] + nums[len-1] + nums[len-2])) < Math.abs(target - answer)) {
answer = nums[i] + nums[len-1] + nums[len-2]
continue
}
}
var left = i + 1
var right = len- 1
while (left < right) {
var sum = nums[i] + nums[left] + nums[right]
if (sum < target) {
left++
while (nums[left] === nums[left-1]) {
left++
}
} else if (sum > target) {
right--
while (nums[right] === nums[right+1]) {
right--
}
} else {
return target
}
if (Math.abs(sum - target) < Math.abs(answer - target)) {
answer = sum
}
}
}
return answer
}
复杂度分析
时间复杂度:O(n2)
空间复杂度:O(1)
测试结果
✔ Accepted
✔ 125/125 cases passed (72 ms)
✔ Your runtime beats 99.17 % of javascript submissions
✔ Your memory usage beats 7.69 % of javascript submissions (35.7 MB)