本课主要介绍容器部分里面的二分查找函数。涉及的函数有 3 个,这 3 个函数的强两个输入参数都和迭代器有关,或者说参数是可以迭代的,而第三个参数则是你要查找的值。
1. binary_search
binary_search 的返回结果是 bool 值,如果找得到,就返回 true,找不到,就返回 false。
我们抛开那些很玄乎的术语,直接上代码。先用一个普通数组作为例子。
int x[1001],n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>x[i];
sort(x+1,x+1+n); // 排序,数组必须要有序,binary_search 是不负责排序的
int t;
while(m--){ // 表示有 m 次查询
cin>>t;
if(binary_search(x+1,x+1+n,t))
printf("找得到数字 %d\n",t);
else
printf("找不到数字 %d\n",t);
}
第二个例子是基于vector的二分查找。上面的例子做个平移吧,逻辑不变。
vector <int> x;
int n,m;
cin>>n>>m;
int t;
for(int i=1;i<=n;i++){
cin>>t;
x.push_back();
}
sort(x.begin(),x.end()); // 排序,数组必须要有序,binary_search 是不负责排序的
while(m--){ // 表示有 m 次查询
cin>>t;
if(binary_search(x.begin(),x.end(),t))
printf("找得到数字 %d\n",t);
else
printf("找不到数字 %d\n",t);
}
binary_search 的前两个参数还可以是指向 vector 元素的迭代器,那么就是在这两个迭代器范围内查找了。
2. lower_bound
lower_bound 函数输入的参数和 binary_search 类似,只不过,它返回的是第一个大于等于查找值的地址(迭代器)。如果找不到,返回的是第二个输入参数的指针,记住,还是左闭右开原则。
int x[1001],n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>x[i];
sort(x+1,x+1+n); // 排序,数组必须要有序,binary_search 是不负责排序的
int t;
int *p;
while(m--){ // 表示有 m 次查询
cin>>t;
p = lower_bound(x+1,x+1+n,t)
if(p!=x+1+n)
printf("数组中第一个大于等于 %d 的数的下标是 %d\n",t,p-x);
else
printf("找不到数字 %d\n",t);
}
如果是针对 vector,上述的代码要改成
vector <int> x;
int n,m;
cin>>n>>m;
int t;
for(int i=1;i<=n;i++){
cin>>t;
x.push_back(t);
}
sort(x.begin(),x.end()); // 排序,数组必须要有序,binary_search 是不负责排序的
vector <int>:: iterator it;
while(m--){ // 表示有 m 次查询
cin>>t;
it = lower_bound(x.begin(),x.end(),t)
if(it!=x.end())
printf("动态数组中第一个大于等于 %d 的数的下标是 %d\n",t, it-x.begin());
else
printf("动态数组中找不到数字 %d\n",t);
}
3. upper_bound
upper_bound 和 lower_bound 非常类似,区别是函数返回的是第一个大于查找值的地址(迭代器)。
4. 重要补充说明
- 对于 set 和 map 来说,有成员函数 upper_bound 和 lower_bound 的,所以,不应该用外面的的那个 upper_bound 和 lower_bound。
map <string ,int> m;
int n,t;
cin>>n;
string a;
for(int i=1;i<=n;i++){
cin>>a>>t;
m[a] = t;
}
map <string ,int>::iterator it;
//it = lower_bound(m.begin(),m.end(),"123"); // 这是错误的
it = m.lower_bound("123"); // 这是正确的
cout<<it->first<<" "<<it->second;
}
对于 set 容器,可以用外面的 lower_bound 和 upper_bound,但是效率上远远没有自己的成员函数快。
set <string> s;
int n;
cin>>n;
string a;
for(int i=1;i<=n;i++){
cin>>a;
s.insert(a);
}
set <string>::iterator it;
//it = lower_bound(s.begin(),s.end(),"123"); //语法上这句也是可以的,但是效率比较低
it = s.lower_bound("123"); //这是正确的,会快很多
cout<<*it;
}
-
上面的 3 个二分查找函数都需要用到 运算符
<
。如果你的数组、动态数组存放的是结构体变量,记得要定义<
运算符。 -
定义
<
运算符 的时候,要用上const 关键字
和采用引用传参
。这是 STL 模板套用的时候需要的,否则 upper_bound 函数会出错的。
const 关键字的意思是传进去的参数是不能被修改的,只能读,不能改。引用传参意思是形参和实参是一样。表面看,这很矛盾,既然你都不打算修改参数的值了,为什么还要采用 引用传参
呢。引用传参
的好处是数据不用被复制一遍,提高了效率。你定义函数的时候加上const 关键字
和采用 引用传参
没有任何坏处,别的模板套用你的运算符函数的时候,一定能套得上。
下面举个例子,演示怎么加上 const 关键字
和 引用传参
struct Point{
int x,y;//横坐标和纵坐标
bool operator < (const Point& other) const{
return x<other.x||(x==other.x&&y<other.y);
}
// 左边的 const 表示传进去的参数 other 不能修改
// 右边的 const 表示它自己不能修改(仅仅是执行 < 的过程中不能修改)
};
第二种,就是把运算符写在结构体外面的写法了
struct Point{
int x,y;//横坐标和纵坐标
};
bool operator < (const Point& i,const Point& j){
return i.x<j.x||(i.x==j.x&&i.y<j.y);
};