Recherche dichotomique et méthodes associées en C++ - Bibliothèque STL

05 Mar 2022 05 Mar 2022 5641 vues ESSADDOUKI Mostafa 10 min de lecture
Introduction et syntaxe de base
1 Introduction au langage C++ 2 Entrée-sortie en C++ - cin et cout 3 Inférence de type avec le mot-clé auto en C++ 4 Classe std::string et les chaînes de caractères en C++ 5 Les structures conditionnelles (if et switch) en C++ (C++17 et C++20) 6 Les boucles en C++ (C++17 et C++20) 7 La gestion des fichiers en C++
Pointeurs et fonctions
8 Introduction aux pointeurs en C++ - Déclaration et interêts 9 Les références en C++ - déclaration et interêts 10 Les tableaux en C++ - Déclaration et interêts 11 Introduction aux fonctions en C++ 12 Passer des arguments à une fonction en C++ 13 Déclarer un paramètre const en C++ 14 Les fonctions Lambda en C++ 15 Fonctions utiles (Mathématiques et caractères) en C++
Programmation OO
16 Classes et objets en C++ 17 Spécificateurs d'accès en C++ 18 Constructeurs et destructeur d'une classe en C++ 19 Fonctions membres en C++ 20 Membres statiques d'une classe en C++ 21 Fonctions en ligne en C++ - inline 22 Fonctions et classes amies en C++ - friend 23 Surcharge des fonctions en C++ 24 Surcharge des opérateurs en C++ 25 Héritage en C++ 26 La gestion d'exceptions en C++ : déclaration, utilisation et personnalisation 27 fonctions et classes templates en C++ 28 Les nouveautés C++20 pour améliorer les templates en C++
Structures de données
29 Introduction aux structures de données 30 Les structures en C++ et la différence avec les structures en C 31 Les listes chaînées en C++ 32 Les piles en C++ 33 File d'attente en C++ 34 Arbre binaire de recherche : définition et mise en oeuvre en C++
La bibliothèque standard (STL)
35 Introduction à la bibliothèque de Template Standard STL 36 Les itérateurs en C++ - définition, déclaration et exemples 37 La classe array en C++ (bibliothèque STL) <array> 38 La classe vector de la bibliothèque STL <vector> 39 La classe deque en C++ ( Bibliothèque STL) 40 La classe list en C++ (bibliothèque STL) <list> 41 La classe stack (Pile) en C++ (bibliothèque STL) <stack> 42 La classe queue (File d'attente) en C++ (bibliothèque STL) <queue> 43 La file d'attente prioritaire (classe priority_queue) - Bibliothèque STL 44 Les ensembles en C++ (Classe set <set> - Bibliothèque STL) 45 Les dictionnaires en C++ : Classe map (Bibliothèque STL) 46 Introduction aux algorithmes de la bibliothèque STL (programmation compétitive) 47 Tri et méthodes associées en C++ - Bibliothèque STL 48 Recherche dichotomique et méthodes associées en C++ - Bibliothèque STL 49 Appliquer un prédicat ou une fonction aux éléments d'une séquence en C++ - Bibliothèque STL 50 Recherche dans une séquence et méthodes associées en C++ - Bibliothèque STL

Algorithmes de recherche dichotomique

Définition

Les méthodes de recherche dichotomique supposent qu'une séquence cible est déjà triée. Ces méthodes présentent des caractéristiques de complexité souhaitables par rapport à la recherche générique sur une séquence non spécifiée.

Complexité

Tous les algorithmes ci-dessous fournissent une complexité logarithmique \(O(\log N)\) (où \(N = distance(fwd\_debut, fwd\_fin)\)) si un itérateur à accès aléatoire est donné, sinon c'est une complexité linéaire.

1. Algorithme binary_search

Description

L'algorithme binary_search permet de trouver un élément particulier dans une séquence triée.

La fonction renvoie true si la séquence contient la valeur val. Plus précisément, elle renvoie vrai si la séquence cible contient un élément x tel que ni x < val ni val < x.

Si fct_comp est fourni, elle renvoie vrai si la séquence cible contient un élément x tel que ni fct_comp(x, val) ni fct_comp(val, x).

Condition importante

La séquence cible doit être triée selon operator< ou fct_comp si elle est fournie.

Syntaxe

   
binary_search C++
bool binary_search(fwd_debut, fwd_fin, val, [fct_comp]);

Arguments

  • fwd_debut, fwd_fin : une paire de ForwardIterators représentant la séquence cible
  • val : la valeur à rechercher
  • fct_comp (optionnel) : un opérateur de comparaison

Exemple 1 : Recherche dichotomique simple

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

bool fct_comp (int i, int j) { return (i < j); }

int main () {
    int vals[] = {1, 2, 3, 4, 5, 4, 3, 2, 1};
    vector<int> v(vals, vals + 9);

    // Trier le vecteur (obligatoire pour binary_search)
    sort(v.begin(), v.end());

    // Recherche de la valeur 3
    if (binary_search(v.begin(), v.end(), 3))
        cout << "La valeur 3 est trouvée.\n";
    else
        cout << "La valeur 3 est introuvable.\n";

    // Tri avec fonction de comparaison personnalisée
    sort(v.begin(), v.end(), fct_comp);

    // Recherche de la valeur 8 avec la même fonction de comparaison
    if (binary_search(v.begin(), v.end(), 8, fct_comp))
        cout << "La valeur 8 est trouvée.\n";
    else
        cout << "La valeur 8 est introuvable.\n";

    return 0;
}
Résultat
La valeur 3 est trouvée.
La valeur 8 est introuvable.

Explication :

  • Le vecteur est d'abord trié avec sort() (indispensable).
  • binary_search retourne true car 3 est présent.
  • 8 n'est pas présent dans le vecteur, donc false.

2. Algorithme lower_bound

Description

L'algorithme lower_bound trouve une partition dans une séquence triée. Il renvoie un itérateur correspondant au premier élément non inférieur à la valeur val.

Plus précisément, l'algorithme renvoie un itérateur résultat qui partitionne la séquence de sorte que :

  • Tous les éléments avant résultat sont inférieurs à val
  • résultat et tous les éléments après lui sont non inférieurs à val (c'est-à-dire >= val)

Syntaxe

   
lower_bound C++
ForwardIterator lower_bound(fwd_debut, fwd_fin, val, [fct_comp]);

Arguments

  • fwd_debut, fwd_fin : une paire de ForwardIterators représentant la séquence cible
  • val : la valeur pour partitionner la séquence
  • fct_comp (optionnel) : un opérateur de comparaison

Exemple 2 : Utilisation de lower_bound

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void afficher(int a) {
    cout << a << " ";
}

int main () {
    int vals[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};
    vector<int> v(vals, vals + 10);

    // Trier le vecteur
    sort(v.begin(), v.end());
    
    // Affichage du vecteur trié
    cout << "Vecteur trié : ";
    for_each(v.begin(), v.end(), afficher);
    cout << '\n';

    // lower_bound pour la valeur 4
    vector<int>::iterator low;
    low = lower_bound(v.begin(), v.end(), 4);

    cout << "La partition est en position " << (low - v.begin()) << '\n';
    cout << "Valeur à cette position : " << *low << '\n';

    return 0;
}
Résultat
Vecteur trié : 1 2 2 3 3 4 5 8 9 13 
La partition est en position 5
Valeur à cette position : 4

Explication :

  • Le vecteur trié est : 1, 2, 2, 3, 3, 4, 5, 8, 9, 13
  • lower_bound(v.begin(), v.end(), 4) retourne l'itérateur vers le premier élément >= 4
  • C'est la position 5 (le premier 4)
  • Tous les éléments avant la position 5 sont inférieurs à 4

3. Algorithme upper_bound

Description

L'algorithme upper_bound trouve une partition dans une séquence triée. Il renvoie un itérateur correspondant au premier élément strictement supérieur à la valeur val.

Syntaxe

   
upper_bound C++
ForwardIterator upper_bound(fwd_debut, fwd_fin, val, [fct_comp]);

Exemple 3 : Utilisation de upper_bound

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void afficher(int a) {
    cout << a << " ";
}

int main () {
    int vals[] = {1, 2, 3, 2, 3, 4, 5, 13, 8, 9, 6};
    vector<int> v(vals, vals + 10);

    // Trier le vecteur
    sort(v.begin(), v.end());
    
    // Affichage du vecteur trié
    cout << "Vecteur trié : ";
    for_each(v.begin(), v.end(), afficher);
    cout << '\n';

    // upper_bound pour la valeur 4
    vector<int>::iterator up;
    up = upper_bound(v.begin(), v.end(), 4);

    cout << "Le premier élément supérieur à 4 se trouve à la position " << (up - v.begin()) << '\n';
    cout << "Valeur à cette position : " << *up << '\n';

    return 0;
}
Résultat
Vecteur trié : 1 2 2 3 3 4 5 8 9 13 
Le premier élément supérieur à 4 se trouve à la position 6
Valeur à cette position : 5

Explication :

  • upper_bound(v.begin(), v.end(), 4) retourne l'itérateur vers le premier élément > 4
  • C'est la position 6 (valeur 5)
  • Tous les éléments avant la position 6 sont <= 4

4. Algorithme equal_range

Description

L'algorithme equal_range trouve une plage de certains éléments dans une séquence triée. Il renvoie une paire d'itérateurs correspondant à la plage semi-ouverte égale à la valeur val.

La paire retournée est équivalente à :

make_pair(lower_bound(...), upper_bound(...))

Syntaxe

   
equal_range C++
pair<ForwardIterator,ForwardIterator> equal_range(fwd_debut, fwd_fin, val, [fct_comp]);

Exemple 4 : Trouver tous les éléments égaux à une valeur

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void afficher(int a) {
    cout << a << " ";
}

int main () {
    int vals[] = {1, 2, 3, 2, 3, 4, 5, 13, 4, 9, 4};
    vector<int> v(vals, vals + 11);

    // Trier le vecteur
    sort(v.begin(), v.end());
    
    // Affichage du vecteur trié
    cout << "Vecteur trié : ";
    for_each(v.begin(), v.end(), afficher);
    cout << '\n';

    // equal_range pour la valeur 4
    pair<vector<int>::iterator, vector<int>::iterator> limites;
    limites = equal_range(v.begin(), v.end(), 4);

    cout << "Les valeurs répétées de 4 se trouvent dans l'intervalle ["
         << (limites.first - v.begin()) << ", "
         << (limites.second - v.begin()) << "[\n";

    // Affichage des valeurs dans l'intervalle
    cout << "Valeurs dans cet intervalle : ";
    for (vector<int>::iterator it = limites.first; it != limites.second; ++it) {
        cout << *it << " ";
    }
    cout << '\n';

    return 0;
}
Résultat
Vecteur trié : 1 2 2 3 3 4 4 4 5 9 13 
Les valeurs répétées de 4 se trouvent dans l'intervalle [5, 8[
Valeurs dans cet intervalle : 4 4 4

Explication :

  • equal_range retourne deux itérateurs : le premier (lower_bound) et le dernier (upper_bound) des éléments égaux à 4.
  • L'intervalle [5, 8[ contient trois fois la valeur 4 (positions 5, 6, 7).
  • La borne supérieure (position 8) pointe vers la valeur 5, premier élément > 4.

Récapitulatif des algorithmes de recherche dichotomique

AlgorithmeRetourneCondition sur la séquenceUtilité
binary_searchboolTriéeVérifier si une valeur existe
lower_boundItérateurTriéePremier élément >= val
upper_boundItérateurTriéePremier élément > val
equal_rangePaire d'itérateursTriéeIntervalle des éléments == val
Relation entre les algorithmes

Pour une séquence triée et une valeur val :

  • lower_bound donne la première position où insérer val sans casser l'ordre
  • upper_bound donne la dernière position où insérer val sans casser l'ordre
  • equal_range combine les deux en une seule opération
  • binary_search teste simplement si val est présent
 Exercice pratique

Utilisation des algorithmes dichotomiques

 Niveau : Intermédiaire

On considère le vecteur suivant (non trié) :

vector<int> v = {8, 3, 5, 1, 9, 3, 7, 3, 6, 2};

Questions :

  1. Quelle opération faut-il effectuer avant d'utiliser les algorithmes de recherche dichotomique ?
  2. Après avoir trié le vecteur, quelles seront les positions retournées par lower_bound, upper_bound et equal_range pour la valeur 3 ?
  3. Que retournerait binary_search pour la valeur 4 ?
  4. Écrire un code qui affiche toutes les positions où se trouve la valeur 3 en utilisant equal_range.
Points clés à retenir
  • Les algorithmes de recherche dichotomique nécessitent que la séquence soit triée au préalable.
  • Complexité : O(log N) pour les itérateurs à accès aléatoire (vector, deque), O(N) sinon.
  • binary_search : teste simplement l'existence d'une valeur (retourne bool).
  • lower_bound : retourne le premier élément non inférieur à val (>= val).
  • upper_bound : retourne le premier élément strictement supérieur à val (> val).
  • equal_range : retourne une paire [lower_bound, upper_bound[ représentant tous les éléments égaux à val.
  • Ces algorithmes sont très efficaces pour les recherches dans de grands ensembles de données triés.
  • Ils sont souvent utilisés en combinaison avec sort() et les conteneurs associatifs (set, map).

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.