Module 8 - cours du 27 octobre (quiz 7) - matière du final

STL : conteneurs et itérateurs

Leçon Moodle « Itérateurs et conteneurs » | Simulateur de hockey : exercice « Conteneurs » (afficherEquipe, afficherTroisEtoiles) | Horstmann, chapitre sur la bibliothèque standard | Sera mis en pratique au TP4

vector, deque, listset, map, multimapunordered_mapstack, queue, priority_queueItérateurs : begin, end, ++, *InvalidationChoisir un conteneur

1. La bibliothèque standard de structures de données

La STL (Standard Template Library) repose sur trois piliers indépendants : les conteneurs (structures qui stockent des objets), les itérateurs (une façon uniforme de parcourir n'importe quel conteneur) et les algorithmes (module 9), qui travaillent sur des itérateurs et fonctionnent donc avec tous les conteneurs. Les foncteurs et lambdas (module 9) paramètrent les algorithmes. Tout est générique : vector<T>, map<K, V>.

FamilleConteneursOrganisationPoints forts
Séquentielsvector, deque, list, array, forward_listordre d'insertion, choisi par le programmeurvector : accès par indice et insertion en fin ; deque : aussi en tête ; list : insertion et suppression n'importe où sans déplacer les autres
Associatifs ordonnésset, multiset, map, multimaparbre équilibré trié par operator< de la clérecherche, insertion, suppression en O(log n) ; parcours trié
Associatifs non ordonnésunordered_set, unordered_map (+ multi)table de hachagerecherche en O(1) en moyenne ; pas d'ordre
Adaptateursstack, queue, priority_queueinterface restreinte au-dessus d'un autre conteneurLIFO, FIFO, file de priorité (le plus grand d'abord)

Opérations communes et spécifiques

Pièges
  • m[cle] insère la clé avec une valeur par défaut si elle n'existe pas : pour tester la présence, utilisez find ou count. Et operator[] n'existe pas sur une map constante.
  • Les clés d'un set ou d'une map sont constantes : on ne modifie pas une clé en place (on supprime et on réinsère). Le type de clé doit avoir operator< (ou on fournit un comparateur, module 9).
  • list n'a ni [] ni at : on avance avec un itérateur. sort de <algorithm> ne marche pas sur une liste (itérateurs non aléatoires) : utiliser l.sort().
  • pop_back, pop_front, pop ne retournent pas l'élément : lire back() / front() / top() avant.

2. Les itérateurs

Un itérateur est un objet qui désigne un élément d'un conteneur et sait passer au suivant : un pointeur généralisé. Chaque conteneur fournit begin() (premier élément) et end() (position après le dernier : ne jamais le déréférencer). Les opérations : *it (l'élément), it->membre, ++it, et la comparaison it != fin.

CatégorieConteneursOpérations
Accès aléatoirevector, deque, array, string++ -- + n - n it[n] < > ; sort exige cette catégorie
Bidirectionnellist, set, map, multiset, multimap++ -- seulement : pas de it + 2, utiliser std::advance(it, 2) ou std::next(it, 2)
Avant (forward)forward_list, unordered_*++ seulement

