TP 3
TP 3
TP 3
TP 3 : tri et complexité
notions : Allocation 1D, Complexité, Tris
Le tri est l’opération consistant à ordonner un ensemble d’éléments en fonctions de clés sur lesquelles
est définie une relation d’ordre. Les algorithmes de tri sont fondamentaux dans de nombreux domaines,
en particulier la gestion où les données sont presque toujours triées selon un critère avant d’être traitées.
La notion de complexité permet de donner un sens numérique à l’idée intuitive de coût d’un algo-
rithme, en temps de calcul et en place mémoire. Il est cependant important de différencier la complexité
théorique, qui donne des ordres de grandeurs de ces coûts sans être liée à une machine, et la complexité
pratique, qui dépend d’une machine et de la manière dont l’algorithme est programmé. La littérature
évoque généralement la complexité théorique i.e. le nombre de comparaisons effectuées sur les clés et
le nombre de permutations effectuées sur les données dans le cas d’un tri. Nous illustrerons aussi la
complexité pratique par le temps d’exécution des fonctions.
Le tri interne est un tri opérant sur des données présentes en mémoire, tandis que le tri externe travaille
sur des données appartenant à des fichiers. Les tris internes seuls seront exposés, et trois catégories se
distinguent en fonction de leur complexité théorique (n est le nombre d’éléments à trier) :
– Les tris triviaux sont en O(n2 ) : tri bulle, tri par sélection, tri par insertion, ....
– Les tris en O(nx ) avec 1 < x < 2 : tri shell
– les tris en O(nlog(n)) : Tri par tas (Heap Sort), tri rapide (Quick Sort), tri MergeSort.
1
85 85 85 90
71 ... 71 ... 90 ... 85 ...
55 49 55 90 55 71 55 71
20 14 32 90 20 14 32 49 20 14 32 49 20 14 32 49
Pour ajouter un élément à un tas de n éléments (donc le dernier élément du tableau est d’indice
n − 1), on place ce nouvel élément à l’indice n. Ensuite, on doit vérifier la propriété du tas, c’est à dire
que le père, d’indice (n − 1)/2 dans le tableau doit être supérieur à l’élément ajouté. Si c’est le cas, c’est
un tas et on s’arrête. Sinon, on échange les éléments d’indices n et (n − 1)/2. Et on itère entre le père de
(n − 1)/2 qui est d’indice ((n − 1)/2 − 1)/2 et l’élément échangé. On s’arrête bien sûr à la racine.
L’exemple de la figure ?? illustre les étapes de l’ajout du nœud de valeur 90 dans le tas initial (tableau)
suivant : 85 71 36 55 49 26 27 20 14 32. Le tableau devient successivement :
Étape 1 : 85 71 36 55 49 26 27 20 14 32 90 : échange de 90 et de son père (49)
Étape 2 : 85 71 36 55 90 26 27 20 14 32 49 : échange de 90 et de son père (71)
Étape 3 : 85 90 36 55 71 26 27 20 14 32 49 : échange de 90 et de son père (85)
Étape 4 : 90 85 36 55 71 26 27 20 14 32 49 : tas final
L’exemple suivant illustre les étapes de la suppression du nœud de valeur 85 dans le tas initial
(tableau) suivant : 85 71 36 55 49 26 27 20 14 32 17 Quand on supprime la première valeur (85), on
l’échange avec la dernière valeur du tas (17). On doit maintenant se débrouiller pour que les n-1 premiers
éléments du tableau soit un tas. Il faut, à partir de la nouvelle racine (17) rétablir les propriétés du tas :
la valeur qui vient d’être changée doit respecter la relation fondamentale : le père est supérieur à ses
deux fils. Après avoir mis 17 à la racine, il faut donc, comme cette relation n’est pas vérifiée, échanger
17 d’indice 0 et le plus grand de ses fils d’indices 1 et 2 (ici le nœud 71 d’indice 1). On itère ensuite
cette opération entre le nœud venant d’être modifié (le nœud d’indice 1 qui vaut maintenant 17 et ses
fils d’indice 3 et 4). Il faut alors échanger 17 et 55. On procède ainsi jusqu’au dernier niveau (un nœud
d’indice supérieur ou égal à n/2 : le nœud 20 d’indice 7 ici). Notez que la valeur 85 est maintenant à
l’indice n-1 dans le tableau, qui forme un tas entre les indices 0 et n-2. Elle n’apparaı̂t pas dans l’arbre
représentant le tas.
17 71 71 71
71 36. . . 17 36. . . 55 36. . . 55 36. . .
55 49 55 49 17 49 20 49
20 14 32 20 14 32 20 14 32 17 14 32
2
2 Tri
On construit un tas à partir du tableau en commençant par un tas ne comportant qu’un seul nombre :
le premier nombre du tableau. Le tas initial est donc le tableau limité à son premier élément. On ajoute
alors le deuxième nombre du tableau avec la fonction d’ajout dans un tas et le tableau forme alors un
tas entre les éléments d’indice 1 et 2. Puis on ajoute le troisième pour former un tas entre les indices 1
et 3, etc.
Pour effectuer le tri, on supprime le premier élément qui est le plus grand du tas (donc du tableau) en
l’échangeant avec le ne élément du tas (le dernier) en utilisant la fonction suppression dans un tas (§??).
On a alors le plus grand élément en position n-1 et la partie d’indice 0..n-2 du tableau forme encore un
tas. On recommence en supprimant le premier par échange avec celui d’indice n-2, . . . Le résultat est un
tableau dont les éléments sont ordonnés par ordre croissant.
3 Travail à réaliser
Exercice 1 Ecrire les fonctions suivantes pour réaliser le tri par tas :
– void augmentetas(double* tas, int n) : ajoute le nombre dont l’indice est n au tas tas exis-
tant. L’élément à ajouter est déjà dans le tableau tas . Avant exécution de cette fonction, ce
tableau forme déjà un tas entre les indices 0 et n-1. Après exécution de cette fonction, ce tableau
forme un tas entre les indices 0 et n.
– void constructiontas(double* tas, int n) qui établit un tas à partir du tableau tas de n éléments.
Avant l’exécution de cette fonction, le tableau tas contient n éléments non organisés en tas.
Après exécution, les éléments du tableau tas ont été réorganisés pour former un tas entre les
indices 0 et n-1.
– void descendretas(double* tas, int n) : Cette fonction fait descendre le premier élément (la racine
du tas) à sa place. Avant l’appel, les éléments entre les indices 1 et n respectent les conditions d’un
tas, mais pas l’élément d’indice 0. Après l’exécution de cette fonction, les éléments d’indice 0 à n
forment un tas.
– void suppressiontas(double* tas, int n) : supprime le premier nombre de tas en l’échangeant
avec le dernier (indice n-1) et en réorganisant le tas ensuite. On suppose qu’avant l’appel de cette
fonction, le paramètre tas est un tas de n éléments (tableau). Après exécution, le paramètre
tas est un tas de n-1 éléments, l’élément supprimé (celui qui était le premier) est à l’indice n-1
3
Exercice 2 Comparer avec d’autres tris
Écrire les fonctions de tri vues en cours :
– void tri_insertion(double* tab, int n) : qui trie le tableau tab de manière croissante en utilisant une
approche par insertion.
– void tri_rapide(double* tab, int n) : qui trie le tableau tab de manière croissante en utilisant une
approche par tri rapide (quick sort).