03. [Python+Golang+PHP]最接近的三数之和,多种语言实现最优解demo

一、问题描述:最接近的三数之和

给你一个长度为 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
  • -10^{4} <= target <= 10^{4}

二、实现方案

方法思路

最优实现方案:排序+双指针:

  1. 排序数组:排序有助于使用双指针技术高效地查找和调整当前和。
  2. 双指针遍历:固定第一个元素,然后在剩余部分使用双指针来调整第二个和第三个元素,使得三数之和尽可能接近目标值。
  3. 跳过重复元素:在遍历过程中跳过重复的元素,以提高效率。
  4. 动态更新最接近值:在每次计算三数之和时,与当前记录的最近值比较,更新为更接近目标值的和。

复杂度分析

  • 时间复杂度: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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值