给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
3 < = n u m s . l e n g t h < = 3000 − 1000 < = n u m s [ i ] < = 1000 − 1 0 4 < = t a r g e t < = 1 0 4 3 <= nums.length <= 3000\\ -1000 <= nums[i] <= 1000\\ -10^4 <= target<= 10^4 3<=nums.length<=3000−1000<=nums[i]<=1000−104<=target<=104
基本结构和3Sum很类似,但是数据规模缩小了。
暴力的过程依然是 O ( N 3 ) O(N^3) O(N3).
还是和昨天一样,先排序,利用双指针的结构来处理,整体的时间复杂度 O ( N 2 ) O(N^2) O(N2)。
这里双指针寻找的yz组合,要求是xyz和最靠近target,即y+z最接近 ∣ t a r g e t − x ∣ |target-x| ∣target−x∣。
所以在固定x之后,固定y,然后双指针处理寻找yz组合。也就是双指针在不断的枚举可能的yz组合,并且记录xyz结果。
双指针部分的加速,当然也可以选择不加速,数据规模也可以允许。
为了加速,当 x = n u m [ i ] , y = n u m [ i + 1 ] , z = n u m [ i + 2 ] , s u m = x + y + z > t a r g e t x= num[i],y=num[i+1],z=num[i+2],sum=x+y+z>target x=num[i],y=num[i+1],z=num[i+2],sum=x+y+z>target,此时已经找到了一个比target大的最小的组合,所以后面就不用遍历了。
当 x = n u m [ i ] , y = n u m [ n − 2 ] , z = n u m [ n − 1 ] , , s u m = x + y + z < t a r g e t x= num[i],y=num[n-2],z=num[n-1],,sum=x+y+z
当然也可以使用二分,理论上的时间复杂度是 O ( N 2 L o g N ) O(N^2LogN) O(N2LogN),。
public int threeSumClosest(int[] nums, int target) {
int n = nums.length,ans = 1<<30,diff=1<<30;
if(n==3) return nums[0]+nums[1]+nums[2];
Arrays.sort(nums);
for(int i= 0;i<n-2;i++){
if(i>0&&nums[i]==nums[i-1]) continue;
int sum = nums[i]+nums[i+1]+nums[i+2];
if(sum>=target){
return sum-target<diff?sum:ans;
}
sum = nums[i]+nums[n-1]+nums[n-2];
if(sum<target){
int d = target - sum;
if(d<diff){
ans = sum;
diff = d;
}
continue;
}
for(int j = i+1;j<n-1;j++){
int z = find(nums,j+1,n-1,target-nums[i]-nums[j]);
sum = nums[i];
sum+= nums[j];
sum+= z;
if(sum==target) return target;
int d = Math.abs(sum-target);
if(d<diff){
ans = sum;
diff = d;
}
}
}
return ans;
}
// find 1st >= target
public int find(int[] arr, int left ,int right,int target){
if(arr[left]>target) return arr[left];
if(arr[right]<target) return arr[right];
int mid = 0,l = left,r = right;
while(l<r){
mid = l+(r-l)/2;
if(arr[mid]<target){
l = mid+1;
}
else{
r = mid;
}
}
if(l>left){
if( Math.abs(arr[l-1] -target)<=Math.abs(arr[l] -target) ){
return arr[l-1];
}
}
return arr[l];
}
时间复杂度 O ( N 2 log N ) O(N^2 \log N) O(N2logN)
空间复杂度 O ( log N ) O(\log N) O(logN)
public int threeSumClosest(int[] nums, int target) {
int n = nums.length,ans = 1<<30,diff=1<<30;
if(n==3) return nums[0]+nums[1]+nums[2];
Arrays.sort(nums);
for(int i=0; i<n-2; i++){
if(i>0&&nums[i]==nums[i-1]) continue;
int j=i+1,k=n-1;
//加速
int sum = nums[i]+nums[i+1]+nums[i+2];
if(sum>=target){
return sum-target<diff?sum:ans;
}
sum = nums[i]+nums[n-1]+nums[n-2];
//加速
if(sum<target){
int d = target - sum;
if(d<diff){
ans = sum;
diff = d;
}
continue;
}
while(j<k){
sum = nums[i]+nums[j]+nums[k];
int d = Math.abs(sum - target);
if(d<diff){
diff = d;
ans = sum;
}
if(sum==target){
return target;
}
else if(sum>target){
k--;
while(j<k&& nums[k]==nums[k+1]){
k--;
}
}
else{
j++;
while(j<k&& nums[j-1]==nums[j]){
j++;
}
}
}
}
return ans;
}
时间复杂度 O ( N 2 ) O(N^2) O(N2)
空间复杂度 O ( log N ) O(\log N) O(logN)
加速部分,思路启发来源灵神
Array
Two Pointers
Sort