LeetCode-1300.转变数组后最接近目标值的数组和
这里是题目描述:LeetCode-1300.转变数组后最接近目标值的数组和
我们可以确定,最后要返回的结果整数value
的所在的范围是[0,m]
,其中m
是所给的数组arr
中的最大数字。如果用暴力法,我们需要对从0
到m
的所有数字进行尝试,每次尝试都要遍历一遍arr
,计算用当前的数字替换arr
中所有大于它的数字后arr
所有元素的和;最后选择元素和与target
相差最小的数字。设数组的长度为n
,则时间复杂度为O(nm)
接下来使用二分法进行优化。在结果整数value
所在的可能范围[0,m]
上进行二分查找,二分查找的两端指针p1
、p2
分别初始化为0
和m
;每一次二分查找的值为mid
,mid=(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]
中,bottom
和top
分别是花朵开放所需的最少与最多天数,在区间[bottom,top]
上进行二分查找。
首先遍历一遍所给的数组bloomDay
,求出花朵开放所需的最少与最多天数bottom
和top
。
接下来开始二分查找。初始化指向二分查找可能区间两端的值d1=bottom
、d2=top
。每一趟二分查找的中间值mid=(d1+d2)/2
,并且每趟二分查找遍历一遍数组bloomDay
,计算出当前mid
下bloomDay
的各个小于等于mid
的连续子区间长度,并根据子区间长度算子区间能制作的需要连续的k
朵花的花束的个数;所有子区间能制作的花束数量之和就是当前mid
下能得到的花束总数。如果花束总数小于m
,则继续在区间[mid+1,d2]
中二分搜索,令d1=mid+1
;如果花束总数大于等于m
,则继续在区间[d1,mid-1]
中二分搜索,令d2=mid-1
。不断循环进行二分搜索,直到d1>d2
,跳出循环
跳出循环后,返回d1
就是所求的最终结果
时间复杂度:设数组bloomDay
长度为n
,bloomDay
的最大最小值之间的相差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;
}
}