プレーン配列でできるときはいつでもstd::algorithm
を使用するのが好きです。今、私には2つの疑問があります。 std::lower_bound
を使用したいとします。引数として指定した値が見つからない場合はどうなりますか?
int a[] = {1,2,3,4,5,6};
int* f = std::lower_bound(a,a+6,20);
* fを出力したときの結果は20です。
std::find
を使用しても同じことが起こります。
int a[] = {1,2,3,4,5,6};
int* f = std::find(a,a+6,20);
* fを出力したときの結果は20です。
std::lower_bound
は、バイナリ検索アルゴリズムを実装しているため、std::find
よりもパフォーマンスが優れています。配列が大きい場合、たとえば最大10要素の場合、std :: findのパフォーマンスは向上しますか?舞台裏でstd :: lower_boundはstd :: advanceとstd :: distanceを呼び出します..多分私はこれらの呼び出しも節約するかもしれませんか?どうもありがとう
AFG
私が持っている結果は20です。(後で編集:* fを印刷したときの結果は20です。)
いいえ、得られる結果はa+6
です。未定義の動作を呼び出す逆参照。 20を印刷するか、「シャーリーマクレーン」を印刷するか、車を爆破する可能性があります。
これが見つからない場合、戻り値が元の引数であるのは常に当てはまりますか?
20は配列内の他のどの値よりも大きいため、この場合、戻り値は常に2nd引数になります。値が見つからないが、既存の値よりも小さい場合、戻り値は次に大きい項目を指します。
cppreference.com から、std :: lower_boundの戻り値は、「value
以上の最初の要素を指すイテレータです。そうでない場合はlast
です。要素が見つかりました。」
パフォーマンスの面で...
それを測定します。ここで他のアドバイスはあなたの実際の経験的証拠に立ち向かうことはありません。
舞台裏でstd :: lower_boundはstd :: advanceとstd :: distanceを呼び出します..多分私はこれらの呼び出しも節約するかもしれませんか?
ほぼ間違いなくそうではありません。これらの呼び出しは、ほぼ確実に、単一の(またはごく少数の)命令に最適化されています。
lower_bound
とfind
によって返されるイテレータには1つの重要な違いがあります。 lower_bound
がアイテムを見つけられない場合、ソート順を保持するためにアイテムを挿入する必要があるイテレータを返します。 find
がアイテムを見つけられない場合、終了イテレータ(つまり、find
の2番目の引数)を返します。あなたの例では、配列の終わりから何かを見つけようとしているので、両方とも同じイテレータを返します-しかし、それは完全な偶然です。
あなたの例では、f
はa+6
と等しいので、逆参照してはいけません。とにかく持っているので、あなたはUBの領域にいますが、値20は配列a
の直後のスタックにあると思います。
確かに、配列が十分に小さい場合、線形検索はバイナリ検索よりも高速になる可能性があります。 10は「大きい」ではなく「小さい」です。小さな配列に対して多くの検索を実行しているプログラムがある場合は、それぞれの時間を計って確認できます。
std::advance
とstd::distance
のオーバーヘッドは基本的にないはずです。中途半端なC++コンパイラはすべてをインライン化し、ポインタの加算と減算に変わります。
次の実装を使用できます
int a [] = {1,2,3,4,5,6};
int f = lower_bound(a、a + 6,20)-a;
これで、配列に20が存在する場合、配列aの要素のインデックスが返されます(0ベースのインデックスが使用されます)。 20が配列に存在しない場合、6、つまり配列の長さを返します。
最悪の場合、検索対象のアイテムは(n-1)番目のインデックスに存在します[nが配列のサイズの場合]。その場合、fはn-1になります。
検索対象のアイテムが配列に存在しない場合にのみ、fは配列のサイズとn以下になります。
それがあなたの質問に答えることを願っています。