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);
知らせ:
- 最初の 2 つのパラメータは ForwardIterator タイプです (これは一般に満たすのが簡単で、あらゆる種類の RandomAccessIterator が満たされます。ベクトルの最も一般的な並べ替えであるベクトルの反復子は RandomAccessIterator です。さまざまな反復子の関係を参照してください)
- カスタム比較関数を使用する場合、クラス名の代わりにオブジェクトが渡されます。!これは、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()
- value < element (または comp(value, element)) が true (つまり、厳密に大きな反復子)となる範囲 [first, last) の最初の要素へのポインタを返します。そのような要素が見つからない場合は last を返します。
- [first,last) は、式 !(value < element) または !comp(value, element) に従って分割する必要があります。つまり、式が true であるすべての要素は、式が false であるすべての要素の前になければなりません。完全にソートされた [first, last) はこの条件を満たします。
- カスタム比較関数がない場合は、operator< を使用して要素を比較し、カスタム比較関数を定義している場合は、comp を使用して要素を比較します
lower_bound()
-
要素がelement<value (または comp(element, value)) を満たさない (つまり、以上の) 条件を満たさない場合、範囲 [first, last) 内の最初の要素を指す反復子を返します。要素が見つかった場合は最後に戻ります。
-
[first,last) は、式要素 < 値 (または comp(element, value)) を基準にして分割する必要があります。つまり、式が true であるすべての要素が、式が false であるすべての要素よりも前にある必要があります。完全にソートされた [first, last) はこの条件を満たします。
-
カスタム比較関数がない場合は、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()
注意点:
- 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() です。より複雑な場合には、カスタム比較関数を使用する必要があります。
参考文献:
- https://en.cppreference.com/w/cpp/algorithm/upper_bound
- https://en.cppreference.com/w/cpp/algorithm/ lower_bound
- コードケースの部分は C 言語の中国語 Web サイトから取得しています。