可以对有序连续的空间进行二分查找。
lower_bound默认查找大于等于某值所在连续空间的位置。
upper_bound默认查找大于某值所在连续空间的位置。
一般连续空间就是数组或者STL中的vector
template
inline _ForwardIterator
lower_bound(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __val, _Compare __comp);
template
inline _ForwardIterator
lower_bound(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __val);
/*--------------------------------------------------*/
/*--------------------------------------------------*/
template
inline _ForwardIterator
upper_bound(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __val, _Compare __comp);
template
inline _ForwardIterator
upper_bound(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __val);
上面是两个函数的声明, 其中注意重载中有一个类模板可以提供比较函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjbaQ3do-1680014417298)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230328214428829.png)]
返回值是返回迭代器。
void Func1() {
std::vector vec{ 1,3,5,7,9 };
auto iter=std::lower_bound(vec.begin(), vec.end(), 3);
std::cout << iter - vec.begin() << " " << (*iter) << std::endl;
std::cout << vec.end() - vec.begin() << std::endl;
auto iter2 = std::upper_bound(vec.begin(), vec.end(), 3);
std::cout << iter2 - vec.begin() << " " << *iter2 << std::endl;
}
输出结果:
1 3
5
2 5
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-blxcRXwX-1680014417300)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230328214935558.png)]
在平时比较时,常用到less(小到大排序)与greater(大到小排序),它们重载()运算符使得两个值可以进行比较, 在使用lower_bound或upper_bound时也常常用到。
/// One of the @link comparison_functors comparison functors@endlink.
template
struct greater : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x > __y; }
};
/// One of the @link comparison_functors comparison functors@endlink.
template
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
对于自定义类型也是可以自己重载<, >符号达到比较的效果。
void Func2() {
std::vector vec = { 2,4,6,8,10 };
auto iter1 = std::lower_bound(vec.begin(), vec.end(), 4, std::less());
std::cout << iter1 - vec.begin() << " " << *iter1 << std::endl;
auto iter2 = std::upper_bound(vec.begin(), vec.end(), 4, std::less());
std::cout << iter2 - vec.begin() << " " << *iter2 << std::endl;
auto iter3 = std::lower_bound(vec.begin(), vec.end(), 4, std::greater());
if (iter3==vec.end()) {
/*return;*/这里找不到
}
//std::cout << iter3 - vec.begin() << " " << *iter3 << std::endl;
//大到小排序
std::vector vec2 = { 5,4,3,2,1 };
auto iter4 = std::lower_bound(vec2.begin(), vec2.end(), 2, std::greater());
std::cout << iter4 - vec2.begin() << " " << *iter4 << std::endl; //iter4-vec2.begin()得到的是该地址的下标
}
1 4
2 6
3 2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5W2FGcne-1680014417300)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230328220253481.png)]
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址(iter),不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址(iter),不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小greater()的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
首先,大家都说用这两个函数之前必须是在有序的数组中,但是都没有说明为什么是在有序的数组,因为他们的底层实现是二分查找(这个也是我在别人的题解的时候知道的)我们先来看lower_bound(后有lower_bound的底层实现,更容易理解lower_bound的原理)
template
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val);
template
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
函数作用:
前提是有序的情况下,lower_bound返回指向第一个值不小于val的位置,也就是返回第一个大于等于val值的位置。(通过二分查找)
参数、返回值含义:
原型2的第四个参数(比较器)可以用来排序,再来获取大于等于val值的迭代器。
void Func4() {
class Student {
public:
int id;
std::string name;
Student(int id_,std::string name_)
:id(id_)
,name(name_)
{}
};
std::vector vec{ {1,"苹果"},{2,"香蕉"},{3,"梨子"} };
auto iter = std::lower_bound(vec.begin(), vec.end(), Student(0, "西瓜"), [](const Student& s1, const Student& s2)->bool {
return s1.id < s2.id;
});
std::cout << iter - vec.begin() << " " << (*iter).id<<" "<<(*iter).name << std::endl;
}
输出结果:
0 1 苹果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LlEhRzgt-1680014417301)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230328223150019.png)]
类重载()就是函数对象或者仿函数。
struct CompareV
{
bool operator() (const Stu& s1, const Stu& s2)// 排名升序
{
return s1._num < s2._num;
}
};
int lower_bound(vector& nums, int x) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int mid = left +(right - left) / 2;
if (x > nums[mid]) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
return left;
}
用法和上面类似。只是把lower_bound的大于等于换成大于。仿函数等等全是相同的用法
int upper_bound(vector& nums, int x) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int mid = left +(right - left) / 2;
if (x >= nums[mid]) { //这里是大于等于
left = mid + 1;
}
else {
right = mid - 1;
}
}
return left;
}
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。
lower_bound()的返回值是第一个大于等于 target 的值的地址,用这个地址减去first,得到的就是第一个大于等于target的值的下标。
在数组中:
int n = lower_bound(a , a + x , target) - a;//x 为数组的长度
在vector中:
int n=std::lower_bound(vec.begin(),vec.end(),target)-vec.begin()
class Solution {
public:
int searchInsert(vector& nums, int target) {
return lower_bound(nums.begin(),nums.end(),target)-nums.begin();
}
};
普通方法,二分:
int l = 0,r = nums.size() - 1;
while(l <= r)
{
int mid = l +( (r-l) >> 1);
if(nums[mid] == target)
return mid;
else if(nums[mid] > target)
r = mid-1;
else if(nums[mid] < target)
l = mid + 1;
}
return l;