开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第17天,点击查看活动详情。
最接近的三数之和
描述
给定一个包括 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