Tris élémentairesDans le dialogue d’impression, choisissez « Enregistrer au format PDF » comme destination.
Retour

Algorithmique 1 · C3 Recherche et tris · Chapitre 2 · 30 min

Tris élémentaires

Dérouler le tri par sélection et le tri par insertion, et comprendre leur invariant.

La leçon précédente s'est achevée sur une dette : la dichotomie exige un tableau trié. Il est temps de le trier. Les deux algorithmes de cette leçon ne sont pas les plus rapides — ils sont les plus instructifs, parce qu'on peut les dérouler entièrement à la main.

Ce que trier veut dire

Trier un tableau, c'est le réorganiser pour que T[0] ≤ T[1] ≤ … ≤ T[n − 1]. Deux exigences, pas une seule :

La seconde est facile à oublier, et c'est pourtant elle qui interdit les fausses solutions. Un algorithme qui remplirait le tableau de zéros produirait une suite parfaitement croissante — et un résultat évidemment faux.

Les deux tris qui suivent travaillent en place : ils réorganisent le tableau existant sans en créer un second. La seule opération dont ils disposent pour cela est l'échange de deux cases — exactement le schéma tmp de la leçon 2.

Le tri par sélection

L'idée tient en une phrase : chercher le plus petit élément restant, et l'amener à sa place définitive.

Pour i de 0 à n − 2 Faire    indiceMin ← i    Pour j de i + 1 à n − 1 Faire        Si T[j] < T[indiceMin] Alors            indiceMin ← j        FinSi    FinPour    tmp ← T[i]    T[i] ← T[indiceMin]    T[indiceMin] ← tmpFinPour

Deux boucles imbriquées : celle de i compte les tours, celle de j cherche le minimum dans ce qui reste. On mémorise l'indice du minimum, pas sa valeur — c'est l'indice qui servira à échanger.

Animation · 9 étapes

Tri par sélection de [5, 2, 9, 1, 6]

  1. Le tableau de départPrincipe : à chaque tour, chercher le plus petit élément de la partie non triée et l'amener à sa place définitive.
  2. Tour 1 — le minimum est 1, en case 3Le trouver a demandé 4 comparaisons : il faut voir toutes les cases restantes pour être sûr d'avoir le plus petit.
  3. Tour 1 — on échange T[0] et T[3]1 est à sa place DÉFINITIVE : plus rien de plus petit ne reste. Le 5 qui occupait la case 0 est parti en case 3.
  4. Tour 2 — le minimum restant est 2, en case 1On ne regarde plus la case 0 : elle est définitive.
  5. Tour 2 — il est déjà à sa placeL'échange T[1] ↔ T[1] a bien lieu dans la plupart des écritures : il ne coûte rien et évite un test supplémentaire.
  6. Tour 3 — le minimum restant est 5, en case 3Deux comparaisons cette fois : la partie non triée rétrécit à chaque tour.
  7. Tour 3 — on échange T[2] et T[3]Le 9 est renvoyé plus loin. Notez qu'il « saute » : le tri par sélection n'est pas stable.
  8. Tour 4 — le minimum restant est 6, en case 4Une seule comparaison suffit : il ne reste que deux cases.
  9. Tour 4 — on échange T[3] et T[4] : c'est finin − 1 tours suffisent : dès que les 4 premières cases sont définitives, la dernière l'est forcément, puisqu'il ne reste qu'une valeur.

L'invariant de ce tri — la propriété vraie à la fin de chaque tour — est le suivant : après le tour i, les cases 0 à i contiennent les i + 1 plus petites valeurs, à leur place définitive. Définitive est le mot fort : ces cases ne bougeront plus jamais.

Remarquez aussi qu'on s'arrête à n − 2 : quand les n − 1 premières cases sont définitives, la dernière l'est forcément, puisqu'il ne reste qu'une valeur pour l'occuper.

Le tri par insertion

Autre idée, celle du joueur de cartes : prendre l'élément suivant et le glisser à sa place parmi ceux déjà rangés.

Pour i de 1 à n − 1 Faire    valeur ← T[i]    j ← i − 1    TantQue j ≥ 0 ET T[j] > valeur Faire        T[j + 1] ← T[j]        j ← j − 1    FinTantQue    T[j + 1] ← valeurFinPour

Animation · 9 étapes

Tri par insertion de [5, 2, 9, 1, 6]

  1. Le tableau de départPrincipe : comme des cartes en main. La case 0 seule est déjà « triée » — un élément unique l'est toujours.
  2. On prend T[1] = 2On le retire mentalement du tableau et on cherche où le glisser à gauche.
  3. 2 se glisse avant 55 est décalé d'une case vers la droite pour libérer la place. Un décalage, une insertion.
  4. On prend T[2] = 9La partie gauche [2, 5] est triée. Où va 9 ?
  5. 9 reste où il estUne seule comparaison (9 > 5) et c'est réglé : aucun décalage. Sur un tableau déjà trié, ce tri ne fait que n − 1 comparaisons.
  6. On prend T[3] = 1Le plus petit élément du tableau, tout au fond. Ça va coûter cher.
  7. 1 remonte jusqu'en tête2, 5 et 9 sont tous décalés d'un cran. Trois décalages pour une insertion : c'est le pire cas de ce tri, un tableau à l'envers.
  8. On prend T[4] = 6Dernier élément à insérer dans [1, 2, 5, 9].
  9. 6 se glisse entre 5 et 9 : c'est finiDifférence essentielle avec la sélection : ici la partie gauche était triée mais PAS définitive — le 9 vient encore de bouger au dernier tour.

