16. 最接近的三数之和
难度:中等
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
class Solution {
public int threeSumClosest(int[] nums, int target) {
}
}
解题思路:
- 双指针法
- 先让数组有序,也就是需要先对数组进行排序
- 然后每次固定一个元素,再去寻找另外两个元素,也就是双指针
我们在声明变量三数之和sum时,可以直接以nums[0] +nums[1]+nums[2]的和赋值,绝对不能赋值为0因为这个sum必须是数组中某3个元素的和。
接下来先对原数组进行排序
Arrays.sort(nums);
java中底层这里用的快排 意思就是说 最好时间复杂度为O(nlgn)最坏为O(n^2)
对于已经排好序的数组我们开始遍历
这里可以先进行一次去重,也就是
if (i!=0&&nums[i]==nums[i-1]) continue;
如果在下标不为0的情况下,如果当前值和上一个值相等就跳到下一次循环(已经计算完当前值和位置的所有情况)
然后我们可以定义两个指针 分别指向当前元素的下一个元素和数组最后一个元素,这个时候我们可以保证nums[i]+nums[j]+nums[k]是最大的(数组有序)
于是int mid = nums[i]+nums[j]+nums[k];
当mid与target的距离小于sum与target的距离时就更新sum。
判断当sum==target的时候直接return sum; 题目只要求一个结果。
然后可以比较mid 和 target 的值
如果mid > target 说明减少mid才能靠近target
此时应该k-1
(为了避免出现重复的步骤,比如中间是111222的情况,我们可以进行二次去重)
(也就是k-1还是大鱼j并且下标k+1元素值和k元素相等说明重复了,直接k-1一次)
如果mid < target 说明增加mid才能靠近target
此时应该j+1
(也就是j+1还是小鱼k并且下标j-1元素值和j元素相等说明重复了,直接j+1一次)
实际代码如下:
顺带整个遍历数组的同时遍历了当前元素后的元素,时间复杂度为O(N2)和快排相加还是O(N2) 空间复杂度应为O(logN) (快排空间)
package com.czh.factory;
import org.junit.Test;
import java.util.Arrays;
/**
* @author zhCoding
* @Description:
* @create 23:57
*/
public class AlmostThree {
@Test
public void test1(){
int[] arr = new int[]{87,6,-100,-19,10,-8,-58,56,14,-1,-42,-45,-17,10,20,-4,13,-17,0,11,-44,65,74,-48,30,-91,13,-53,76,-69,-19,-69,16,78,-56,27,41,67,-79,-2,30,-13,-60,39,95,64,-12,45,-52,45,-44,73,97,100,-19,-16,-26,58,-61,53,70,1,-83,11,-35,-7,61,30,17,98,29,52,75,-73,-73,-23,-75,91,3,-57,91,50,42,74,-7,62,17,-91,55,94,-21,-36,73,19,-61,-82,73,1,-10,-40,11,54,-81,20,40,-29,96,89,57,10,-16,-34,-56,69,76,49,76,82,80,58,-47,12,17,77,-75,-24,11,-45,60,65,55,-89,49,-19,4};
System.out.println(threeSumClosest(arr, -275));
}
public int threeSumClosest(int[] nums, int target) {
if (nums==null||nums.length<3) throw new RuntimeException("输入数组有误!");
int sum = nums[0] +nums[1]+nums[2];
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i!=0&&nums[i]==nums[i-1]) continue;
int j=i+1,k=nums.length-1;
while (j<k){
int mid = nums[i]+nums[j]+nums[k];
if (Math.abs(sum-target)>Math.abs(mid-target)){
sum = mid;
}
if (mid==target) return target;
if (mid > target){
k--;
while ((j+1<k && nums[k+1]==nums[k]))
k--;
}
if (mid < target){
j++;
while ((j+1<k && nums[j-1]==nums[j]))
j++;
}
}
}
return sum;
}
}