Skip to main content Informatique en prépa - Lycée Malherbe 

4- Tris

I. C'est quoi un tri ?

Trier une liste ou une collection d'éléments, ça consiste à les réordonner pour qu'ils soient en ordre croissant ou décroissant.

On peut écrire une condition nécessaire et suffisante pour qu'une liste soit triée dans l'ordre croissant :

On va en général définir des fonctions de tri, qui prennent en argument une liste et la trient. On distingue deux sortes de fonctions :

II. Pourquoi trier ?

Raisons de présentation : dans une table, une liste, ou d'autres données destinées à un affichage, il est souvent utile de trier les éléments. Imaginez un dictionnaire où les mots sont dans un ordre aléatoire !

Raisons informatiques : en informatique, on se retrouve souvent dans une situation où il est plus rapide de trier une liste avant de l'utiliser.

III. Comment trier ?

a) Le tri à bulle

Idée : à chaque étape, on compare deux éléments adjacents. S'ils sont dans le bon ordre, on les laisse. Sinon, on les échange.

Python

Loading editor...

def tri_bulle(L):
    """Trie la liste L "in place" avec l'algorithme du tri à bulle"""
    for i in range(len(L) - 1):
        for j in range(len(L) - 1 - i):
            if L[j] > L[j+1]:
                L[j], L[j+1] = L[j+1], L[j]

Par exemple :

Python

Loading editor...

L = [4, 1, 2, 8, 3, 1, 9]
tri_bulle(L)
print(L)

La fonction animer_tri(fonction_de_tri, liste) permet de voir le déroulement d'un tri : chaque élément est une barre, et les éléments comparés sont en rouge. Essayez avec d'autres listes !

Python
code masqué : pas besoin de le lire pour suivre, exécutez-le simplement

Quelle est la complexité du tri à bulle ? Avec n = len(L).

Pour chaque valeur de i, la boucle interne fait n - 1 - i itérations en . Au total, on fait itérations : la complexité est en .

Réfléchissez à d'autres manières de trier une liste. Comment procédez-vous, naturellement, si on vous demande de trier la liste [4, 1, 2, 8, 3, 1, 9] ?

b) Tri par sélection

Idée : on prend d'abord le minimum de la liste, et on le place au début. Puis on répète sur le reste de la liste.

Python

Loading editor...

def tri_selection(L):
    """Trie la liste L "in place" avec l'algorithme du tri par sélection"""
    for i in range(len(L) - 1):
        el_min = L[i]
        for j in range(i + 1, len(L)):
            if L[j] < el_min:
                el_min, L[j] = L[j], el_min
        L[i] = el_min
Python
code masqué : pas besoin de le lire pour suivre, exécutez-le simplement

Quelle est la complexité du tri par sélection ? Avec n = len(L).

Comme pour le tri à bulle, la boucle interne fait itérations pour chaque valeur de i : la complexité est en .

c) Tri par insertion

Idée : on insère, un par un, chaque élément à sa place dans une nouvelle liste triée.

Python

Loading editor...

def insere(L, x):
    """Insère l'élément x à sa place dans la liste triée L in-place"""
    L.append(x)
    for i in range(len(L) - 1, 0, -1):
        if L[i] < L[i-1]:
            L[i], L[i-1] = L[i-1], L[i]
        else: 
            break
Python

Loading editor...

L = [1, 3, 5, 8]
insere(L, 4)
print(L)

Quelle est la complexité de la fonction insere ? Avec n = len(L).

L'ajout L.append(x) est en temps constant. La boucle fait ensuite au plus itérations, chacune en temps constant (une comparaison et éventuellement un échange). Au total, la complexité est en .

Python

Loading editor...

def tri_insertion(L):
    """renvoie une nouvelle liste contenant les éléments de L, triée par insertion."""
    L_triee = []
    for x in L:
        insere(L_triee, x)
    return L_triee
Python

Loading editor...

print(tri_insertion([4, 1, 2, 8, 3, 1, 9]))

Quelle est la complexité de la fonction tri_insertion ? Avec n = len(L).

On appelle n fois la fonction insere, sur une liste de taille au plus n, donc en : la complexité est en .

Pouvez-vous écrire une version in-place du tri par insertion ?

d) Tri par insertion en place

