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

05 Mar 2022 05 Mar 2022 6149 vues ESSADDOUKI Mostafa 13 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 tri dans la STL

Définition

Le tri est l'un des algorithmes les plus élémentaires appliqués aux données. Il signifie organiser les données d'une manière particulière, qui peut être croissante ou décroissante.

Opérateurs de comparaison

Chaque algorithme de tri a deux versions :

  • Une qui utilise operator< par défaut
  • Une qui prend un objet fonction appelé opérateur de comparaison (fonction binaire retournant un booléen)

Un opérateur de comparaison retourne true si le premier argument est inférieur au second, false sinon.

Propriété importante

Les opérateurs de comparaison doivent être transitifs. Cela signifie que pour tous les éléments a, b et c :

si fct_comp(a, b) et fct_comp(b, c), alors fct_comp(a, c).

Si a est ordonné avant b et b avant c, alors a doit être ordonné avant c.

Prérequis

Les éléments de la séquence cible doivent être permutables, constructibles par déplacement et assignables par déplacement.

Tous les algorithmes expliqués dans ce cours se trouvent dans l'en-tête <algorithm>.

1. Algorithme sort

Description

L'algorithme sort trie les éléments d'une séquence dans l'ordre croissant (par défaut) ou selon un opérateur de comparaison fourni.

Caractéristiques
  • Complexité : O(N log N) en moyenne
  • Stabilité : Non stable (les éléments équivalents peuvent perdre leur ordre relatif)
  • En place : Oui

Syntaxe

   
sort C++
void sort([ep], it_debut, it_fin);
void sort([ep], it_debut, it_fin, [fct_comp]);

Arguments

  • ep (optionnel) : politique d'exécution (std::execution::seq par défaut)
  • it_debut, it_fin : une paire de RandomAccessIterators représentant la séquence cible
  • fct_comp (optionnel) : opérateur de comparaison binaire

Exemple 1 : Utilisation de sort

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

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

bool ordre_descendant(int i, int j) {
    return i > j;  // retourne true si i > j (ordre décroissant)
}

int main() {
    // Exemple sur un tableau
    int t1[5] = {1, 5, 8, 4, 2};
    sort(t1, t1 + 5);

    cout << "Tableau trié : ";
    for_each(t1, t1 + 5, afficher);
    cout << '\n';

    // Exemple sur un vecteur
    vector<int> v1;
    v1.push_back(9);
    v1.push_back(4);
    v1.push_back(1);
    v1.push_back(5);
    v1.push_back(7);
    
    sort(v1.begin(), v1.end());
    cout << "Vecteur trié : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    // Tri avec fonction de comparaison personnalisée (ordre décroissant)
    int t2[] = {4, 3, 13, 5, 6, 8, 4, 2, 17};
    sort(t2, t2 + 9, ordre_descendant);
    cout << "Tableau trié décroissant : ";
    for_each(t2, t2 + 9, afficher);
    cout << '\n';

    return 0;
}
Résultat
Tableau trié : 1 2 4 5 8 
Vecteur trié : 1 4 5 7 9 
Tableau trié décroissant : 17 13 8 6 5 4 4 3 2

2. Algorithme stable_sort

Description

