「便利な」C ++バイナリ検索アルゴリズムはどこで入手できますか?


106

std::binary_search標準ライブラリの<algorithm>ヘッダーのようなC ++ STLコンテナーと互換性のあるバイナリ検索アルゴリズムが必要ですが、要素が存在するかどうかを示す単純なブール値ではなく、結果を指すイテレーターを返す必要があります。

(補足として、binary_searchのAPIを定義したとき、標準委員会は一体何を考えていましたか?)

ここでの主な懸念は、バイナリ検索の速度が必要であることです。そのため、以下で説明するように、他のアルゴリズムでデータを見つけることができますが、データがソートされているという事実を利用して、バイナリの利点を得たいと思います。線形検索ではなく検索。

これまでlower_boundupper_boundデータが欠落している場合失敗します。

//lousy pseudo code
vector(1,2,3,4,6,7,8,9,0) //notice no 5
iter = lower_bound_or_upper_bound(start,end,5)
iter != 5 && iter !=end //not returning end as usual, instead it'll return 4 or 6

注:コンテナーと互換性がある限り、std名前空間に属さないアルゴリズムを使用しても問題ありません。同様に、言いますboost::binary_search


2
編集に関して:それがstd :: equal_rangeが解決策である理由です。それ以外の場合は、等価性(または等価性を高めるため)をテストする必要があります
Luc Hermitte

(lower / upper)_boundを使用した後、等しいかどうかをテストする必要があります(以下の回答を参照)。
Luc Touraille、2009年

lower_boundおよびupper_boundのドキュメントには、範囲をソートする必要があると記載されているため、バイナリ検索として実装できます。
vividos 2009年

@vividos、万歳!あなたは私が知る必要があるドキュメントの一部を見つけました!ありがとう!
Robert Gould、

Robert、lower / upper_bound / equal_rangeアルゴリズムは、ソートされていない範囲では機能しません。あなたが彼らがあなたが取った要素のサンプルで動作するのを見るのは幸運です。
Luc Hermitte、

回答:


97

そのような関数はありませんがstd::lower_boundstd::upper_boundまたはを使用して簡単な関数を作成できますstd::equal_range

簡単な実装は

template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
    // Finds the lower bound in at most log(last - first) + 1 comparisons
    Iter i = std::lower_bound(begin, end, val);

    if (i != end && !(val < *i))
        return i; // found
    else
        return end; // not found
}

もう1つの解決策は、を使用することですstd::set。これは、要素の順序を保証iterator find(T key)し、指定されたアイテムへの反復子を返すメソッドを提供します。ただし、要件はセットの使用と互換性がない場合があります(たとえば、同じ要素を複数回保存する必要がある場合)。


はい、これは機能します。現在、同様の実装がありますが、状況のコンテキスト(この場合は並べ替えられたデータ)を利用していないという意味で、「単純な」実装です。
Robert Gould、

5
lower_boundはソートされたデータでのみ使用できるため、コメントは本当にわかりません。複雑さは、findを使用するよりも低くなります(編集を参照)。
Luc Touraille、2009年

4
Lucの答えを補足するには、Matt Austernの古典的な記事「なぜSetを使用すべきでないか」および「代わりに何を使用するべきか(C ++レポート12:4、2000年4月)」を参照して、ソートされたベクトルによるバイナリ検索が通常std :: setよりも望ましい理由を理解してください。 、これはツリーベースの連想コンテナです。
ZunTzu

16
使用しないでください*i == val!むしろ使用!(val < *i)。その理由は、をlower_bound使用しているためです(つまり<、同等性である必要はありません)。(同等同等の違いの説明については、Scott Meyersの実効STLを参照してください。)==T
gx_

1
@CanKavaklıoğluに要素がありませんend。C ++標準ライブラリの範囲は、半分開いた間隔で表されます。終了反復子は、最後の要素の後に「ポイント」ます。そのため、アルゴリズムによって返され、値が見つからなかったことを示します。
Luc Touraille、2015年

9

見てくださいstd::equal_range。すべての結果の範囲にイテレータのペアを返します。


cplusplus.com/reference/algorithm/equal_rangeによると、std :: equal_rangeのコストはstd :: lower_boundの約2倍です。std :: lower_boundの呼び出しとstd :: upper_boundの呼び出しがラップされているようです。データに重複がないことがわかっている場合、それは過剰であり、std :: lower_bound(トップの回答で示されている)が最良の選択です。
Bruce Dawson

@BruceDawson:cplusplus.comは、動作を指定するための参照実装のみを提供します。実際の実装では、お気に入りの標準ライブラリを確認できます。例えば、中llvm.org/svn/llvm-project/libcxx/trunk/include/algorithm我々は、呼び出しがLOWER_BOUNDするとUPPER_BOUNDは(いくつかの手動バイナリ検索後)ばらばらの間隔で行われていることがわかります。そうは言っても、特に複数の値が一致する範囲では、より高価になる可能性があります。
Matthieu M.17年

6

それらのセットがあります:

http://www.sgi.com/tech/stl/table_of_contents.html

検索する:

別のメモ:

彼らはおそらく、コンテナを検索すると複数の結果が出てくる可能性があると考えていました。しかし、存在をテストする必要があるだけの場合には、最適化されたバージョンも便利です。


