Definition, usage and difference of C++ upper_bound() and lower_bound() (used in binary search)

C++ upper_bound() and lower_bound() are very useful tools for binary search problems. If you use them proficiently, you don’t have to worry about the boundaries of binary search (you don’t need to reinvent the wheel)

1. Call method

There are two ways to call upper_bound:

template <class ForwardIterator, class T>  
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare>  
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

Notice:

  1. The first two parameters are ForwardIterator type (this is generally easier to satisfy, all kinds of RandomAccessIterator are satisfied, and the most common sorting of vector, the iterator of vector is RandomAccessIterator, see: the relationship between various iterators
  2. If you use a custom comparison function, the object is passed in instead of the class name ! ! This is different from building some ordered data structures, such as priority_queue, map, set, etc. Generally, the class name is passed in instead of an object! (For example, the comp in sort can be passed to greater() instead of greater, see below), which involves the knowledge of function objects , you can refer to: What is a C++ function object (Function Object)?
    For building priority_queue, the class name is passed in here, not an object!
template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

The same is true for lower_bound:

template <class ForwardIterator, class T>  
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare>  
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

2. Definition and difference between upper_bound() and lower_bound()

definition

The introduction to upper_bound() and lower_bound() in the document is worth reading in depth:
the following is from the official document:
upper_bound()

  1. Returns a pointer to the first element in the range [first, last) such that value < element (or comp(value, element)) is true (i.e. a strictly large iterator ), or last if no such element is found.
  2. [first,last) must be partitioned according to the expression !(value < element) or !comp(value, element), that is, all elements whose expression is true must be before all elements whose expression is false! A fully sorted [first, last) meets this condition.
  3. If there is no custom comparison function, use operator< to compare elements, and if you define a custom comparison function, use comp to compare elements

lower_bound()

  1. Returns an iterator pointing to the first element in the range [first, last) such that the element does not satisfy element<value (or comp(element, value)), (that is, greater than or equal to) , if no such element is found, Then return last.

  2. [first,last) must be partitioned relative to the expression element < value (or comp(element, value)), that is, all elements for which the expression is true must precede all elements for which the expression is false. A fully sorted [first, last) meets this condition.

  3. If there is no custom comparison function, use operator< to compare elements, and if you define a custom comparison function, use comp to compare elements

2.1 lower_bound:

Returns an iterator pointing to the first element in the range [first, last) that does not satisfy element < value (or comp(element, value)), (i.e. greater or equal to), or last if no such element is found.

The value here is the val in the template, and the element is the element to be compared

Note: In the C++ comparison process, A less than B (A < B), means that if sorted according to "small to large" (can be customized), A should be placed before B.
So here does not compare less than val is the meaning of greater or equal to val, that is, lower_bound returns [first, last) the iterator of the first element that does not satisfy element < value (or does not satisfy comp(element, value)), If none are satisfied, return last iterator

Note:
1.

The range [first, last) must be partitioned with respect to the expression element < value (or comp(element, value)), i.e., all elements for which the expression is true must precede all elements for which the expression is false. A fully-sorted range meets this criterion.

That is, if you sort from big to small (greater), but use small to large and the largest value as the comparison function, you will not be able to find the first element, but return last.
Examples are as follows:

vector<int> vec = {
    
    1, 2, 3, 4, 5, 6};
// 从大到小排序
// greater必须加上()因为需要传入一个对象
sort(vec.begin(), vec.end(), greater<int>()); 
// 以默认的从小到大作为比较函数以及最大的值(此为6)作为value
auto iter = lower_bound(vec.begin(), vec.end(), 6);

The iter returned here is vec.end(), because no element satisfies element < 6.
2. comp(element, value), the second parameter is value ! !

Example: For a custom comparison function:

#include <iostream>     // std::cout
#include <algorithm>    // std::lower_bound
#include <vector>       // std::vector
using namespace std;
//以普通函数的方式定义查找规则
bool mycomp(int i,int j) {
    
     return i>j; }

//以函数对象的形式定义查找规则
class mycomp2 {
    
    
public:
    bool operator()(const int& i, const int& j) {
    
    
        return i > j;
    }
};

int main() {
    
    
    int a[5] = {
    
     1,2,3,4,5 };
    //从 a 数组中找到第一个不小于 3 的元素
    int *p = lower_bound(a, a + 5, 3);
    cout << "*p = " << *p << endl;

    vector<int> myvector{
    
     4,8, 9, 5,3,1,7, 2 };
    //根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素
    vector<int>::iterator iter = lower_bound(myvector.begin(), myvector.end(),3,mycomp2());
    cout << "*iter = " << *iter;
    return 0;
}

Output result:

*p = 3
*iter = 3

In the second example, all elements before 3 are greater than 3, and all elements after are less than 3

2.2 upper_bound()

Points to note:

  1. comp (value, element), the first parameter is value ! !
    Examples are as follows:
#include <iostream>     // std::cout
#include <algorithm>    // std::upper_bound
#include <vector>       // std::vector
using namespace std;
//以普通函数的方式定义查找规则
bool mycomp(int i, int j) {
    
     return i > j; }
//以函数对象的形式定义查找规则
class mycomp2 {
    
    
public:
    bool operator()(const int& i, const int& j) {
    
    
        return i > j;
    }
};
int main() {
    
    
    int a[5] = {
    
     1,2,3,4,5 };
    //从 a 数组中找到第一个大于 3 的元素
    int *p = upper_bound(a, a + 5, 3);
    cout << "*p = " << *p << endl;
    vector<int> myvector{
    
     4,5,3,1,2 };
    //根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素
    vector<int>::iterator iter = upper_bound(myvector.begin(), myvector.end(), 3, mycomp2());
    cout << "*iter = " << *iter;
    return 0;
}

output:

*p = 4
*iter = 1

In actual situations, what we encounter the most is given an array sorted from small to large, find the first position greater than a certain element or greater than or equal to a certain element, and find the first position greater than a certain element The position is upper_bound(), and the position greater than or equal to a certain element is lower_bound() . For more complicated cases, you need to use a custom comparison function.

References:

  1. https://en.cppreference.com/w/cpp/algorithm/upper_bound
  2. https://en.cppreference.com/w/cpp/algorithm/lower_bound
  3. The code case part comes from the C language Chinese website

Guess you like

Origin blog.csdn.net/Sansipi/article/details/127895961