2009-01-15 15 views
83

Ich brauche einen binären Suchalgorithmus, der kompatibel ist mit den C++ STL-Containern, etwa std::binary_search in der Standard-Bibliothek <algorithm> Header, aber ich brauche es den Iterator, der auf das Ergebnis verweist, nicht eine einfache Boolean sagen mir, ob das Element existiert.Wo kann ich einen "nützlichen" C++ - Binärsuchalgorithmus bekommen?

(Auf einer Seite zur Kenntnis, was zum Teufel das Standard-Komitee Denken war, als sie die API für binary_search ?! definiert)

Mein hier Hauptanliegen ist, dass ich die Geschwindigkeit einer binären Suche benötigen, so dass, obwohl ich kann die Daten mit anderen Algorithmen finden, wie unten erwähnt, möchte ich die Tatsache nutzen, dass meine Daten sortiert sind, um die Vorteile einer binären Suche, nicht eine lineare Suche zu erhalten.

bisher lower_bound und upper_bound fehlschlagen, wenn das Datum fehlt:

//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 

Hinweis: Ich bin auch in Ordnung unter Verwendung eines Algorithmus, der dem std-Namespace gehört nicht so lange seine kompatibel mit Behälter. Wie zum Beispiel, boost::binary_search.

+2

In Bezug auf die Bearbeitung: Deshalb ist std :: equal_range die Lösung. Andernfalls müssen Sie auf Gleichheit prüfen (oder Äquivalenz ist mehr) –

+0

Sie müssen auf Gleichheit prüfen, nachdem Sie (lower/outer) _bound verwendet haben (siehe Antwort unten). –

+0

"mehr ... präzise" –

Antwort

79

Es gibt keine solche Funktionen, aber Sie können eine einfache Verwendung std::lower_bound, std::upper_bound oder std::equal_range schreiben.

Eine einfache Implementierung

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 
} 

Eine andere Lösung, die eine std::set, die die Reihenfolge der Elemente gewährleistet und ein Verfahren iterator find(T key) bietet wäre verwenden könnte, die einen Iterator auf das gegebene Element zurückgibt. Ihre Anforderungen sind jedoch möglicherweise nicht mit der Verwendung eines Satzes kompatibel (z. B. wenn Sie das gleiche Element mehrmals speichern müssen).

+0

ja das funktioniert, und ich habe eine ähnliche Implementierung, aber es ist eine "naive" Implementierung, in dem Sinne, dass es den Kontext der Situation nicht ausnutzt In diesem Fall sortierte Daten. –

+4

Ich verstehe Ihren Kommentar nicht wirklich, da lower_bound nur für sortierte Daten verwendet werden kann. Die Komplexität ist geringer als bei der Suche (siehe Bearbeiten). –

+0

Entschuldigung, ich habe vergessen, dass sie sortiert werden mussten, also ist Ihre Implementierung korrekt und nutzt die sortierten Daten aus! –

8

Sie sollten sich std::equal_range ansehen. Es wird ein Paar Iteratoren auf den Bereich aller Ergebnisse zurückgeben.

+0

Laut http://www.cplusplus.com/reference/algorithm/equal_range/ sind die Kosten von std :: equal_range ungefähr doppelt so hoch wie von std :: lower_bound. Es scheint, dass es einen Aufruf an std :: lower_bound und einen Aufruf an std :: upper_bound umschließt. Wenn Sie wissen, dass Ihre Daten keine Duplikate haben, dann ist das Overkill und std :: lower_bound (wie in der oberen Antwort gezeigt) die beste Wahl. –

+0

@BruceDawson: cplusplus.com gibt nur eine * Referenz * -Implementierung zur Angabe des ** Verhaltens **; Für eine tatsächliche Implementierung können Sie Ihre bevorzugte Standardbibliothek überprüfen. Zum Beispiel können wir in https://llvm.org/svn/llvm-project/libcxx/trunk/include/algorithm sehen, dass die Aufrufe von lower_bound und upper_bound in disjunkten Intervallen durchgeführt werden (nach manueller binärer Suche). Vor diesem Hintergrund ist es wahrscheinlich teurer, insbesondere in Bereichen mit mehreren übereinstimmenden Werten. –

