目录
题目描述
请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
输入格式:
输入第一行有两个数,第一个数为数组长度n(≤10^6),第二个数为需要查找的数。
接下来有n个整数,以空格或换行符分隔。
输出格式:
输出待查找的数的位置。
输入样例:
5 4 1 2 4 4 5
输出样例:
3
样例解释:
有5个数,查找4出现的位置,4第一次出现在第3个位置,所以输出3。
解题思路
二分需要使用在有单调性的数据中,单调递增或单调递减
使用二分求解需要定义查找规则,此题要查找某个数,所以规则是与要查找的数比大小。
举个例子:
根据题目输入样例
5 4
1 2 4 4 5需要查找4,最先出现的位置
序列为1 2 4 4 5存在sz数组中下标从1开始到n结束
第一次二分
左指针l=1
右指针r=n
中间值mid=(n+1)/2=3
对比发现
sz[mid]是4与4相等,由于最先出现的位置肯定靠左所以将右区间往左移动到mid,此时r=mid
第二次二分
mid=(l+r)/2=(1+3)/2=2
对比发现
sz[mid]是2比4小,可以得出要找的值在2的右边,所以左区间往右移动此时l=mid+1
此时l==r都指向3所以3所在下标的元素sz[3]即为第一个要查找的m的值4下标即为3
详细代码(使用自带函数):
lower_bound查找数组中第一个小于等于某个数的元素的地址
减去数组的开始地址后即要查找元素的下标
#include<iostream> using namespace std; const int N=1e6+7; int sz[N]; int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) { cin>>sz[i]; } cout<<lower_bound(sz+1,sz+1+n,m)-sz; }
详细代码(手写二分):
#include<iostream> using namespace std; const int N=1e6+7; int sz[N]; int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) { cin>>sz[i];//存储数组下标从1开始 } int l=1,r=n;//左端点最小为1,右端点最大为n,因为下标为1到n while(l<r)//如果l>=r则说明已经找到答案了 { int mid=(l+r)/2;//查找中间值,假设为可能值,可以用mid=(l+r)>>1; if(sz[mid]<m)//如果查找到的值小于需要查找的值,说明答案靠右,因为要查找的值需要变大一些,左区间往右靠。 { l=mid+1; } else//如果查找到的值大于需要查找的值,说明答案靠左,因为要查找的值需要变小一些,右区间往左靠。 { r=mid; } } if(sz[l]>=m)//找到了输出这个数的下标。 { cout<<l; } else//如果没找到输出n长度+1 { cout<<n+1; } }
>>位运算
感兴趣的可以看
>>是位运算与<<的方向相反
举个例子
2>>1的意思是2/2=1
即把2看作二进制10
将整体往右移动一位
即10移动变为1
1的十进制为1
2<<1的意思是2*2=4
即把2看作二进制10
将整体往左移动一位
即10移动变为100
100的十进制为4