Fonctions utilitaires (<iterator>) : std::distance(a, b) (nombre d'éléments entre deux itérateurs), std::advance(it, n), std::next(it, n), std::prev(it).

Insérer et supprimer avec des itérateurs

Invalidation d'itérateurs
  • vector : push_back et insert peuvent réallouer : tous les itérateurs, pointeurs et références deviennent invalides. erase invalide ceux qui suivent la position.
  • list, set, map : seuls les itérateurs vers l'élément supprimé sont invalidés ; les insertions n'invalident rien.
  • Supprimer en parcourant : toujours it = c.erase(it); dans la branche de suppression, et ++it seulement dans l'autre branche.

Pas à pas : supprimer les nombres pairs pendant le parcours

Le schéma correct : it = v.erase(it) remplace ++it quand on supprime. Suivez l'itérateur et le contenu.

3. Choisir le bon conteneur

BesoinConteneur
Accès par indice, ajout en fin, parcours rapide (le choix par défaut)vector
Ajouts et retraits fréquents aux deux extrémitésdeque
Insertions et suppressions fréquentes au milieu, itérateurs stables, pas d'accès par indicelist
Éléments uniques, parcours trié, test d'appartenance rapideset
Association clé → valeur, parcours trié par clé (dictionnaire, comptage)map
Plusieurs valeurs par clémultimap
Association sans besoin d'ordre, vitesse maximaleunordered_map
Dernier entré, premier sorti / premier entré, premier sorti / le plus prioritaire d'abordstack / queue / priority_queue

Complexités à retenir : vector [] O(1), push_back O(1) amorti, insert au milieu O(n) ; list insert/erase O(1) à une position connue, mais trouver la position O(n) ; set/map O(log n) par opération ; unordered O(1) en moyenne.

Exemple du simulateur de hockey : afficher une équipe par numéro de chandail

L'exercice « Conteneurs » demande d'afficher les membres d'une équipe triés par numéro, à partir d'une liste de tous les membres : on copie la liste, on retire l'équipe adverse, on recopie dans un conteneur trié. Un set avec un comparateur (foncteur, module 9) trie automatiquement.

Devinez l'affichage : map, set et itérateurs

Explication

stock["kiwi"] += 2 crée kiwi à 0 puis ajoute 2. insert({"kiwi", 10}) ne remplace pas une clé existante. Le parcours d'une map est trié par clé (kiwi avant pomme). Le set ignore le doublon 5 : {1, 2, 3, 5}, l'élément après 3 est 5. find ne crée pas de clé.

4. À retenir

Quiz rapide - Module 8

Que désigne v.end() ?

  • Le dernier élément
  • Le premier élément
  • La position après le dernier élément ; on ne le déréférence jamais
  • La taille du vecteur
L'intervalle d'un conteneur est [begin, end[ : end sert de sentinelle de fin de boucle.

Pourquoi écrire it != v.end() plutôt que it < v.end() ?

  • C'est plus rapide
  • < n'existe que pour les itérateurs à accès aléatoire (vector, deque) ; != fonctionne pour tous les conteneurs
  • < ne compile jamais sur des itérateurs
  • Par convention seulement
Un code écrit avec != reste correct si on remplace le vector par une list.

Que fait m["x"] sur une map<string, int> m qui ne contient pas « x » ?

  • Lance out_of_range
  • Retourne 0 sans rien insérer
  • Retourne m.end()
  • Insère la clé « x » avec la valeur 0 et retourne une référence vers cette valeur
D'où l'intérêt de m["x"]++ pour compter, et le danger de tester la présence avec [].

Quel conteneur pour insérer et supprimer souvent au milieu, sans jamais accéder par indice ?

  • list
  • vector
  • map
  • stack
Insertion et suppression en O(1) à une position connue par itérateur, sans déplacer les autres éléments.

Quel est le contenu de s après set<int> s; s.insert(3); s.insert(1); s.insert(3); s.insert(2); et dans quel ordre est-il parcouru ?

  • {3, 1, 3, 2} dans l'ordre d'insertion
  • {3, 1, 2} dans l'ordre d'insertion
  • {1, 2, 3} en ordre croissant
  • {3, 2, 1} en ordre décroissant
Unique et trié par operator< ; size() vaut 3.

Comment supprimer correctement l'élément désigné par it au cours d'un parcours de vector ?

  • v.erase(it); ++it;
  • it = v.erase(it); sans incrémenter
  • v.erase(it); --it;
  • delete it;
erase invalide it et retourne l'itérateur sur l'élément suivant.

Après auto it = v.begin(); v.push_back(5); sur un vector :

  • it désigne toujours le premier élément
  • it désigne 5
  • it vaut end()
  • it peut être invalide si push_back a réalloué
Toute insertion dans un vector peut déplacer tous les éléments en mémoire.

Comment accéder à la clé et à la valeur via un itérateur de map ?

  • it->first et it->second
  • it.key() et it.value()
  • *it et it[1]
  • it->cle et it->valeur
Un élément de map est une pair<const K, V>.

Pourquoi sort(l.begin(), l.end()) ne compile-t-il pas pour une list ?

  • Les listes sont toujours triées
  • sort n'existe pas dans la STL
  • sort exige des itérateurs à accès aléatoire ; list n'offre que des bidirectionnels : utiliser l.sort()
  • Les listes ne sont pas copiables
La liste fournit ses propres sort, remove, unique, merge, splice.

Que retourne st.pop() sur une stack<int> ?

  • L'élément retiré
  • Rien (void) : il faut lire top() avant
  • Un itérateur
  • true si la pile n'est pas vide
Même convention pour pop_back, pop_front et queue::pop.

Quelle est la complexité de la recherche d'une clé dans une map de n éléments ?

  • O(1)
  • O(n)
  • O(n log n)
  • O(log n)
Arbre binaire équilibré. unordered_map : O(1) en moyenne.

Que fait std::advance(it, 3) ?

  • Avance l'itérateur de 3 positions, même pour une list
  • Retourne le troisième élément
  • Insère 3 éléments
  • Ne compile que pour vector
it + 3 ne compile que pour les itérateurs à accès aléatoire ; advance s'adapte (3 incrémentations pour une list).

Peut-on modifier la clé d'un élément de set via son itérateur ?

  • Oui, avec *it = nouvelleValeur
  • Oui, avec it->first = ...
  • Non : les éléments d'un set sont constants ; il faut supprimer puis réinsérer
  • Oui, si le set est non const
Modifier une clé casserait l'ordre de l'arbre.

Quel type d'itérateur utiliser dans une méthode const pour parcourir un attribut vector<Moteur> moteurs_ ?

  • vector<Moteur>::iterator
  • vector<Moteur>::const_iterator (ou auto it = moteurs_.cbegin())
  • Moteur*
  • reverse_iterator
Dans une méthode const, begin() retourne déjà un const_iterator ; un iterator ne compile pas.

Exercices à développement

map

Compter les mots

Écrivez map<string, int> compterMots(const vector<string>& mots), puis une fonction qui affiche les mots apparaissant au moins deux fois, par ordre alphabétique, et enfin une fonction qui retourne le mot le plus fréquent (le premier dans l'ordre alphabétique en cas d'égalité).

Solution
listItérateurs

Insertion triée dans une liste

Écrivez void insererTrie(list<int>& l, int val) qui insère val dans une liste triée en gardant le tri, avec un itérateur (pas d'indice). Puis void retirerPairs(list<int>& l) de deux façons : avec une boucle et erase, puis avec remove_if de la liste.

Solution
Simulateur de hockeyset, multimap

Les trois étoiles

Match possède list<Joueur*> joueurs_. Écrivez void Match::afficherTroisEtoiles() const qui affiche les trois joueurs ayant marqué le plus de buts (toutes équipes confondues), en copiant les joueurs dans un conteneur approprié. Proposez une version avec multimap et une avec vector + tri (module 9).

Solution

Un set ne conviendrait pas : deux joueurs avec le même nombre de buts seraient considérés comme un doublon par un comparateur sur les buts.

Conception

Quel conteneur ?

Pour chaque situation, choisissez le conteneur et justifiez : (1) l'historique des commandes d'un éditeur avec « annuler » ; (2) les clients qui attendent au comptoir ; (3) le répertoire téléphonique nom → numéro ; (4) les numéros de chandail déjà attribués (vérifier rapidement qu'un numéro est libre) ; (5) les tâches à exécuter par priorité ; (6) les notes d'un étudiant par cours, un cours pouvant avoir plusieurs notes ; (7) une file d'attente où l'on retire aussi par l'avant et par l'arrière.

Solution
  1. stack : dernier entré, premier annulé.
  2. queue : premier arrivé, premier servi.
  3. map<string, string> (ou unordered_map si l'ordre alphabétique est inutile).
  4. set<int> (ou unordered_set) : unicité et count en O(log n) ou O(1).
  5. priority_queue : top() donne la plus prioritaire.
  6. multimap<string, double>, ou map<string, vector<double>>.
  7. deque : pop_front et pop_back en O(1).
AffichageItérateurs

Devinez l'affichage

Vérifiez votre réponse

Explication

it désigne 15 ; insert met 10 avant 15 (l = 4 8 10 15 16 23 42) ; erase(it) retire 15 et retourne l'itérateur sur 16 ; taille 6. Après push_front(1) et remove(8) : 1 4 10 16 23 42, affiché à l'envers. Le vecteur construit à partir de la liste (1 4 10 16 23 42) : v[1] = 4, dernier = 42, 23 est à l'indice 4.