6

Es gibt eine Reihe von ihnen:

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

Suchen nach:

Auf einem separaten Hinweis:

Sie wurden wahrscheinlich denken, dass die Behälter der Suche mehr als ein Ergebnis bezeichnen könnte. Aber in den seltenen Fällen, in denen Sie nur auf Existenz testen müssen, wäre eine optimierte Version auch schön.

+2

binary_search gibt keinen Iterator zurück, wie ich bereits erwähnt habe, deshalb suche ich nach einer Alternative. –

+1

Ja, ich weiß. Aber es passt in die Menge der binären Suchalgorithmen. Es ist also schön, dass andere davon wissen. –

+6

binary_search ist nur, wie so viele andere Dinge in der STL, falsch benannt. Ich hasse es, dass. Das Testen auf Existenz ist nicht dasselbe wie das Suchen nach etwas. – OregonGhost

1

std :: lower_bound() :)

+0

OP: "bisher low_bound und oter_bound scheitern, weil ..." –

1

Aktivieren Sie diese Funktion, qBinaryFind:

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

Führt eine binäre Suche des Bereichs [begin, end) und gibt die Position eines Auftretens des Wertes. Wenn dort keine Vorkommen von Wert sind, gibt Ende zurück.

Die Artikel im Bereich [Anfang, Ende] müssen aufsteigend sortiert sein; siehe qSort().

Wenn es viele Vorkommen des gleichen Werts gibt, könnte einer von ihnen zurückgegeben werden. Verwenden Sie qLowerBound() oder qUpperBound(), wenn Sie feinere Kontrolle benötigen.

Beispiel:

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) 

Die Funktion wird im <QtAlgorithms> Header enthalten, der ein Teil der Qt Bibliothek ist.

+0

Leider ist dieser Algorithmus nicht mit STL-Containern kompatibel. –

3

Wenn std :: lower_bound für Sie zu niedrig ist, sollten Sie boost::container::flat_multiset überprüfen. Es ist ein Drop-in-Ersatz für std :: multiset als sortierten Vektor mit binärer Suche implementiert.

+1

Gute Verbindung; und auch ein guter Link * in * der Link: http://lafstern.org/matt/col1.pdf, der beschreibt, wie Suchvorgänge, die mit einem sortierten Vektor implementiert sind, statt gesetzt sind (obwohl beide log (N) sind), signifikant * bessere Proportionalitätskonstanten und sind ~ doppelt so schnell (der Nachteil ist eine größere INSERTION-Zeit). –

0

Eine Lösung, die Position innerhalb des Bereichs zurückkehren könnte so sein, nur mit Operationen auf Iteratoren (es sollte funktionieren, auch wenn Iterator nicht Arithmetik der Fall ist):

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

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

Beispiel: ein Array Betrachten, A = [1,2,3,4,5,6,7,8,9] Angenommen, Sie möchten den Index von 3 suchen Zunächst Start = 0 und Ende = 9-1 = 8 Jetzt, seit dem Start < = Ende; Mitte = 4; (Array [Mitte] welches 5 ist!) = 3 Jetzt liegt 3 links von der Mitte als kleiner als 5. Daher suchen wir nur den linken Teil des Arrays Daher jetzt Start = 0 und Ende = 3 ; mid = 2. Seit array [mid] == 3 haben wir die Nummer gefunden, nach der wir gesucht haben. Daher geben wir seinen Index zurück, der gleich der Mitte ist.

+1

Es ist gut, Code zu haben, aber Sie könnten die Antwort verbessern, indem Sie eine kurze Erklärung dazu geben, wie es für Leute funktioniert, die neu in der Sprache sind. – Taegost

+0

Jemand hat fälschlicherweise [den Post als minderwertig markiert] (https://stackoverflow.com/review/low-quality-posts/18801852). Eine [Nur-Code-Antwort ist keine schlechte Qualität] (https://meta.stackoverflow.com/a/358757/1364007). Wird versucht, die Frage zu beantworten? Wenn nicht, markieren Sie als 'keine Antwort' oder empfehlen Sie das Löschen (falls in der Review-Warteschlange). b) Ist es technisch falsch? Downvote oder Kommentar –

Verwandte Themen