C++ : TP 4 - Tris de vecteurs
© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 10/01/2000 -
Dernière mise à jour : 25/01/2001

Remarques préliminaires :
-
Tous les exercices de ce TP doivent être effectués dans le répertoire dirtrivect qui sera considéré comme répertoire courant.
-
Recopier les fichiers dirvector/Makefile et dirvector/config.h dans le répertoire courant.
-
Les exercices de cette séance de TP sont consacrés à quelques méthodes de tri.
Il en existe de très nombreuses, certaines ont déjà été étudiées dans d'autres modules.
Il serait impossible d'en faire une étude exhaustive dans le cadre de ce cours, et cela relèverait plus d'un module d'algorithmique que d'apprentissage d'un langage.
Il est cependant impensable qu'un informaticien n'en ait pas quelques notions.
De plus les méthodes étudiées ici (et dans quelques TPs ultérieurs) sont un excellent support à l'apprentissage de la manipulation des classes conteneurs (les vectors), ce qui est notre objectif présent.
Elles ne présentent pas de difficultés algorithmiques majeures.
Enfin, leur efficacité relative sera comparée ultérieurement.
-
Rappel :
Chemins d'accès aux sources des corrigés.
Sommaire
Tri par insertion
exo_01
La première méthode de tri étudiée ici permet de conserver au vecteur sa propriété d'être trié pendant toute la durée de sa construction (invariant de boucle !).
Cette méthode est connue sous le nom de tri par insertion.
Recopier le fichier dirvector/exo_04.cxx dans le fichier exo_01.cxx.
Dans l'espace de noms anonyme, ne conserver que la déclaration de classdef et la fonction EditVString().
Supprimer Exo_04().
Les différentes versions de la fonction Exo_01() seront constituées d'une boucle dans laquelle :
-
chaque ligne d'un fichier est lue.
Comme précédemment, il est recommandé de créer un petit fichier texte d’au maximum 10 lignes courtes.
-
la ligne lue est insérée à sa place dans l'ordre alphabétique dans un vecteur (voir plus loin).
Enfin le vecteur trié est édité en utilisant EditVString().
L'insertion de la ligne peut être faite de multiples façons qui devront être testées, successivement.
exo_01a
-
Liste des fichiers créés ou modifiés :
exo_01.cxx,
Makefile
Dans la fonction Exo_01a(), allonger le vecteur d'une unité pour pouvoir y ranger la nouvelle chaîne en utilisant la fonction resize().
Si aucune chaîne n'a encore été insérée, ajouter la chaîne lue et passer à la boucle suivante.
Dans le cas contraire, rechercher, à partir de la fin du vecteur, la position de la chaîne lue en utilisant un indice de type CVString::size_type.
Utiliser un schéma alternatif en sortie de boucle.
Dans le fichier Makefile, modifier la cible clean pour qu'elle efface aussi les exécutables à 3 caractères après le _ : exo_01a par exemple.
Compiler et tester.
Corrigés :
exo_01.cxx
-
Makefile
Sommaire
exo_01b
-
Liste des fichiers créés ou modifiés :
exo_01.cxx
Toujours dans le fichier exo_01.cxx, recopier la fonction Exo_01a() dans la fonction Exo_01b().
Pour allonger le vecteur d'une unité, y ranger la nouvelle chaîne au moyen de la fonction push_back().
Modifier la fonction Exo_01b() en conséquence.
Remarquer que cela ne fait que déplacer, sans le résoudre, le probleme du vecteur vide.
Compiler et tester.
Corrigés :
exo_01.cxx
Sommaire
exo_01c
-
Liste des fichiers créés ou modifiés :
exo_01.cxx
Après avoir recopié la fonction Exo_01b() dans Exo_01c(), remplacer le type de l'indice par un type int.
Cette modification permet à i de prendre des valeurs négatives et simplifie beaucoup le programme
[1].
Vous pouvez enfin, dans cette version, remplacer le schéma alternatif final par l'utilisation de l'opérateur ?: ou de la fonction max (i, j) qui renvoie le plus grand de deux entiers.
Il y a même encore plus simple, à chercher ...
Compiler et tester.
Corrigés :
exo_01.cxx
Sommaire
Utilisation de la fonction insert()
exo_01d
-
Liste des fichiers créés ou modifiés :
exo_01.cxx
Recopier la fonction Exo_01c() dans Exo_01d().
Au lieu de commencer par augmenter la taille du vecteur avant de placer la nouvelle valeur, il est plus simple de rechercher la position à laquelle la nouvelle chaîne doit être placée, puis l'insérer au moyen de la fonction membre insert(), analogue à celle des strings :
ident.insert (Pos, Valeur); |
où Pos est la position de l'itérateur à laquelle l'insertion de la Valeur doit être faite : une copie de Valeur est placée devant l'élément désigné par Pos.
Compiler et tester.
Corrigés :
exo_01.cxx
Sommaire
Utilisation de la fonction sort()
exo_01e
-
Liste des fichiers créés ou modifiés :
exo_01.cxx
La bibliothèque C++ offre un grand nombre d'outils permettant la manipulation des conteneurs, parmi lesquels plusieurs fonctions de tri.
Sauf cas particulier, l'utilisateur a tout intérêt (et toute sécurité) à utiliser les algorithmes déjà implémentés (bien et efficacement) plutôt que de les réécrire lui-même (mal et faux !).
Réécrire l'algorithme en utilisant la fonction générique sort() à laquelle il suffit de passer commes paramètres données :
-
un itérateur sur la première position (incluse) de l'intervalle à trier,
-
un itérateur sur la dernière position (exclue) de l'intervalle à trier.
Compiler et tester.
Corrigés :
exo_01.cxx
Sommaire
Tri à bulles
exo_02
Il est fréquent qu'il y ait à trier ou retrier un vecteur déjà construit.
Parmi les méthodes disponibles dans ce type de problème, nous ne nous interesserons ici qu'à celles ne nécessitant pas la création d'un autre vecteur, intermédiaire ou résultat.
Il s'agit donc de trier le vecteur sur lui-même (la méthode dite du quick-sorti, déjà étudiée comme illustration de la récursivité appartient au même ensemble de méthodes et sera envisagée plus loin).
La méthode du tri par remontée des bulles, ou plus simplement tri à bulles, consiste à balayer le vecteur du début à la fin en comparant les couples d'éléments adjacents et en les permutant s'ils ne sont pas ordonnés.
Ce premier passage a pour effet d'amener la plus grande valeur en fin de fichier.
Le balayage est recommencé sur les N-1 premiers éléments, puis sur les N-2, etc..., jusqu'à ce que le vecteur soit entièrement trié.
La première partie de tous les exercices suivants consistera à charger le vecteur à partir du fichier, préalablement à toute opération de tri.
exo_02a
-
Liste des fichiers créés ou modifiés :
exo_02.cxx
Copier le fichier exo_01.cxx dans exo_02.cxx et supprimer toutes les fonctions Exo_01?() sauf Exo_01a().
Trier le vecteur par ordre croissant comme indiqué ci-dessus.
Compiler et tester.
Corrigés :
exo_02.cxx
Sommaire
Première amélioration
exo_02b
-
Liste des fichiers créés ou modifiés :
exo_02.cxx
Lorsqu'un balayage du vecteur a été effectué sans qu'aucune permutation n'ait été effectuée, il est inutile de continuer : le vecteur est trié. Cette première amélioration est particulièrement sensible lorsque le vecteur original est lui-même trié, un seul passage étant nécessaire.
Effectuer les modifications (un booléen peut être utilisé).
Compiler et tester.
Corrigés :
exo_02.cxx
Sommaire
Deuxième amélioration
exo_02c
-
Liste des fichiers créés ou modifiés :
exo_02.cxx
A chaque parcours, la tranche de vecteur parcourue est raccourcie de l'élément terminal.
Or il arrive fréquemment que plusieurs éléments de la fin de la tranche soient déjà ordonnés entre eux.
La tranche de vecteur peut donc en une seule fois être raccourcie de cette séquence.
L'amélioration demandée ici consiste à raccourcir le vecteur jusqu'à la position de la dernière permutation effectuée lors du balayage précédent.
Il s'avère que le booléen utilisé dans l'exercice précédent devient inutile.
Faire les modifications.
Compiler et tester.
Corrigés :
exo_02.cxx
Sommaire
Tri à bulles "aller-retour"
exo_02d
-
Liste des fichiers créés ou modifiés :
exo_02.cxx
On remarque qu'en un seul passage, la plus grande valeur est amenée à la fin du vecteur.
En revanche, si le plus petit élément est en dernière position, il ne remonte que d'une seule position à chaque balayage.
L'idée est donc d'alterner les balayages du début vers la fin et de la fin vers le début.
A chaque fois, le vecteur est raccourci à la dernière permutation effectuée, soit en fin, soit en début de vecteur.
Faire les modifications.
Compiler et tester.
Corrigés :
exo_02.cxx
Sommaire
Quick-sort
exo_03
-
Liste des fichiers créés ou modifiés :
exo_03.cxx
Vous avez étudié la méthode récursive de quick-sort en algorithmique avec réalisation en Ada.
Il vous est demandé de la traduire en C++. La principale difficulté est l'impossibilité d'imbriquer des fonctions les unes dans les autres.
Compiler et tester.
Corrigés :
exo_03.cxx
Sommaire
Solution la plus simple ...
exo_04
-
Liste des fichiers créés ou modifiés :
exo_04.cxx
Après avoir chargé le vecteur à partir du fichier, utiliser la fonction générique sort() pour le trier par ordre croissant.
Compiler et tester.
Corrigés :
exo_04.cxx
Sommaire
Téléchargement des corrigés :
tptrivect.zip
Chemins d'accès aux sources des corrigés :
~mathieu/PARTAGE/src/tp/tpC++/tptrivect/dirtrivect
[1]
Cette simplification rend aussi le programme faux !!! En effet, si on suppose le type int codé sur 2 octets, les valeurs permises appartiennent à [-32768, +32767].
Les valeurs permises par le type unsigned int (ou size_type) appartiennent à l'intervalle [0, 65536].
En conséquence, l'algorithme est faux dès que le vecteur contient plus de 32767 éléments, ce qui est parfaitement possible dans la réalité (mais pas dans le contexte de ce TP!).

© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique