1.为什么要学习二分查找:
因为二分查找的思路虽然简单,但是在细节上往往容易出错,所以写一篇文章帮助自己复习巩固二分查找。
2.二分查找的目标:
3.思路:
1) 我们可以假设一开始整个数组都是灰色的。
2)设立两个指针,一个为 l = -1(从左往右),一个为 r = n(由右往左),从左边到 l 指针的范围为蓝色区域,从右边到 r 指针的范围为红色区域( l 初始值应该为左边界值 -1,r 初始值应为右边界值 +1)。
3)如图中左边的二分查找的伪代码,使用循环最终实现2)中图的结果。m为中间的指针(是分界线),我们以蓝色区域举例,如果m在满足蓝色区域条件(IsBlue),那么 l 更新为 m,否则m在红色区域,r 更新为 m。经过多次循环就能得到结果,一般来说,是需要再进行一次判断和题目要求才看是否return l or r(也有可能是-1)。
4)疑惑:为什么 l=-1,r=n?
这样可以避免出现都为蓝色或者红色区域的情况。
5)举例各查找要求中IsBlue的条件和return的指针情况。可以看看是否完全理解。
4.示例代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> v(100005);
int n,q;
int BinarySerach1(int x){//二分查找的代码1
int l=-1,r=n,m;
while(l+1!=r){
m=(l+r)/2;
if(v[m]<x){//if是IsBlue的函数
l=m;
}else{
r=m;
}
}
if(v[r]==x){//注意这里的判断数组的下标是要和返回的r一致
return r;
}
return -1;
}
int BinarySerach2(int x){//二分查找的代码2
int l=-1,r=n,m;
while(l+1!=r){
m=l+r >> 1;//>> 1是右移,除2
if(v[m]<=x){
l=m;
}else{
r=m;
}
}
if(v[l]==x){注意这里的判断数组的下标是要和返回的l一致
return l;
}
return -1;
}
int main(){
cin >> n >> q;
for(int i=0;i<n;i++){
cin >> v[i];
}
while(q--){
int t;
cin >> t;
int index1=BinarySerach1(t);
int index2=BinarySerach2(t);
cout << index1 << ' ' << index2 << '\n';
}
return 0;
}
5.注意点:
要在递增或者递减前提下,如果没有说明要先排序。
可能会出现没有红蓝区域的情况,这个时候需要对索引进行判断,查看是否在合理区间内[0,n)
6.复习视频链接:(如果还不懂的话)
7.拓展:C++ STL的二分查找(不要忘记先排序哦)
1)binary_search(a.begin(), a.end(), val)函数,返回的是一个bool类型,val是你想查找的值,如果找到了返回true,没有则false
2)lower_bound( )和upper_bound( ),一个有包含等于,可以通过upper(上界)来记忆有没有等于
lower_bound( begin,end,num):二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
可以通过*it(返回的地址)来得到值,可以同时使用upper和lower来确认这个值的个数。如
int target = -(a[i] + b[j]);
auto lower = lower_bound(c.begin(), c.end(), target);
auto upper = upper_bound(c.begin(), c.end(), target);
count += (upper - lower);