二分模板:leetcode704
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=left+((right-left)>>1);
if(nums[mid]==target) return mid;
else nums[mid]>target? right=--mid:left=++mid;
}
return -1;
}
};
找指定值的第一个下标
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=left+((right-left)>>1);
if(nums[mid]==target){
if(!mid||nums[mid-1]-target) return mid;
else right=--mid;
}
else nums[mid]>target? right=--mid:left=++mid
}
return -1;
}
};
找指定值的最后一个下标
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
while(left<=right){
int mid=left+((right-left)>>1);
if(nums[mid]==target){
if(mid==nums.size()-1||nums[mid+1]-target) return mid;
else left=++mid;
}
else nums[mid]>target? right=--mid:left=++mid;
}
return -1;
}
};
由查找指定值的第一个下标或最后一个下标的思路可以联系到解决一些0-1问题,这类问题主要思路是查找数组中第一个0或最后一个0,整体思路与二分类似,如以下几题。
1.
Leetcode 278. 第一个错误的版本https://leetcode.cn/problems/first-bad-version/
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4 输出:4 解释:调用 isBadVersion(3) -> false 调用 isBadVersion(5) -> true 调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
示例 2:
输入:n = 1, bad = 1 输出:1
提示:
1 <= bad <= n <= 231 - 1
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
int l=1,r=n;
while(l<=r){
int mid=l+((r-l)>>1);
if(isBadVersion(mid)){
if(!mid||!isBadVersion(mid-1)) return mid;
else r=--mid;;
}else l=++mid;
}
return -1;
}
};
2.
Leetcode 1482. 制作 m 束花所需的最少天数https://leetcode.cn/problems/minimum-number-of-days-to-make-m-bouquets/
给你一个整数数组 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 。
示例 3:
输入:bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3 输出:12 解释:要制作 2 束花,每束需要 3 朵。 花园在 7 天后和 12 天后的情况如下: 7 天后:[x, x, x, x, _, x, x] 可以用前 3 朵盛开的花制作第一束花。但不能使用后 3 朵盛开的花,因为它们不相邻。 12 天后:[x, x, x, x, x, x, x] 显然,我们可以用不同的方式制作两束花。
示例 4:
输入:bloomDay = [1000000000,1000000000], m = 1, k = 1 输出:1000000000 解释:需要等 1000000000 天才能采到花来制作花束
示例 5:
输入:bloomDay = [1,10,2,9,3,8,4,7,5,6], m = 4, k = 2 输出:9
提示:
bloomDay.length == n
1 <= n <= 10^5
1 <= bloomDay[i] <= 10^9
1 <= m <= 10^6
1 <= k <= n
class Solution {
public:
bool check(vector<int> bloomDay, int m, int k, int t) {
int x=0; //一共能有几束
int y=0;
for(auto i:bloomDay){
if(t>=i){
y++;
}else y=0;
if(y==k){
x++;
y=0;
}
}
return x>=m;
}
int minDays(vector<int>& bloomDay, int m, int k) {
if(m>bloomDay.size()/k) return -1;
int l=1,r=-1;
for(auto i:bloomDay) r=max(r,i);
while(l<=r){
int mid=l+((r-l)>>1);
if(check(bloomDay,m,k,mid)){
if(mid==1||!check(bloomDay,m,k,mid-1)) return mid;
else r=--mid;
}else l=++mid;
}
return -1;
}
};
按照题目要求的最小天数,并且题目说明了第一天,第二天不符合的原因,我们只需要看哪一天满足条件,并且所有比他小的天数都不符合即可。同时需要注意check函数里要判断连续。
二分题单:
洛谷:
【算法1-6】二分查找与二分答案 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
leetcode:
分享丨【题单】二分算法(二分答案/最小化最大值/最大化最小值/第K小) - 力扣(LeetCode)
牛客:
(33条未读私信) 【二分_CCPC/ICPC区域赛知识点练习_牛客竞赛OJ】 (nowcoder.com)
未完待续。。。