➻ Par exemple, la recherche d’une valeur particulière peut amener à parcourir tout l’arbre (donc être en ). 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. ➻ La structure de maximier n’est pas utilisée pour implémenter les primitives usuelles (recherche, insertion, suppression) sur les ensembles. Maximier - Utilité FIL – USTL [email protected] SDC - Licence – p.1/32 SDC - Licence – p.4/32 ➻ Un maximier est par contre utilisé pour extraire le maximum parmi un ensemble de valeurs. 2 6 3 7 2 3 1 9 6 7 8 5 2 6 3 7 2 ➻ On supprime la feuille utilisée en dernier. 3 1 8 6 ➻ On recommence l’opération avec le successeur utilisé. 7 SDC - Licence – p.5/32 5 ➻ On remplace cet élément par le plus grand de ses successeurs. La suppression de l’élément maximal est, par contre, plus efficace. Maximier - Cueillette ➻ On a un ordre partiel : ➠ Le père est plus grand que ses deux fils. ➠ Pas d’ordre entre les frères. SDC - Licence – p.2/32 ➻ À la racine des sous-arbres principaux on trouve donc les ème et ème valeurs selon l’ordre décroissant. Maximier – Tri Arbre N.E. Oussous ➻ Un arbre binaire non vide est un maximier si et seulement si ➠ sa racine porte la valeur maximale. ➠ ses sous-arbres principaux sont des maximiers. ➻ Un arbre vide est considéré comme un maximier. Maximier - Définition Structures de Données et Complexité 3 1 6 7 Cueillette - Code 2 3 8 SDC - Licence – p.3/32 5 SDC - Licence – p.6/32 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 2 6 7 9 Maximier - Exemple ➻ Par réorganisations successives, on peut transformer tous les sous-arbres de hauteur en maximiers, puis ceux de hauteur , etc ➻ Les sous-arbres de hauteur sont donc des pré-maximiers. SDC - Licence – p.11/32 ➻ Il faudra donc parcourir les sous-arbres par hauteurs croissantes (les sous-arbres les plus profonds en premier). SDC - Licence – p.10/32 Fabrication d’un maximier ➻ Un arbre binaire quelconque contient des maximiers : toutes ses feuilles. SDC - Licence – p.8/32 ➻ La réorganisation nécessite le parcours d’une seule branche, avec un à chaque nœud parcouru. traitement en ➠ É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. Réorganisation - Code Maximier ➻ Si le pré-maximier n’est pas un maximier, il faut Pré-Maximier 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 ; SDC - Licence – p.7/32 ➻ La transformation en maximier (Réorganisation) se fait par une méthode analogue à celle utilisée pour la suppression du maximum. ➻ Un pré-maximier n’est pas nécessairement un maximier, cela dépend de la valeur portée à la racine. ➻ Un Pré-Maximier est un arbre dont les sous-arbres principaux sont des maximiers. Pré-Maximier 7 6 6 7 3 3 9 9 2 2 1 3 3 1 2 2 5 2 6 3 7 2 3 1 6 8 7 6 5 2 6 3 7 2 3 1 9 6 Fabrication - Exemple 6 7 8 9 7 7 Réorganisation - Exemple 8 8 SDC - Licence – p.12/32 5 SDC - Licence – p.9/32 5 La structure de Tas un arbre = un tableau un curseur = un indice du tableau un nœud = une case du tableau ➻ Elle consiste à ranger les valeurs portées par les nœuds ➠ par profondeur croissante ➠ de gauche à droite (pour une profondeur donnée). SDC - Licence – p.16/32 ➻ 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. SDC - Licence – p.13/32 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 Cet algorithme s’exprime facilement de façon récursive Fabrication - Code récursif 7 9 2 8 3 9 2 10 1 11 6 12 SDC - Licence – p.17/32 T(7) 5 7 Racine Successeur gauche Successeur droite Prédecesseur Est_Feuille Possède 2 successeurs Possède 1 seul successeur Dernier nœud interne 7 6 T(6) T(9) T(10) T(11) T(12) T(5) 3 5 6 T(8) T(4) T(3) 6 4 1 5 T(2) T(1) 8 3 2 7 SDC - Licence – p.15/32 SDC - Licence – p.18/32 Tas dans 1 tableau d’indices 1..n 2 3 3 8 SDC - Licence – p.14/32 En pratique, cette méthode de tri est employée dans le cas d’une implémentation d’arbre bien particulière : le tas. 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 Description informelle de l’algorithme : L’utilisation des maximiers peut amener à une méthode de tri appelée Tri-arbre ou Heapsort. Tri-Arbre : Heapsort 1 2 6 7 9 Tas - Exemple 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. for h := 2 to hauteur(arbre) do begin Réorganiser tous les sous-arbres de hauteur h ; end; // for Cet algorithme peut aussi s’exprimer (informellement) de façon itérative par Fabrication - Code itératif ... T[low(T)+1] ... ... T[low(T)+4] T[high(T)] T[low(T)+5] T[low(T)+2] T[low(T)+6] 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 SDC - Licence – p.22/32 SDC - Licence – p.19/32 Réorganiser un Tas : Récursif 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)+3] T[low(T)] Tas - Cas général Réorganiser Tas : Complexité ! # $% & " ( # $% & ' ➻ La solution est donc et " Ainsi SDC - Licence – p.23/32 ➻ On est dans le cas 2 du théorème général avec ➻ Le pire des cas est atteint lorsque le dernier niveau est rempli exactement à moitié. ➻ Les sous-arbres des fils ont chacun une taille au plus égale à Réorganiser Tas : Complexité ➻ On aboutit à la relation de récurrence suivante : SDC - Licence – p.24/32 SDC - Licence – p.21/32 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 Réorganiser un Tas : Itératif . ➻ Le temps d’exécution de Reorganiser sur un sous-arbre de taille enraciné en un nœud i est la somme du temps et le temps d’exécution de Reorganiser sur un sous-arbre enraciné sur l’un des deux fils du nœud i. SDC - Licence – p.20/32 ➻ 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. ➻ Les moins ➠ Les arbres auront une taille maximale, celle du tableau. ➠ Un nœud est associé à une case (fixée) du tableau. La structure de Tas ' , * + ) Fabriquer un Tas ➻ Les feuilles sont situées dans les dernières cases du tableau : T[( +1)..n]. - . ➻ Le temps d’exécution de Fabriquer_tas : Chaque appel à Reorganiser coûte . Il existe appels de ce type. Donc, le temps d’exécution est au plus . 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; ➻ On parcourt les sous-arbres par hauteurs croissantes en parcourant les cases du tableau en ordre inverse. ) , * + ) * + ) ➻ Une analyse plus fine est basée sur les propriétés des tas : ➠ La hauteur d’un tas à éléments est . ➠ sous-arbres de hauteur . Fabriquer Tas : Complexité , SDC - Licence – p.25/32 8 9 8 7 10 5 16 10 7 6 9 14 8 3 7 8 3 10 9 7 10 : 3 0 , /* + 1 2 ( 5 SDC - Licence – p.26/32 ➻ Donc, le temps d’exécution de Fabriquer_tas peut être borné par ➻ On a l’égalité : Fabriquer Tas : Complexité 14 4 2 2 1 4 ) 9 SDC - Licence – p.28/32 9 16 1 6 5 ➻ Le coût total pour Fabriquer_tas sera 2 est 3 4 ➻ Le temps de calcul de Reorganiser sur un nœud de hauteur . 1 4 3 ➻ Il lui correspond l’arbre suivant : 2 1 ➻ Soit le tableau suivant : Exemple ; ; ) ) 9 8 A 1 3 1 143 6 : 3 B>C ? # $% @ 3 <> = ) ( 8 5 ) ) 76 1 2 ( 5 ( 5 ) ( 5 SDC - Licence – p.29/32 ➻ Ainsi, on peut construire un tas à partir d’un tableau non ordonné en un temps linéaire. 8 10 6 7 7 10 10 1 9 4 8 2 5 7 4 8 6 9 Le tri par tas : Heapsort 9 3 10 2 14 7 SDC - Licence – p.27/32 3 SDC - Licence – p.30/32 ➻ 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. ➻ On décrémente la taille du tableau en ne considérant que le sous-tableau T[1..(n-1)]. ➻ On le place à sa position finale en l’échangeant avec T[n]. ➻ L’élément maximum de T se trouve à la racine T[1]. ➻ On construit un tas par Fabriquer_Tas à partir de T[1..n], n=length(T). 9 8 14 5 16 3 2 3 1 1 16 4 2 4 Exemple 1 7 5 8 6 9 7 8 10 9 14 SDC - Licence – p.31/32 16 10 ➻ La complexité de Tri_Tas est donc en , 4 * + ) 4 . . SDC - Licence – p.32/32 appels à Reorganiser prend un temps en 3 3 2 ➻ Chacun des . ➻ L’appel à Fabriquer_Tas prend un temps en 2 9 1 16 8 7 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 ; Tri Tas : Code ) 1 14 10 10 7 9 4 8 6 3 5 i 2 4 3 2 1 1 Exemple * + , )