二分是一种在数学上求根的范围的方法,一般进行折半查验,最后得出其范围。在算法学习中,一般有整数二分和实数二分。在二分学习中应该注意边界以及左右区间的开闭情况,以防止出现死循环。
我们用一个例题来讲解整数二分的模板,见链接
https://www.acwing.com/problem/content/791/
我们先看二分代码的模板
while(left<right)
{
int ans;
int mid = left+(right-left)/2;
if(check(mid)){
ans = mid;
...
}
else{
'''
}
}
mid的取值有很多种写法但都有一定缺陷:
总的来说以下几种:
mid = (l+r)/2 适用于l>=0,r>=0;l+r无溢出。
mid = (l+r)>>1适用于l+r无溢出
mid =left+(right-left)/2,适用于l-r无溢出
综合来说(l+r)>>1更好
如何解决该问题,我们只要每次二分到该答案并记录下来就可以了
见代码:
#include <iostream>
using namespace std;
int n,q;
int a[1000085];
int k;
int main()
{
cin>>n;
cin>>q;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<q;i++)
{
cin>>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<<l<<" ";
int l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(a[mid]<=k) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
return 0;
}
关键就在于a[mid]>=k,如果大于等于那么这个k一定在mid的左边,那么左边(l)不变,改变右边
则r=mid,反之就是l=mid+1
代码执行完后r=l,就是我们查找到答案在的位置;在这个题目中我们还需要把两边位置输出,所有就要用a[l]和k进行比较,所以我们后面还有一串代码就是将右边也进行一次二分查找;
在查找右边代码的时候我们需要注意l+r+1;+1是为了死循环,因为后面我们的r更新是-1了;