Chapitre 9 — Tri fusion
Diviser, trier puis fusionner pour obtenir un tri stable en O(n log n)
| Positionnement dans le parcours — Ce chapitre introduit une méthode de tri efficace fondée sur le paradigme « diviser pour régner ». Il consolide la récursivité, prépare l’analyse des relations de récurrence et fournit une base importante pour les tris externes et le traitement des listes chaînées. |
Fiche pédagogique du chapitre
Objectifs d’apprentissage
À la fin de ce chapitre, l’étudiant devra être capable de :
- expliquer le principe « diviser, résoudre et combiner » appliqué au tri fusion ;
- décomposer un tableau en sous-tableaux jusqu’aux cas élémentaires ;
- fusionner correctement deux séquences déjà triées ;
- écrire la version récursive du tri fusion ;
- réaliser une trace complète des divisions et des fusions ;
- justifier la correction de l’algorithme à l’aide d’un invariant ou d’une induction ;
- établir la complexité temporelle O(n log n) ;
- évaluer la mémoire auxiliaire nécessaire ;
- expliquer pourquoi le tri fusion est stable ;
- adapter le tri fusion aux listes chaînées et aux données volumineuses.
Prérequis
- Tableaux à une dimension et indices.
- Fonctions, procédures et paramètres.
- Récursivité et cas de base.
- Complexités O(n), O(log n) et O(n log n).
- Comparaison et copie d’éléments.
Organisation proposée
Partie | Contenu principal | Durée indicative |
|---|---|---|
| 9.1 | Principe, division, tri des parties et recomposition | 2 h |
| 9.2 | Fusion de deux tableaux triés et invariants | 3 h |
| 9.3 | Version récursive, arbre d’appels et correction | 3 h |
| 9.4 | Complexité, mémoire, stabilité et listes chaînées | 3 h |
| Applications | Données structurées, fichiers et tris externes | 2 h |
| TD / TP | Traces, implémentation et mesures expérimentales | 4 à 6 h |
Introduction
Les tris élémentaires étudiés précédemment, comme le tri par sélection, le tri à bulles et le tri par insertion, peuvent nécessiter un nombre quadratique d’opérations lorsque la taille du tableau augmente. Le tri fusion adopte une stratégie différente : il divise le problème en sous-problèmes plus petits, trie ceux-ci puis fusionne leurs résultats.
Cette méthode fournit un temps d’exécution en O(n log n) dans tous les cas. Elle est stable et se prête particulièrement bien aux listes chaînées, au tri de fichiers volumineux et aux traitements parallèles. Son principal coût est l’utilisation de mémoire auxiliaire lors de la fusion de tableaux.
| Idée directrice — Trier une séquence devient simple lorsque l’on sait fusionner efficacement deux séquences déjà triées. |
9.1 Principe
9.1.1 Le paradigme « diviser pour régner »
Le tri fusion suit les trois étapes classiques du paradigme « diviser pour régner » : diviser le problème, résoudre récursivement les sous-problèmes et combiner leurs solutions.
Étape | Dans le tri fusion | But |
|---|---|---|
| Diviser | Séparer le tableau en deux parties de tailles presque égales. | Réduire la taille du problème. |
| Résoudre | Trier récursivement chaque partie. | Obtenir deux séquences triées. |
| Combiner | Fusionner les deux séquences triées. | Construire une séquence globale triée. |
| Observation — Une séquence contenant zéro ou un seul élément est déjà triée. Cette propriété fournit le cas de base de la récursion. |
9.1.2 Division du tableau
À chaque niveau, le tableau est partagé autour d’un indice médian. Pour un intervalle [gauche, droite], le milieu peut être calculé par gauche + (droite - gauche) div 2. Cette écriture évite un dépassement de capacité possible avec (gauche + droite) div 2 dans certains langages.
Exemple de divisions successives [38, 27, 43, 3, 9, 82, 10] / \ [38, 27, 43, 3] [9, 82, 10] / \ / \ [38, 27] [43, 3] [9, 82] [10] / \ / \ / \ [38] [27] [43] [3] [9] [82] |
La division n’effectue pas encore le tri. Elle produit uniquement des sous-problèmes plus petits jusqu’à obtenir des séquences élémentaires.
9.1.3 Tri des deux parties
Chaque moitié est triée avec le même algorithme. L’appel récursif ne retourne qu’après avoir complètement trié sa sous-partie. L’ordre d’exécution est généralement profondeur d’abord : la branche gauche est résolue, puis la branche droite, avant la fusion.
Ordre conceptuel du traitement TriFusion(tableau complet) TriFusion(moitié gauche) ... divisions ... ... fusions ... TriFusion(moitié droite) ... divisions ... ... fusions ... Fusion(moitié gauche, moitié droite) |
| Point essentiel — La fusion d’un niveau ne commence que lorsque les deux sous-parties concernées sont déjà triées. |
9.1.4 Fusion des parties triées
La fusion compare les premiers éléments non encore copiés des deux sous-tableaux. Le plus petit est ajouté au résultat, puis l’indice correspondant avance. Dès qu’un sous-tableau est épuisé, les éléments restants de l’autre sont recopiés.
Exemple de recomposition [38] + [27] -> [27, 38] [43] + [3] -> [3, 43] [27, 38] + [3, 43] -> [3, 27, 38, 43] [9] + [82] -> [9, 82] [9, 82] + [10] -> [9, 10, 82] [3, 27, 38, 43] + [9, 10, 82] -> [3, 9, 10, 27, 38, 43, 82] |
9.1.5 Vue globale de l’algorithme
1. Si la séquence contient au plus un élément, terminer.
2. Calculer le milieu et séparer la séquence en deux parties.
3. Trier récursivement la partie gauche.
4. Trier récursivement la partie droite.
5. Fusionner les deux parties triées dans l’ordre croissant.
Pseudo-code — Vue abstraite du tri fusion Fonction TriFusion(T) : Tableau Si Longueur(T) <= 1 Alors Retourner T FinSi milieu <- Longueur(T) div 2 gauche <- SousTableau(T, 0, milieu - 1) droite <- SousTableau(T, milieu, Longueur(T) - 1) gauche <- TriFusion(gauche) droite <- TriFusion(droite) Retourner Fusionner(gauche, droite) FinFonction Cette version crée explicitement des sous-tableaux. Une version par indices évite certaines copies intermédiaires. |
9.1.6 Invariant global
À la fin de chaque appel TriFusion portant sur un intervalle donné, cet intervalle contient exactement les mêmes éléments qu’au départ, mais classés dans l’ordre demandé. Cette propriété se propage des cas de base vers le tableau complet.
| Invariant de correction — Chaque appel récursif préserve le multiensemble des valeurs et retourne une séquence triée. |
9.2 Fusion de deux tableaux
9.2.1 Précondition et objectif
La procédure de fusion reçoit deux tableaux A et B triés selon le même ordre. Elle doit produire un tableau R trié contenant tous les éléments de A et de B, sans perte ni duplication artificielle.
Élément du contrat | Description |
|---|---|
| Précondition | A et B sont triés dans le même ordre. |
| Postcondition | R est trié et contient exactement les éléments de A et B. |
| Entrées | Deux tableaux et leurs longueurs. |
| Sortie | Un tableau de longueur |A| + |B|. |
| Coût attendu | Temps linéaire O(|A| + |B|). |
9.2.2 Comparaison des premiers éléments
Trois indices sont utilisés : i pour A, j pour B et k pour le tableau résultat. Tant que les deux tableaux possèdent des éléments non copiés, A[i] et B[j] sont comparés. Le plus petit est placé dans R[k].
État initial A = [3, 12, 20, 31] i = 0 B = [5, 9, 18, 40] j = 0 R = [_, _, _, _, _, _, _, _] k = 0
Comparer 3 et 5 -> copier 3, puis avancer i et k. |
| Invariant de boucle — Avant chaque comparaison, R[0..k-1] contient les k plus petits éléments de A et B, dans l’ordre. |
9.2.3 Copie dans le tableau résultat
Pseudo-code — Fusionner deux tableaux triés Fonction Fusionner(A, B) : Tableau i <- 0 j <- 0 k <- 0 Creer R de taille Longueur(A) + Longueur(B) TantQue i < Longueur(A) ET j < Longueur(B) Faire Si A[i] <= B[j] Alors R[k] <- A[i] i <- i + 1 Sinon R[k] <- B[j] j <- j + 1 FinSi k <- k + 1 FinTantQue TantQue i < Longueur(A) Faire R[k] <- A[i] i <- i + 1 k <- k + 1 FinTantQue TantQue j < Longueur(B) Faire R[k] <- B[j] j <- j + 1 k <- k + 1 FinTantQue Retourner R FinFonction Le test <= choisit l’élément de gauche en cas d’égalité et préserve ainsi la stabilité. |
9.2.4 Trace complète d’une fusion
Fusion de A = [3, 12, 20, 31] et B = [5, 9, 18, 40].
Étape | A[i] | B[j] | Choix | R après copie |
|---|---|---|---|---|
1 | 3 | 5 | 3 de A | [3] |
2 | 12 | 5 | 5 de B | [3, 5] |
3 | 12 | 9 | 9 de B | [3, 5, 9] |
4 | 12 | 18 | 12 de A | [3, 5, 9, 12] |
5 | 20 | 18 | 18 de B | [3, 5, 9, 12, 18] |
6 | 20 | 40 | 20 de A | [3, 5, 9, 12, 18, 20] |
7 | 31 | 40 | 31 de A | [3, 5, 9, 12, 18, 20, 31] |
Reste | A épuisé | 40 | Copier le reste de B | [3, 5, 9, 12, 18, 20, 31, 40] |
9.2.5 Traitement des éléments restants
La boucle principale s’arrête dès que l’un des deux tableaux est épuisé. Les éléments restants de l’autre tableau sont déjà triés et supérieurs ou égaux au dernier élément copié. Ils peuvent donc être ajoutés sans nouvelles comparaisons entre les deux sources.
| Erreur fréquente — Oublier la copie des éléments restants conduit à perdre une partie des données lorsque les deux tableaux n’ont pas la même longueur ou ne s’épuisent pas simultanément. |
9.2.6 Fusion dans un tableau global
Dans une implémentation par indices, la fusion porte sur deux intervalles contigus T[gauche..milieu] et T[milieu+1..droite]. Un tableau auxiliaire reçoit temporairement les éléments, puis ceux-ci sont recopiés dans T.
Pseudo-code — Fusion d’intervalles contigus Procedure FusionnerIntervalles(T, gauche, milieu, droite, Aux) i <- gauche j <- milieu + 1 k <- gauche TantQue i <= milieu ET j <= droite Faire Si T[i] <= T[j] Alors Aux[k] <- T[i] i <- i + 1 Sinon Aux[k] <- T[j] j <- j + 1 FinSi k <- k + 1 FinTantQue TantQue i <= milieu Faire Aux[k] <- T[i] i <- i + 1 ; k <- k + 1 FinTantQue TantQue j <= droite Faire Aux[k] <- T[j] j <- j + 1 ; k <- k + 1 FinTantQue Pour k allant de gauche à droite Faire T[k] <- Aux[k] FinPour FinProcedure |
9.2.7 Stabilité lors de la fusion
Un tri est stable lorsque deux éléments ayant la même clé conservent leur ordre relatif initial. Pendant la fusion, si les clés sont égales, il faut sélectionner en premier l’élément provenant de la partie gauche.
Séquence initiale | Clé | Identité |
|---|---|---|
| Étudiant A | 14 | Arrivé avant B |
| Étudiant B | 14 | Arrivé après A |
Avec le test A[i] <= B[j], A est copié avant B lorsque les clés sont égales. Remplacer <= par < peut inverser leur ordre et rendre l’implémentation instable.
9.3 Version récursive
9.3.1 Cas de base
Le cas de base est atteint lorsque l’intervalle contient zéro ou un seul élément. Aucun traitement n’est nécessaire, car cet intervalle est déjà trié.
Pseudo-code — Test du cas de base Si gauche >= droite Alors Retourner FinSi |
| Rôle du cas de base — Il garantit la terminaison de la récursion et empêche la subdivision infinie des intervalles. |
9.3.2 Division et appels récursifs
Lorsque gauche < droite, l’intervalle est partagé en deux. Les deux appels récursifs portent sur des intervalles strictement plus petits, ce qui rapproche l’exécution du cas de base.
Pseudo-code — Tri fusion par indices Procedure TriFusion(T, gauche, droite, Aux) Si gauche >= droite Alors Retourner FinSi milieu <- gauche + (droite - gauche) div 2 TriFusion(T, gauche, milieu, Aux) TriFusion(T, milieu + 1, droite, Aux) FusionnerIntervalles(T, gauche, milieu, droite, Aux) FinProcedure Le tableau auxiliaire peut être créé une seule fois par l’appel principal et réutilisé à tous les niveaux. |
9.3.3 Arbre des appels
Arbre des appels pour un tableau de 8 éléments TriFusion(0,7) ├─ TriFusion(0,3) │ ├─ TriFusion(0,1) │ │ ├─ TriFusion(0,0) │ │ └─ TriFusion(1,1) │ └─ TriFusion(2,3) │ ├─ TriFusion(2,2) │ └─ TriFusion(3,3) └─ TriFusion(4,7) ├─ TriFusion(4,5) └─ TriFusion(6,7) |
L’arbre possède log2(n) niveaux de division lorsque n est une puissance de deux. À chaque niveau, la somme des tailles des intervalles fusionnés est n.
9.3.4 Ordre des fusions
Les fusions sont réalisées lors du retour des appels récursifs. Les plus petits intervalles sont donc fusionnés en premier, puis les résultats remontent progressivement vers l’intervalle initial.
Niveau | Fusions typiques pour 8 éléments | Volume total traité |
|---|---|---|
1 | 4 fusions de 1 + 1 | 8 éléments |
2 | 2 fusions de 2 + 2 | 8 éléments |
3 | 1 fusion de 4 + 4 | 8 éléments |
9.3.5 Trace complète sur un petit tableau
Considérons T = [7, 2, 6, 3].
Divisions et fusions [7, 2, 6, 3] / \ [7, 2] [6, 3] / \ / \ [7] [2] [6] [3] \ / \ / [2, 7] [3, 6] \ / [2, 3, 6, 7] |
Action | Intervalle avant | Intervalle après |
|---|---|---|
| Fusion des indices 0 et 1 | [7, 2] | [2, 7] |
| Fusion des indices 2 et 3 | [6, 3] | [3, 6] |
| Fusion des indices 0 à 3 | [2, 7] + [3, 6] | [2, 3, 6, 7] |
9.3.6 Terminaison
La terminaison est assurée car chaque appel portant sur un intervalle de taille supérieure à un le divise en deux intervalles strictement plus petits. La taille de l’intervalle est un variant entier positif qui diminue jusqu’à un.
| Variant — droite - gauche + 1 diminue strictement le long de chaque branche récursive. |
9.3.7 Justification de la correction
La correction peut être démontrée par induction sur la taille n de l’intervalle.
Étape de preuve | Argument |
|---|---|
| Initialisation | Pour n <= 1, l’intervalle est déjà trié. |
| Hypothèse | Supposons que TriFusion trie correctement tout intervalle de taille strictement inférieure à n. |
| Hérédité | Les deux moitiés, de taille inférieure à n, sont triées par l’hypothèse ; Fusionner les combine correctement. |
| Conclusion | TriFusion trie tout intervalle fini. |
9.3.8 Version itérative ascendante
Le tri fusion peut aussi être écrit sans récursion. On fusionne d’abord des blocs de taille 1, puis 2, puis 4, jusqu’à couvrir tout le tableau. Cette version est appelée tri fusion ascendant ou bottom-up.
Pseudo-code — Tri fusion itératif Procedure TriFusionIteratif(T, n, Aux) taille <- 1 TantQue taille < n Faire gauche <- 0 TantQue gauche < n - 1 Faire milieu <- Min(gauche + taille - 1, n - 1) droite <- Min(gauche + 2 * taille - 1, n - 1) Si milieu < droite Alors FusionnerIntervalles(T, gauche, milieu, droite, Aux) FinSi gauche <- gauche + 2 * taille FinTantQue taille <- 2 * taille FinTantQue FinProcedure |
| Comparaison — La version itérative évite la pile d’appels, mais conserve le même coût temporel et le même besoin principal de mémoire auxiliaire. |
9.4 Analyse
9.4.1 Complexité temporelle
Pour trier n éléments, l’algorithme effectue deux appels sur des sous-tableaux d’environ n/2 éléments, puis une fusion linéaire. La relation de récurrence est donc : T(n) = 2T(n/2) + Theta(n).
Interprétation par niveaux Niveau 0 : 1 problème de taille n -> travail de fusion n Niveau 1 : 2 problèmes de taille n/2 -> travail total n Niveau 2 : 4 problèmes de taille n/4 -> travail total n ... Nombre de niveaux : log2(n) Travail total : n × log2(n) |
Cas | Complexité du tri fusion | Explication |
|---|---|---|
| Meilleur cas | Theta(n log n) | La version classique divise et fusionne même si le tableau est déjà trié. |
| Cas moyen | Theta(n log n) | Le nombre de niveaux et le volume fusionné restent identiques. |
| Pire cas | Theta(n log n) | Chaque niveau traite au plus n éléments. |
| Résultat principal — Le tri fusion garantit O(n log n), indépendamment de l’ordre initial des données. |
9.4.2 Nombre de comparaisons
Fusionner deux séquences de longueurs p et q demande au plus p + q - 1 comparaisons entre éléments. Pour n éléments, le nombre total de comparaisons est de l’ordre de n log n.
Taille n | Ordre de grandeur n log2(n) | Tri quadratique n² |
|---|---|---|
16 | 64 | 256 |
1 024 | 10 240 | 1 048 576 |
1 000 000 | environ 20 000 000 | 1 000 000 000 000 |
9.4.3 Mémoire supplémentaire
Pour les tableaux, la fusion classique utilise un tableau auxiliaire de taille n, soit O(n) mémoire. La pile récursive occupe en plus O(log n) cadres d’appels. Le terme dominant reste O(n).
Ressource | Coût | Commentaire |
|---|---|---|
| Tableau auxiliaire | O(n) | Stocke temporairement les éléments pendant les fusions. |
| Pile récursive | O(log n) | Un cadre par niveau de division. |
| Variables locales | O(1) par appel | Indices, milieu et limites. |
| Mémoire totale | O(n) | Le tableau auxiliaire domine asymptotiquement. |
| Pratique recommandée — Créer un unique tableau auxiliaire au début et le réutiliser évite des allocations répétées à chaque appel. |
9.4.4 Stabilité
Le tri fusion est naturellement stable lorsque la fusion choisit l’élément gauche en cas d’égalité. Cette propriété est importante pour les tris multicritères et les enregistrements contenant plusieurs champs.
Exemple de stabilité Avant le tri par note : (Amina, 14), (Yassine, 12), (Omar, 14) Après tri stable : (Yassine, 12), (Amina, 14), (Omar, 14) Amina reste avant Omar parmi les éléments de note 14. |
9.4.5 Tri fusion et listes chaînées
Le tri fusion est particulièrement adapté aux listes chaînées. Une liste peut être divisée à l’aide de deux pointeurs, un lent et un rapide. La fusion s’effectue en réorientant les liens entre nœuds, sans recopier les valeurs dans un tableau auxiliaire de taille n.
Aspect | Tableau | Liste chaînée |
|---|---|---|
| Division | Calcul direct du milieu par indice. | Recherche du milieu en O(n) avec pointeurs lent/rapide. |
| Fusion | Copie généralement vers un tableau auxiliaire. | Réenchaînement des nœuds. |
| Mémoire auxiliaire | O(n) pour les éléments. | O(log n) pour la récursion, hors nœuds existants. |
| Accès | Accès direct par indice. | Accès séquentiel. |
| Pertinence | Très bon tri stable général. | L’un des meilleurs choix pour une liste chaînée. |
Pseudo-code — Fusion de deux listes chaînées triées Fonction FusionnerListes(L1, L2) : Noeud Si L1 = NUL Alors Retourner L2 Si L2 = NUL Alors Retourner L1 Si L1.valeur <= L2.valeur Alors L1.suivant <- FusionnerListes(L1.suivant, L2) Retourner L1 Sinon L2.suivant <- FusionnerListes(L1, L2.suivant) Retourner L2 FinSi FinFonction |
9.4.6 Avantages et limites
Critère | Avantages | Limites |
|---|---|---|
| Temps | O(n log n) dans tous les cas. | Peu adaptatif dans sa version classique. |
| Stabilité | Préserve l’ordre des clés égales. | Dépend du test utilisé pendant la fusion. |
| Mémoire | Très favorable aux listes chaînées. | O(n) auxiliaire pour les tableaux. |
| Prévisibilité | Performances régulières. | Constantes parfois supérieures au tri rapide en mémoire vive. |
| Données massives | Excellent pour le tri externe et la fusion de fichiers. | Nécessite une organisation des lectures et écritures. |
| Parallélisme | Les deux moitiés peuvent être triées indépendamment. | La fusion finale doit combiner les résultats. |
9.4.7 Comparaison avec d’autres tris
Algorithme | Meilleur | Moyen | Pire | Mémoire | Stable |
|---|---|---|---|---|---|
Insertion | O(n) | O(n²) | O(n²) | O(1) | Oui |
Sélection | O(n²) | O(n²) | O(n²) | O(1) | Non en général |
Tri fusion | O(n log n) | O(n log n) | O(n log n) | O(n) | Oui |
Tri rapide | O(n log n) | O(n log n) | O(n²) | O(log n) moyen | Non en général |
Tri par tas | O(n log n) | O(n log n) | O(n log n) | O(1) | Non |
Applications du tri fusion
Application 1 — Trier des résultats d’étudiants
Des enregistrements Étudiant peuvent être triés par moyenne croissante ou décroissante. La stabilité permet de conserver l’ordre alphabétique établi auparavant lorsque deux étudiants ont la même moyenne.
Pseudo-code — Comparaison d’étudiants Fonction Avant(E1, E2) : Booleen Si E1.moyenne < E2.moyenne Alors Retourner VRAI Si E1.moyenne > E2.moyenne Alors Retourner FAUX Retourner E1.nom <= E2.nom FinFonction Une autre stratégie consiste à trier d’abord par nom, puis par moyenne avec un tri stable. |
Application 2 — Fusionner des journaux déjà triés
Deux serveurs produisent des journaux triés par date. La procédure de fusion peut construire un journal global en temps linéaire, sans retrier toutes les lignes depuis le début.
Fusion chronologique Serveur A : 08:01, 08:04, 08:09 Serveur B : 08:02, 08:05, 08:07 Global : 08:01, 08:02, 08:04, 08:05, 08:07, 08:09 |
Application 3 — Tri externe de fichiers volumineux
Lorsque les données ne tiennent pas entièrement en mémoire, le fichier est découpé en blocs. Chaque bloc est trié en mémoire et enregistré sur disque. Les blocs triés sont ensuite fusionnés, éventuellement par groupes, jusqu’à produire un fichier global trié.
1. Lire un bloc pouvant tenir en mémoire.
2. Trier le bloc et l’écrire dans un fichier temporaire.
3. Répéter pour tous les blocs.
4. Fusionner les fichiers temporaires triés.
5. Écrire le résultat final et supprimer les fichiers temporaires.
| Application majeure — Le tri fusion est la base de nombreux tris externes utilisés par les bases de données et les systèmes de traitement massif. |
Application 4 — Trier une liste chaînée
Dans une liste chaînée, l’insertion d’éléments au milieu ne nécessite pas de décalages. Le tri fusion exploite cet avantage et réalise les fusions en modifiant uniquement les références entre nœuds.
Application 5 — Traitement parallèle
Les deux moitiés étant indépendantes, elles peuvent être triées simultanément sur deux cœurs ou deux machines. Les résultats sont ensuite fusionnés. Cette propriété facilite la parallélisation, sous réserve de gérer le coût des communications et de la fusion.
Erreurs fréquentes et bonnes pratiques
Erreur | Conséquence | Bonne pratique |
|---|---|---|
| Cas de base absent ou incorrect | Récursion infinie ou indices invalides. | Arrêter lorsque gauche >= droite. |
| Milieu mal calculé | Perte ou duplication d’un intervalle. | Utiliser gauche + (droite - gauche) div 2. |
| Sous-tableaux non triés avant fusion | Résultat incorrect. | Appeler TriFusion sur les deux moitiés avant Fusionner. |
| Éléments restants non copiés | Valeurs perdues. | Prévoir deux boucles de copie finale. |
| Test < au lieu de <= | Perte possible de stabilité. | Choisir la partie gauche en cas d’égalité. |
| Tableau auxiliaire recréé à chaque appel | Allocations nombreuses. | Créer Aux une seule fois et le réutiliser. |
| Confusion entre indices inclusifs et exclusifs | Décalages et dépassements. | Documenter clairement la convention. |
Travaux dirigés
TD 1 — Identifier les étapes
Pour T = [11, 4, 8, 2, 7, 5], représenter toutes les divisions puis toutes les fusions du tri fusion.
TD 2 — Fusion manuelle
Fusionner A = [1, 6, 12, 15] et B = [2, 3, 9, 20, 25]. Indiquer à chaque étape les valeurs de i, j et le contenu de R.
TD 3 — Compléter le pseudo-code
Compléter les conditions manquantes d’une procédure de fusion puis justifier les tests utilisés.
Pseudo-code à compléter TantQue ............... Faire Si ............... Alors R[k] <- A[i] i <- i + 1 Sinon R[k] <- B[j] j <- j + 1 FinSi k <- k + 1 FinTantQue |
TD 4 — Détecter une erreur de stabilité
On fusionne des enregistrements avec le test A[i].clé < B[j].clé. Expliquer pourquoi le tri peut devenir instable et proposer la correction.
TD 5 — Analyser la complexité
Pour n = 32, déterminer le nombre de niveaux de division, le volume total d’éléments traité par niveau et l’ordre de grandeur du travail total.
TD 6 — Mémoire auxiliaire
Comparer la mémoire utilisée par une implémentation qui crée deux nouveaux sous-tableaux à chaque appel et une implémentation qui réutilise un tableau Aux unique.
TD 7 — Version descendante et ascendante
Comparer le tri fusion récursif descendant et le tri fusion itératif ascendant selon la pile d’appels, l’ordre des fusions et la lisibilité.
TD 8 — Listes chaînées
Décrire comment diviser une liste simplement chaînée en deux moitiés à l’aide de deux pointeurs, puis indiquer comment fusionner deux listes triées.
TD 9 — Tri multicritère stable
On souhaite classer des étudiants par moyenne décroissante, puis conserver l’ordre alphabétique pour les égalités. Proposer une stratégie utilisant la stabilité du tri fusion.
TD 10 — Choisir un algorithme
Pour chacun des contextes suivants, dire si le tri fusion est adapté : petit tableau presque trié, liste chaînée volumineuse, données ne tenant pas en mémoire, tableau en mémoire avec contrainte stricte O(1) supplémentaire.
Corrigés indicatifs des travaux dirigés
Corrigé du TD 1
Trace attendue [11, 4, 8, 2, 7, 5] -> [11, 4, 8] | [2, 7, 5] -> [11, 4] | [8] | [2, 7] | [5] -> [11] | [4] | [8] | [2] | [7] | [5] Fusions : [4, 11], puis [4, 8, 11] [2, 7], puis [2, 5, 7] Fusion finale : [2, 4, 5, 7, 8, 11] |
Corrigé du TD 2
Étape | Comparaison | Élément copié | Résultat partiel |
|---|---|---|---|
1 | 1 / 2 | 1 | [1] |
2 | 6 / 2 | 2 | [1, 2] |
3 | 6 / 3 | 3 | [1, 2, 3] |
4 | 6 / 9 | 6 | [1, 2, 3, 6] |
5 | 12 / 9 | 9 | [1, 2, 3, 6, 9] |
6 | 12 / 20 | 12 | [1, 2, 3, 6, 9, 12] |
7 | 15 / 20 | 15 | [1, 2, 3, 6, 9, 12, 15] |
Reste | A épuisé | 20 puis 25 | [1, 2, 3, 6, 9, 12, 15, 20, 25] |
Corrigé du TD 3
Conditions complétées TantQue i < Longueur(A) ET j < Longueur(B) Faire Si A[i] <= B[j] Alors ... FinSi FinTantQue La conjonction garantit que les deux valeurs comparées existent ; <= conserve la stabilité. |
Corrigé du TD 4
Lorsque les clés sont égales, le test strict < sélectionne l’élément de B, même si celui de A apparaissait plus tôt dans la séquence initiale. Le test doit être A[i].clé <= B[j].clé pour choisir la partie gauche en cas d’égalité.
Corrigé du TD 5
Pour n = 32 = 2^5, il existe 5 niveaux de fusion. Chaque niveau traite au total 32 éléments. Le travail principal est donc proportionnel à 32 × 5 = 160, soit Theta(n log n).
Corrigé du TD 6
Créer des sous-tableaux à chaque appel provoque de nombreuses allocations et copies temporaires. Le pic mémoire reste généralement O(n), mais les coûts constants et la pression sur l’allocateur augmentent. Un unique tableau Aux de taille n, réutilisé, est plus efficace et plus prévisible.
Corrigé du TD 7
Critère | Version récursive | Version itérative |
|---|---|---|
| Pile d’appels | O(log n) | Aucune pile récursive. |
| Ordre | Divisions descendantes puis fusions au retour. | Blocs de tailles 1, 2, 4, 8... |
| Lisibilité | Très proche du principe diviser pour régner. | Plus de gestion explicite des bornes. |
| Complexité | O(n log n) | O(n log n) |
Corrigé du TD 8
Un pointeur lent avance d’un nœud tandis qu’un pointeur rapide avance de deux nœuds. Lorsque le rapide atteint la fin, le lent se trouve près du milieu. La liste est coupée à cet endroit. La fusion compare les têtes et relie au résultat le nœud possédant la plus petite clé.
Corrigé du TD 9
On peut d’abord trier les étudiants par nom croissant, puis appliquer un tri fusion stable par moyenne décroissante. Les étudiants ayant la même moyenne conservent alors l’ordre alphabétique issu du premier tri. On peut également utiliser un comparateur multicritère explicite.
Corrigé du TD 10
Contexte | Choix | Justification |
|---|---|---|
| Petit tableau presque trié | Plutôt tri par insertion | Simple, en place et proche de O(n) dans ce cas. |
| Liste chaînée volumineuse | Tri fusion | Fusion par réenchaînement, temps garanti O(n log n). |
| Données hors mémoire | Tri fusion externe | Fusion séquentielle efficace des blocs triés. |
| Tableau avec O(1) auxiliaire strict | Tri par tas ou autre tri en place | Le tri fusion classique nécessite O(n) mémoire. |
Travail pratique — Implémentation et étude expérimentale du tri fusion
Objectifs
- Implémenter une procédure de fusion stable.
- Implémenter le tri fusion récursif par indices.
- Tester la correction sur des cas variés.
- Mesurer le nombre de comparaisons et de copies.
- Comparer les performances avec le tri par insertion.
- Observer l’influence de la taille et de l’ordre initial des données.
Travail demandé
1. Écrire FusionnerIntervalles en utilisant un tableau auxiliaire unique.
2. Écrire TriFusion récursif et une fonction d’appel principale.
3. Ajouter des compteurs de comparaisons et d’affectations.
4. Tester des tableaux vides, unitaires, triés, inversés, aléatoires et contenant des doublons.
5. Vérifier la stabilité sur des enregistrements (clé, ordreInitial).
6. Comparer les temps avec un tri par insertion pour plusieurs tailles.
7. Tracer ou présenter un tableau des résultats.
8. Rédiger une conclusion reliant les mesures à O(n log n).
Jeux d’essai minimaux
Cas | Entrée | Sortie attendue |
|---|---|---|
Vide | [] | [] |
Un élément | [5] | [5] |
Déjà trié | [1, 2, 3, 4] | [1, 2, 3, 4] |
Ordre inverse | [5, 4, 3, 2, 1] | [1, 2, 3, 4, 5] |
Doublons | [4, 2, 4, 1, 2] | [1, 2, 2, 4, 4] |
Valeurs négatives | [3, -1, 0, -5] | [-5, -1, 0, 3] |
Architecture suggérée
Pseudo-code — Organisation du programme Algorithme TP_TriFusion Lire ou generer T Creer Aux de meme taille que T comparaisons <- 0 affectations <- 0 TriFusion(T, 0, Longueur(T) - 1, Aux) Afficher T Afficher comparaisons, affectations et temps FinAlgorithme |
Grille d’évaluation indicative
Critère | Points |
|---|---|
Fusion correcte et stable | 4 |
Récursion et gestion correcte des bornes | 4 |
Réutilisation du tableau auxiliaire | 2 |
Jeux d’essai et validation | 3 |
Instrumentation et mesures | 3 |
Comparaison avec un autre tri | 2 |
Qualité du code et compte rendu | 2 |
Pistes d’extension
- Implémenter la version itérative ascendante.
- Trier une liste chaînée.
- Ajouter un ordre décroissant paramétrable.
- Trier des enregistrements avec un comparateur multicritère.
- Paralléliser les deux appels récursifs au-dessus d’un seuil.
- Simuler un tri externe avec plusieurs fichiers temporaires.
Synthèse du chapitre
Notion | À retenir |
|---|---|
| Principe | Diviser la séquence, trier chaque moitié, puis fusionner. |
| Cas de base | Une séquence de taille 0 ou 1 est déjà triée. |
| Fusion | Sélection linéaire du plus petit élément courant des deux parties. |
| Complexité temporelle | Theta(n log n) dans le meilleur, le moyen et le pire cas. |
| Mémoire | O(n) pour un tableau, généralement O(log n) pour une liste chaînée hors données. |
| Stabilité | Oui si la partie gauche est choisie en cas d’égalité. |
| Forces | Prévisible, stable, adapté aux listes et au tri externe. |
| Limite principale | Mémoire auxiliaire pour les tableaux. |
Glossaire
Terme | Définition |
|---|---|
| Diviser pour régner | Méthode consistant à décomposer un problème, résoudre les parties puis combiner les résultats. |
| Fusion | Combinaison ordonnée de deux séquences déjà triées. |
| Cas de base | Situation élémentaire arrêtant la récursion. |
| Stable | Préserve l’ordre relatif des éléments ayant la même clé. |
| Tableau auxiliaire | Zone temporaire utilisée pour stocker les résultats d’une fusion. |
| Tri externe | Tri de données ne tenant pas entièrement en mémoire principale. |
| Top-down | Version récursive qui divise avant de fusionner. |
| Bottom-up | Version itérative qui fusionne des blocs de tailles croissantes. |
| Invariant de fusion | Propriété selon laquelle le résultat partiel contient les plus petits éléments déjà traités, dans l’ordre. |
Auto-évaluation
Je suis capable de… | Oui | À revoir |
|---|---|---|
expliquer les trois étapes du tri fusion ; | ☐ | ☐ |
fusionner manuellement deux tableaux triés ; | ☐ | ☐ |
écrire la procédure de fusion complète ; | ☐ | ☐ |
identifier le cas de base et les deux appels récursifs ; | ☐ | ☐ |
réaliser une trace des divisions et des fusions ; | ☐ | ☐ |
justifier la complexité Theta(n log n) ; | ☐ | ☐ |
expliquer le coût mémoire O(n) sur un tableau ; | ☐ | ☐ |
préserver la stabilité pendant la fusion ; | ☐ | ☐ |
adapter la méthode à une liste chaînée ; | ☐ | ☐ |
choisir le tri fusion dans un contexte approprié. | ☐ | ☐ |
Conclusion
Le tri fusion illustre de manière complète la méthode « diviser pour régner ». Sa fusion linéaire, sa complexité garantie en Theta(n log n) et sa stabilité en font un algorithme de référence. Il est particulièrement pertinent pour les listes chaînées, les tris externes et les applications exigeant des performances prévisibles. Sa compréhension prépare directement l’étude du tri rapide, des relations de récurrence et d’autres algorithmes récursifs avancés.