【算法】3Sum Closest 最接近的三数之和

文章目录

  • 3Sum Closest 最接近的三数之和
    • 问题描述:
    • 分析
    • 代码
      • 二分
      • 双指针
    • Tag

3Sum Closest 最接近的三数之和

问题描述:

给你一个长度为 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<=30001000<=nums[i]<=1000104<=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| targetx
所以在固定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+zx=num[i],y=num[n2],z=num[n1],,sum=x+y+z<target,说明此时固定x的最大组合都比target小,此时的xyz就是当前固定x的最佳组合,但是依旧需要继续遍历i.

当然也可以使用二分,理论上的时间复杂度是 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)

加速部分,思路启发来源灵神

Tag

Array

Two Pointers

Sort

你可能感兴趣的:(数据结构与算法,算法,数据结构)