La boucle démarre à 1, pas à 0 : une case seule est déjà triée, il n'y a rien à faire au premier élément. La boucle interne ne cherche pas, elle décale — chaque élément trop grand recule d'une case pour ouvrir un trou, et la valeur mise de côté vient s'y loger.

Un détail de la condition j ≥ 0 ET T[j] > valeur mérite l'attention : l'ordre des deux tests n'est pas indifférent. Le premier protège le second. Quand j atteint −1, la machine constate que j ≥ 0 est FAUX et n'évalue même pas T[j], ce qui serait un débordement d'indice. Cette évaluation paresseuse est garantie par tous les langages courants, mais elle vous impose de mettre le garde-fou en premier.

Comparer les deux sur le même tableau

Les deux animations partent du même tableau [5, 2, 9, 1, 6]. Rejouez-les côte à côte : le comportement diffère profondément.

Tri par sélectionTri par insertion
Invariantles i premières cases sont définitivesles i premières cases sont triées entre elles
Opération de baseéchanger deux casesdécaler d'une case
Nombre de comparaisonstoujours le même, quel que soit le tableaudépend du désordre initial
Tableau déjà triémême travail completn − 1 comparaisons, aucun décalage
Nombre d'échangesau plus n − 1autant que de décalages

La différence d'invariant est le point le plus important de la leçon. Dans le tri par sélection, une case placée ne bouge plus jamais : elle est définitive. Dans le tri par insertion, la partie gauche est triée mais pas définitive — regardez le 9 dans l'animation, il est encore déplacé au dernier tour.

Quiz · 1 question

Sur un tableau DÉJÀ trié, lequel des deux fait le moins de travail ?

  • Le tri par sélection, car il fait peu d'échanges
  • Le tri par insertion, car chaque élément est bien placé du premier coup
  • Les deux font exactement le même travail

Réponse : Le tri par insertion compare chaque élément à son voisin de gauche, constate qu'il est plus grand, et n'effectue aucun décalage : n − 1 comparaisons au total. Le tri par sélection, lui, cherche quand même le minimum dans toute la partie restante à chaque tour : il ne profite d'aucun ordre préexistant.

Quiz · 1 question

Dans le tri par sélection, pourquoi mémorise-t-on l'indice du minimum plutôt que sa valeur ?

  • Parce qu'un indice occupe moins de place en mémoire
  • Parce que l'échange a besoin de savoir OÙ se trouve le minimum pour le déplacer
  • Parce que la valeur pourrait changer pendant la boucle

Réponse : Connaître la valeur 1 ne dit pas où elle est. L'échange T[i] ↔ T[indiceMin] a besoin de la position. C'est une constante de l'algorithmique sur tableaux : on manipule des indices, les valeurs suivent.

Quiz · 1 question

Le tri par sélection sur 5 éléments effectue 4 + 3 + 2 + 1 = 10 comparaisons. Combien en fera-t-il sur 10 éléments ?

  • 20deux fois plus
  • 45somme des entiers
  • 100n fois n

Réponse : 9 + 8 + … + 1 = 45, soit n(n − 1)/2 — la formule de la somme des entiers vue à la leçon 4. Doubler la taille a plus que doublé le travail : il a été multiplié par 4,5. C'est ce comportement que la leçon 8 va nommer.

À vous

Exercice de code

Complétez la recherche du minimum et l'échange. L'échange reprend exactement le schéma de la leçon 2.

Point de départ

// Complétez le tri par sélection.
// Rappel de l'invariant : après le tour i, les cases 0 à i sont à leur
// place DÉFINITIVE.
function triSelection(t) {
  const a = [...t]; // on travaille sur une copie, l'original reste intact

  for (let i = 0; i < a.length - 1; i++) {
    let indiceMin = i;

    for (let j = i + 1; j < a.length; j++) {
      // à compléter : retenir l'indice du plus petit élément restant
    }

    // à compléter : échanger a[i] et a[indiceMin]
  }

  return a;
}

console.log(triSelection([5, 2, 9, 1, 6]));  // attendu : [1, 2, 5, 6, 9]
console.log(triSelection([1, 2, 3]));        // attendu : [1, 2, 3]

Solution

function triSelection(t) {
  const a = [...t];

  for (let i = 0; i < a.length - 1; i++) {
    // On mémorise l'INDICE, pas la valeur : c'est lui qui servira à échanger.
    let indiceMin = i;

    for (let j = i + 1; j < a.length; j++) {
      if (a[j] < a[indiceMin]) indiceMin = j;
    }

    const tmp = a[i];
    a[i] = a[indiceMin];
    a[indiceMin] = tmp;
  }

  // n - 1 tours suffisent : la dernière case est forcément à sa place.
  return a;
}

console.log(triSelection([5, 2, 9, 1, 6]));
console.log(triSelection([1, 2, 3]));

À retenir

Flashcards · 2 cartes

Quel est l'invariant du tri par sélection ?
Après le tour i, les cases 0 à i contiennent les plus petites valeurs, à leur place DÉFINITIVE : elles ne bougeront plus.
Quel est l'invariant du tri par insertion ?
Après le tour i, les cases 0 à i sont triées entre elles — mais pas définitives : un élément plus petit peut encore venir s'y insérer.