Vous avez vu l'année dernière le tri par insertion et le tri par sélection et tous les deux avaient une complexité en \(\mathcal{O}\left(n^2\right)\).
1. Diviser pour régner (divide and conquer)
C’est une méthode de conception d’algorithmes qui consiste à :
Diviser un problème en sous-problèmes plus petits et de même nature ;
Résoudre récursivement ces sous-problèmes ;
Combiner les solutions obtenues pour construire la solution du problème initial.
Des exemples classiques d’algorithmes « diviser pour régner » sont le tri fusion (merge sort),
le tri rapide (quick sort) ou encore la recherche dichotomique.
2. Exercice : (sujet 9 - 2025) recherche dichotomique dans un tableau trié.
On s’intéresse à la recherche dichotomique dans un tableau trié d’entiers.
Compléter la fonction suivante en respectant la spécification.
def dichotomie(tab, x):
"""
tab : tableau d'entiers trié dans l'ordre croissant
x : nombre entier
La fonction renvoie True si tab contient x et False sinon
"""
debut = 0
fin = len(tab) - 1
while debut <= fin:
m = ...
if x == tab[m]:
return ...
if x > tab[m]:
debut = m + 1
else:
fin = ...
return ...
tab = [15,16,18,19,23,24,28,29,31,33]
valeur_cherchee = 29
if dichotomie(tab, valeur_cherchee):
print(f"{valeur_cherchee} est présente dans la table tab.")
else:
print(f"{valeur_cherchee} n'est pas présente dans la table tab.")
>>>
3. Le tri fusion (merge sort)
Le tri fusion est un algorithme de tri fondé sur la stratégie du diviser pour régner.
Il consiste à découper la liste à trier en sous-listes de plus en plus petites, jusqu’à n’obtenir que des listes d’un seul élément, puis à les fusionner progressivement dans l’ordre croissant.
Diviser : on coupe la liste en deux parties de taille aussi égale que possible ;
Régner : on trie récursivement les deux sous-listes obtenues ;
Combiner : on fusionne les deux listes triées pour obtenir une liste triée complète.
Cet algorithme garantit un temps d’exécution en \(\mathcal{O}\left(n \log(n)\right)\) dans tous les cas, ce qui le rend très fiable même pour de grandes quantités de données.
Observe l'animation ci-dessous : chaque cadre est une liste réellement présente dans la mémoire de Python. Les découpages créent de nouvelles listes (des copies), la fusion construit une nouvelle liste final élément par élément, et les listes locales disparaissent dès que Python libère leur mémoire — exactement ce que fait le code des exercices suivants.
liste non triéeliste triéeélément comparé / milieuélément ajouté à finalliste en cours de traitement
Clique sur « Démarrer / Redémarrer » pour lancer l'animation.
4. Exercice : fusion !
Compléter la fonction fusion(liste1,liste2), qui prend comme paramètres deux listes triées et qui renvoie une liste triée composée des éléments des deux autres.
def fusion(liste1,liste2):
'''
fusion prend deux listes triées et retourne une grande liste triée contenant les éléments des deux autres.
'''
final = [] # liste qui sera retournée
curseur1 = 0 # curseur de la liste1
curseur2 = 0 # curseur de la liste2
while ...................................................... : # tant qu'il reste des éléments non traités dans une des deux listes
if ............... : # le plus petit élément est dans la liste 1
final.append(liste1[curseur1])
curseur1 += 1
else: # le plus petit élément est celui de la liste 2
......................
......................
if ......................... : # il reste des éléments dans la liste 2 à traiter
for i in range(curseur2, len(liste2)):
final.append(.......)
else : # il reste des éléments dans la liste 1 à traiter
for i in range(curseur1, len(liste1)):
final.append(.......)
return final
# données de test
liste_a = [2, 5, 7, 8, 9, 11, 13, 15, 19, 21, 25, 29, 35, 38, 39, 40, 52, 69, 78, 89]
liste_b = [3, 4, 5, 7, 12, 14, 15, 18, 23, 30, 32, 33, 34, 42, 53]
print(f"Fusion des deux listes : {fusion(liste_a, liste_b)}.")
>>>
5. Exercice : tri fusion
Compléter la fonction tri_par_fusion(liste) en vous appuyant sur la fonction fusion (fusion de deux listes triées) de l'exercice précédent.
def fusion(liste1, liste2):
"""Fusionne deux listes triées en une liste triée (avec doublons conservés)."""
# RECOPIER LA FONCTION COMPLÉTÉE DE L'EXERCICE PRÉCÉDENT
def tri_par_fusion(liste):
"""Retourne une nouvelle liste triée par la méthode "diviser pour régner" (merge sort)."""
if ...: # Il y a un élément dans la liste ou moins.
return liste
else:
moitie = ... # indice correspondant au milieu de la liste
gauche = tri_par_fusion(...) # partie gauche
droite = tri_par_fusion(...) # partie droite
return fusion(gauche, droite)
liste = [142, 92, 86, 483, 87, 997, 515, 243, 193, 175, 680, 226, 794, 332,
534, 305, 656, 393, 66, 659, 361, 362, 387, 469, 274, 343, 471, 443, 982, 786, 636,
587, 768, 966, 816, 959, 438, 784, 584, 196, 515, 249, 737, 704, 635, 572, 101, 209,
298, 832, 338, 750, 922, 566, 704, 113, 6, 560, 62, 451, 98, 708, 69, 600, 669, 404,
685, 707, 440, 741, 423, 129, 545, 237, 178, 870, 654, 849, 453, 894, 60, 140, 777,
894, 403, 672, 248, 627, 282, 723, 176, 994, 342, 806, 259, 422, 733, 644, 400, 3]
print(tri_par_fusion(liste))
>>>
6. Exercice : comparaison des complexités des tris.
À faire dans le cahier.
Ce code a permis de générer les 3 images ci-dessous :
Il peut sembler que les courbes soient similaires mais :
Comparer les performances de chaque tri.
À quelle courbe vue en seconde ressemblent les deux premières ci-dessus ?
Quel est l'aspect de la troisième courbe ?
Dans les deux premières courbes :
Prendre deux points de la courbe dont les abscisses sont à peu près le double l'une de l'autre.
Calculer une approximation du rapport de ces ordonnées.
Dans les deux premières courbes :
Prendre deux points de la courbe dont les abscisses sont à peu près le triple l'une de l'autre.
Calculer une approximation du rapport de ces ordonnées.
Dans les deux premières courbes :
Prendre deux points de la courbe dont les abscisses sont à peu près le quadruple l'une de l'autre.
Calculer une approximation du rapport de ces ordonnées.
Dans la troisième courbe :
Prendre deux points de la courbe dont les abscisses sont à peu près le quadruple l'une de l'autre.
Calculer une approximation du rapport de ces ordonnées.
7. À retenir !
Les tris par insertion et par sélection sont de complexités quadratiques en \(\mathcal{O}\left(n^2 \right)\).
Le tri par fusion est de complexité linéarithmique en \(\mathcal{O}\left(n \log(n)\right)\).