Chap 3
Chap 3
Chap 3
Algorithmes de tri
39
40 CHAPITRE 3. ALGORITHMES DE TRI
Le tri par insertion consiste à insérer les éléments de la suite les uns
après les autres dans une suite triée initialement vide. Lorsque la suite est
stockée dans un tableau la suite triée en construction est stockée au début
3.1. TRI PAR SÉLECTION, TRI PAR INSERTION, TRI À BULLE. 41
du tableau. Lorsque la suite est représentée par une liste chainée on insère
les maillons les uns après les autres dans une nouvelle liste initialement vide.
Algorithme 13: TriParInsertion<
entrée : T [1, n] est un tableau d’entiers, n 1.
résultat : les éléments de T sont ordonnés par ordre croissant.
début
pour i := 2 à n faire
j := i # indice de l’élément à insérer dans T [1, i 1]
v := T [i] # valeur de l’élément à insérer
tant que j > 1 et v < T [j 1] faire
T [j] := T [j 1] # j 1 est une case libre
j := j 1 # j est une case libre
T [j] := v
Dans le meilleur des cas le tri par insertion requiert seulement O(n) traite-
ments. C’est le cas lorsque l’élément à insérer reste à sa place, donc quand la
suite est déja triée.On peut montrer que le tri par insertion est quadratique
en moyenne.
Le tri à bulle consiste à parcourir le tableau, tant qu’il n’est pas trié,
et à permuter les couples d’éléments consécutifs mal ordonnés. On sait que
le tableau est trié si lors d’un parcours, aucun couple d’éléments n’a été
permuté.
n 2x n
n/2 n/2 2x n
log n
n/4 n/4 n/4 n/4 2x n
1 1 1 ....
Exercice 4
1. Qu’est-ce qui, dans l’algorithme d’interclassement, garantit la stabi-
lité du tri fusion ?
2. Écrivez l’algorithme de fusion lorsque les données sont stockées dans
une liste chaı̂née.
3.2. TRI PAR FUSION ET TRI RAPIDE 45
On peut montrer que la complexité du tri rapide est O(n log n) en moyenne,
mais aussi O(n2 ) dans le pire des cas (voir une étude de la complexité en
TD). En pratique, c’est l’algorithme le plus utilisé et très souvent, le plus
rapide.
1 2 3
a b c
e1 <= e2 <= > e1 > e2
a b c b a c
e2 > e3
<= > e2 <= e3 <= >
a b c a c b b a c b c a
a c b c a b b c a c b a
h log(n!)
or, d’après la formule de Stirling 1
p ⇣ n ⌘n
log(n!) ⇠ log 2⇡n + log = ⇥(nlog n)
e
n
1. sans utiliser la formule de Stirling, on peut remarquer que ( n2 ) 2 n! nn , ce qui
conduit directement à log(n!) = ⇥(n log n).
48 CHAPITRE 3. ALGORITHMES DE TRI
0
13
hauteur log2 n 1 2
11 7
3 4 5 6
10 3 5 1
7 8 9
4 8 2
Habituellement les tas sont représentés dans des tableaux. Les valeurs du
tas sont stockées dans les premières cases du tableau. Si le tas est composé
de n éléments, ces derniers apparaissent donc aux indices 0, 1,. . . , n 1.
La racine du tas figure dans la case d’indice 0. La disposition des éléments
du tas dans le tableau correspond à un parcours de l’arbre par niveau, en
partant de la racine et de gauche à droite. Le fils gauche du nœud qui figure
à l’indice i, s’il existe, se trouve à l’indice FilsG(i) = 2 ⇥ i + 1, et son fils
3.4. TRI PAR TAS. 49
fils droit
0 1 2 3 4 5 7 8 9 10 11
13 11 7 10 3 5 1 4 8 2 0 5
6 n−1
fils gauche
On définit ensuite une propriété sur les tas : chaque nœud est dans une
relation d’ordre fixée avec ses fils.
Un tas possède la propriété max-heap (resp. min-heap) si tout nœud
porte une valeur supérieure ou égale (resp. inférieure ou égale) à celle de ses
fils : T [pere(i)] T [i] (resp. T [pere(i)] T [i]). Un tax-max (resp. tas-min)
est un tas qui vérifie la propriété max-heap (resp. min-heap).
Le plus grand élément d’un tas-max T est T [0] ; son plus petit élément
est sur une feuille.
Pour le tri par tas on utilise des tas-max. Les opérations et les techniques
présentées dans ce chapitre s’appliquent de la même façon à des tas basés
sur l’ordre inverse. Les opérations essentielles sur les tas sont :
— la construction du tas,
— l’extraction du maximum,
— l’ajout d’une nouvelle valeur,
— la modification de la valeur d’un nœud.
L’opération Entasser est une opération de base sur les tas. Elle est utilisée
notamment pour la construction des tas ou encore pour l’extraction de la
valeur maximale. Elle consiste à reconstruire le tas lorsque seule la racine
viole (éventuellement) la propriété de supériorité entre un nœud et ses fils,
en faisant descendre dans l’arbre l’élément fautif par des échanges successifs.
50 CHAPITRE 3. ALGORITHMES DE TRI
9 14 14
14 7 9 7 13 7
10 13 5 1 10 13 5 1 10 9 5 1
4 8 2 4 8 2 4 8 2
Fonction ExtraireLeMax(T, n)
entrée : T est un tableau, n est la taille du tas stocké dans T .
sortie : Retourne la valeur maximale et met à jour le tas.
début
max := T [0]
T [0] := T [n 1]
Entasser(0, T, n 1)
retourner hmax, T, n 1i
fin
Principe général du tri par tas. Supposons que l’on ait à trier une
suite de n valeurs stockée dans un tableau T . On commence par construire
un tas dans T avec les valeurs de la suite. Ensuite, tant que le tas n’est pas
vide on répète l’extraction de la valeur maximale du tas. Chaque fois, la
valeur extraite est stockée dans le tableau immédiatement après les valeurs
du tas. Lorsque le processus se termine on a donc la suite des valeurs triée
dans le tableau T .
Construction du tas.
Les valeurs de la suite à trier sont stockées dans le tableau T . La procé-
dure consiste à parcourir les noeuds qui ont des fils et à leur appliquer l’opé-
ration Entasser, en commençant par les noeuds qui sont à la plus grande
profondeur dans l’arbre. Il suffit donc de parcourir les noeuds dans l’ordre
décroissant des indices et en partant du dernier noeud qui a des fils, le noeud
d’indice b(n/2)c 1. L’ordre dans lequel les noeuds sont traités garantit que
les sous-arbres sont des tas.
52 CHAPITRE 3. ALGORITHMES DE TRI
Fonction ConstruireUnTas(T, n)
entrée : T est un tableau, n est un entier.
sortie : Structure les n premiers éléments de T sous forme de tas.
début
pour i := bn/2 1c à 0 faire
Entasser(i, T, n)
finpour
retourner T
fin
2h
Comme n, on en déduit que C(n) 2Kn. L’algorithme ConstruitTasMax
est linéaire dans le pire des cas. Cette complexité est atteinte si T est stric-
tement croissant.
Exercice 6 Construisez un tas à partir de T = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
Fonction TriParTas(T, n)
entrée : T est un tableau de n éléments.
sortie : Trie le tableau T par ordre croissant.
début
ConstruireUnTas(T, n)
pour i := n 1 à 1 faire
Echanger(T, 0, i)
Entasser(0, T, i)
retourner T
1 2 3 4 5 6 7 8 9 10
T 1 3 1 2 3 2 1 3 2 2
1 2 3
Nombres d’apparitions 3 4 3
1 2 3 4 5 6 7 8 9 10
1 2 3
Indices des premiers à placer 1 4 8 T 1 3 1 2 3 2 1 3 2 2
La suite des objets à trier est parcourue deux fois, et la table N b conte-
nant le nombre d’occurrences de chaque clé est parcourue une fois pour
l’initialiser. La complexité finale est donc O(n + k) si les clés des éléments
à trier sont comprises entre 1 et k. Le tri par dénombrement est dit linéaire
(modulo le fait que k doit être comparable à n).
Fonction TriParDenombrements(T, n, k)
entrée : T est un tableau de n éléments 2 {1, . . . , k}.
sortie : R contient les éléments de T triés par ordre croissant.
début
/* Initialisations */
pour i := 1 à k faire
N b[i] := 0
/* Calcul des nombres d’apparitions */
pour i := 1 à n faire
N b[T [i]] := N b[T [i]] + 1
/* Calcul des indices du premier */
N b[k] := n N b[k] + 1
/* Élément de chaque catégorie */
pour i := k 1 à 1 faire
N b[i] := N b[i + 1] N b[i]
/* Recopie des éléments originaux du tableau T dans R */
pour i := 1 à n faire
R[N b[T [i]]] := T [i]
N b[T [i]] := N b[T [i]] + 1
retourner R
Considérons l’exemple suivant dans lequel les clés sont des nombres en-
tiers à au plus trois chi↵res. Le fractionnement consiste simplement à prendre
chacun des chi↵res de l’écriture décimale des clés. La colonne de gauche
contient la suite des valeurs à trier, la colonne suivante contient ces mêmes
valeurs après les avoir triées par rapport au chi↵re des unités, . . . Dans la
dernière colonne les valeurs sont e↵ectivement triées. Du fait que le tri par
dénombrement est stable, si des valeurs ont le même chi↵re des centaines,
alors elles apparaı̂tront dans l’ordre croissant de leurs chi↵res des dizaines,
et si certaines ont le même chi↵re des dizaines alors elles apparaı̂tront dans
l’ordre croissant des chi↵res des unités.
56 CHAPITRE 3. ALGORITHMES DE TRI
Supposons que l’on ait n valeurs dont les clés sont fractionnées en c
fragments avec k valeurs possibles pour chaque fragment. Le coût du tri par
base est alors O(c⇥n+c⇥k) puisqu’on va e↵ectuer c tris par dénombrement
sur n éléments avec des clés qui auront k valeurs possibles.
Si k = O(n) on peut dire que le tri par base est linéaire. Dans la pratique,
sur des entiers codés sur 4 octets que l’on fragmente en 4, le tri par base est
aussi rapide que le Quick Sort.
Exercice 9 [CLRS] Montrez comment trier n entiers compris entre 0 et
n2 1 en temps O(n).