一、问题描述:最接近的三数之和
给你一个长度为 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)。
提示:
3 <= nums.length <= 1000-1000 <= nums[i] <= 1000-<= target <=
二、实现方案
方法思路
最优实现方案:排序+双指针:
- 排序数组:排序有助于使用双指针技术高效地查找和调整当前和。
- 双指针遍历:固定第一个元素,然后在剩余部分使用双指针来调整第二个和第三个元素,使得三数之和尽可能接近目标值。
- 跳过重复元素:在遍历过程中跳过重复的元素,以提高效率。
- 动态更新最接近值:在每次计算三数之和时,与当前记录的最近值比较,更新为更接近目标值的和。
复杂度分析
- 时间复杂度:O(n²),排序 O(n log n) + 双指针遍历 O(n²)。
- 空间复杂度:O(1)(排序的空间复杂度取决于语言实现,通常为 O(log n))。
代码实现:
- Python:
def threeSumClosest(nums, target):
nums.sort()
n = len(nums)
closest = nums[0] + nums[1] + nums[2] # 初始化为前三个数的和
min_diff = abs(closest - target)
for i in range(n - 2):
# 跳过重复的起始元素
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, n - 1
while left < right:
current_sum = nums[i] + nums[left] + nums[right]
current_diff = current_sum - target
# 如果差值更小,更新最接近的和
if abs(current_diff) < min_diff:
closest = current_sum
min_diff = abs(current_diff)
if min_diff == 0: # 差值为0时直接返回
return closest
# 根据当前和调整指针
if current_diff < 0:
# 需要更大的和,左指针右移并跳过重复元素
temp_left = nums[left]
while left < right and nums[left] == temp_left:
left += 1
else:
# 需要更小的和,右指针左移并跳过重复元素
temp_right = nums[right]
while left < right and nums[right] == temp_right:
right -= 1
return closest
- Golang:
package main
import (
"fmt"
"sort"
"math"
)
func threeSumClosest(nums []int, target int) int {
sort.Ints(nums)
n := len(nums)
closest := nums[0] + nums[1] + nums[2]
minDiff := int(math.Abs(float64(closest - target)))
for i := 0; i < n-2; i++ {
// 跳过重复的起始元素
if i > 0 && nums[i] == nums[i-1] {
continue
}
left, right := i+1, n-1
for left < right {
currentSum := nums[i] + nums[left] + nums[right]
currentDiff := currentSum - target
// 找到更接近的值时更新
if abs(currentDiff) < minDiff {
closest = currentSum
minDiff = abs(currentDiff)
if minDiff == 0 { // 直接返回最优解
return closest
}
}
// 调整指针
if currentDiff < 0 {
// 需要更大的和,左指针右移并跳过重复
tempLeft := nums[left]
for left < right && nums[left] == tempLeft {
left++
}
} else {
// 需要更小的和,右指针左移并跳过重复
tempRight := nums[right]
for left < right && nums[right] == tempRight {
right--
}
}
}
}
return closest
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func main() {
nums1 := []int{-1, 2, 1, -4}
fmt.Println(threeSumClosest(nums1, 1)) // 输出 2
nums2 := []int{0, 0, 0}
fmt.Println(threeSumClosest(nums2, 1)) // 输出 0
}
- php :
function threeSumClosest($nums, $target) {
sort($nums);
$n = count($nums);
$closest = $nums[0] + $nums[1] + $nums[2];
$minDiff = abs($closest - $target);
for ($i = 0; $i < $n - 2; $i++) {
// 跳过重复的起始元素
if ($i > 0 && $nums[$i] == $nums[$i-1]) {
continue;
}
$left = $i + 1;
$right = $n - 1;
while ($left < $right) {
$currentSum = $nums[$i] + $nums[$left] + $nums[$right];
$currentDiff = $currentSum - $target;
// 找到更接近的值时更新
if (abs($currentDiff) < $minDiff) {
$closest = $currentSum;
$minDiff = abs($currentDiff);
if ($minDiff == 0) { // 直接返回最优解
return $closest;
}
}
// 调整指针
if ($currentDiff < 0) {
// 需要更大的和,左指针右移并跳过重复
$tempLeft = $nums[$left];
while ($left < $right && $nums[$left] == $tempLeft) {
$left++;
}
} else {
// 需要更小的和,右指针左移并跳过重复
$tempRight = $nums[$right];
while ($left < $right && $nums[$right] == $tempRight) {
$right--;
}
}
}
}
return $closest;
}
// 测试示例
$nums1 = [-1, 2, 1, -4];
echo threeSumClosest($nums1, 1) . PHP_EOL; // 输出 2
$nums2 = [0, 0, 0];
echo threeSumClosest($nums2, 1) . PHP_EOL; // 输出 0
1037

被折叠的 条评论
为什么被折叠?



