web-dev-qa-db-fra.com

Où puis-je obtenir un algorithme de recherche binaire "utile" C++?

J'ai besoin d'un algorithme de recherche binaire compatible avec les conteneurs C++ STL, quelque chose comme std::binary_search dans l'en-tête <algorithm> de la bibliothèque standard, mais j'en ai besoin pour renvoyer l'itérateur qui pointe vers le résultat, pas un simple booléen me disant si l'élément existe.

(Sur une note de côté, à quoi diable pensait le comité habituel quand ils ont défini l'API pour binary_search ?!)

Ma principale préoccupation ici est que j'ai besoin de la vitesse d'une recherche binaire. Ainsi, même si je peux trouver les données avec d'autres algorithmes, comme mentionné ci-dessous, je souhaite profiter du fait que mes données sont triées pour bénéficier des avantages d'un binaire. recherche, pas une recherche linéaire.

jusqu'ici lower_bound et upper_bound échouent si la donnée est manquante:

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

Remarque: Je peux également utiliser un algorithme qui n'appartient pas à l'espace de noms std tant qu'il est compatible avec les conteneurs. Comme, disons, boost::binary_search.

91
Robert Gould

Il n’existe pas de telles fonctions, mais vous pouvez en écrire une simple avec std::lower_bound , std::upper_bound ou std::equal_range .

Une simple implémentation pourrait être

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
}

Une autre solution consisterait à utiliser un std::set, qui garantit le classement des éléments et fournit une méthode iterator find(T key) qui renvoie un itérateur à l'élément donné. Cependant, vos exigences pourraient ne pas être compatibles avec l'utilisation d'un ensemble (par exemple, si vous devez stocker le même élément plusieurs fois).

83
Luc Touraille

Vous devriez jeter un oeil à std::equal_range. Il retournera une paire d'itérateurs à la plage de tous les résultats.

8
Luc Hermitte

Il y en a un ensemble:

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

Rechercher:

Sur une note séparée:

Ils pensaient probablement que la recherche de conteneurs pouvait entraîner plus d'un résultat. Mais parfois, il suffit de tester l’existence d’une version optimisée de Nice.

6
Martin York

Si std :: lower_bound est trop bas pour votre goût, vous pouvez vérifier boost :: container :: flat_multiset . C'est un remplacement instantané de std :: multiset implémenté en tant que vecteur en utilisant la recherche binaire.

3
ZunTzu

std :: lower_bound () :)

1
moogs

Vérifiez cette fonction, qBinaryFind :

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

Effectue une recherche binaire de la plage [begin, end) et retourne la position d'une occurrence de valeur. S'il y a n'y a aucune occurrence de valeur, retourne fin.

Les éléments dans la plage [begin, end) doit être trié par ordre croissant; voir qSortir ().

S'il y a beaucoup d'occurrences du même valeur, l’un d’eux pourrait être revenu. Utilisez qLowerBound () ou qUpperBound () si vous avez besoin de plus de précision contrôle.

Exemple:

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)

La fonction est incluse dans l'en-tête <QtAlgorithms> qui fait partie de la bibliothèque Qt .

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

Exemple: considérons un tableau, A = [1,2,3,4,5,6,7,8,9] Supposons que vous souhaitiez rechercher l'index de 3 Initialement, début = 0 et fin = 9-1 = 8 Maintenant, depuis début <= fin; milieu = 4; (array [mid] qui vaut 5)! = 3 Maintenant, 3 est situé à gauche de mid, il est inférieur à 5. Par conséquent, nous ne cherchons que la partie gauche du tableau Par conséquent, début = 0 et fin = 3; mid = 2.Depuis un tableau [mid] == 3, nous avons donc obtenu le nombre que nous recherchions. Par conséquent, nous retournons son indice qui est égal à mid.

0

L'implémentation la plus courte, se demandant pourquoi elle n'est pas incluse dans la bibliothèque standard:

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

De https://en.cppreference.com/w/cpp/algorithm/lower_bound

0
trozen

Une solution renvoyant la position à l'intérieur de la plage pourrait être la suivante, en n'utilisant que des opérations sur les itérateurs (elle devrait fonctionner même si l'itérateur ne permet pas de calculer)

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
Michele Belotti