Algorithmique et Programmation Impérative 2 Maximier – Tri arbre N.E. Oussous [email protected] FIL – USTL API2 - LST«A» – p.1/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses 2 sous-arbres principaux sont des maximiers. API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses 2 sous-arbres principaux sont des maximiers. ➻ À la racine des 2 sous-arbres principaux on trouve donc les 2ème et 3ème valeurs selon l’ordre décroissant. API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses 2 sous-arbres principaux sont des maximiers. ➻ À la racine des 2 sous-arbres principaux on trouve donc les 2ème et 3ème valeurs selon l’ordre décroissant. ➻ On a un ordre partiel : API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses 2 sous-arbres principaux sont des maximiers. ➻ À la racine des 2 sous-arbres principaux on trouve donc les 2ème et 3ème valeurs selon l’ordre décroissant. ➻ On a un ordre partiel : ➠ Le père est plus grand que ses deux fils. API2 - LST«A» – p.2/33 Maximier - Définition ➻ Un arbre vide est considéré comme un maximier. ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses 2 sous-arbres principaux sont des maximiers. ➻ À la racine des 2 sous-arbres principaux on trouve donc les 2ème et 3ème valeurs selon l’ordre décroissant. ➻ On a un ordre partiel : ➠ Le père est plus grand que ses deux fils. ➠ Pas d’ordre entre les frères. API2 - LST«A» – p.2/33 Maximier - Exemple 9 7 8 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.3/33 Maximier - Utilité ➻ La structure de maximier n’est pas utilisée pour implémenter les primitives usuelles (recherche, insertion, suppression) sur les ensembles. API2 - LST«A» – p.4/33 Maximier - Utilité ➻ La structure de maximier n’est pas utilisée pour implémenter les primitives usuelles (recherche, insertion, suppression) sur les ensembles. ➻ Par exemple, la recherche d’une valeur particulière peut amener à parcourir tout l’arbre (donc être en Θ(n)). Elle nécessite de plus la gestion d’une pile dont la hauteur est celle de l’arbre lui-même. Cette recherche serait donc inefficace. API2 - LST«A» – p.4/33 Maximier - Utilité ➻ La structure de maximier n’est pas utilisée pour implémenter les primitives usuelles (recherche, insertion, suppression) sur les ensembles. ➻ Par exemple, la recherche d’une valeur particulière peut amener à parcourir tout l’arbre (donc être en Θ(n)). Elle nécessite de plus la gestion d’une pile dont la hauteur est celle de l’arbre lui-même. Cette recherche serait donc inefficace. ➻ Un maximier est par contre utilisé pour extraire le maximum parmi un ensemble de valeurs. API2 - LST«A» – p.4/33 Maximier - Cueillette La suppression de l’élément maximal est, par contre, plus efficace. ➻ On remplace cet élément par le plus grand de ses successeurs. API2 - LST«A» – p.5/33 Maximier - Cueillette La suppression de l’élément maximal est, par contre, plus efficace. ➻ On remplace cet élément par le plus grand de ses successeurs. ➻ On recommence l’opération avec le successeur utilisé. API2 - LST«A» – p.5/33 Maximier - Cueillette La suppression de l’élément maximal est, par contre, plus efficace. ➻ On remplace cet élément par le plus grand de ses successeurs. ➻ On recommence l’opération avec le successeur utilisé. ➻ On supprime la feuille utilisée en dernier. API2 - LST«A» – p.5/33 Cueillette - Exemple 9 7 8 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 8 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 8 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 7 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 7 6 2 3 3 2 7 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 7 6 2 6 3 3 2 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 7 6 2 6 3 3 2 1 5 6 API2 - LST«A» – p.6/33 Cueillette - Exemple 8 7 7 6 2 6 3 3 2 5 1 API2 - LST«A» – p.6/33 Cueillette - Code procedure Supprimer_Max(var T : Maximier) ; X : Curseur ; Begin X := Racine(T) ; While not Est_Feuille(X) Do Begin Determiner quel successeur possède la valeur maximale ; Recopier cette valeur; Descendre X du coté où se trouvait la valeur maximale ; end ; // while Supprimer(X); End ; // Supprimer_Max API2 - LST«A» – p.7/33 Pré-Maximier ➻ Un Pré-Maximier est un arbre dont les 2 sous-arbres principaux sont des maximiers. API2 - LST«A» – p.8/33 Pré-Maximier ➻ Un Pré-Maximier est un arbre dont les 2 sous-arbres principaux sont des maximiers. ➻ Un pré-maximier n’est pas nécessairement un maximier, cela dépend de la valeur portée à la racine. API2 - LST«A» – p.8/33 Pré-Maximier ➻ Un Pré-Maximier est un arbre dont les 2 sous-arbres principaux sont des maximiers. ➻ Un pré-maximier n’est pas nécessairement un maximier, cela dépend de la valeur portée à la racine. ➻ La transformation en maximier (Réorganisation) se fait par une méthode analogue à celle utilisée pour la suppression du maximum. API2 - LST«A» – p.8/33 Pré-Maximier → Maximier ➻ Si le pré-maximier n’est pas un maximier, il faut API2 - LST«A» – p.9/33 Pré-Maximier → Maximier ➻ Si le pré-maximier n’est pas un maximier, il faut ➠ Échanger la valeur portée à la racine avec la valeur maximale de ses successeurs, API2 - LST«A» – p.9/33 Pré-Maximier → Maximier ➻ Si le pré-maximier n’est pas un maximier, il faut ➠ Échanger la valeur portée à la racine avec la valeur maximale de ses successeurs, ➠ Réorganiser le successeur avec lequel a eu lieu l’échange. API2 - LST«A» – p.9/33 Pré-Maximier → Maximier ➻ Si le pré-maximier n’est pas un maximier, il faut ➠ Échanger la valeur portée à la racine avec la valeur maximale de ses successeurs, ➠ Réorganiser le successeur avec lequel a eu lieu l’échange. ➻ La réorganisation nécessite le parcours d’une seule branche, avec un traitement en Θ(1) à chaque nœud parcouru. API2 - LST«A» – p.9/33 Réorganisation - Exemple 2 8 9 3 7 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 2 8 9 3 7 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 9 8 2 3 7 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 9 8 2 3 7 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 9 8 7 3 2 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 9 8 7 3 2 6 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Exemple 9 7 8 3 6 2 3 2 7 1 5 6 API2 - LST«A» – p.10/33 Réorganisation - Code procedure Reorganiser(var T : Maximier) ; X : Curseur ; Begin X := Racine(T) ; Repeat if not Est_Vide(sag(X)) then begin G := X; G := sag(G) ; if not Est_Vide(sad(X)) then begin D := X; D := sad(D) ; if Valeur(D) > Valeur(G) and Valeur(D) > Valeur(X) then begin Changer_Valeur(X, Valeur(D)); X := D; end else if Valeur(G) > Valeur(D) and Valeur(G) > Valeur(X) then begin Changer_Valeur(X, Valeur(G)); X := G; end else exit; end else begin Changer_Valeur(X, Valeur(G)); X := G; end; end else if not Est_Vide(sad(X)) then begin Changer_Valeur(X, Valeur(D)); X := D; end else exit; until false ; // Repeat End ; API2 - LST«A» – p.11/33 Fabrication d’un maximier ➻ Un arbre binaire quelconque contient des maximiers : toutes ses feuilles. API2 - LST«A» – p.12/33 Fabrication d’un maximier ➻ Un arbre binaire quelconque contient des maximiers : toutes ses feuilles. ➻ Les sous-arbres de hauteur 2 sont donc des pré-maximiers. API2 - LST«A» – p.12/33 Fabrication d’un maximier ➻ Un arbre binaire quelconque contient des maximiers : toutes ses feuilles. ➻ Les sous-arbres de hauteur 2 sont donc des pré-maximiers. ➻ Par réorganisations successives, on peut transformer tous les sous-arbres de hauteur 2 en maximiers, puis ceux de hauteur 3, etc. . . API2 - LST«A» – p.12/33 Fabrication d’un maximier ➻ Un arbre binaire quelconque contient des maximiers : toutes ses feuilles. ➻ Les sous-arbres de hauteur 2 sont donc des pré-maximiers. ➻ Par réorganisations successives, on peut transformer tous les sous-arbres de hauteur 2 en maximiers, puis ceux de hauteur 3, etc. . . ➻ Il faudra donc parcourir les sous-arbres par hauteurs croissantes (les sous-arbres les plus profonds en premier). API2 - LST«A» – p.12/33 Fabrication - Exemple 2 9 6 6 7 7 1 3 2 3 5 8 API2 - LST«A» – p.13/33 Fabrication - Exemple 2 9 6 6 7 1 3 2 7 3 5 8 API2 - LST«A» – p.13/33 Fabrication - Exemple 2 9 6 7 6 3 3 2 8 1 5 7 API2 - LST«A» – p.13/33 Fabrication - Exemple 2 9 6 7 6 3 3 2 8 1 5 7 API2 - LST«A» – p.13/33 Fabrication - Exemple 2 9 8 7 6 3 3 2 7 1 5 6 API2 - LST«A» – p.13/33 Fabrication - Exemple 2 9 8 7 6 7 3 3 2 1 5 6 API2 - LST«A» – p.13/33 Fabrication - Exemple 9 7 8 6 2 7 3 3 2 1 5 6 API2 - LST«A» – p.13/33 Fabrication - Code récursif Cet algorithme s’exprime facilement de façon récursive procedure Fabriquer_maximier(C : Curseur) Begin if not Est_Vide(sag(X)) then begin G := X; G := sag(G) ; Fabriquer_maximier(G) ; end; // if if not Est_Vide(sad(X)) then begin D := X; D := sad(D) ; Fabriquer_maximier(D); end; // if // on a maintenant un pré-maximier Reorganiser(C); End ; // Fabriquer_maximier API2 - LST«A» – p.14/33 Fabrication - Code itératif Cet algorithme peut aussi s’exprimer (informellement) de façon itérative par for h := 2 to hauteur(arbre) do begin Réorganiser tous les sous-arbres de hauteur h ; end; // for Mais l’interface d’arbre proposée jusqu’ici ne permet pas d’implémenter aisément cette version itérative, faute de moyen efficace pour parcourir tous les sous-arbres d’une hauteur donnée. API2 - LST«A» – p.15/33 Tri-Arbre : Heapsort L’utilisation des maximiers peut amener à une méthode de tri appelée Tri-arbre ou Heapsort. Description informelle de l’algorithme : Tri_Arbre(var T : Tableau) Begin Constituer un arbre contenant les valeurs de T; Fabriquer un maximier à partir de cet arbre; for i:=low(T) to high(T) do begin Ranger le maximum dans T[i] ; Retirer le maximum de l’arbre ; end; // for End ; // Tri_Arbre En pratique, cette méthode de tri est employée dans le cas d’une implémentation d’arbre bien particulière : le tas. API2 - LST«A» – p.16/33 La structure de Tas ➻ La structure de tas permet de représenter, dans un tableau, un arbre quasi-équilibré, dont les feuilles à profondeur maximale sont situées le plus à gauche possible. API2 - LST«A» – p.17/33 La structure de Tas ➻ La structure de tas permet de représenter, dans un tableau, un arbre quasi-équilibré, dont les feuilles à profondeur maximale sont situées le plus à gauche possible. ➻ Elle consiste à ranger les valeurs portées par les nœuds API2 - LST«A» – p.17/33 La structure de Tas ➻ La structure de tas permet de représenter, dans un tableau, un arbre quasi-équilibré, dont les feuilles à profondeur maximale sont situées le plus à gauche possible. ➻ Elle consiste à ranger les valeurs portées par les nœuds ➠ par profondeur croissante API2 - LST«A» – p.17/33 La structure de Tas ➻ La structure de tas permet de représenter, dans un tableau, un arbre quasi-équilibré, dont les feuilles à profondeur maximale sont situées le plus à gauche possible. ➻ Elle consiste à ranger les valeurs portées par les nœuds ➠ par profondeur croissante ➠ de gauche à droite (pour une profondeur donnée). API2 - LST«A» – p.17/33 La structure de Tas ➻ La structure de tas permet de représenter, dans un tableau, un arbre quasi-équilibré, dont les feuilles à profondeur maximale sont situées le plus à gauche possible. ➻ Elle consiste à ranger les valeurs portées par les nœuds ➠ par profondeur croissante ➠ de gauche à droite (pour une profondeur donnée). un arbre = un tableau un curseur = un indice du tableau un nœud = une case du tableau API2 - LST«A» – p.17/33 Tas - Exemple 9 7 8 6 3 2 1 9 3 2 7 1 5 6 2 3 4 5 6 7 8 9 10 11 12 7 8 6 3 7 5 2 3 2 1 6 API2 - LST«A» – p.18/33 Tas dans 1 tableau d’indices 1..n T(1) T(2) T(4) T(8) T(3) T(5) T(6) T(7) T(9) T(10) T(11) T(12) Racine Successeur gauche Successeur droite Prédecesseur Est_Feuille Possède 2 successeurs Possède 1 seul successeur Dernier nœud interne 1 2i 2i + 1 i 2 2i > n 2i < n 2i = n n 2 API2 - LST«A» – p.19/33 Tas - Cas général T[low(T)] T[low(T)+1] T[low(T)+3] ... ... T[low(T)+2] T[low(T)+4] ... ... Racine Successeur gauche Successeur droite Prédecesseur Est_Feuille Possède 2 successeurs Possède 1 seul successeur Dernier nœud interne T[low(T)+5] T[low(T)+6] T[high(T)] low(T ) 2i − low(T ) + 1 2i − low(T ) + 2 1 (i + low(T ) − 1) 2 2i − low(T ) + 1 > high(T ) 2i − low(T ) + 1 < high(T ) 2i − low(T ) + 1 = high(T ) 1 (high(T ) + low(T ) − 1) 2 API2 - LST«A» – p.20/33 La structure de Tas ➻ Les moins API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. la suppression d’un nœud ou l’insertion d’un nouveau nœud sont impossibles. API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. ➻ Les plus API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. ➻ Les plus ➠ Pas besoin de pointeurs. API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. ➻ Les plus ➠ Pas besoin de pointeurs. ➠ Le passage d’un nœud à ses successeurs se fait par adressage calculé. API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. ➻ Les plus ➠ Pas besoin de pointeurs. ➠ Le passage d’un nœud à ses successeurs se fait par adressage calculé. ➠ Possibilité de calculer également l’emplacement du prédécesseur. API2 - LST«A» – p.21/33 La structure de Tas ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. ➻ Les plus ➠ Pas besoin de pointeurs. ➠ Le passage d’un nœud à ses successeurs se fait par adressage calculé. ➠ Possibilité de calculer également l’emplacement du prédécesseur. ➠ Possibilité de parcourir l’arbre de bas en haut. API2 - LST«A» – p.21/33 Réorganiser un Tas : Itératif procedure Reorganiser( C : Curseur ) ; I : Curseur ; begin I := C ; repeat if Possede_2_Successeurs(I) then begin if T[sag(I)] > T[sad(I)] and T[sag(I)] > T[I] then begin Echanger(T[I],T[sag(I)]); I := sag(I); end else if T[sad(I)] > T[sag(I)] and T[sad(I)] > T[I] then begin Echanger(T[I],T[sad(I)]); I := sad(I); end else // T[I] est la plus grande des 3 valeurs exit; end else if Possede_1_Seul_Successeur(I) and T[sag(I)]>T[I] then begin Echanger(T[I],T[sag(I)]); exit; end else exit; Until false ; end; // Reorganiser API2 - LST«A» – p.22/33 Réorganiser un Tas : Récursif Reorganiser(T, C) g : Curseur ; d : Curseur ; Begin g := sag(C) ; d := sad(C) ; // on détermine max(T[C], T[g], T[d]) if g <= high(T) and T[g] > T[C] then max := g else max := C ; if d <= high(T) and T[d] > T[max] then max := d ; if max <> C then begin // c’est un pré-maximier Echanger(T[C], T[max]) ; Reorganiser(T, max) ; end; // if End; // Reorganiser API2 - LST«A» – p.23/33 Réorganiser Tas : Complexité ➻ Le temps d’exécution de Reorganiser sur un sous-arbre de taille n enraciné en un nœud i est la somme du temps Θ(1) nécessaire pour corriger la relation entre les éléments T[C], T[sag(C)] et T[sad(C)], et le temps d’exécution de Reorganiser sur un sous-arbre enraciné sur l’un des deux fils du nœud i. API2 - LST«A» – p.24/33 Réorganiser Tas : Complexité ➻ Le temps d’exécution de Reorganiser sur un sous-arbre de taille n enraciné en un nœud i est la somme du temps Θ(1) et le temps d’exécution de Reorganiser sur un sous-arbre enraciné sur l’un des deux fils du nœud i. ➻ Les sous-arbres des fils ont chacun une taille au plus égale à 2n 3 . Car c’est un arbre binaire presque complet. API2 - LST«A» – p.24/33 Réorganiser Tas : Complexité ➻ Le temps d’exécution de Reorganiser sur un sous-arbre de taille n enraciné en un nœud i est la somme du temps Θ(1) et le temps d’exécution de Reorganiser sur un sous-arbre enraciné sur l’un des deux fils du nœud i. ➻ Les sous-arbres des fils ont chacun une taille au plus égale à 2n 3 . ➻ Le pire des cas est atteint lorsque le dernier niveau est rempli exactement à moitié. API2 - LST«A» – p.24/33 Réorganiser Tas : Complexité ➻ On aboutit à la relation de récurrence suivante : 2n + Θ(1) T (n) 6 T 3 API2 - LST«A» – p.25/33 Réorganiser Tas : Complexité ➻ On aboutit à la relation de récurrence suivante : 2n + Θ(1) T (n) 6 T 3 ➻ On est dans le cas 2 du théorème général avec 3 a = 1 b = 2 f (n) = Θ(1) Ainsi nlogb a = n0 = 1 et f (n) = Θ(nlogb a ) API2 - LST«A» – p.25/33 Réorganiser Tas : Complexité ➻ On aboutit à la relation de récurrence suivante : 2n + Θ(1) T (n) 6 T 3 ➻ On est dans le cas 2 du théorème général avec 3 a = 1 b = 2 f (n) = Θ(1) Ainsi nlogb a = n0 = 1 et f (n) = Θ(nlogb a ) ➻ La solution est donc T (n) = O(log2 n) API2 - LST«A» – p.25/33 Fabriquer un Tas ➻ Les feuilles sont n situées dans les dernières cases du tableau : T[( 2 +1)..n]. En supposant que le tableau est indicé de 1 à n. ➻ On parcourt les sous-arbres par hauteurs croissantes en parcourant les cases du tableau en ordre inverse. API2 - LST«A» – p.26/33 Fabriquer un Tas ➻ Les feuilles sont n situées dans les dernières cases du tableau : T[( 2 +1)..n]. ➻ On parcourt les sous-arbres par hauteurs croissantes en parcourant les cases du tableau en ordre inverse. procedure Fabriquer_Tas(var T : Arbre) ; begin for i:=Dernier_arbre_h2 downto Racine do begin Reorganiser(i); // Reorganiser(T,i) end; end Fabriquer_Tas; Racine ≡ 1 et Dernier_arbre_h2 ≡ n2 . API2 - LST«A» – p.26/33 Fabriquer un Tas ➻ Les feuilles sont n situées dans les dernières cases du tableau : T[( 2 +1)..n]. ➻ On parcourt les sous-arbres par hauteurs croissantes en parcourant les cases du tableau en ordre inverse. procedure Fabriquer_Tas(var T : Arbre) ; begin for i:=Dernier_arbre_h2 downto Racine do begin Reorganiser(i); // Reorganiser(T,i) end; end Fabriquer_Tas; ➻ Le temps d’exécution de Fabriquer_tas : Chaque appel à Reorganiser coûte O(log2 n). Il existe O(n) appels de ce type. Donc, le temps d’exécution est au plus O(n log n). API2 - LST«A» – p.26/33 Exemple ➻ Soit le tableau suivant : 1 2 3 4 5 6 7 8 9 10 4 1 3 2 16 9 10 14 8 7 API2 - LST«A» – p.27/33 Exemple ➻ Soit le tableau suivant : 1 2 3 4 5 6 7 8 9 10 4 1 3 2 16 9 10 14 8 7 ➻ Il lui correspond l’arbre suivant : 1 4 2 3 1 3 4 5 6 7 2 16 9 10 8 9 10 14 8 7 API2 - LST«A» – p.27/33 Exemple 1 4 2 3 1 3 4 2 i 8 9 10 14 8 7 5 6 7 16 9 10 API2 - LST«A» – p.28/33 Exemple 1 4 i 2 3 1 3 4 5 6 7 2 16 9 10 8 9 10 14 8 7 API2 - LST«A» – p.28/33 Exemple 1 4 2 3 1 i 3 4 5 6 7 14 16 9 10 8 9 10 2 8 7 API2 - LST«A» – p.28/33 Exemple 1 4 i 2 3 1 10 4 5 6 7 14 16 9 3 8 9 10 2 8 7 API2 - LST«A» – p.28/33 Exemple 1 i 4 2 3 16 10 4 5 6 7 14 7 9 3 8 9 10 2 8 1 API2 - LST«A» – p.28/33 Exemple 1 16 2 3 14 10 4 5 6 7 8 7 9 3 8 9 10 2 4 1 API2 - LST«A» – p.28/33 Fabriquer Tas : Complexité ➻ Une analyse plus fine est basée sur les propriétés des tas : API2 - LST«A» – p.29/33 Fabriquer Tas : Complexité ➻ Une analyse plus fine est basée sur les propriétés des tas : ➠ La hauteur d’un tas à n éléments est h = ⌊log2 n⌋. API2 - LST«A» – p.29/33 Fabriquer Tas : Complexité ➻ Une analyse plus fine est basée sur les propriétés des tas : ➠ La hauteur d’un tas à n éléments est h = ⌊log2 n⌋. ➠ 1 sous-arbre de hauteur h, 2 sous-arbres de hauteur h − 1, 4 sous-arbres de hauteur h − 2 API2 - LST«A» – p.29/33 Fabriquer Tas : Complexité ➻ Une analyse plus fine est basée sur les propriétés des tas : ➠ La hauteur d’un tas à n éléments est h = ⌊log2 n⌋. ➠ 2h−i sous-arbres de hauteur i. ➻ Le temps de calcul de Reorganiser sur un nœud de hauteur h est O(h). API2 - LST«A» – p.29/33 Fabriquer Tas : Complexité ➻ Une analyse plus fine est basée sur les propriétés des tas : ➠ La hauteur d’un tas à n éléments est h = ⌊log2 n⌋. ➠ 2h−i sous-arbres de hauteur i. ➻ Le temps de calcul de Reorganiser sur un nœud de hauteur h est O(h). ➻ Le coût total pour Fabriquer_tas sera ! h h X X i h−i h 2 · O(i) = O 2 T (n) = i 2 i=0 i=0 API2 - LST«A» – p.29/33 Fabriquer Tas : Complexité ➻ On a l’égalité : ∞ X 1/2 i = = 2 i 2 2 (1 − 1/2) i=0 Obtenue en remplaçant x par 1/2 et k par i dans la formule : ∞ X k=0 kx k x = (1 − x)2 API2 - LST«A» – p.30/33 Fabriquer Tas : Complexité ➻ On a l’égalité : ∞ X 1/2 i = = 2 i 2 2 (1 − 1/2) i=0 ➻ Donc, le temps d’exécution de Fabriquer_tas peut être borné par ⌊log2 n⌋ X i = O n O n i 2 i=0 ∞ X i=0 i 2i ! = O(n · 2) = O(n) API2 - LST«A» – p.30/33 Fabriquer Tas : Complexité ➻ On a l’égalité : ∞ X 1/2 i = = 2 i 2 2 (1 − 1/2) i=0 ➻ Donc, le temps d’exécution de Fabriquer_tas peut être borné par ⌊log2 n⌋ X i = O n O n i 2 i=0 ∞ X i=0 i 2i ! = O(n · 2) = O(n) ➻ Ainsi, on peut construire un tas à partir d’un tableau non ordonné en un temps linéaire. API2 - LST«A» – p.30/33 Le tri par tas : Heapsort ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). API2 - LST«A» – p.31/33 Le tri par tas : Heapsort ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). ➻ L’élément maximum de T se trouve à la racine T[1]. API2 - LST«A» – p.31/33 Le tri par tas : Heapsort ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). ➻ L’élément maximum de T se trouve à la racine T[1]. ➻ On le place à sa position finale en l’échangeant avec T[n]. API2 - LST«A» – p.31/33 Le tri par tas : Heapsort ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). ➻ L’élément maximum de T se trouve à la racine T[1]. ➻ On le place à sa position finale en l’échangeant avec T[n]. ➻ On décrémente la taille du tableau en ne considérant que le sous-tableau T[1..(n-1)]. API2 - LST«A» – p.31/33 Le tri par tas : Heapsort ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). ➻ L’élément maximum de T se trouve à la racine T[1]. ➻ On le place à sa position finale en l’échangeant avec T[n]. ➻ On décrémente la taille du tableau en ne considérant que le sous-tableau T[1..(n-1)]. ➻ On transforme T[1..(n-1)] en tas puisque les fils de la racine restent des tas. Il y aura éventuellement à descendre la nouvelle racine à sa place. API2 - LST«A» – p.31/33 Exemple 1 16 2 3 14 10 4 5 6 7 8 7 9 3 8 9 10 2 4 1 API2 - LST«A» – p.32/33 Exemple 1 1 2 3 14 10 4 5 6 7 8 7 9 3 8 9 10 2 4 16 i API2 - LST«A» – p.32/33 Exemple 1 14 2 3 8 10 4 5 6 7 4 7 9 3 8 9 10 2 1 16 i API2 - LST«A» – p.32/33 Exemple 1 1 2 3 8 10 4 5 6 7 4 7 9 3 8 9 2 14 10 i 16 API2 - LST«A» – p.32/33 Exemple 1 10 2 3 8 9 4 5 6 7 4 7 1 3 8 9 2 14 10 i 16 API2 - LST«A» – p.32/33 Exemple 1 2 8 10 i 2 3 8 9 4 5 6 7 4 7 1 3 9 10 14 16 API2 - LST«A» – p.32/33 Exemple 1 9 8 10 i 2 3 8 3 4 5 6 7 4 7 1 2 9 10 14 16 API2 - LST«A» – p.32/33 Exemple 1 8 2 3 7 3 4 5 6 7 4 2 1 9 8 9 10 10 14 16 i API2 - LST«A» – p.32/33 Exemple 1 7 2 3 4 3 4 5 6 1 2 8 8 9 10 10 14 16 7 i 9 API2 - LST«A» – p.32/33 Exemple 1 4 2 3 2 3 4 5 1 7 8 9 10 10 14 16 i 6 7 8 9 API2 - LST«A» – p.32/33 Exemple 1 3 2 3 2 1 4 4 i 8 9 10 10 14 16 5 6 7 7 8 9 API2 - LST«A» – p.32/33 Exemple 1 2 2 3 1 3 i 4 5 6 7 4 7 8 9 8 9 10 10 14 16 API2 - LST«A» – p.32/33 Exemple 1 1 2 2 3 i 3 4 5 6 7 4 7 8 9 8 9 10 10 14 16 API2 - LST«A» – p.32/33 Exemple 1 1 2 2 3 i 3 4 5 6 7 4 7 8 9 8 9 10 10 14 16 1 2 3 4 5 6 7 8 9 10 1 2 3 4 7 8 9 10 14 16 API2 - LST«A» – p.32/33 Tri Tas : Code Tri_Tas(T) Begin Fabriquer_Tas(T) ; n := Length(T) ; for i:=n downto 2 do begin Echanger(T[1], T[i]) ; Reorganiser(T[1..n-1], 1) ; end; End ; API2 - LST«A» – p.33/33 Tri Tas : Code Tri_Tas(T) Begin Fabriquer_Tas(T) ; n := Length(T) ; for i:=n downto 2 do begin Echanger(T[1], T[i]) ; Reorganiser(T[1..n-1], 1) ; end; End ; ➻ L’appel à Fabriquer_Tas prend un temps en O(n). API2 - LST«A» – p.33/33 Tri Tas : Code Tri_Tas(T) Begin Fabriquer_Tas(T) ; n := Length(T) ; for i:=n downto 2 do begin Echanger(T[1], T[i]) ; Reorganiser(T[1..n-1], 1) ; end; End ; ➻ L’appel à Fabriquer_Tas prend un temps en O(n). ➻ Chacun des n − 1 appels à Reorganiser prend un temps en O(log n). API2 - LST«A» – p.33/33 Tri Tas : Code Tri_Tas(T) Begin Fabriquer_Tas(T) ; n := Length(T) ; for i:=n downto 2 do begin Echanger(T[1], T[i]) ; Reorganiser(T[1..n-1], 1) ; end; End ; ➻ L’appel à Fabriquer_Tas prend un temps en O(n). ➻ Chacun des n − 1 appels à Reorganiser prend un temps en O(log n). ➻ La complexité de Tri_Tas est donc en O(n log n). API2 - LST«A» – p.33/33