Objectifs de cette leçon
- Implémenter les tris (bulles, fusion, rapide)
- Comparer la complexité de chaque algorithme de tri
- Choisir le bon tri en fonction du contexte
Algorithmes de tri 🔍
Trier des données est l'un des problèmes les plus étudiés en informatique. Il existe des dizaines d'algorithmes, chacun avec des forces et faiblesses différentes. Comprendre ces algorithmes, c'est comprendre la complexité algorithmique en pratique.
1. Bubble Sort (Tri à bulles) — O(n²) 🫧
Principe : On parcourt le tableau en comparant les éléments adjacents. Si deux éléments sont dans le mauvais ordre, on les échange. Les plus grands éléments "remontent" vers la fin comme des bulles dans un verre.
Exécution pas à pas
pseudo1/12🧩FONCTION tri_bulles(tableau)FONCTION tri_bulles(tableau)Le programme exécute cette action, puis passe à la suivante.On définit une fonction appelée "tri_bulles(tableau)". Cette fonction contient des instructions qui seront exécutées quand on l'appelle.
def bubble_sort(arr): n = len(arr) for i in range(n): for j in range(0, n - i - 1): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] return arr
Pas à pas sur [5, 3, 8, 1] :
- 5>3 → échange → [3, 5, 8, 1] — 5 remonte
- 5<8 → rien → [3, 5, 8, 1]
- 8>1 → échange → [3, 5, 1, 8] — 8 a remonté jusqu'à la fin !
- Deuxième tour : 3<5, 5>1 → échange → [3, 1, 5, 8]
- Dernier tour : 3>1 → échange → [1, 3, 5, 8] ✅
⏱ Complexité : O(n²) dans tous les cas. Simple à comprendre, lent en pratique.
2. Insertion Sort (Tri par insertion) — O(n²) 📥
Principe : On construit le tableau trié un élément à la fois. On prend chaque élément et on l'insère à sa bonne place parmi les éléments déjà triés (comme quand on trie des cartes à jouer).
Exécution pas à pas
pseudo1/13🧩FONCTION tri_insertion(tableau)FONCTION tri_insertion(tableau)Le programme exécute cette action, puis passe à la suivante.On définit une fonction appelée "tri_insertion(tableau)". Cette fonction contient des instructions qui seront exécutées quand on l'appelle.
def insertion_sort(arr): for i in range(1, len(arr)): key = arr[i] # L'élément à insérer j = i - 1 while j >= 0 and arr[j] > key: arr[j + 1] = arr[j] # Décalage j -= 1 arr[j + 1] = key # Insertion return arr
Pas à pas sur [5, 3, 8, 1] :
- i=1, clé=3. 5>3 → décale → [5, 5, 8, 1] → insère 3 → [3, 5, 8, 1]
- i=2, clé=8. 5<8 → rien → [3, 5, 8, 1]
- i=3, clé=1. 8>1, 5>1, 3>1 → décale → [3, 5, 8, 8] → [3, 5, 5, 8] → [3, 3, 5, 8] → insère 1 → [1, 3, 5, 8] ✅
💡 Performant sur les petits tableaux et les tableaux presque triés (O(n) au meilleur cas).
3. Merge Sort (Tri fusion) — O(n log n) 🧩
Principe : Diviser pour régner (divide and conquer).
- Diviser le tableau en deux moitiés
- Trier récursivement chaque moitié
- Fusionner les deux moitiés triées
Exécution pas à pas
pseudo1/31🧩FONCTION tri_fusion(tableau)FONCTION tri_fusion(tableau)Le programme exécute cette action, puis passe à la suivante.On définit une fonction appelée "tri_fusion(tableau)". Cette fonction contient des instructions qui seront exécutées quand on l'appelle.
def tri_fusion(tab): if len(tab) <= 1: return tab milieu = len(tab) // 2 gauche = tri_fusion(tab[:milieu]) droite = tri_fusion(tab[milieu:]) return fusionner(gauche, droite) def fusionner(g, d): resultat = [] i = j = 0 while i < len(g) and j < len(d): if g[i] <= d[j]: resultat.append(g[i]) i += 1 else: resultat.append(d[j]) j += 1 resultat.extend(g[i:]) resultat.extend(d[j:]) return resultat tab = [8, 3, 5, 1, 9, 2] print(tri_fusion(tab)) # [1, 2, 3, 5, 8, 9]
Visualisation : [8, 3, 5, 1, 9, 2]
- Diviser → [8, 3, 5] et [1, 9, 2]
- Diviser → [8] [3, 5] et [1, 9] [2]
- Trier → [8] [3, 5] et [1, 9] [2]
- Fusionner → [3, 5, 8] et [1, 2, 9]
- Fusionner → [1, 2, 3, 5, 8, 9] ✅
⏱ Toujours O(n log n) — même complexité dans le meilleur et le pire cas. Stable et prévisible.
4. Quick Sort (Tri rapide) — O(n log n) en moyenne ⚡
Principe : Choisir un pivot, partitionner le tableau autour de ce pivot, puis trier récursivement chaque partition.
Exécution pas à pas
pseudo1/12🧩FONCTION tri_rapide(tableau)FONCTION tri_rapide(tableau)Le programme exécute cette action, puis passe à la suivante.On définit une fonction appelée "tri_rapide(tableau)". Cette fonction contient des instructions qui seront exécutées quand on l'appelle.
def tri_rapide(tab): if len(tab) <= 1: return tab pivot = tab[len(tab) // 2] gauche = [x for x in tab if x < pivot] milieu = [x for x in tab if x == pivot] droite = [x for x in tab if x > pivot] return tri_rapide(gauche) + milieu + tri_rapide(droite) tab = [8, 3, 5, 1, 9, 2] print(tri_rapide(tab)) # [1, 2, 3, 5, 8, 9]
💡 Rapide en pratique mais O(n²) dans le pire cas (pivot mal choisi). C'est le tri le plus utilisé dans les langages.
5. Comparaison des tris ⚖️
| Algorithme | Meilleur | Moyen | Pire | Mémoire | Stable ? |
|---|---|---|---|---|---|
| Bubble | O(n) | O(n²) | O(n²) | O(1) | ✅ |
| Insertion | O(n) | O(n²) | O(n²) | O(1) | ✅ |
| Merge | O(n log n) | O(n log n) | O(n log n) | O(n) | ✅ |
| Quick | O(n log n) | O(n log n) | O(n²) | O(log n) | ❌ |
Quand utiliser quoi ?
- Insertion : petits tableaux (< 50 éléments) ou presque triés
- Merge : quand la stabilité et la prévisibilité comptent
- Quick : usage général, grands tableaux
- Bubble : à éviter en pratique (pédagogique uniquement)
6. Recherche dichotomique (Binary Search) — O(log n) 🎯
Une fois le tableau trié, on peut chercher très rapidement :
Exécution pas à pas
pseudo1/19🧩FONCTION recherche_dichotomique(tableau, cible)FONCTION recherche_dichotomique(tableau, cible)Le programme exécute cette action, puis passe à la suivante.On définit une fonction appelée "recherche_dichotomique(tableau, cible)". Cette fonction contient des instructions qui seront exécutées quand on l'appelle.
def binary_search(arr, target): left, right = 0, len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1 tab = [1, 3, 5, 7, 9, 11, 13] print(binary_search(tab, 7)) # 3
💡 Avec 1 million d'éléments triés, la recherche dichotomique trouve la réponse en ~20 étapes (log₂ 1 000 000 ≈ 20). Une recherche linéaire en prendrait 500 000 en moyenne !
Exercices pour toi 🎯
- Implémente Bubble Sort avec une optimisation : arrête-toi si aucune permutation n'a eu lieu pendant un tour
- Merge Sort récursif : trace les appels récursifs pour
[4, 2, 7, 1] - Binary Search : trouve le point d'insertion d'une valeur dans un tableau trié
- Compare les temps d'exécution des 4 tris sur
[100, 1000, 10000]éléments
Exécution pas à pas
État de la mémoire
2 variables⚡ Simulateur — Tri à bulles
Étape 1/21- Compare chaque paire d'éléments adjacents
- Les échange s'ils sont dans le mauvais ordre
- Le plus grand élément « remonte » à sa place à chaque passe
- Complexité : O(n²) dans le pire cas
Bubble Sort : [5, 3, 8, 1].