二分: 整数二分和浮点数二分
一定是单调的才可以二分。
整数二分:
模板一:
找中间点
判断是否满足check函数(),此函数需要自己根据题意调整,假设红色区域为满足check函数,说明mid在红色区域内,此时要在mid到r之间找,l=mid,否则r=mid-1。
由于此时l=mid 所以中间点应为(l+r+1)/2
模板二:
中间点 m i d = ( l + r ) / 2 mid=(l+r)/ 2 mid=(l+r)/2
如果mid满足check函数,比如说绿色区域,此时更新区间为l到mid 即r = mid ,否则 l = mid + 1。
注意::如何选择模板,我们可以先看check函数满足时,如果更新区间时l=mid 此时需要将中心点(l+r+1)/2。所以选择模板一喽!
为什么要l+r+1呢 对于mid=(l+r)/2这个等式,如果l=r-1,即区间只有两个数,此时mid=l,更新完l=mid时陷入循环。
来个小题感受一下check函数的建立过程:::
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
数据范围
1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
首先我们从122334里面找3,我们让mid=(l+r)/2=0+5)/2=2,q[mid]=2如果我让check函数为小于等于3即q[mid]<=3,左边区域不满足,此时在右边寻找,此时l=mid 用模版一。
#include<iostream>
#include<stdio.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int n,q,k;
scanf("%d%d",&n,&q);
for(int i=0; i<n; i++) scanf("%d",&a[i]);
while(q--)
{
scanf("%d",&k);
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(a[mid]>=k)r=mid;
else l=mid+1;
}
if(a[l]!=k) cout<<"-1 -1"<<endl;
else
{
cout<<r<<" ";
int r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<=k)l=mid;
else r=mid-1;
}
cout<<r<<endl;
}
}
}
浮点数二分:
浮点数的模板比较简单,以一个题说明(数的三次方根)
给定一个浮点数n,求它的三次方根。
输入格式
共一行,包含一个浮点数n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
数据范围
−10000≤n≤10000−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
double n;
cin>>n;
double l=-100,r=100;
while(r-l>1e-8)
{
double mid=(l+r)/2;
if(mid*mid*mid<=n) l=mid;
else r=mid;
}
printf("%.6lf\n",l);
return 0;
}
说明: 当r-l足够小时,我们就假定已经找完,用公式表示为 r − l > 1 e − 6 r-l>1e-6 r−l>1e−6 ,此时有个精度问题如果要求保留六位小数则 r − l > 1 e − 8 r-l>1e-8 r−l>1e−8 ,总要比保留的位数多两位。
二分模版
基础版本
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0,r = nums.size() -1;
while( l <= r){ //如果这里是留l < r 说明是左闭右开的区间
int mid = (l + r) >> 1;
if( nums[mid] < target) l = mid +1;
else if(nums[mid] > target) r = mid -1; //如果是l < r 这里的r = mid
else if (nums[mid] == target) return mid;
}
return -1;//(当留l > r 的时候,说明没有找到,返回-1)
}
};