On va faire une fonction insere qui prend en argument une liste L dont les k premiers éléments (d'indices 0, 1, ..., k-1) sont triés dans l'ordre croissant, un indice k et un élément x.

La fonction insère alors l'élément x dans les k premiers éléments de L, en écrasant l'élément d'indice k.

Python

Loading editor...

def insere(L, k, x):
    #boucle sur (k-1), (k-2), ..., 1, 0
    for i in range(k-1, -1, -1):
        #Si x est plus grand que l'élément actuel, on le range juste après, et on s'arrête
        if x > L[i]:
            L[i+1] = x
            return
        #sinon, on décale l'élément d'un cran pour laisser de la place à x
        else:
            L[i+1] = L[i]
    #si on arrive au bout, c'est que x est le plus petit élément
    L[0] = x
Python

Loading editor...

L = [1, 3, 5, 8, 4]   # les 4 premiers éléments sont triés
insere(L, 4, 4)       # on insère l'élément d'indice 4 parmi eux
print(L)

On utilise ensuite cette fonction pour réaliser le tri :

Python

Loading editor...

def tri_insertion(L):
    """Trie L en ordre croissant, par insertion, in-place"""
    #On insère le (k+1)ème élément dans les k premiers.
    for k in range(len(L)):
        insere(L, k, L[k])
Python
code masqué : pas besoin de le lire pour suivre, exécutez-le simplement

Comparez le nombre de comparaisons des différents tris sur une liste déjà triée, par exemple [1, 2, 3, 4, 5, 6, 7, 8, 9], puis sur une liste triée dans l'ordre inverse.

Python
code masqué : pas besoin de le lire pour suivre, exécutez-le simplement

e) Propriétés

Que se passe-t-il dans chacun des algorithmes si la liste est déjà triée ? Si elle est "presque" triée ?

Si on regarde la fonction du tri par insertion en place, en particulier l'insertion, elle ne fait qu'une seule itération si l'élément est déjà à sa place. Donc tout se passe comme si la liste était simplement parcourue une fois, pour une complexité dans le meilleur cas en .

f) Caractéristiques d'un tri

Définitions

Un algorithme de tri est :

  • en place s'il modifie directement la liste, sans utiliser une autre liste de même taille ;
  • stable s'il ne change pas l'ordre relatif des éléments égaux ;
  • comparatif s'il n'accède aux éléments qu'en les comparant deux à deux.

La stabilité compte quand on trie des données selon un critère : par exemple, des élèves déjà rangés par ordre alphabétique, que l'on trie par note. Avec un tri stable, les élèves qui ont la même note restent dans l'ordre alphabétique.

Python

Loading editor...

def tri_insertion_cle(L, cle):
    """Tri par insertion en place, selon la valeur de cle(x) pour chaque élément x."""
    for i in range(1, len(L)):
        x = L[i]
        j = i
        while j > 0 and cle(L[j - 1]) > cle(x):   # > strict : on ne dépasse pas un élément égal
            L[j] = L[j - 1]
            j -= 1
        L[j] = x

eleves = [("Alice", 14), ("Bob", 12), ("Chloé", 14), ("David", 12), ("Emma", 16)]
tri_insertion_cle(eleves, lambda e: e[1])
eleves

Bob est toujours avant David, et Alice avant Chloé : le tri par insertion est stable.

TriEn placeStableComparatifComplexité (pire cas)
À bullesouiouioui
Par sélectionouinonoui
Par insertionouiouioui
Par comptagenonouinon

Montrer que le tri par sélection n'est pas stable, en trouvant une liste de couples pour laquelle il change l'ordre de deux éléments égaux.

Avec [(2, "a"), (2, "b"), (1, "c")] triée selon le premier élément : le minimum (1, "c") est échangé avec le premier élément (2, "a"), ce qui donne [(1, "c"), (2, "b"), (2, "a")] : (2, "b") est passé devant (2, "a").

g) Tri par comptage

Si les éléments à trier sont des entiers entre et (des notes entre 0 et 20, par exemple), on peut trier sans aucune comparaison : on compte combien de fois apparaît chaque valeur, puis on reconstruit la liste triée.

Python

Loading editor...

def tri_comptage(L, m):
    """Trie une liste d'entiers compris entre 0 et m - 1."""
    compte = [0] * m
    for x in L:
        compte[x] += 1
    resultat = []
    for v in range(m):
        for k in range(compte[v]):
            resultat.append(v)
    return resultat

tri_comptage([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5], 10)

La complexité est en : on parcourt la liste une fois, puis les valeurs possibles. Quand est petit devant , c'est plus rapide que les tris comparatifs ! En revanche, il faut connaître l'intervalle des valeurs, et il utilise une liste supplémentaire de taille .

Exercices

Exercice 1

Écrire une fonction est_triee(L) qui renvoie True si la liste L est triée dans l'ordre croissant, False sinon. Quelle est sa complexité ?

Python

Loading editor...

def est_triee(L):
    for i in range(len(L) - 1):
        if L[i] > L[i + 1]:
            return False
    return True

Complexité dans le pire cas.

Exercice 2

Adapter le tri par sélection pour trier une liste dans l'ordre décroissant.

Python

Loading editor...

Il suffit de chercher le maximum au lieu du minimum :

def tri_selection_decroissant(L):
    for i in range(len(L)):
        i_max = i
        for j in range(i + 1, len(L)):
            if L[j] > L[i_max]:
                i_max = j
        L[i], L[i_max] = L[i_max], L[i]
Exercice 3

On veut trier une liste de 1 000 000 de notes (des entiers entre 0 et 20). Quel tri choisir ? Écrire l'appel correspondant.

Le tri par comptage, en , soit environ un million d'opérations, contre environ pour un tri en : tri_comptage(notes, 21).