最接近的三数之和(Medium)
给你一个长度为 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
提示:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
Related Topics
数组
双指针
排序
思路分析
这道题最简单粗暴的方法就是遍历数组中所有三个数的组合,这种遍历方式固定的方法就是使用三层循环,时间复杂度也就是O(n^3)。但是这道题要求三个数的和最小,那么就可以进行优化,不使用三层循环,而是在将数组变成有序数组之后,在第二层使用双指针的方法。这样就只用使用两层循环,将时间复杂度降低为 O(n^2)。
我们设置一些变量,minMod:三数和到target的距离、difference:三数和与target的差。在这里我们考虑几种情况,第一种:minMod等于0,说明满足条件直接输出结果。第二种:minMod不等于0,说明还要完成遍历查看是否有更加符合条件的三数和,第二种情况又分为两种,一种是difference大于零,这种说明需要增加三数和的大小,所以将左指针右移增大三数和,同时判断与target的距离是否变小。另一种是difference小于零,这种情况同理。这样第二层只需完成一次循环就可以得到结果。
代码实现
import java.util.Arrays;
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int minMod = Integer.MAX_VALUE;
int difference = 0;
int result = nums[0] + nums[1] + nums[nums.length-1];
for (int i = 0; i < nums.length - 2; i++ ){
int left = i + 1;
int right = nums.length - 1;
while (left < right){
int result_temp = nums[i] + nums[right] + nums[left];
difference = target - result_temp;
if (difference == 0) return nums[i] + nums[right] + nums[left];
if (difference > 0){
if (Math.abs(difference) <= minMod){
minMod = Math.abs(difference);
result = result_temp;
}
left++;
}else if (difference < 0){
if (Math.abs(difference) <= minMod){
minMod = Math.abs(difference);
result = result_temp;
}
right--;
}
}
}
return result;
}
}
//leetcode submit region end(Prohibit modification and deletion)
代码优化
1.元素重复:在排序之后,重复的数字会排列在一起,重复遍历这些数字是没有意义的,所以可以添加去重的操作。
2.target与每层遍历结果的界限问题:由于数组是有序的,所以每次遍历的left与right之间肯定存在着最值,nums[left] + nums[left+1]是最小值,nums[right] + nums[right-1]是最大值,如果target小于或大于这两个值,就没有必要继续循环,所以加上一个判断就可以减少循环的次数。
最后代码
import java.util.Arrays;
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int minMod = Integer.MAX_VALUE;
int difference = 0;
int result = nums[0] + nums[1] + nums[nums.length-1];
for (int i = 0; i < nums.length - 2; i++ ){
int left = i + 1;
int right = nums.length - 1;
while (left < right){
int min = nums[i] + nums[left] + nums[left + 1];
if(target < min){
if(Math.abs(result - target) > Math.abs(min - target))
result = min;
break;
}
int max = nums[i] + nums[right] + nums[right - 1];
if(target > max){
if(Math.abs(result - target) > Math.abs(max - target))
result = max;
break;
}
int result_temp = nums[i] + nums[right] + nums[left];
difference = target - result_temp;
if (difference == 0) return nums[i] + nums[right] + nums[left];
if (difference > 0){
if (Math.abs(difference) <= minMod){
minMod = Math.abs(difference);
result = result_temp;
}
left++;
}else if (difference < 0){
if (Math.abs(difference) <= minMod){
minMod = Math.abs(difference);
result = result_temp;
}
right--;
}
}
while(i<nums.length-2 && nums[i] == nums[i+1]) i++;
}
return result;
}
}
//leetcode submit region end(Prohibit modification and deletion)