5. Les algorithmes de tri

publicité
5. Les algorithmes de tri
Exercice 5.1 : Le tri à bulles
Le tri à bulles ou tri par propagation est un algorithme de tri qui consiste à faire remonter
progressivement les plus grands éléments d'un tableau, comme les bulles d'air remontent à la
surface d'un liquide.
L'algorithme parcourt le tableau, et compare les couples d'éléments successifs. Lorsque deux
éléments successifs ne sont pas dans l'ordre croissant, ils sont échangés. Après chaque parcours
complet du tableau, l'algorithme recommence l'opération. Lorsqu'aucun échange n'a lieu pendant un
parcours, cela signifie que le tableau est trié. On arrête alors l'algorithme.
1. Écrire les étapes du tri à bulle de la liste l = [8;3;6;2;10].
2. Implémenter une fonction récursive bulle qui prend en entrée une liste l, qui parcours cette
liste et échange deux éléments successifs de la liste lorsque c'est nécessaire. Cette fonction
retournera la nouvelle liste ainsi créée. Tester cette fonction.
Astuce : De la même manière qu'on peut isoler le premier élément de liste avec un match sur a :: r,
il est possible d'isoler les deux premiers éléments en utilisant un match sur a :: s :: r.
3. Implémenter une fonction récursive tri_bulle qui appelle la fonction bulle jusqu'à ce que
la liste soit entièrement triée.
Astuce : On arrêtera l'algorithme lorsque la liste retournée par bulle est identique à la liste sur
laquelle on applique bulle.
Exercice 5.2 : Le tri rapide
La méthode consiste à placer un élément d'une liste (appelé pivot) à sa place définitive, en
permutant tous les éléments de telle sorte que tous ceux qui sont inférieurs au pivot soient à sa
gauche et que tous ceux qui sont supérieurs au pivot soient à sa droite. Cette opération s'appelle le
partitionnement. Pour chacune des sous-liste, on définit un nouveau pivot et on répète l'opération de
partitionnement. Ce processus est répété récursivement, jusqu'à ce que l'ensemble des éléments soit
trié.
NB : Dans notre implémentation le pivot choisi sera toujours le premier élément de la liste.
Concrètement, pour partitioner une sous-liste :
• on sort le pivot de la liste;
• on place tous les éléments inférieurs au pivot dans une première sous-liste ;
• on place tous les éléments supérieurs au pivot dans une seconde sous-liste;
1. Écrire les étapes du tri rapide de la liste l = [8;3;6;2;10].
2. Implémenter une fonction récursive partition qui prend en entrée un entier pivot et une
liste l et qui retourne un 2-uplet contenant la sous-liste des éléments inférieurs au pivot et la
sous-liste des éléments supérieurs au pivot. Tester la fonction partition.
Ex : partition 8 [3;6;2;10] va retourner ([2;6;3] , [10]).
3. Implémenter une fonction récursive tri_rapide qui prend une liste l en entrée, qui appelle la
fonction partition et concatène les sous-listes retournées par partition pour former une liste
triée.
Exercice 5.3 : Le tri fusion
Le tri fusion est un algorithme de tri qui consiste à fusionner deux sous-listes triées en une liste
triée. Le principe de cet algorithme tend à adopter une formulation récursive :
• on découpe les données à trier en deux parties plus ou moins égales;
• on trie les 2 sous-parties ainsi déterminées;
• on fusionne les deux sous-parties pour retrouver les données de départ.
Donc chaque instance de la récursion va faire appel à nouveau au programme, mais avec une
séquence de taille inférieure à trier. La terminaison de la récursion est garantie, car les découpages
seront tels qu'on aboutira à des sous-parties d'un seul élément; le tri devient alors trivial.
Une fois les éléments triés indépendamment les uns des autres, on va fusionner les sous-séquences
ensemble jusqu'à obtenir la séquence de départ, triée.
1. Implémenter une fonction récursive couper qui prend en entrée une liste l et qui retourne un
2-uplet contenant deux listes de taille égale. Tester la fonction couper.
NB : Si la liste l est de taille 2n+1, la première liste du 2-uplet sera de taille n+1 et la seconde de
taille n.
Astuces : Il est possible d'utiliser un ou logique dans un match. Ainsi pour dire «dans le cas où ma
liste est vide ou ne contient qu'un seul élément», on écrira dans le match : |([]|[_])->.
Si on veut retourner la liste (vide ou ne contenant qu'un élément, selon les cas) on utilisera
la mot-clef as : |([]|[_]) as singleton -> singleton.
2. Implémenter une fonction récursive fusionner qui prend en entrée deux liste triées l1 et l2
et qui retourne une liste triée contenant l'ensemble des éléments des deux listes. A chaque itération
on comparera la tête de l1 avec la tête de l2. Si la tête de l1 (resp. l2) est inférieure à la tête de
l2 (resp. l1) on ajoutera la tête de l1 (resp. de l2) dans la liste et on comparera la tête de l2
(resp. l1) avec le second élément de l1 (resp.l2), et ainsi de suite.
3. Implémenter la fonction récursive tri_fusion qui prend en entrée une liste l et qui fait appel
à la fonction couper pour obtenir des singletons à partir de l, puis à la fonction fusionner
pour réassembler les singletons, puis les doublons etc.
Téléchargement