Vous êtes sur la page 1sur 11

Chapitre 9 : Algorithmes de tri en Python

9.1. Problématique
Dans ce cours, on va se restreindre au tri des éléments d’une liste, mais rien n’empêche
l’application des fonctions de tri qui seront présentées dans la suite aux autres conteneurs
séquentiels. Nous allons supposer que :

 Les listes sont homogènes (contenant toutes des éléments de même nature, par
exemple des listes d’entiers uniquement ou de réels uniquement…)
 Les éléments de la liste peuvent être ordonnés en utilisant une relation d’ordre par
exemple l’opérateur de comparaison ≤.

Trier une liste c’est ordonner ses éléments par ordre croissant ou décroissant. La liste sera
elle-même une donnée et un résultat. Toutes les modifications opérées lors du processus du
traitement seront enregistrées sur la liste L et pas sur une copie d’elle-même.

Dans la littérature, plusieurs algorithmes de tri existent. Trancher entre un algorithme et un


autre fait intervenir la notion de complexité algorithmique : Bien évidemment, on a besoin de
classer les éléments en un minimum de temps. Une fois triée, une liste peut servir pour
d’autres traitements comme :

 L’insertion d’une nouvelle donnée à sa bonne position en prenant en considération


l’ordre établi ;
 Les algorithmes dichotomiques qui utilisent les listes triées pour accélérer la
recherche…
On distingue deux grandes catégories de tri : Tris lents (par sélection, à bulles, par
insertion…) et les tris rapides (par fusion, quicksort,…)
Hypothèses : L est une liste d’entier de taille n=len(L).

9.2. Tri par sélection


 Principe :
- Commencer à chercher l’indice du minimum de la liste L ;
- Permuter le 1er élément de la liste avec l’élément minimum trouvé ;
- Chercher l’indice du minimum des éléments de la sous liste L[1 :] et le permuter avec
le 2ème élément…
- Continuer ce principe jusqu’à ce que la liste devienne triée.
 D’une manière générale, on échange l’élément à la position i avec le minimum de
la sous liste L[i+1 :]. L’algorithme se termine au bout de n-1 boucles quand on a
trouvé les n-1 minimums successifs.
Exemple : L=[4,5,1,-6,2]
- Etape N°1 : le minimum de L est -6, on le permute avec 4 : L=[-6,5,1,4,2] ;
- Etape N°2 : le minimum de [5,1,4,2] est 1, on le permute avec 5 : L=[-6,1, 5,4,2]
- Etape N°3 : le minimum de [5,4,2] est 2, on le permute avec 5 : L=[-6,1,2,4,5]
- Etape N°4 : le minimum de [4,5] est 4, il est à sa bonne place.
 Implémentation :
Application :

1) Modifier la fonction précédente pour éviter les permutations inutiles.


2) Utiliser les fonctions prédéfinies pour retourner directement l’indice du minimum
3) Afficher l’état de la liste après chaque permutation.

 Analyse de la complexité :
Pour faciliter le calcul du temps d’exécution, nous allons considérer la version
suivante de triselect :

Algorithme Unités de temps


def triselect(L): T(n)
n=len(L) 1+n
for i in range(n-1): n-1 boucles
imin=i 1
for j in range(i+1,n): (n-i-1) boucles
if L[j]<L[imin]: 3
imin=j 1
if i!=imin: 1
L[i],L[imin]=L[imin],L[i] 6

 O(T(n))=O(n²) : L’algorithme du tri est quadratique, pas très pratique quand la taille
des données s’accroit.

9.3. Tri à bulles :


 Principe : Parcourir la liste en comparant à chaque fois deux éléments successifs(L[i]
et L[i+1]), si l’ordre croissant n’est pas respecté (L[i]>L[i+1]) alors on permute les
deux éléments. On répète le parcours de la liste jusqu’à ce qu’on ne permute plus. On
est sûre d’obtenir une liste triée au bout de n-1 parcours mais parfois le processus
s’arrête un peu plus tôt.
 Exemple :
 Implémentation :

 Analyse de complexité :
Pour simplifier l’étude on va se baser sur la 2ème version de tribulles :
Algorithme Unités de temps
def tribulles(L): T(n)
n=len(L) 1+n
for i in range(n-1): n-1 boucles
for j in range(n-i-1): n-i-1 boucles
if L[j]>L[j+1]: 3
L[j],L[j+1]=L[j+1],L[j] 6

 O(T(n))=O(n²) : La complexité du tri à bulle est aussi quadratique.

9.4. Tri par insertion :


 Principe : A chaque étape, on cherche la bonne position d’insertion d’un élément se