3
前に述べたように、binary_searchはイテレータを返しません。そのため、別の方法を探しています。
Robert Gould、

1
はい、知っています。しかし、それはバイナリ検索アルゴリズムのセットに適合します。他の人が知っているのはいいことです。
マーティンヨーク

8
binary_searchは、STLの他の多くのものと同様に、間違った名前が付けられています。嫌いです。存在のテストは、何かを検索することと同じではありません。
OregonGhost 2009年

2
これらの二分探索関数は、探している要素のインデックスを知りたい場合には役に立ちません。このタスクのために、独自の再帰関数を作成する必要があります。これ、template <class T> int bindary_search(const T&item)がC ++の次のバージョンに追加されることを願っています。
Kemin Zhou

3

std :: lower_boundが低すぎて好みに合わない場合は、boost :: container :: flat_multisetを確認することをお勧めします。これは、バイナリ検索を使用してソートされたベクトルとして実装されたstd :: multisetのドロップイン置換です。


1
良いリンク。また、リンクの適切なリンク:lafstern.org/matt/col1.pdfは、セットではなくソートされたベクトルを使用して実装されたルックアップ(どちらもlog(N)です)の比例定数が大幅に向上し、2倍の速さ(デメリットは挿入時間が長いことです)。
Dan Nissenbaum 2013

2

標準ライブラリに含まれていない理由を疑問に思う、最も短い実装:

template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
    // Note: BOTH type T and the type after ForwardIt is dereferenced 
    // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. 
    // This is stricter than lower_bound requirement (see above)

    first = std::lower_bound(first, last, value, comp);
    return first != last && !comp(value, *first) ? first : last;
}

https://en.cppreference.com/w/cpp/algorithm/lower_boundから


これが標準ライブラリにない2つの理由が考えられます。実装は簡単だと考えていますが、主な理由は、値が* firstと交換可能でない場合、operator()()の逆バージョンが必要になる可能性があることです。
user877329

1

この関数qBinaryFindを確認します。

RandomAccessIterator qBinaryFind ( RandomAccessIterator begin, RandomAccessIterator end, const T & value )

範囲[begin、end)のバイナリ検索を実行し、valueの出現位置を返します。値の出現がない場合は、endを返します。

[begin、end)の範囲のアイテムは昇順で並べ替える必要があります。qSort()を参照してください。

同じ値の発生が多数ある場合、それらのいずれかが返される可能性があります。より細かい制御が必要な場合は、qLowerBound()またはqUpperBound()を使用してください。

例:

QVector<int> vect;
 vect << 3 << 3 << 6 << 6 << 6 << 8;

 QVector<int>::iterator i =
         qBinaryFind(vect.begin(), vect.end(), 6);
 // i == vect.begin() + 2 (or 3 or 4)

この関数は<QtAlgorithms>Qtライブラリの一部であるヘッダーに含まれています。


1
残念ながら、このアルゴリズムはSTLコンテナーと互換性がありません。
bartolo-otrit 2013


0
int BinarySearch(vector<int> array,int var)
{ 
    //array should be sorted in ascending order in this case  
    int start=0;
    int end=array.size()-1;
    while(start<=end){
        int mid=(start+end)/2;
        if(array[mid]==var){
            return mid;
        }
        else if(var<array[mid]){
            end=mid-1;
        }
        else{
            start=mid+1;
        }
    }
    return 0;
}

例:配列A = [1,2,3,4,5,6,7,8,9]を考えます。最初に3のインデックスを検索するとします。最初はstart = 0で、end = 9-1 = 8です。 、開始<=終了以来; mid = 4; (array [mid] is 5)!= 3ここで、3は5より小さいため、midの左側にあります。したがって、配列の左側の部分のみを検索するため、start = 0とend = 3になります。mid = 2.array [mid] == 3なので、探していた数が得られます。したがって、midに等しいインデックスを返します。


1
コードを用意するのは良いことですが、その言語を初めて使う人のためにコードがどのように機能するかについて簡単な説明を提供することで、答えを改善することができます。
テゴスト2018

誰かがあなたの投稿に誤って低品質のフラグを付けましたコードのみの答えは低品質ではありません。質問に答えようとしますか?そうでない場合は、「回答ではない」としてフラグを立てるか、削除を推奨します(レビューキューにある場合)。b)技術的に間違っていますか?反対票またはコメント。
Wai Ha Lee

0

範囲内の位置を返すソリューションは、イテレータの演算のみを使用して次のようになります(イテレータが算術でなくても機能するはずです)。

template <class InputIterator, typename T>
size_t BinarySearchPos(InputIterator first, InputIterator last, const T& val)
{       
    const InputIterator beginIt = first;
    InputIterator element = first;
    size_t p = 0;
    size_t shift = 0;
    while((first <= last)) 
    {
        p = std::distance(beginIt, first);
        size_t u = std::distance(beginIt, last);
        size_t m = p + (u-p)/2;  // overflow safe (p+u)/2
        std::advance(element, m - shift);
        shift = m;
        if(*element == val) 
            return m; // value found at position  m
        if(val > *element)
            first = element++;
        else
            last  = element--;

    }
    // if you are here the value is not present in the list, 
    // however if there are the value should be at position u
    // (here p==u)
    return p;

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.