算法-基础算法-二分
原理
对于一个数列(arr)来说,如果其具有单调性(单调递增,单调递减),那么可以利用这种单调性来快速的查找一个目标数字(具体过程如下):
1:
因为具有单调性,所以对于数列中的任意一个数字x,必然可以将数列分为大于x,和小于等于x两部分。
2:
利用上述性质,我们每次取当前目标数列(最开始是arr本身,后面可能是从下标l开始到r结束的arr的一部分)的下标的中间值(下标为(l+r)/2 ; (l+r+1)/2 ))所对应的值mid
3:
每次用mid对应的值和查找的目标值x比较,如果mid>x就将目标数列更新为从下标从l=l开始到r=mid(等号右边是新的值,l=l代表与上一次一致)因为目标值一定在更新后的数列里边,这样我们就缩小了范围,一直缩小直到l==r,这时候l和r对应的就是x(目标值)-----这里讲解的事单调递增的情况
整数二分
整数二分针对的是离散的数据,比如说一个数列3,1,2,4,6就是离散的(但这个数列不具有单调性)
关键点:
1.关注边界条件
取等于条件的是符合条件的,可以进行单独讨论
不取等于条件的时候需要+1(l)-1®
2.合法性检查
检查最后的结果是否符合要求
3.两种情况的讨论
要找==“第一个 ≥x”的位置,mid = (l + r) / 2 是向下取整,是安全的;
但如果要找“最后一个 ≤x”的位置,应该用 mid = (l + r + 1) / 2 防止死循环。(也就是说如果符合条件更新的是l==就需要用(l+r+1)/2,更新r用(l+r)/2)
模板1:找第一个 ≥x 的位置
const int N=1e5+10;
int arr[N];//假设是递增序列
int n;
int search(int x)
{
int l=0,r=n,mid=0;
while(l<r)//l到r的开区间
{
mid=(l+r)/2;//边界条件
if(arr[mid]>=x)
{
r=mid;
}
else//小于的情况,不应该包含mid所在
{
l=mid+1;
}
}
if(x==arr[r])return r;
return -1;//没找到
}
py代码
# 整数
arr=[]
x=int(input("请输入一个整数:"))
def binary_search_leftmost(arr, x):
low, high = 0, len(arr) - 1
while low < high:
mid = (low + high) // 2
if arr[mid] >= x:
high = mid
else:
low = mid + 1
return low if arr[low] == x else -1
实数二分
实数针对的是一个区间,比如说[1,5],要寻找其上满足某函数的一个数,下面以x的开根号为例
模版二y=sqrt(x)
int sqrt_search(double x,double accur,double l,double r)
{
double mid=0;
while((r-l)>accur)
{
double mid =(l+r)/2;
if(mid*mid<n)l=mid;
else r=mid
}
return mid;
}
py
#实数二分
def bin_search_sqrt(x):
low=0
high=x
mid=(low+high)/2
while abs(mid**2-x)>1e-6:
if mid**2<x:
low=mid
else:
high=mid
mid=(low+high)/2
return mid
答案二分
关键:
check函数
通过观察我们发现,每个二分都存在一个判断的条件来调节l,r。
check函数是针对mid的函数,就像整数二分的==>==号。
check的作用是查看当前元素是否满足某个性质,而查找的目标元素一定是满足这个性质(check函数)的边界元素。
二分答案oiwiki------>>>从答案可能的取值出发考虑问题,而不是进行过程的模拟
解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。若满足单调性,则满足使用二分法的条件。把这里的枚举换成二分,就变成了「二分答案」。
注意类似最大的最小值等问题
枚举:枚举所有答案可能的取值,直到符合条件的取值---------->二分:将可能的答案区间进行二分,通过判断mid来确定范围
例题:
蓝桥云课肖恩的苹果树
#include<bits/stdc++.h>
using namespace std;
int n,m;
bool check(int x,int a[] ){
int last=0,current=1;
int res=1;
for(;current<n;current++){
if(a[current]-a[last]>=x){
last=current;
if(++res==m)return true;
}
else{
;
}
}
return false;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
int a[n];
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a, a + n);
int l=1;
int r=a[n-1]-a[0];
while(l<r){
int mid=l+r+1>>1;
if(check(mid,a))l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
二分算法详解与应用






