定义:二分查找算法也称折半搜索算法,对数搜索算法,是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半,时间复杂度是log(n)
- 一是有序数组(这里可能是整体有序比如[1,2,3,4,5],也有可能是局部有序比如[4,5,1,2,3]),
- 二是特定元素(也有可能是满足特定的条件)。由定义我们大概就知道了二分法的应用场景,在有序数组中找特定值都可以考虑用二分法
二分法的步骤:
我们要确定一个区间[L,R]我们要找到一个性质(由题目条件决定),并且该性质满足一下两点: ①满足二段性 ②答案是二段性的分界点
注:由答案和数据的关系进行二分查找的算法是最常见的
我们要在一组升序的数组找一个数的下标,那我们肯定是先拿中间的与他进行比较,比较大小的判断,其实就相当于是这个性质,且这个性质满足二段性,将大于和小于我们要查找的值分为两段,而我们的查找结果就是分界点
有二段性的答案就是满足二分查找法的重要标志之一
所谓二段性,就是数组一定有一段左侧区域严格满足某一性质(即is_blue返回true),右侧严格满足另一性质,这两种性质必须是严格互斥的,这两段中间即为二段性边界。
二分法的使用条件:
①上下界确定 ②区间内有序(也可以是局部有序)
二分法的目的:
二分查找用于在多条记录中快速找到待查找的记录,它的思想是:每次将查找的范围缩小一半,直到最后找到记录或者找不到记录返回
标准解题模板:
def check():
**# check为题目中判断条件**
pass
def bin_search(a, n, x):
l, r = -1, n + 1
while l + 1 != r:
mid = (l+r) // 2
if check(mid):
l = mid
else:
r = mid
return (l or r) # 选取需要的部分进行返回
模板分析:
左右指针均指向数组外,这样移动时直接使 *l*,r=mid即可——*相当于自动完成了+1和-1的操作*
返回左边界,则当≤目标值时移动 *l* ; 返回右边界,则当≥目标值时移动 r
终止条件为r=*l*+1 ——*这里相当于左闭右开,即此时区间内只有left对应的元素*
下面给大家带来两个例题供大家学习和参考
例题一:可凑成的最大花束数
思路:利用答案二分是最常考的一种方式,假设可以打包成每组x个花束,那么我们就能得知x*k<=sum的,我们只需要将所有的花相加得到sum,再判断其和x,k的关系即可,若是x*k<=sum,则表示x还能取得更多,更新l=mid,若是x*k>=sum,表示不能够满足,更新r=mid。
代码实现
#include <iostream>
using namespace std;
using ll = long long;
const int N = 200001;
ll n,k,a[N];
bool check(ll x){
//检查x*k和res的关系
ll res=0;
for(int i=1;i<=n;i++){
res+=min(a[i],x);
}
return res/k>=x;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>k;
ll sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];sum+=a[i];
}
ll l=0,r=sum+1;
ll mid=0;
while((l+1)!=r){
mid=(r+l)>>1;
if(check(mid)){
l=mid;
}else{
r=mid;
}
}
cout<<l;
return 0;
}
例题二:最大通过数
思路:单纯用贪心算法无法同时考虑两个数组,观察发现我们只需要知道左入口通关的任务数x和右入口通关的任务数y,寻找min(x+y)即可,循环一次就加一次耗费时间太长,由于只要计算可以到达的最大卡关之和不需要考虑具体的xy,所以我们可以通过前缀和求x和y,再通过定x用二分法求y的方式进行查找最优x+y。
代码实现:
#include <iostream>
using namespace std;
using ll = long long;
const int N = 200001;
ll n,m,k,a[N],b[N];
bool check(ll x){
//x代表一共通过的任务
ll res=1e10+1; //表示可以通过的最小的能源
int j=0;//右入口
for(int i=0;i<=n;i++){
j=x-i;
if(j<0)break;
if(j>m)continue;
else res=min(res,(a[i]+b[j]));
}
if(res>k)return true;
else{
return false;
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
cin>>a[i];a[i]+=a[i-1];
}
for(int i=1;i<=m;i++){
cin>>b[i];b[i]+=b[i-1];
}
ll l=0,r=n+m+1;
while(l+1<r){
ll mid=(r+l)>>1;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
cout<<l;
return 0;
}