trouvant à la position i dans la séquence d’éléments qui le précède (L[ :i]). On
compare l’élément L[i] avec L[i-1], s’il est plus petit alors on décale L[i-1](L[i]L[i-
1]) et on fait reculer la valeur du compteur i. Bien évidemment :
 On ne peut pas décrémenter le compteur d’une manière infinie
 Le premier élément à considérer dans l’insertion est le 2ème pas le premier.
On note qu’à chaque étape i, la sous liste L[ :i] est déjà triée pour i=1..len(L).

 Exemple :
 Implémentation :

 Analyse de complexité :

Algorithme Unités de temps


def tri(L): T(n)
n=len(L) 1+n
for i in range(1,n): n-1 boucles
pos=i 1
x=L[i] 2
while pos>0 and L[pos-1]>x: i boucles au pire des cas
L[pos]=L[pos-1] 3
pos=pos-1 2
if pos!=i:L[pos]=x 1+2
Le temps d’exécution est donné par :

( ) ∑

 O(T(n))=O(n²)

9.4. Tri par fusion:


 Principe :
C’est un algorithme de tri basé sur le principe « Diviser pour régner ». On subdivise la
liste en deux listes de tailles presque égales ; on tri (on règne) ces dernières puis on les
fusionne (on combine) pour obtenir une liste triée.
L’algorithme utilise deux fonctions :
- Fonction fusion : Fusionne deux listes supposées triées en une seule. Par exemple
fusion([1,2,4],[2,3,8]) renvoie [1,2,2,3,4,8]. L’algorithme de la fusion procède comme
suit :
 Comparer les premiers éléments des deux sous listes
 Ajouter le plus petit d’entre eux dans une liste résultat
 Avancer dans la liste d’où est ce que l’élément a été pris
 Si on termine tous les éléments d’une liste et qu’il reste encore des éléments dans
la seconde, on effectue une copie des éléments restants directement

- Fonction tri : C’est une fonction récursive. Elle consiste à :

 Couper en deux la liste initiale


 Trier par appels récursifs les deux sous listes
 Fusionner les deux listes triées en utilisant la fonction fusion.

 Implémentation :
 Analyse de complexité :

Algorithme Unités de temps


def fusion(A,B): T(n)
C=[] 1
i,j=0,0 2
n1,n2=len(A),len(B) 2+2n
while i<n1 and j<n2: 2*min(n1,n2)
if A[i]<B[j]: 3
C.append(A[i]) 1
i+=1 2
else:
C.append(B[j]) 1
j+=1 2
if i==n1:C.extend(B[j:]) n2-j+1+1
if j==n2:C.extend(A[i:]) n1-i+1+1
return C 1
 O(T(n))=O(n)
def trifusion(L): T(n)
if L==[] or len(L)==1:return L 3+n
n=len(L) 1+n
m=n//2 2
A=L[:m] n/2
B=L[m:] n/2
A1=trifusion(A) T(n/2)
B1=trifusion(B) T(n/2)
C=fusion(A1,B1) O(n)
return C 1

Le temps d’exécution du tri est donné par :

( ) ( )

On en déduit que : a=2, b=2 et k=1. Or bk=21 = a : O(T(n))=O(nkLogb(n)) = O(nlog2(n)). Le tri


par fusion est un algorithme quasi linéaire plus rapide que les tris abordés précédemment.

n n² nlogn
100 10000 665
1000 1000000 9966
100000 10000000000 1660964

9.4. Tri rapide ou quicksort:


 Principe :
C’est aussi un algorithme basé sur le paradigme « diviser pour régner » :
- Diviser : Il s’agit de choisir un élément appelé pivot p et de subdiviser la liste initiale
L en deux sous listes A et B contenant respectivement les éléments de L qui sont
inférieurs à p et ceux qui lui sont supérieurs ou égaux.
- Régner : Le même principe est appliqué pour tirer les deux sous-listes A et B, jusqu’à
obtention de sous listes de tailles ≤1.
- Combiner : Aucun effort de combinaison n’est nécessaire étant donné que les sous
listes sont déjà triées.
 Implémentation :
- Fonction de partitionnement : On va choisir le 1er élément de la liste L comme pivot.

- La fonction du tri :

 Analyse de complexité :

Algorithme Unités de temps


def part(L): T(n)
p=L[0] 2
A=[] 1
B=[] 1
for i in range(1,len(L)): n-1
if L[i]<p:A.append(L[i]) 3
else:B.append(L[i]) 1
return A+[p]+B 3
O(T(n))=O(n)

def QS(L): T(n)


if len(L)==1 or L==[]:return L 3+n
x=L[0] 2
L=part(L) n+10
pos=L.index(x) n+1
return QS(L[:pos])+[x]+QS(L[pos+1:]) T(n/2)+1+T(n/2)

D’où :

( ) ( )

 O(T(n))=O(nlog2(n)) : Le tri rapide est un tri quasi linéaire.

Vous aimerez peut-être aussi