Structures de données IFT-2000 Abder Alikacem Standard Template library Semaine 5 Édition Septembre 2009 Département d’informatique et de génie logiciel
Plan Description générale Conteneurs Conteneurs séquentiels Vector, list, deque Conteneurs associatifs Set, map Adaptateurs Stack, queue Itérateurs Algorithmes Foncteurs
STL : Standard Template Library La STL est une bibliothèque de C++ qui permet de mettre en œuvre d’autres structures de données plus complexes et de faciliter également l’écriture de programmes. En quelques sortes, cette bibliothèque propose des algorithmes clés en main pour beaucoup de problèmes de programmation. Étant donné la complexité des programmes écrits en C++, à chaque fois que cela est possible, il recommandé d’utiliser des STL. Facteurs de qualité des logiciels : Fiabilité : écrite par des spécialistes (Andrew Koenig) Réutilisabilité : portabilité Compréhensabilité et facilité de maintenance => EFFICACITE
STL : Standard Template Library Il s’agit donc d’une bibliothèque générique qui fournit des solutions pour gérer un ensemble de données en utilisant des algorithmes efficaces. Du point de vue du programmeur, STL fournit un groupe de classes répondant à divers besoins. Tous les composants de STL sont des templates Une STL est développée selon les axes suivants : conteneurs – itérateurs - algorithmes
STL : Standard Template Library Conteneurs: En C++, une classe contenant un ensemble d’éléments d'un certain type est appelée conteneur. Ce sont donc eux qui contiendront les informations que l’on veut stocker. Itérateurs: utilisés pour parcourir les items qui se retrouvent dans les conteneurs, ils jouent un peu le rôle de pointeurs sur des éléments d’un conteneur. Algorithmes: utilisés pour faire des traitements sur les éléments qui se retrouvent dans les conteneurs.
STL : Standard Template Library Les algorithmes sont indépendants du type de données stockées. Il s’agit ainsi de connecter des algorithmes à des structures de données, par le biais des itérateurs.
Les conteneurs Les conteneurs contiennent les structures de données linéaires telles que les listes, piles, tables, etc, utilisées pour stocker des données. L’utilisation des templates dans une STL permet l’adaptation à tous les types de données. Les conteneurs les plus utilisés sont : vector Tableau list Liste doublement chaînée slist List simplement chaînée queue File- FIFO (first-in, first-out) deque Tableau avec des opération efficaces d’insertion et de suppression aux extrémités set Ensemble avec des éléments distincts stack Pile - LIFO (last in, first out) map Table de hachage multiset Comme l’ensemble mais autorise des valeurs répétées multimap Comme la table de hachage mais autorise des clefs multiples
Les méthodes des conteneurs push_front Insère un élément avant le premier (non disponible pour vector) pop_front Supprime le premier élément (non disponible pour vector) push_back Insère élément après le dernier pop_back Supprime le dernier élément empty Booléen indiquant si le conteneur est vide size Retourne le nombre d’éléments du conteneur insert Insère un élément à une position donnée erase Supprime un élément à une position donnée clear Supprime tous les éléments resize Redonne une taille au conteneur front Retourne l’adresse du premier élément back Retourne l’adresse du dernier élément remove Supprime le premier élément (non disponible pour vector) sortTrier reverseOrdre inverse findTrouver un élément swapéchange deux éléments de deux vecteurs
Les conteneurs Les conteneurs sont classés suivant deux types: séquentiels: les éléments sont ordonnés et leurs positions sont indépendantes des caractéristiques des éléments eux-mêmes associatifs: les éléments sont triés selon un certain critère; la position d’un élément est un endroit identifié par un « nom » et peut dépendre de la valeur de l’élément
Conteneurs séquentiels vector: tableau dynamique qui ne se remplit qu’à la fin - extensible et relocalisable - accès aléatoire par index en O(1) - insertions et retraits à la fin en O(1) - insertions et retraits lents au milieu deque: tableau dynamique qui peut s’étendre par les deux extrémités - exactement pareil que Vector pour la complexité - pas d’insertion au milieu - bon compromis vitesse d’accès / souplesse du remplissage list: liste doublement chaînée - insertions et retraits n’importe où en O(1) - accès au début et à la fin en O(1) - accès lent aux autres éléments Une séquence est un conteneur dans lequel les éléments sont organisés linéairement (il y a un premier, un suivant …, un dernier). On distingue trois séquences:
Les vecteurs (vector) Les conteneurs vector et deque fournissent l’accès aux éléments à travers l’indexation [ ]. Ceci permet un accès direct à un élément par le biais d’un indice. Les méthodes les plus importantes sont : - size( ) ; empty( ) ; clear( ) ; resize( ) ; - front( ) ; back( ) ; -push_front(valeur); push_back(valeur); -pop_front( ); pop_back( ); Les opérateurs les plus utilisées sont : =, = =, <, [ ].
Les vecteurs (vector) Les vecteurs sont donc des listes implémentées à l'aide de tableaux. Ils remplacent avantageusement les tableaux standards du C car leur taille est implicite et peut être redéfinie. Les éléments sont insérés à la fin du tableau. La déclaration vector v; crée un tableau d’entiers. Dans ce cas, le constructeur par défaut nous retourne un tableau vide. On peut définir un vecteur d'entiers de taille 100 de la façon suivante: vector monvecteur(100); D’une manière générale vector v; //Déclaration d’un Vector de type "T". vector v(size_type n); //Déclaration d’un Vector de type "T" et de taille "n". vector v(size_type n,const T& t); //Déclaration d’un Vector de type "T" //et de taille "n" contenant la valeur "t".
Les vecteurs (vector) Exemple 1 vector v(3); // Déclaration d’un vector de 3 éléments. v[0] = 7; v[1] = v[0] + 3; v[2] = v[0] + v[1]; // v[0] == 7, v[1] == 10, v[2] == 17 reverse(v.begin(), v.end()); // v[0] == 17, v[1] == 10, v[2] == 7
Les vecteurs (vector) Exemple 2 int main() { vector unTableau; for (int i = 1; i < 10; ++i) { unTableau.push_back(i); } for (int j = 0; j < unTableau.size(); ++j) { cout << unTableau[j] << ' '; } cout << endl; return 0; }
Les vecteurs (vector) Exemple 3 #include using namespace std; int main ( ) { vector vect1, vec2(3); vec1.push_back(10) ; vec1.push_back(12) ; vec1.push_back(20) ; vec2[0] = 1; vec2[2] = 10; vec2[3] = 20; cout << “Les deux vecteurs ” cout << (vec1 != vec2 ? “ sont différents “ : “ sont identiques “) ; return 0 ; }
Les vecteurs (vector) Exemple 4 # include # include using namespace std; int main (void) { vector v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); vector v2(3); v2[0] = 1, v2[1] = 2; v2[2] =3; if (v1==v2) cout << " OK " ; else cout << " ???? (stupeur) "; return 0; }
Les vecteurs (vector) Exemple 5 Un tableau à deux dimensions est un vecteur de vecteurs. Le constructeur de vecteurs peut initialiser la taille du tableau et aussi la valeur initiale. #include using namespace std; int main(){ // Déclarer un tableau à deux dimensions et initialiser à 0 vector > vI2Matrix(3, vector (2,0)); vI2Matrix[0][0] = 0; vI2Matrix[0][1] = 1; vI2Matrix[1][0] = 10; vI2Matrix[1][1] = 11; vI2Matrix[2][0] = 20; vI2Matrix[2][1] = 21; cout << "Itérer par indice:" << endl; for(int ii=0; ii < 3; ii++) { for(int jj=0; jj < 2; jj++) { cout << vI2Matrix[ii][jj] << endl; } return 0; }
Les vecteurs (vector) Exemple 6 #include #include using namespace std; int main(){ // Vecteur de 3 éléments initialisé à 0 vector vI1Matrix(3,0); // Vecteur de 4 éléments contenant chacun un autre vecteur // vecteur vI1Matrix qui a été initialisé à 0 vector > vI2Matrix(4, vI1Matrix); // Vecteur 5 éléments contenant chaucn un vecteur de dimension deux vector > > vI3Matrix(5, vI2Matrix); //Ou alors si on veut en une seule déclaration : //vector > > vI3Matrix(2, vector > (3, vector (4,0)) ); for(int kk=0; kk<4; kk++) { for(int jj=0; jj<3; jj++) { for(int ii=0; ii<2; ii++) { cout << vI3Matrix[ii][jj][kk] << endl; } } return 0; }
Les vecteurs – Capacité Il est important de distinguer la taille et la capacité d’un vector car il maintient à l’interne un tableau qui n’est pas nécessairement rempli Taille: nombre d’éléments effectivement contenus dans le tableau interne Capacité: taille du tableau interne. Elle est augmentée lorsqu’on veut insérer un nouvel item et que le tableau est plein Initialement la capacité est 0 Aussitôt qu’on ajoute un premier élément, on alloue un tableau de taille 1 (capacité = 1) Lorsque la capacité est réajustée, il y a allocation d’un nouveau tableau et copie d’éléments dans ce nouveau tableau La réallocation est donc coûteuse On peut fixer la capacité du vecteur: vector unTableau; unTableau.reserve(10);
Les vecteurs – Capacité Dans la plupart des implémentations, la capacité est doublée chaque fois qu’elle doit être augmentée Dans.NET on ajoute un nombre de cellules qui croît à chaque réallocation selon la suite de Fibonacci Dans tous les cas, le principe est le même: plus le tableau est grand, plus il sera agrandi lors d’un réallocation Pourquoi? Tout simplement parce plus il est grand, plus l’allocation coûte cher (beaucoup d’éléments à recopier). On veut donc diminuer le nombre de ces réallocations. Bien sûr, cela a pour effet de causer une éventuelle sur-utilisation de mémoire
Les vecteurs – Capacité int main() { vector unTableau; cout << "capacite initiale: " << unTableau.capacity() << endl; for (int i = 1; i < 10; ++i) { unTableau.push_back(i); cout << "capacite: " << unTableau.capacity() << endl; } for (int j = 0; j < unTableau.size(); ++j) { cout << unTableau[j] << ' '; } cout << endl; return 0; } Exemple 7
Les listes (list) Liste doublement chaînée Un élément pointe sur son prédécesseur et son successeur Les listes sont des séquences ordonnées d‘éléments d'un certain type. Elles sont implémentées à l'aide de listes doublement chaînées: On peut définir une liste de 100 entiers de la façon suivante: list maliste(100);
Les listes (list) Les méthodes les plus importantes sont : size( ) ; empty( ) ; clear( ) ; resize( ) ; front( ) ; back( ) ; push_fron(valeur); push_back(valeur); pop_front( ); pop_back( ); remove (valeur); Avantages : ajout/suppression se font en O(1) Inconvénients : l’accès aux éléments se fait en O(n).
Les listes (list) - Caractéristiques Pas d’accès direct à un élément au milieu de la liste On peut insérer ou retirer un élément n’importe où à un coût constant L’insertion ou le retrait d’élément n’invalidera jamais les pointeurs, références et itérateurs sur d’autres éléments, comme c’est le cas lors de réallocation avec les vectors et les deques Plusieurs fonctions pour déplacer, insérer ou retirer des éléments (efficaces parce qu’elle ne font que réaffecter des pointeurs) Exemples – unique() pour éliminer les duplications d’éléments consécutifs – reverse() beaucoup d’autres
Les listes (list) Exemple 1 #include int main ( ) { list liste1 ; liste1.push_back(20) ; liste1.push_back(12) ; liste1.push_back(15) ; liste1.sort( ) ; liste1.reverse() ; cout << “le début de liste est “<< liste1.front cout << “ la fin de liste est “<<liste1.back ; return 0 ; }
Les listes (list) Exemple 2 int main() { list uneListe; for (char c = 'a'; c <= 'z'; ++c){ uneListe.push_back(c); } while (!uneListe.empty()){ cout << uneListe.front() << ' '; uneListe.pop_front(); } cout << endl; return 0; }
Les listes (list) Exemple 3 int main() { list uneListe; for (char c='a'; c<='z'; ++c){ uneListe.push_back(c); } uneListe.remove_if(voyelle); uneListe.reverse(); while (!uneListe.empty()){ cout << uneListe.front() << ' '; uneListe.pop_front(); } cout << endl; return 0; } bool voyelle(char c) { return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); }
Les listes (list) Exemple 4 #include using namespace std; list list1; // Ajoute des valeurs à la fin de la liste, initialement vide. // la fonction membre "push_back" ajoute un élément à la fin de la liste int value1 = 10; int value2 = -3; list1.push_back (value1); list1.push_back (value2); list1.push_back (5); list1.push_back (1); // Imprime les éléments de la liste Output cout << endl << "List values:" << endl; // Répéter aussi longtemps qu’il reste des éléments dans la liste while (list1.size() > 0) { int value = list1.front(); // Output la valeur value. cout << value << endl; // Supprime l’élément du début de la liste. list1.pop_front(); }
Liste (list) et vecteur (vector) Fonctions communes à vector et list void push back(const Object& x):Ajoute x à la fin du conteneur. Notons que si un objet est ajouté à la fin d'un vecteur alors sa taille est automatiquement augmentée. void pop back(): Enlève le dernier objet du conteneur const Object& back() const: Retourne l'objet situé µa la fin du conteneur const Object& front() const: Retourne l'objet situé au début du conteneur Pour les conteneurs de type list seulement void push front(const Object& x): Ajoute x au début du conteneur void pop front(): Enlève le premier objet du conteneur Pour les conteneurs de type vector seulement Object& operator[] (int i): Retourne l'objet situé µa la position i du conteneur Object& at (int i): Comme le précédent mais avec un contrôle de la plage des valeurs. Déclenche une exception out of range lorsqu'un indice es tincorrect. void resize( int nouvelle taille): Redéfini la taille du conteneur int capacity() const: Retourne la capacité du conteneur void reserve(int nouvelle capacite): Redéfini la capacité du conteneu
Double end queue (deque) Tableau dynamique Peut s’étendre dans les deux sens La fonction push_back() pour ajouter à la fin La fonction push_front() pour ajouter au début Une deque se comporte comme un vecteur à la différence que l’ajout et la suppression d’une valeur au début de la structure se fait en O(1).
Double end queue (deque) Cette structure est intéressante dans le cas où les insertions et suppressions se font aux extrémités et où on a besoin d’un accès efficace aux éléments. Les méthodes les plus importantes sont comme dans vector. Exemple: #include using namespace std; int main ( ) { deque DD1, DD2(3); DD1.push_back(1); DD1.push_back(2); DD1.push_back(3); DD2[0] =1; DD2[1]=2; DD2[3]=3; cout << "Deque " ; (DD1 == DD2) ? cout << "identiques" : cout <<"différents"; cout << endl; return 0 ; }
STL – L’adaptateur de conteneurs Stack ( la pile ) Peut être implémentée avec Vector, List, Deque Principe LIFO : push et pop à la même extrémité Queue ( la file ) Dérive d’un deque avec en plus les méthodes push(valeur) et pop( ). Principe FIFO Priority_queue ( la file par priorité ) Dépend d’une fonction de comparaison: priority_queue, fcomp >
La pile (stack) #include using namespace std; stack s; s.push(1); s.pop(); int n = s.size(); if(s.empty()){…} int i = s.top(); Lib « stack » stack est générique Déclaration (pile d’entiers) Empiler Dépiler Taille de la pile Pile vide? Sommet de la pile
La pile (stack) #include using namespace std; int main() { stack EntStack; int Num; cout << "introduire un entier (ou CTRL z pour terminer): "; cin >> Num; while(!cin.fail()) { EntStack.push(Num); cout << "Introduire un entier (or CTRL z pour terminer): "; cin >> Num; } cout << endl << "Les nombres dans l’ordre inverse sont comme ci-dessous" << endl; while (!EntStack.empty()) { Num = EntStack.top(); EntStack.pop(); cout << Num << endl; } return 0; } L’attribut privé est un deque. Les méthodes les plus importantes sont : push(valeur), pop( ), top( ), empty() et size()
La file (queue) #include using namespace std; queue f; int n =f.size(); f.push(10); f.pop(); int tete =f.front(); int fin = f.back(); Lib « queue » queue est générique Déclaration Taille Enfiler Défiler Tête de la file Fin de la file
Conteneurs associatifs Nous nous intéresserons à deux types de conteneur associatif: set: les éléments sont triés et ne peuvent apparaître qu’une seule fois À moins d’indication contraire, les éléments sont ordonnés en utilisant l’opérateur < par défaut Il faut donc que cet opérateur soit défini pour le type d’objet qui est stocké dans le set Normalement implémenté en un arbre équilibré, ce fournit donc un algorithme efficace pour la rercherche d’une valeur map: les éléments sont des paires clé/valeur et sont triés en fonction de la clé; chaque clé ne peut apparaître qu’une seule fois Il existe des versions (multiset et multimap) qui permettent les répétitions
Ensembles (set) Les méthodes les plus importantes sont : -size ( ); empty ( ); -count(elem): retourne le nombre d’occurrences de l’élément dans le set (qui, évidemment, peut être supérieur à 1 seulement dans le cas d’un multiset) -find(elem): retourne la position de la première occurrence de l’élément dans le set (ou multiset) -insert(elem) : insère un nouvel élément -erase(elem) : retire toutes les occurrences de l’élément et retourne le nombre d’éléments retirés -clear() : vide le conteneur Les opérateurs les plus utilisés sont: =, = =, <, [ ]
Ensembles (set) Exemple 1 #include using namespace std; int main( ) { set Ens; Ens.insert(10); Ens.insert(20); Ens.insert(14) ; Ens.erase(10) ; cout << “le cardinal de l’ensemble Ens est “ << Ens.size() ; return 0 ; }
Dictionnaire (map) Les éléments sont une paire formée d’une clé jumelée à une valeur Les éléments sont triés selon la clé Chacune des clés ne peut exister qu’une seule fois (mais peut être répétée dans un multimap) Contrairement aux sets, on peut accéder directement aux éléments #include map coll; coll["VAT"] = 0.15; // VAT est la clé coll["Pi"] = ; coll["an arbitrary number"] = ; coll["Null"] = 0;
Map - Caractéristique L’indice utilisé pour identifier un élément est la clé Le type d’un élément de map est une paire pair où Key est le type de la clé, et T le type des items contenus dans le map Tout comme le set, le map fournit les méthodes count(clé) et find(clé) On ne peut pas modifier une clé contenue dans le map, mais on peut modifier l’item qui lui est associé Pour retirer un élément, il suffit d’appeler la méthode erase(cle), qui élimine toutes ses occurrences et retourne le nombre d’éléments retirés La classe set peut être vu comme un cas particulier de la classe map où les deux composantes d'une paire sont identiques. On utilise un set lorsque la clef est la seule information qui nous intéresse.
Conteneurs - Requis L’élément qui sera inclus dans un conteneur doit posséder: constructeur de recopie opérateur = destructeur possiblement, un constructeur par défaut possiblement, un test d’égalité (opérateur ==) possiblement, un critère d’ordre (operateur < et autres) De plus, tous les conteneurs ont un attribut size_type, qui est en fait un entier non signé pour représenter le nombre d’items dans le conteneur
Les itérateurs Exemple d’itérateur
Les itérateurs Exemple d’itérateur int c[5] = {3,4,5,6,7}; int* it=&c[0]; int* itFin = &c[5]; //Itérateur de fin de conteneur Tester si on a fini de parcourir le conteneur : it!= itFin void affiche( int* itDebut, int* itFin) { while( itDebut!= itFin) { cout << *itDebut; itDebut++; } }
Les itérateurs et la STL Ils servent de lien entre les conteneurs et les algorithmes car permettant d’accéder aux informations de la même manière. Un itérateur peut être vu comme un pointeur sur un élément d’un conteneur. Par définition donc, un itérateur permet de récupérer une donnée pour la manipuler et de passer à la suivante pour mettre en place l’itération. Par ailleurs, chaque conteneur fourni un type d’itérateur. Exemple le type list donne un itérateur de type : list :: iterator. Tous les conteneurs définissent deux types d’itérateur container::iterator //permet de naviguer en mode lecture/écriture container::const_iterator //permet de naviguer en mode lecture seulement
Tous les conteneurs offrent les mêmes fonctions de base permettant aux itérateurs d’accéder aux éléments begin() Retourne un itérateur pointant au premier élément du conteneur end() Retourne un itérateur pointant après le dernier élément du conteneur Les itérateurs et la STL
Première version: Une méthode hasNext() pour savoir si on est arrivé à la fin Une méthode next() pour passer à l’élément suivant Ce type d’itérateur est utilisé en Java Deuxième version: L’itérateur est manipulé comme s’il s’agissait d’un pointeur La manipulation se fait par la surcharge des opérateurs *, ++, --, ==, != et =. Ce type d’itérateur est utilisé en C++ Implémentation Les itérateurs et la STL
Exemple 1#include using namespace std; int main( ) { list list1; for (int i = 1; i<=40; i++) list1.push_back(i+i); list :: iterator i; for (i = list1.begin( ); i!=list1.end( ); i++) cout <<*i << “ “ ; list :: reverse_iterator rev; for (rev = list1.rbegin( ); rev!=list1.rend( ); rev++) cout <<*rev << “ “ ; return 0 ; } Les itérateurs et la STL
Dans l’exemple précédent, on utilise également un itérateur inversé (reverse_iterator) qui permet le parcours dans le sens inverse. Les principaux opérateurs sont * donnant accès à la valeur, ++ et -- pour incrémenter et décrémenter une valeur. - Opérateur * : Retourne l’élément de la position courante - Opérateur ++ : Fait pointer l’itérateur à l’élément suivant - Opérateur == : Indique si 2 itérateurs pointent sur le même élément - Opérateur = : Assigne un itérateur Les itérateurs et la STL
#include using namespace std;... list words; list ::iterator words_iter;... unsigned int total_length = 0; for (words_iter=words.begin(); words_iter != words.end(); words_iter++) { total_length += (*words_iter).length(); // correct // total_length += *words_iter.length(); // faux !! } cout << "Total length is " << total_length << endl; Exemple 2 Les itérateurs et la STL
#include // création d’une liste de caractères list coll; for (char c = ‘a’; c<=‘z’; c++) coll.push_back(c); // impression de la liste à l’aide d’un itérateur list ::const_iterator pos; for(pos = coll.begin(); pos != coll.end(); pos++) cout << *pos << ‘ ‘; Exemple 3 Les itérateurs et la STL
Les algorithmes La STL fournit un nombre important d’algorithmes génériques les plus utilisés, tels que les algorithmes de parcours, de recherche et de tri. Ils s’appliquent sur les conteneurs. Les algorithmes ne sont pas des fonctions membres des classes des conteneurs. Ce sont plutôt des fonctions globales qui opèrent avec des intérateurs. Pour manipuler cette bibliothèque, il suffit d‘incorporer dans l’entête #include On distingue deux types d’algorithmes: ceux qui modifient les conteneurs et ceux qui ne le modifient pas. Voir les nombreux exemples fournis dans la semaine 5!
Algorithmes de non modification (non mutating algorithms) find Trouver la première occurrence d’une valeur donnée. find_ifTrouver la première apparition d’un prédicat. find_first_of Trouver la première occurrence d’une valeur d’une séquence dans une autre. adjacent_find Trouver la première occurrence d’une paire de valeurs adjacente count Compter l’apparition d’une valeur dans une séquence. count_if Compter le nombre de fois qu’un prédicat apparait dans une séquence. equal Comparaison élément à élément le contenu de deux conteneurs. Les conteneurs peuvent de types différents max_elementTrouver le plus grand élément d’une séquence. min_elementTrouver le plus petit élément d’une séquence. search Recherche la première occurrence d’un sous ensemble d’éléments.
reverse()inverse l'ordre des éléments du conteneur partition() met au debut tous les éléments qui respectent la condition passée en paramètre sort() trie les éléments stable_sort() trie les éléments de manière à ce que les éléments égaux restent dans le même ordre Algorithmes qui altèrent l’ordre des données
Algorithmes de modification (mutating algorithms) transform for_each Merge Modifie et copie les éléments d’un conteneur Réalise sur tous les éléments l’opération passée en paramètre Fusionne deux conteneurs triés en un seul ensemble trié de valeurs copy Copier une séquence replace Remplacer les éléments d’une séquence par une valeur donnée. replace_if Remplacer les éléments égaux à une prédicat remove Supprimer toutes les occurences d’un élément donné dans le conteneur remove_if Supprimer tous les éléments qui respectent à un prédicat passé en paramètre reverse Inverser une séquence. random_shuffle Aléatoirement réorganiser les éléments en utilisant une distribution uniforme. fill Remplacer les éléments d’une séquence avec une valeur. generate Remplacer les éléments d’une séquence avec le résultat d’une opération donnée. accumulate Sommer les éléments d’une séquence. unique Élimine tous les éléments qui sont égaux à leur prédécesseur
Les algorithmes Remarques sur remove() les listes ont leur propre méthode remove(), plus efficace Ne retire pas réellement les éléments du conteneur Les éléments sont retiré de la position où ils se trouvent et les reste est décalé en conséquence Par contre, comme la taille du conteneur ne change pas, on se retrouve à avoir à la fin des valeurs invalides La fonction remove() retourne un itérateur pointant sur la première position invalide Pour “nettoyer le conteneur, on peut appeler erase à partir de cette position
Pour encore plus de flexibilité, certains algorithmes permettent l’usage de fonctions définies par l’usager Les fonctions sont appelées à l’interne par l’algorithme et généralement appliquées à chaque élément du conteneur Un prédicat est une fonction qui retourne une valeur booléenne (vrai ou faux) Les prédicats peuvent être utilisés pour spécifier un critère de recherche ou de tri Utilisation de prédicats dans les algorithmes
Algorithmes de manipulation d’ensembles includes Vérifie si les éléments d’un conteneur forment un sous-ensemble d’un autre conteneur set_unionRéalise l’union ensembliste de deux conteneurs Set_intersection Réalise l’intersection ensembliste de deux conteneurs binary_search Trouver une valeur donnée en utilisant la recherche dichotomique
Les foncteurs Un foncteur est un objet se comportant comme une fonction Repose sur la surcharge de l’opérateur () class PrintInt { public: void operator() (int elem) { cout << elem << “ “; } }; int main() { vector coll;... for_each (coll.begin(), coll.end(), PrintInt()); cout << endl; return 0; }
Les foncteurs void ajouter10(int & elem) { elem += 10; } int main() { vector unTableau;... for_each(unTableau.begin(), unTableau.end(), ajouter10);... } template void ajouter(int & elem) { elem += laValeur; } int main() { vector unTableau;... for_each(unTableau.begin(), unTableau.end(), ajouter );... } class Ajouter { public: Ajouter(int v) : laValeur(v) {} void operator()(int&elem)const { elem += laValeur; } private: int laValeur; }; int main() { vector unTableau; … int increment; cin >> increment; for_each(unTableau.begin(), unTableau.end(), Ajouter(increment)); … } …connue à l’exécution …connue à la compilation Valeur connue d’avance Exemple. Ajout d’une valeur à tous les éléments d’un vector