C++ upper_bound() と lower_bound() の定義、使用法、違い (二分探索で使用)

C++ の upper_bound() と lower_bound() は、二分探索の問題に対して非常に便利なツールです。これらを上手に使用すれば、二分探索の境界について心配する必要はありません (車輪を再発明する必要はありません)。

1.呼び出し方法

upper_bound を呼び出すには 2 つの方法があります。

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);

知らせ:

  1. 最初の 2 つのパラメータは ForwardIterator タイプです (これは一般に満たすのが簡単で、あらゆる種類の RandomAccessIterator が満たされます。ベクトルの最も一般的な並べ替えであるベクトルの反復子は RandomAccessIterator です。さまざまな反復子の関係を参照してください)
  2. カスタム比較関数を使用する場合、クラス名の代わりにオブジェクトが渡されますこれは、priority_queue、map、set などの順序付けされたデータ構造の構築とは異なります。一般に、オブジェクトの代わりにクラス名が渡されます。(たとえば、sort の comp は、greater ではなく、greater() に渡すことができます。以下を参照してください)。これには、関数オブジェクトの知識が含まれます。「C++ 関数オブジェクト (関数オブジェクト) とは何ですか?」を参照してください。
    priority_queue を構築する場合、ここではオブジェクトではなくクラス名が渡されます。
template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

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. upper_bound() と lower_bound() の定義と違い

意味

このドキュメントの upper_bound() と lower_bound() の紹介は、深く読む価値があります。
以下は公式ドキュメントからのものです:
upper_bound()

  1. value < element (または comp(value, element)) が true (つまり、厳密に大きな反復子)となる範囲 [first, last) の最初の要素へのポインタを返します。そのような要素が見つからない場合は last を返します。
  2. [first,last) は、式 !(value < element) または !comp(value, element) に従って分割する必要があります。つまり、式が true であるすべての要素は、式が false であるすべての要素の前になければなりません。完全にソートされた [first, last) はこの条件を満たします。
  3. カスタム比較関数がない場合は、operator< を使用して要素を比較し、カスタム比較関数を定義している場合は、comp を使用して要素を比較します

lower_bound()

  1. 要素がelement<value (または comp(element, value)) を満たさない (つまり、以上の) 条件を満たさない場合、範囲 [first, last) 内の最初の要素を指す反復子を返します。要素が見つかった場合は最後に戻ります。

  2. [first,last) は、式要素 < 値 (または comp(element, value)) を基準にして分割する必要があります。つまり、式が true であるすべての要素が、式が false であるすべての要素よりも前にある必要があります。完全にソートされた [first, last) はこの条件を満たします。

  3. カスタム比較関数がない場合は、operator< を使用して要素を比較し、カスタム比較関数を定義している場合は、comp を使用して要素を比較します

2.1 下限:

要素 < 値 (または comp(element, value)) (つまり、以上) を満たさない範囲 [first, last) 内の最初の要素を指す反復子を返します。そのような要素が見つからない場合は last を返します。

ここでの値はテンプレート内の val であり、要素は比較される要素です

注: C++ の比較プロセスでは、A が B より小さい (A < B) ということは、「小さいものから大きいもの」に従って並べ替えた場合 (カスタマイズ可能)、A を B の前に配置する必要があることを意味します
したがって、ここでの「val 未満の比較」は val 以上の意味です。つまり、lower_bound は、要素 < 値を満たさない (または comp(element, を満たさない) 最初の要素の反復子を [first, last) 返します。 value))、どれも満たされない場合は、最後のイテレータを返します

注:
1.

範囲 [first, last) は、式要素 < 値 (または comp(element, value)) に関して分割する必要があります。つまり、式が true であるすべての要素が、式が false であるすべての要素よりも前になければなりません。完全にソートされた範囲はこの基準を満たします。

つまり、大きいものから小さいもの (大きいもの) に並べ替えるときに、比較関数として小さいものから大きいもの、および最大の値を使用すると、最初の要素を見つけることはできず、最後の要素が返されます。
例は次のとおりです。

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);

element < 6 を満たす要素がないため、ここで返される iter は vec.end() です。
2. comp(element, value)、2 番目のパラメータは value です

例: カスタム比較関数の場合:

#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;
}

出力結果:

*p = 3
*iter = 3

2 番目の例では、3 より前のすべての要素は 3 より大きく、その後のすべての要素は 3 未満です。

2.2 upper_bound()

注意点:

  1. comp (値, 要素)、最初のパラメータは value です
    例は次のとおりです。
#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;
}

出力:

*p = 4
*iter = 1

実際の状況で最もよく遭遇するのは、配列が小さいものから大きいものへとソートされ、特定の要素より大きい、または特定の要素以上の最初の位置を見つけ、特定の要素より大きい最初の位置を見つけることです。は upper_bound() で、特定の要素以上の位置は lower_bound() です。より複雑な場合には、カスタム比較関数を使用する必要があります。

参考文献:

  1. https://en.cppreference.com/w/cpp/algorithm/upper_bound
  2. https://en.cppreference.com/w/cpp/algorithm/ lower_bound
  3. コードケースの部分は C 言語の中国語 Web サイトから取得しています。

おすすめ

転載: blog.csdn.net/Sansipi/article/details/127895961