【LeetCode】最接近的三数之和

78 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第17天,点击查看活动详情

最接近的三数之和

原题:leetcode-cn.com/problems/3s…

描述

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

难度

中等

示例

输入: nums = [-1,2,1,-4], target = 1
输出: 2
解释: 与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

思路

将数组从小到大排序,枚举所有的元素,随后使用两个指针,left 表示左指针,right 表示右指针,从大于第一个元素的数组中找出两个数 nums[left] 和 nums[right]。设 sum = nums[i] + nums[left] + nums[right],bestSum 表示最接近目标值的三数之和,判断 sum 和 target 的差值是否小于 bestSum 和 target 的差值,如果为 true,将 bestSum 的值修改成 sum。当 sum == target 时,直接返回 target。

为了防止重复计算,第一层循环时需要去掉和上一次循环相同的元素。同样的内层循环也需要跳过 left 和 right 指针指向的相同元素,跳过 left 指针指向的相同元素的方法是从 left 指针开始向后寻找一个不等于当前 left 指针指向的元素的下标,跳过 right 指针指向的相同元素的方法是从 right 指针开始向前寻找一个不等于当前 right 指针指向的元素的下标。内层循环时当 left 大于 right 时退出。

代码

Rust

pub struct Solution {}
​
impl Solution {
    pub fn three_sum_closest(nums: Vec<i32>, target: i32) -> i32 {
        let mut nums = nums;
        nums.sort();
​
        let n = nums.len();
        // 防止后面 best_sum - target 溢出
        let mut best_sum = i32::MAX - target.abs();
        for i in 0..n {
            // 如果元素重复, 跳过
            if i > 0 && nums[i] == nums[i - 1] {
                continue;
            }
            let mut left = i + 1;
            let mut right = n - 1;
            // 从剩下的数组中寻找所有两个数, 加上第一个数之和最接近 target 的目标和
            while left < right {
                let left_value = nums[left];
                let right_value = nums[right];
                let sum = nums[i] + left_value + right_value;
​
                // 相等直接返回
                if sum == target {
                    return target;
                }
                // 比较当前三数之和上一次和的差值
                if (sum - target).abs() < (best_sum - target).abs() {
                    best_sum = sum;
                }
​
                // 和小于目标值,从右边寻找下一个较大的值
                if sum < target {
                    left += 1;
                    // 如果元素相同指针后移
                    while left < right && nums[left] == left_value {
                        left += 1;
                    }
                    continue;
                }
                // 和大于目标值,从左边寻找下一个较小的值
                right -= 1;
                // 如果元素相同指针前移
                while left < right && nums[right] == right_value {
                    right -= 1;
                }
            }
        }
        best_sum
    }
}
#[test]
fn test_three_sum_closest() {
    let nums = vec![-1, 2, 1, -4];
    let target = 1;
    println!("nums = {:?}, target = {}", nums, target);
​
    let sum = Solution::three_sum_closest(nums, target);
    println!("{}", sum);
}

运行结果:

nums = [-1, 2, 1, -4], target = 1
2

Go

func threeSumClosest(nums []int, target int) int {
    sort.Ints(nums)
​
    n := len(nums)
    bestSum := math.MaxInt32
    for i := 0; i < n; i++ {
        // 如果元素重复, 跳过
        if i > 0 && nums[i] == nums[i - 1] {
            continue
        }
​
        left := i + 1
        right := n - 1
        // 从剩下的数组中寻找所有两个数, 加上第一个数之和最接近 target 的目标和
        for left < right {
            leftValue := nums[left]
            rightValue := nums[right]
            sum := nums[i] + leftValue + rightValue
​
            // 相等直接返回
            if sum == target {
                return target
            }
            // 比较当前三数之和上一次和的差值
            if abs(sum - target) < abs(bestSum - target) {
                bestSum = sum
            }
​
            // 和小于目标值,从右边寻找下一个较大的值
            if sum < target {
                left++
                // 如果元素相同指针后移
                for left < right && nums[left] == leftValue {
                    left++
                }
                continue
            }
            // 和大于目标值,从左边寻找下一个较小的值
            right--
            // 如果元素相同指针前移
            for left < right && nums[right] == rightValue {
                right--
            }
        }
    }
    return bestSum
}
​
func abs(a int) int {
    if a > 0 {
        return a
    }
    return -a
}
func TestThreeSumClosest(t *testing.T) {
    nums := []int{-1, 2, 1, -4}
    target := 1
    t.Logf("nums = %v, target = %v\n", nums, target)
​
    sum := threeSumClosest(nums, target)
    t.Log(sum)
}

运行结果:

nums = [-1 2 1 -4], target = 1
2

Java

public class Main {
    
    public static int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
​
        int n = nums.length;
        int bestSum = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            // 如果元素重复, 跳过
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
​
            int left = i + 1;
            int right = n - 1;
            // 从剩下的数组中寻找所有两个数, 加上第一个数之和最接近 target 的目标和
            while (left < right) {
                int leftValue = nums[left];
                int rightValue = nums[right];
                int sum = nums[i] + leftValue + rightValue;
​
                // 相等直接返回
                if (sum == target) {
                    return target;
                }
                // 比较当前三数之和上一次和的差值
                if (Math.abs(sum - target) < Math.abs(bestSum - target)) {
                    bestSum = sum;
                }
​
                // 和小于目标值,从右边寻找下一个较大的值
                if (sum < target) {
                    left++;
                    // 如果元素相同指针后移
                    while (left < right && nums[left] == leftValue) {
                        left++;
                    }
                    continue;
                }
                // 和大于目标值,从左边寻找下一个较小的值
                right--;
                // 如果元素相同指针前移
                while (left < right && nums[right] == rightValue) {
                    right--;
                }
            }
        }
        return bestSum;
    }
​
    public static void main(String[] args) {
        int[] nums = new int[]{-1, 2, 1, -4};
        int target = 1;
        System.out.printf("nums = %s, target = %d\n", Arrays.toString(nums), target);
​
        int sum = threeSumClosest(nums, target);
        System.out.println(sum);
    }
}

运行结果:

nums = [-1, 2, 1, -4], target = 1
2