L'algorithme stable_sort trie les éléments comme sort, mais préserve l'ordre relatif des éléments avec des valeurs équivalentes (les éléments égaux conservent leur ordre d'origine).

Syntaxe

   
stable_sort C++
void stable_sort([ep], it_debut, it_fin);
void stable_sort([ep], it_debut, it_fin, [fct_comp]);

Exemple 2 : Utilisation de stable_sort

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

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

bool ordre_descendant(int i, int j) {
    return i > j;
}

int main() {
    vector<int> v1;
    v1.push_back(9);
    v1.push_back(4);
    v1.push_back(1);
    v1.push_back(5);
    v1.push_back(7);

    // Tri normal (ordre croissant)
    stable_sort(v1.begin(), v1.end());
    cout << "Vecteur trié (stable) : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    // Tri décroissant avec stable_sort
    stable_sort(v1.begin(), v1.end(), ordre_descendant);
    cout << "Vecteur trié décroissant (stable) : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    return 0;
}
Résultat
Vecteur trié (stable) : 1 4 5 7 9 
Vecteur trié décroissant (stable) : 9 7 5 4 1

3. Algorithme partial_sort

Description

L'algorithme partial_sort réorganise les éléments de la plage [it_debut, it_fin) de telle sorte que les éléments avant it_milieu soient les plus petits éléments de toute la séquence et soient triés par ordre croissant, tandis que les autres éléments sont laissés sans ordre spécifique.

Un tri partiel vous permet de trouver les premiers éléments d'une séquence triée sans avoir à trier la séquence entière. Par exemple, si vous avez la séquence D C B A, vous pouvez effectuer un tri partiel des deux premiers éléments et obtenir le résultat A B D C. Les deux premiers éléments sont les mêmes que si vous aviez trié la séquence entière, mais les autres éléments ne le sont pas.

Syntaxe

   
partial_sort C++
void partial_sort([ep], it_debut, it_milieu, it_fin, [fct_comp]);

Exemple 3 : Utilisation de partial_sort

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

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

bool ordre_descendant(int i, int j) {
    return i > j;
}

int main() {
    vector<int> v1;
    v1.push_back(9);
    v1.push_back(4);
    v1.push_back(1);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(3);
    v1.push_back(14);
    v1.push_back(10);

    cout << "Vecteur original : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    vector<int>::iterator i = v1.begin();

    // Trier partiellement les 4 premiers éléments
    partial_sort(v1.begin(), i + 4, v1.end());
    cout << "Après partial_sort des 4 premiers : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    // Trier partiellement les 4 premiers en ordre décroissant
    partial_sort(v1.begin(), i + 4, v1.end(), ordre_descendant);
    cout << "Après partial_sort décroissant des 4 premiers : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    return 0;
}
Résultat
Vecteur original : 9 4 1 5 7 3 14 10 
Après partial_sort des 4 premiers : 1 3 4 5 9 7 14 10 
Après partial_sort décroissant des 4 premiers : 14 10 9 7 1 3 4 5

Explication :

  • Premier partial_sort : les 4 premiers éléments sont les plus petits de toute la séquence (1,3,4,5) et sont triés.
  • Les éléments restants (9,7,14,10) ne sont pas triés.
  • Deuxième partial_sort (décroissant) : les 4 premiers sont les plus grands (14,10,9,7) et sont triés.

4. Algorithmes is_sorted et is_sorted_until

Description

L'algorithme is_sorted détermine si une séquence est triée.

L'algorithme is_sorted_until renvoie un itérateur pointant sur le premier élément non trié.

Syntaxe

   
is_sorted et is_sorted_until C++
bool is_sorted([ep], it_debut, it_fin, [fct_comp]);
ForwardIterator is_sorted_until([ep], it_debut, it_fin, [fct_comp]);

Exemple 4 : Vérification du tri

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

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

int main() {
    vector<int> v1;
    v1.push_back(9);
    v1.push_back(4);
    v1.push_back(1);
    v1.push_back(5);
    v1.push_back(7);

    // Test avant tri
    cout << "Avant sort :\n";
    if (is_sorted(v1.begin(), v1.end()))
        cout << "Le vecteur est trié\n";
    else
        cout << "Le vecteur n'est pas trié\n";

    // Tri
    sort(v1.begin(), v1.end());

    // Test après tri
    cout << "Après sort :\n";
    if (is_sorted(v1.begin(), v1.end()))
        cout << "Le vecteur est trié\n";
    else
        cout << "Le vecteur n'est pas trié\n";

    cout << '\n';

    // Exemple avec is_sorted_until
    int a[] = {1, 3, 4, 5, 8, 7, 9, 10};

    int* p = is_sorted_until(a, a + 8);
    cout << "Il y a " << (p - a) << " éléments triés dans la liste\n";
    cout << "Le premier élément non trié est " << *p << '\n';

    return 0;
}
Résultat
Avant sort :
Le vecteur n'est pas trié
Après sort :
Le vecteur est trié

Il y a 5 éléments triés dans la liste
Le premier élément non trié est 7

Explication pour is_sorted_until :

  • La séquence {1,3,4,5,8,7,9,10} est triée jusqu'à l'indice 4 inclus (valeurs 1,3,4,5,8).
  • À l'indice 5, on trouve 7 qui est inférieur à 8, donc la séquence n'est plus triée.
  • is_sorted_until retourne un itérateur vers cet élément (7).

5. Algorithme nth_element

Description

L'algorithme nth_element place un élément particulier d'une séquence dans sa position triée correcte.

Cet algorithme de tri partiel modifie la séquence cible de la manière suivante :

  • L'élément à la position pointée par it_nieme se trouve à cette position comme si toute la séquence était triée.
  • Tous les éléments de it_debut à it_nieme-1 seront inférieurs à it_nieme.
  • Tous les éléments après it_nieme seront supérieurs à it_nieme.
  • Les autres éléments sont laissés sans ordre spécifique.

Si it_nieme == it_fin, la fonction n'effectue aucune opération.

Syntaxe

   
nth_element C++
void nth_element([ep], it_debut, it_nieme, it_fin, [fct_comp]);

Exemple 5 : Utilisation de nth_element

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

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

int main() {
    vector<int> v1;
    v1.push_back(6);
    v1.push_back(1);
    v1.push_back(5);
    v1.push_back(4);
    v1.push_back(7);
    v1.push_back(8);
    v1.push_back(14);
    v1.push_back(10);

    cout << "Vecteur original : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    // Place le 4ème élément (index 3) à sa position triée correcte
    nth_element(v1.begin(), v1.begin() + 3, v1.end());

    cout << "Après nth_element (4ème élément) : ";
    for_each(v1.begin(), v1.end(), afficher);
    cout << '\n';

    return 0;
}
Résultat
Vecteur original : 6 1 5 4 7 8 14 10 
Après nth_element (4ème élément) : 1 4 5 6 7 8 14 10

Explication :

  • Le vecteur original est {6,1,5,4,7,8,14,10}
  • Si on trie complètement, on obtiendrait {1,4,5,6,7,8,10,14}
  • Le 4ème élément (index 3) serait 6
  • Après nth_element, l'élément à l'index 3 est bien 6
  • Tous les éléments avant l'index 3 (1,4,5) sont inférieurs à 6
  • Tous les éléments après (7,8,14,10) sont supérieurs à 6, mais non triés

Récapitulatif des algorithmes de tri

AlgorithmeDescriptionStableComplexité
sortTrie toute la séquenceNonO(N log N)
stable_sortTrie toute la séquence en préservant l'ordre des équivalentsOuiO(N log N)
partial_sortTrie les premiers éléments (les plus petits)NonO(N log M) avec M = distance(it_debut, it_milieu)
nth_elementPlace un élément à sa position triée correcteNonO(N)
is_sortedVérifie si la séquence est triée-O(N)
is_sorted_untilTrouve le premier élément non trié-O(N)
 Exercice pratique

Choisir le bon algorithme de tri

 Niveau : Intermédiaire

Pour chaque situation, indiquez quel algorithme de tri serait le plus approprié :

  1. Vous avez une liste de 10 millions d'entiers et vous voulez les trier complètement.
  2. Vous voulez trouver les 100 plus petits éléments d'une liste de 1 million d'éléments, sans avoir besoin de trier le reste.
  3. Vous voulez vérifier rapidement si une liste est déjà triée avant d'effectuer d'autres opérations.
  4. Vous avez une liste avec des éléments en double et vous voulez conserver l'ordre relatif des éléments égaux.
  5. Vous voulez connaître la médiane d'une liste (l'élément du milieu).
Points clés à retenir
  • Les algorithmes de tri sont dans l'en-tête <algorithm>.
  • sort : tri complet, rapide mais non stable.
  • stable_sort : tri complet en préservant l'ordre des éléments équivalents.
  • partial_sort : tri partiel des premiers éléments (les plus petits).
  • nth_element : place un élément à sa position triée (utile pour médianes, quartiles).
  • is_sorted : vérifie si une séquence est triée.
  • is_sorted_until : trouve le premier élément non trié.
  • On peut fournir une fonction de comparaison personnalisée pour changer l'ordre de tri.
  • La fonction de comparaison doit être transitive et définir un ordre strict faible.
  • Choisissez l'algorithme en fonction de vos besoins : tri complet, tri partiel, stabilité, etc.

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.