Chapitre 15
ALGORITHMES DE TRI
Algorithme de tri : c’est, en informatique ou en mathématiques, un algorithme qui permet d’organiser
une collection d’objets selon une relation d’ordre déterminée.
Le but de ce TP est de s’intéresser aux algorithmes de tris, il en existe de nombreux parmi lesquels on peut
citer : le tri bulle, le tri par sélection, le tri par insertion, le tri rapide, le tri fusion... Trier des listes
de données est pertinent car la recherche de certains éléments devient alors quasi instantanée (le minimum, le
maximum, la médiane, les quartiles).
Chaque tri a son propre fonctionnement mis en image avec la vidéo YouTube https: // www. youtube. com/
watch? v= kPRA0W1kECg réalisée par Timo Bingmann.
Chacun a sa caractéristique, certains sont plus efficaces que d’autres pour trier certains types de listes
particulières. On s’intéresse surtout à comparer leur complexité temporelle (nombre maximal d’opérations
réalisées lors de l’exécution du code) mais aussi leur complexité spatiale (place mémoire maximale prise). Les
algorithmes de tri les moins performants ont une complexité quadratique en (O(n2 )) et les plus performants
en O(n ln(n)) où n est la taille de la liste donnée en entrée.
Un tri est comparatif s’il n’utilise que des comparaisons binaires pour trier la liste. Un bon nombre de
tris sont comparatifs.
Un tri est en place s’il n’utilise qu’un nombre très limité de variables et qu’il modifie directement la
structure qu’il est en train de trier. Ce caractère peut être très important si on ne dispose pas de beaucoup
de mémoire.
On dit qu’un tri est dit stable s’il préserve l’ordonnancement initial des éléments que l’ordre considère
comme égaux.
Trois des nombreux algorithmes de tri qui existent
1
Chp 15 - ALGORITHMES DE TRI
I RAPPELS ET COMPLÉMENTS SUR LES LISTES
Exercice 1
Essayez de prédire le résultat des commandes ci-contre si on les exécute dans cet ordre puis vérifier sur Python.
L=[4,7,5]
L.append(2) Remarque.
print(L) . . . . . . . . . . . . . . . . . . . . . . . . . . .
• On retient que L.pop(i) enlève . . . . . . . . . . . . . . . . . . . . . . . .
print(L.append(3)) . . . . . . . . . . . . . . . . . . . . . . . . . . .
de L et renvoie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
print(L.pop(2)) . . . . . . . . . . . . . . . . . . . . . . . . . . .
• Tandis que L.pop() enlève . . . . . . . . . . . . . . . . . . . . . de L
print(L) . . . . . . . . . . . . . . . . . . . . . . . . . . .
• Oubli de pop ? Pas grave ! Utilisez les tranches :
print(L.pop(0)) . . . . . . . . . . . . . . . . . . . . . . . . . . .
L.pop() donnera la tranche . . . . . . . . . . . . . . . . . . . . . . . . . . .
print(L) . . . . . . . . . . . . . . . . . . . . . . . . . . .
L.pop(0) donnera la tranche . . . . . . . . . . . . . . . . . . . . . . . . . . .
print(L.pop()) . . . . . . . . . . . . . . . . . . . . . . . . . . .
print(L.pop()) . . . . . . . . . . . . . . . . . . . . . . . . . . . L.pop(i) donnera la tranche . . . . . . . . . . . . . . . . . . . . . . . . . . .
print(L) . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exercice 2
Chose surprenante en Python, si L est une liste, et si la commande M=L est rentrée, alors une modification
de M entraı̂ne une modification de L (et vice-et-versa). On parle de copie superficielle.
Si ce n’est pas le comportement désirée, et que vous voulez faire évoluer L et M de façon indépendante, la
commande M=L.copy() pare le problème. On parle de copie en profondeur.
Prédire alors les deux print suivants avant de taper le code.
L=[1,2,3]
M=L Remarque.
M[1]=50 Si jamais vous avez oublié la commande copy ce n’est
print(L,M) . . . . . . . . . . . . . . . . . . et . . . . . . . . . . . . . . . . . . pas grave ! Vous vous pouvez également utiliser ceci :
L=[1,2,3] M=L.copy() ⇔ M=[x for x in L] ⇔ M=L[:]
M=L.copy()
En concaténant un seul élément on a :
M[1]=50
print(L,M) . . . . . . . . . . . . . . . . . . et . . . . . . . . . . . . . . . . . . L.append(a) qui équivaut à L+=[a]
Exercice 3 (L’effet border
lineliste).
Rappelons enfin qu’une fonction modifie une liste même sans return. C’est ce qu’on appelle l’effet de bord.
Prédire l’issue des deux codes suivant, puis tester-les.
def f(L): def f(L):
L.pop(0) M=L
M=L*2 M.pop(0)
return M return M
L=[1,2,3] L=[1,2,3]
N=f(L) N=f(L)
print(L,N) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . print(L,N) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Remarque. Si jamais on ne veut pas modifier la liste L en entrée de la fonction, il faut alors créer une nouvelle
variable L2 dans laquelle stocker un à un les éléments de L pour y faire les modifications sans toucher la liste L .
2 PCSI 2024-2025
Chp 15 - ALGORITHMES DE TRI
II TRI À BULLES
Exercice 4 (Remontée d’une bulle).
On dispose d’une liste L de nombres (flottants ou entiers).
1. Écrire une fonction Remontee_Bulle(L) qui modifie la liste L entrée
de sorte à remonter progressivement l’élément le plus petit en première
position en permutant uniquement 2 à 2 des éléments voisins.
On part de l’élément de la fin de la liste, puis s’il est plus petit que son
voisin de gauche on les échange de place (ci-contre 1 et 3 ne sont pas
échangé).
On recommence ensuite en comparant l’avant-dernier élément (1) et son
voisin de gauche (6) et on les échange de place si nécessaire (ce qui est
le cas puisque 1 < 6).
Ainsi de suite (comparaison de 1 et 4, puis 1 et 2, puis 1 et 5...) jusqu’aux
deux premiers éléments de la liste. Ainsi le plus petit élément (1 dans
cette liste) remonte en première position.
2. Quel est la complexité en calcul de cet algorithme ?
Exercice 5 (Tri Bulle).
Le tri bulle est une stratégie de tri locale, qui consiste à trier une liste d’ob-
jets en portant des œillères (trier rien qu’en comparant des éléments voisins
deux à deux).
On fait remonter progressivement les plus petits éléments dans les premières
positions grâce au principe de la fonction Remontee_Bulle.
A la fin de la première exécution de Remontee_Bulle, l’élément le
plus petit se retrouve en tête. Puis après la seconde exécution de
Remontee_Bulle, le second plus petit se retrouve en seconde position, et ainsi
de suite.
C’est de là que provient le nom : l’algorithme imite la remontée de bulles. Si la
liste est de taille n, après n passages, les n bulles seront remontées et classées
dans l’ordre.
On dispose d’une liste L de nombres (flottants ou entiers).
1. Écrire une fonction Tri_Bulle(L) qui étant donné une liste L de nombres
renvoie en sortie la liste L triée par ordre croissant en suivant le principe
du tri bulle décrit ci-dessous.
2. Quel est la complexité en calcul de cet algorithme ?
Exercice 6 (Propriété des tris).
Compléter (avec oui/non) le tableau suivant donnant les propriétés du tri à bulle.
Nom du tri Complexité En place Stable Comparatif
Tri Bulle
3 PCSI 2024-2025
Chp 15 - ALGORITHMES DE TRI
III LE TRI FUSION
Exercice 7 (Fusion de deux listes triées).
1. Écrire une fonction Fusion(L,M), où L et M sont deux listes triées par
ordre croissant. Cette fonction renvoie la liste contenant les éléments de
L et M de façon triée.
Par exemple :
Fusion([1,3,8],[2,3,4,6,7,11,13]) donne [1,2,3,3,4,6,7,8,11,13].
Méthode :
Partir d’une liste N initialement vide, tant que les listes L et M sont non
vides, on rajoutera à N le plus petit des éléments qu’il y a dans les listes
L et M puis on enlèvera cet élément (de la liste L ou M). Une fois que cela
est fait, l’une des listes L et M est vide mais pas forcément l’autre.
On donne un exemple avec deux listes de même taille ci-contre.
2. Tester avec L=[1,3,8], M=[2,4,6,7,11,13] et print(Fusion(L,M)).
Exercice 8 (Le tri fusion).
Créer une fonction TriFusion(L) qui va trier récursivement la liste L.
Pour cela, si la liste a un élément ou moins, on a rien à faire pour la trier. Sinon,
on coupe la liste au milieu, on trie chacune de ces deux sous-listes de façon
récursive. Puis on les fusionne les deux sous-listes triées grâce à la fonction
Fusion créée à l’exercice précédent.
Exercice 9 (Propriété des tris).
1. Compléter (avec oui/non) le tableau suivant donnant les propriétés des 3 tris étudiés.
Nom du tri Complexité En place Stable Comparatif
Tri fusion O(n ln(n))
Tri Bulle
Tri par insertion
4 PCSI 2024-2025
Chp 15 - ALGORITHMES DE TRI
CORRECTION
Exercice 1.
L=[4,7,5]
L.append(2) Remarque.
print(L) affiche [4,7,5,2]
• On retient que L.pop(i) enlève l’élément L[i] de L et
print(L.append(3)) affiche None renvoie L[i] .
print(L.pop(2)) affiche 5
• Tandis que L.pop() enlève l’élément L[-1] de L
print(L) affiche [4,7,2,3]
• Oubli de pop ? Pas grave ! Utilisez les tranches :
print(L.pop(0)) affiche 4
L.pop() donnera la tranche L[:-1]
print(L) affiche [7,2,3]
L.pop(0) donnera la tranche L[1:]
print(L.pop()) affiche 3
print(L.pop()) affiche 2 L.pop(i) donnera la tranche L[:i]+L[i+1:]
print(L) affiche [7]
Exercice 2.
1 L =[1 ,2 ,3] 1 L =[1 ,2 ,3]
2 M=L 2 M = L . copy ()
3 M [1]=50 3 M [1]=50
4 print (L , M ) # L =[1 ,50 ,3] et M =[1 ,50 ,3] 4 print (L , M ) # L =[1 ,2 ,3] et M =[1 ,50 ,3]
Exercice 3.
1 def f ( L ): 1 def f ( L ):
2 L . pop (0) 2 M=L
3 M = L *2 3 M . pop (0)
4 return M 4 return M
5 5
6 L =[1 ,2 ,3] 6 L =[1 ,2 ,3]
7 N=f(L) 7 N=f(L)
8 print (L , N ) # L =[2 ,3] et N =[2 ,3 ,2 ,3] 8 print (L , N ) # L =[2 ,3] et N =[2 ,3]
Exercice 4.
1. Une fonction possible est :
1 def Remontee_Bulle ( L ):
2 n = len ( L )
3 for i in range (n -2 , -1 , -1):
4 if L [ i +1] < L [ i ]:
5 L [ i ] , L [ i +1]= L [ i +1] , L [ i ]
6 return L
2. Cette fonction comporte une simple boucle de longueur n − 1 où n est la longueur de la liste entrée. A
chaque tour de boucle on a un test et au maximum deux affectations dans la liste (soit 3 opérations au
maximum). La complexité est donc linéaire en O(n).
5 PCSI 2024-2025
Chp 15 - ALGORITHMES DE TRI
Exercice 5.
1. Une fonction possible est :
1 def Tri_Bulle ( L ):
2 n = len ( L )
3 for j in range ( n ):
4 Remontee_Bulle ( L )
5 return ( L )
ou encore sans réutiliser Remontee_Bulle :
1 def Tri_Bulle ( L ):
2 n = len ( L )
3 for j in range ( n ):
4 for i in range (n -2 ,j -1 , -1):
5 if L [ i +1] < L [ i ] :
6 L [ i ] , L [ i +1] = L [ i +1] , L [ i ]
7 return ( L )
2. Cette fonction comporte une simple boucle sur j de longueur n où n est la longueur de la liste entrée.
A chaque tour de boucle on exécute une fonction de complexité O(n − j), ce qui équivaut à une double
boucle imbriquée triangulaire.
La complexité est donc quadratique en O(n2 ).
Exercice 6.
Nom du tri Complexité En place Stable Comparatif
Tri Bulle O(n2 ) oui oui oui
Exercice 7.
1. Une fonction possible est :
1 def Fusion (L , M ):
2 # On fusionne les deux listes par ordre croissant
3 N =[] # On part de la liste vide
4 while len ( L )!=0 and len ( M )!=0: # Tant que l ’ une des listes est non vide
5 if L [0] < M [0]: # On regarde l ’ élément le plus petit entre le 1 er de L et de M
6 N . append ( L [0]) # Si c ’ est celui de L on l ’ ajoute à N
7 L . pop (0) # Et on l ’ efface de L
8 else :
9 N . append ( M [0]) # Si c ’ est celui de M on l ’ ajoute à N
10 M . pop (0) # Et on l ’ efface de M
11 return N + L + M # On renvoit la liste N , complétée avec le reste de de L et M
12 # ( on ajoute L et M car l ’ une est vide ...)
2. On teste et on obtient bien le résultat annoncé.
Chp 15 - ALGORITHMES DE TRI
Exercice 8.
Une fonction possible est :
1 def TriFusion ( L ):
2 if len ( L ) <=1: # Une liste vide ou à un élément est déjà trié
3 return L
4 else :
5 m = len ( L )//2 # On définit l ’ indice milieu de m
6 G = L [: m ] # On définit la liste de gauche G
7 D = L [ m :] # On définit la liste de droite D
8 G = TriFusion ( G ) # On trie G par récursivité
9 D = TriFusion ( D ) # On trie D par récursivité
10 M = Fusion (G , D ) # On fusionne les listes G et D triées
11 return M # On renvoie le résultat obtenu
ou en plus condensé :
1 def TriFusion ( L ):
2 # Version plus courte de TriFusion mais fait exactement la m^ e me chose
3 if len ( L ) <=1:
4 return L
5 return Fusion ( TriFusion ( L [: len ( L )//2]) , TriFusion ( L [ len ( L )//2:]))
Exercice 9.
1. Compléter (avec oui/non) le tableau suivant donnant les propriétés des 3 tris étudiés.
Nom du tri Complexité En place Stable Comparatif
Tri fusion O(n ln(n)) oui non oui
Tri Bulle O(n2 ) oui oui oui
Tri par insertion O(n2 ) oui oui oui
2. On obtient le graphique suivant avec une proposition de code comme suit :
Comparatif de la complexité de 4 algorithmes de tri
7 PCSI 2024-2025
Chp 15 - ALGORITHMES DE TRI
1 N =3*10**3
2
3 X =[ n for n in range (0 , N +1 ,100)]
4 YBulles =[]
5 YFusion =[]
6 YInsertion =[]
7 YInsertionDicho =[]
8
9 for n in X :
10 L1 =[ np . random . rand () for i in range ( n )]
11 L2 = L1 . copy ()
12 L3 = L1 . copy ()
13 L4 = L1 . copy ()
14 t0 = time . time () # Temps au début du calcul
15 M = TriFusion ( L1 ) # On la trie
16 t1 = time . time () # Temps à la fin du calcul
17 YFusion . append ( t1 - t0 )
18 t0 = time . time ()
19 M = Tri_Bulle ( L2 )
20 t1 = time . time ()
21 YBulles . append ( t1 - t0 )
22 t0 = time . time ()
23 M = Tri_insertion ( L3 )
24 t1 = time . time ()
25 YInsertion . append ( t1 - t0 )
26 t0 = time . time ()
27 M = Tri _insert ion_dic ho ( L4 )
28 t1 = time . time ()
29 YInsertionDicho . append ( t1 - t0 )
30 print ( " n = " ,n , " et le temps mis est par le TriABulles est " ,t1 - t0 )
31
32 plt . plot (X , YBulles , " o " , label = " Tri Bulle " )
33 plt . plot (X , YFusion , " -" , label = " Tri Fusion " )
34 plt . plot (X , YInsertion , " v " , label = " Tri Insertion " )
35 plt . plot (X , YInsertionDicho , " x " , label = " Tri Insertion Dicho " )
36 plt . legend ()
37 plt . grid ()
38 plt . show ()
8 PCSI 2024-2025