二分查找——LeetCode-1300.转变数组后最接近目标值的数组和、LeetCode-5438.制作 m 束花所需的最少天数

LeetCode-1300.转变数组后最接近目标值的数组和

这里是题目描述:LeetCode-1300.转变数组后最接近目标值的数组和

我们可以确定,最后要返回的结果整数value的所在的范围是[0,m],其中m是所给的数组arr中的最大数字。如果用暴力法,我们需要对从0m的所有数字进行尝试,每次尝试都要遍历一遍arr,计算用当前的数字替换arr中所有大于它的数字后arr所有元素的和;最后选择元素和与target相差最小的数字。设数组的长度为n,则时间复杂度为O(nm)

接下来使用二分法进行优化。在结果整数value所在的可能范围[0,m]上进行二分查找,二分查找的两端指针p1p2分别初始化为0m;每一次二分查找的值为midmid=(p1+p2)/2,对于每个mid,对数组arr进行一趟遍历计算累加和,其中大于mid的数字变为mid加入累加和中;计算累加和与目标值target之间的差值,如果差值为0,则当前mid就是所求的结果直接返回。如果差值小于0,继续在区间[mid+1,p2]中搜索,令p1=mid+1,如果差值大于0,继续在区间[p1,mid-1]中搜索,令p1=mid-1。另外,如果当前差值的绝对值小于记录的最小差值的绝对值,则更新最小差值绝对值为当前差值的绝对值,记录的所求结果target更新为当前的mid。不断循环地进行二分查找,直到p1>p2,跳出循环,返回记录的结果。

经过二分查找优化后,时间复杂度为O(nlogm)

题解代码:

class Solution {
    public int findBestValue(int[] arr, int target) {
        int maxEle=arr[0]; //记录arr中的最大值
        for(int e:arr)
        {
            maxEle=Math.max(maxEle,e);
        }
        //二分查找结果
        int p1=0,p2=maxEle;
        int res=maxEle,minDiff=maxEle*arr.length;
        while(p1<=p2)
        {
            int mid=(p1+p2)/2;
            int sum=0;
            for(int e:arr)
            {
                sum+=Math.min(e,mid);
            }
            int diff=target-sum;
            if(diff==0)
            {
                return mid;
            }
            if(Math.abs(diff)<minDiff || (Math.abs(diff)==minDiff && mid<res))
            {
                res=mid;
                minDiff=Math.abs(diff);
            }
            if(diff>0)
            {
                p1=mid+1;
            }
            else
            {
                p2=mid-1;
            }
        }
        return res;
    }
}
LeetCode-5438.制作 m 束花所需的最少天数

这里是题目描述:LeetCode-5438.制作 m 束花所需的最少天数

本题的基本思想和上一道题一样,采用二分查找。所求的“制作 m 束花所需的最少天数”的可能范围是[bottom,top]中,bottomtop分别是花朵开放所需的最少与最多天数,在区间[bottom,top]上进行二分查找。

首先遍历一遍所给的数组bloomDay,求出花朵开放所需的最少与最多天数bottomtop

接下来开始二分查找。初始化指向二分查找可能区间两端的值d1=bottomd2=top。每一趟二分查找的中间值mid=(d1+d2)/2,并且每趟二分查找遍历一遍数组bloomDay,计算出当前midbloomDay的各个小于等于mid的连续子区间长度,并根据子区间长度算子区间能制作的需要连续的k朵花的花束的个数;所有子区间能制作的花束数量之和就是当前mid下能得到的花束总数。如果花束总数小于m,则继续在区间[mid+1,d2]中二分搜索,令d1=mid+1;如果花束总数大于等于m,则继续在区间[d1,mid-1]中二分搜索,令d2=mid-1。不断循环进行二分搜索,直到d1>d2,跳出循环

跳出循环后,返回d1就是所求的最终结果

时间复杂度:设数组bloomDay长度为nbloomDay的最大最小值之间的相差d。需进行O(logd)次的二分查找,没此查找需要遍历bloomDay,时间开销为O(n)。因此总的时间复杂度为O(nlogd)

空间复杂度O(1)

题解代码:

class Solution {
    public int minDays(int[] bloomDay, int m, int k) {
        if(bloomDay.length<(m*k))
        {
            return -1;
        }
        int bottom=bloomDay[0],top=bloomDay[0];
        for(int i=0;i<bloomDay.length;i++)
        {
            bottom=Math.min(bottom,bloomDay[i]);
            top=Math.max(top,bloomDay[i]);
        }
        if(bloomDay.length==(m*k))
        {
            return top;
        }
        //System.out.println("bottom: "+bottom+" top: "+top);
        int d1=bottom,d2=top;
        while(d1<=d2)
        {
            int mid=(d1+d2)/2;
            //System.out.println("d1: "+d1+" d2: "+d2+" mid: "+mid);
            int n=numBloom(bloomDay,mid,k);
            //System.out.println("n: "+n);
            if(n<m)
            {
                d1=mid+1;
            }
            else //n>=m
            {
                d2=mid-1;
            }
        }
        return d1;
    }
    int numBloom(int[] bloomDay,int day,int k) //返回当前天数下能够得到的花束数量
    {
        int res=0;
        int begin=-1;
        for(int i=0;i<bloomDay.length;i++)
        {
            if(bloomDay[i]<=day)
            {
                if(begin==-1)
                {
                    begin=i;
                }
            }
            else //bloomDay[i]>day
            {
                if(begin!=-1)
                {
                    int len=i-begin;
                    res+=(len/k);
                    begin=-1;
                }
            }
        }
        if(bloomDay[bloomDay.length-1]<=day)
        {
            int len=bloomDay.length-begin;
            res+=(len/k);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值