关于二分查找
1、什么是二分查找
首先什么是二分查找呢?在这里问大家一个问题,如果有一本英语词典,你想从中找一个单词,你知道它的首字母,你是从中间翻开还是一个一个单词往下找,答案很明显是前者。这就是所谓的“二分思想”,面试官为你出的题大部分是不能通过直接排序做到的,二分查找能快速定位所需要的数据节省大量代码运算的时间。
在开始讲写之前我们需要知道二分查找的一个最重要的前提:有序序列!
2、做二分查找题目的一般步骤
最经典的二分查找例题应该是猜数字了吧?给定一个数字在指定范围内查找,比如在1~100里找到77.猜50 则小了,最小数值从0变为50;猜80则大了最大数值从100变为80.循环就可找到答案。
我们逐句来分析一下这题,可以发行以下信息:
- 1~100的最大最小值范围
- 判断猜的数与答案的关系:大了?小了?
- 小了则改变最小值,大了则改变最大值。
由此我们可以总结出做二分查找题的步骤:
-
阅读题目
*这一步很重要,做任何题都需要分析题目,代码怎么说也是人写的,我们可以通过分析题目找到很多对解答问题有利的条件。 -
找出答案存在的范围,即最大值最小值
*我们可以通过分析题目尽可能找到最小的范围以减少运算时间 -
这是最重要的一步,找到答案与你猜的数字的关系,你猜的是大了?还是小了?
-
如果小了则改变最小值为你猜的数+1,如果大了则改变最大值为你猜的数。
*加一的目的是加快循环的推进并使得最后的最小值与最大值能相等
3、例题
给你一个整数数组 bloomDay,以及两个整数 m 和 k 。
现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。
花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。
请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。
示例 1:
输入:bloomDay = [1,10,3,10,2], m = 3, k = 1
输出:3
解释:让我们一起观察这三天的花开过程,x 表示花开,而 _ 表示花还未开。
现在需要制作 3 束花,每束只需要 1 朵。
1 天后:[x, _, _, _, _] // 只能制作 1 束花
2 天后:[x, _, _, _, x] // 只能制作 2 束花
3 天后:[x, _, x, _, x] // 可以制作 3 束花,答案为 3
示例 2:
输入:bloomDay = [1,10,3,10,2], m = 3, k = 2
输出:-1
解释:要制作 3 束花,每束需要 2 朵花,也就是一共需要 6 朵花。而花园中只有 5 朵花,无法满足制作要求,返回 -1 。
class Solution {
/*
分析题目得知我们可以猜一个天数,如果在这个天数内实际开花数量比需要的数量少则表明猜小了,实际开花数量比需要数量多则猜大了
*/
public int minDays(int[] bloomDay, int m, int k) {
int n = bloomDay.length;
if(m*k > n) return -1;
int l = Integer.MAX_VALUE;//int类型的极值
int r = 0;
for(int i : bloomDay){
l = Math.min(l,i);//最小值
r = Math.max(r,i);//最大值
}
while(l < r){
int days = (l+r)/2;//这就是我们猜的数字
if(cheek(bloomDay,days,m,k)){
r = days;
}else{
l = days+1;
}
}
//最后最大值与最小值相等输出的就是我们要找的答案
return r;
}
public boolean cheek(int[] bloomDay,int days,int m,int k){
int flower = 0;
int blous = 0;//有多少束花
/*
分析题目得知如果猜的花束数量已经超过循环的数量则表明已经猜大了,
此时可以直接返回减少运算(这就是分析题目的作用)
*/
for(int i = 0; i < bloomDay.length && blous < m;i++){
if(bloomDay[i] <= days){
flower++;
if(flower == k){
blous++;
flower = 0;
}
}else{
flower = 0;//仔细看题目,连续的才能成一束
}
}
return blous >= m;
}
}