Ce chapitre traite exclusivement des algorithmes classiques de tri. La théorie sera exposée et appliquée sur des tableaux d’entiers (les algorithmes sont généralisables à n’importe quel type et critère de classement).
On considère un tableau d’entier de taille N
dont on veut trier les éléments de manière croissante. Une valeur peut être présente plusieurs fois dans le tableau. Ainsi:
11 | -2 | 1515 | 42 | 2048 | 28 | 11 | -78 |
devient, une fois trié:
-78 | -2 | 11 | 11 | 28 | 42 | 1515 | 2048 |
On suppose l’existence d’une fonction echange
/ swap
(voir chapitre précédent) qui permet de permuter dans un tableau t
les cases d’indices i
et j
.
Le principe du tri sélection est le suivant:
[1, N-1]
et on le permute avec la case d’indice 1.La procédure en C
peut s’écrie comme:
Du point de vue du coût de l’algorithme en terme de comparaisons, on constate que lorsque ideb
vaut 0, il y a (N-1)
comparaisons, lorsque ideb
vaut 1 il y a (N-2)
comparaisons… et lorsque ideb
vaut (N-2)
il y a 1 comparaison. Au final le coût est (N-1) + (N-2) + ... + 1 = n \times (n-1) / 2
qui lorsque N
est très grand tend asymptotiquement vers O(N^2)
.
On peut également déterminer l’invariant de ce tri (i.e. quantité qui ne varie pas entre chaque tour de boucle, c’est utile pour les preuves d’algorithme). Pour le tri sélection, l’invariant est le suivant: à la fin du tour ideb
, le sous tableau t[0..ideb]
contient les ideb+1
plus petits éléments du tableau et ce, dans l’ordre croissant de leurs valeurs.
Le principe du tri intérieur est celui qu’on utilise pour trier des cartes à jouer:
Le c/ est le suivant:
La complexité de cette algorithme n’est pas aussi immédiate que précédemment. En effet dans la boucle while
le nombre de comparaisons dépend de l’état initial du tableau (s’il est déjà trié ou non). En supposant que le tableau est trié, on remarque qu’il n’y a qu’une comparaison par tour de boucle, soit N-1
au total. A l’inverse si le tableau est trié par ordre décroissant, on aura 1
puis 2
puis … puis N-2
comparaisons dans la boucle while
. Ainsi la complexité est de O(N)
dans le meilleur des cas et O(N^2)
dans le pire des cas et en moyenne également.
L’invariant de ce tri est le suivant: au tour de boucle i
, on insère l’élément t[i]
dans le tableau t[0..i-1]
déjà trié.
Le tri fusion utilise le principe de diviser pour régner. Pour cela il fonctionne de la manière suivante:
L’algorithme est un peu plus complexe que les deux précédents et se base sur des procédures auxiliaires. La procédure principale est la suivante:
puis on détaille le coeur du tri (découpage en deux sous-tableaux)
puis la fusion de deux sous-tableaux
La complexité du tri-fusion se calcule comme suit: les comparaisons sont faites uniquement dans la fonction fusion
(où il y a 3 comparaisons par tour de boucle). Pour trier le tableau complet:
3 \times n
comparaisons.3 \times n
comparaisons (cf il y a deux demi-tableaux).Au final il y a 3 \times n
comparaisons par récursion avec en tout i
récursions. i
est tel que 2^i = N, soit
i = ln_2(N). Ainsi la complexité asymptotique du tri-fusion est
N \times ln_2(N).`
La section précédente a présenté 3 tris classiques, d’autres seront vus en TD et TP (tris à bulles et tri rapide).
Ci-desssous figure un récapitulatif des complexités des principaux algorithmes de tri:
Algorithme | Meilleur | Moyenne | Pire |
Tri à bulles | O(n) | O(n^2) | O(n^2) |
Tri sélection | O(n^2) | O(n^2) | O(n^2) |
Tri insertion | O(n) | O(n^2) | O(n^2) |
Tri fusion | O(n x ln_2(n)) | O(n x ln_2(n)) | O(n x ln_2(n)) |
Tri rapide | O(n x ln_2(n)) | O(n x ln_2(n)) | O(n^2) |
D’autres propriétés permettent de caractériser les algorithmes de tri:
O(n)
).