二分就是从中割开将其分为两半,分别处理。
题目背景:给定n个单调不下降的非负整数,m次查询,每次查询给定数字在数列里第一次出现的序号,从1开始编号。
核心思路二分:由于这是一个单调的不下降序列,所以如果当前的数比要找的数k小,那么当前这个数前面的数都比这个数小所以就没有必要去判断了,而为了次数更少我们就可以直接总数列的中间开始判断,如果中间的数mid比他小,那么这个数就一定在mid—最右中间,然后在判断mid和最右的中间midmid,以此类推。
举个栗子:有a数组1 3 3 3 5 7 9 11 13 15 15;从中查询k=5;先判断中间mid为a(1+11)/2=a6比k大所以我们查询的数一定在a1——a6中,然后a1——a5的中间为mid为a(1+5)/2:a3=3,3比5小所以我们要求的一定在a4——a5中——>mid=a(4+5)/2;a4=3还是比k小——>下一个mid=a(5+5)/2=5与k相等,此时就得到我们想要的东西了。
首先刚开始的情况如图1
此时的mid比k大所以我们可以肯定k在L——mid之间,此时我们将R=mid-1;因为mid不等于k所以mid也不用判断了。
如图2
此时的mid比k小且不等于k所以我们就不用考虑mid及mid之前,所以将L=mid+1;由于此时L的位置是4,R的位置是5所以mid的位置是4.5但是由于计算机的整数会自动去掉小数点后的东西,所以mid的位置是4.
如图3![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8620ba39444c81c37033214f98f27b94.png#pic_center)
此时mid依然比k小所以继续将L=mid+1;这样的话L和R就同时处在了k的位置上此时mid就是k。
如图4
这个时候L不小于R了就可以退出二分返回位置,如果该位置上的值与所查询值相等就输出位置,不相等输出-1.
注意!!只能在单调的数列中使用二分查找。
下面是代码部份
首先是单纯的二分部份
int l=0,r=n;
int mid=(l+r)/2;
while(l<r)
{
mid=(l+r)/2;//更新mid的值
if(a[mid]>=k)r=mid+1;//有的情况要加1有的情况不加1看情况决定加不加1.
else l=mid+1;
}
然后附上ac代码
#include<cstdio>
using namespace std;
int n,m,q,a[1000005];
int find(int x) //二分查找
{
int l=1,r=n;
while (l<r)
{
int mid=l+(r-l)/2;
if (a[mid]>=x) r=mid;
else l=mid+1;
}
if (a[l]==x) return l; //找都了就输出他的位置
else return -1; // 没找到输出-1
}
int main()
{
scanf("%d %d",&n,&m); //读入
for (int i=1 ; i<=n ; i++)
scanf("%d",&a[i]); //还是读入
for (int i=1 ; i<=m ; i++)
{
scanf("%d",&q);
int ans=find(q); //看看查找的结果
printf("%d ",ans); //输出
}
return 0;
}