• Aucun résultat trouvé

Option Informatique MP/MP*, TP 1 Judicaël Courant 12 septembre 2015 1 Arbres binaires de recherche

N/A
N/A
Protected

Academic year: 2022

Partager "Option Informatique MP/MP*, TP 1 Judicaël Courant 12 septembre 2015 1 Arbres binaires de recherche"

Copied!
3
0
0

Texte intégral

(1)

1

Option Informatique MP/MP*, TP 1

Judicaël Courant 12 septembre 2015 1 Arbres binaires de recherche

On considère par la suite des arbres binaires. On noteraℎ(𝑡)la hauteur d’un arbre𝑡et|𝑡|sa taille.

On se donne un type Caml des arbres binaires : 1 type 'a bin =

2 | Empty

3 | Node of int * 'a * 'a bin * 'a bin

La première des quatre informations stockées dans unNode est un entier. Par la suite, lorsqu’on manipulera des éléments de type'a bin, on fera en sorte que cette information soit toujours la taille de l’arbre considéré. Lorsqu’une fonction prendra un argument de type'a bin, on pourra donc toujours consulter cette valeur pour connaître la taille de l’arbre. Lorsqu’une fonction construira une valeur de type 'a bin, il faudra toujours faire en sorte que cette valeur soit correctement calculée (pour chacun des nœuds de l’arbre).

1.1 Opérations usuelles sur les arbres

1. Écrire une fonctionsize : 'a abr -> intde complexité constante retournant la taille|𝑡|de l’arbre𝑡 qui lui est passé en argument.

2. Écrire une fonction mknode : 'a -> 'a abr -> 'a abr telle que (mknode x g d crée un nœud étiqueté par x, de fils gauche g, de fils droitd. Elle devra être de complexité constante et la taille de l’arbre devra être correctement calculée.

3. Écrire une fonctionheight : 'a abr -> intretournant la hauteur de l’arbre qui lui est passé en argument.

4. Écrire une fonctioninverse : float abr -> float abr qui remplace l’étiquette de chaque nœud par son inverse (on supposera qu’aucun nœud n’est étiqueté par0).

5. On généralise la question précédente : Écrire une fonctionbin_map : ('a -> 'b) -> 'a bin -> 'a binprenant en argument une fonction𝑓 et un arbre𝑡 et retournant l’arbre obtenu en remplaçant dans𝑡les étiquettes par leurs images respectives par𝑓. Utiliser cette fonction pour récrire la fonction inverse en une ligne.

6. Écrire une fonctionmirror : 'a bin -> 'a bin retournant le mirroir de l’arbre𝑡 donné en argument, c’est-à-dire l’arbre dont la représentation graphique est obtenue par l’application d’une symétrie d’axe vertical à celle de l’arbre𝑡. La tester sur des exemples non-complètement triviaux (i.e. des arbres de profondeur au moins 4).

7. Écrire une fonctionelements : 'a bin -> 'a list retournant tous les éléments de l’arbre qui lui est donné en argument, donnés dans l’ordre préfixe (i.e. le premier élément de la liste est l’élément de l’arbre le plus à gauche). On fera bien attention à ce que cette fonction ait une complexité linéaire en la taille de l’arbre. Vous justifierez rigoureusement cette complexité.

(2)

2

1.2 Arbres binaires de recherche

1. On dira qu’une valeur de 'a bin est un arbre binaire de recherche si ses éléments, donnés par un parcours infixe, sont ordonnés dans l’ordre croissant (pour l’ordre de Caml). Écrire une fonctionmember : 'a -> 'a bin -> boolqui, étant donné un élément𝑥et un arbre binaire de recherche𝑡, indique si𝑥apparaît parmi les étiquettes de𝑡. On fera évidemment en sorte que la complexité en temps de l’exécution demember x tsoit en𝑂(ℎ), oùℎest la hauteur de𝑡.

2. Écrire une fonctioninsere : 'a bin -> 'a -> 'a bininsérant une nouvelle valeur dans un arbre de recherche (l’arbre donné est supposé être un arbre de recherche et le résultat retourné doit en être un aussi).

3. Écrire une fonctionbuild : 'a list -> int -> 'a bin * 'a listtel quebuild l noùl est une liste d’au moinsnéléments construise un arbre binairetavec lesnpremiers éléments de léléments, de façon à ce que cet arbre soit aussi équilibré que possible puis retourne le couple (t, r)oùrest la liste lprivée de sesnpremiers éléments. On demande de plus que, si la listelest triée par ordre croissant alorstsoit un arbre binaire de recherche.

1.3 Arbres bouc-émissaires

Soit𝛼 ∈ [1/2, 1[. On dit qu’un nœud𝑛est𝛼-équilibré en taille si ses fils𝑔et 𝑑vérifient

|𝑔| ≤ 𝛼 ⋅ |𝑛|,

|𝑑| ≤ 𝛼 ⋅ |𝑛|.

On dit qu’un arbre est𝛼-équilibré en taillesi tous ses nœuds sont𝛼-équilibrés en taille.

On dit qu’un arbre est𝛼-équilibré en hauteursi sa hauteur est inférieure ou égale à⌊log(1

u�)𝑛⌋+1.

Dans la suite, on supposera𝛼 > 1/2et pour fixer les idées, dans l’implantation caml, on posera : 1 let alpha = 0.7;;

Les arbres bouc-émissaires sont des arbres binaires de recherche presques𝛼-équilibrés. Nous don- nons ci-dessous une version simplifiée de la version originale.

1. Écrire une fontion rebuild : 'a bin -> 'a bin prenant en argument un arbre binaire de recherche et retournant un arbre de recherche12-équilibré en taille contenant les mêmes éléments.

Cette fonction doit être de complexité linéaire en la taille de l’arbre passé en argument. Vous justifierez la correction et la complexité de votre fonction.

2. Montrer qu’un arbre𝛼-équilibré en taille est nécessairement𝛼-équilibré en hauteur.

3. Écrire une fonctionrebalance : 'a bin -> 'a bin. Si l’arbre passé en argument est vide ou que sa racine est𝛼-équilibrée en taille, elle retourne l’arbre inchangé, sinon elle le reconstruit.1 4. Pour insérer une valeur𝑥dans un arbre bouc-émissaire, on commence par appliquer la fonction rebalanceà la racine de l’arbre. Puis, on compare la valeur de𝑥à celle𝑦de la racine de l’arbre obtenu (si l’arbre obtenu est vide, il suffit de créer un nouveau nœud étiqueté par 𝑥) : on va alors insérer récursivement dans le sous-arbre gauche ou le sous-arbre droit (en commençant là encore par leur appliquer la fonctionrebalance). Écrire une fonctioninsere_be : 'a bin -> 'a -> 'a bineffectuant cette insertion.

5. Montrer que, dans les cas où la fonctionrebalancene reconstruit aucun sous-arbre de t, une insertion faite avecinsere_besur un arbre de taille t se fait en temps𝑂(log |𝑡|).

1 Cet arbre porte sur lui la faute du non-équilibrage de l’arbre: on le sacrifie. Symboliquement, c’est donc un bouc- émissaire, comme peuvent l’être les Roms en France actuellement. Mais, comme on le verra, le sacrifice de l’arbre est conforme au droit européen et au droit international, ne porte atteinte à personne, ne sert pas à se décharger de ses responsabilités et permet réellement de résoudre le problème qui nous intéressait.

(3)

3

6. Montrer que dans le cas où il y a reconstruction d’un sous-arbre, il n’y a reconstruction d’aucun autre sous-arbre.

7. En déduire que, dans le cas le pire, une insertion avecinsere_besur un arbre𝑡prend un temps 𝑂(|𝑡|).

1.4 Analyse de la complexité en coût amorti (difficile)

Étant donné un nœud𝑛d’un arbre, de fils gauche𝑔et de fils droit𝑑, on appelle potentiel du nœud 𝑛et on noteΔ(𝑛)la valeur absolue de|𝑔| − |𝑑|si celle-ci vaut au moins2, et0 sinon. On appelle potentiel d’un arbre𝑡, et on noteΦ(𝑡)la somme des potentiels de ses nœuds.

1. Montrer que le potentiel d’un nœud1/2-équilibré en taille est nul. En déduire que le potentiel d’un arbre1/2-équilibré est nul.

2. Étant donné un arbre𝑡 dont la racine n’est pas 𝛼-équilibrée, montrer que la fonction rebuild s’exécute en un temps𝑂(Φ(𝑡)).

3. Soit un arbre binaire de recherche𝑡. On note𝑇 le temps d’exécution de la fonctionrebalance appliquée à cet arbre et𝑡l’arbre retournée par cette fonction. Montrer qu’il existe une constante 𝐶 > 0, indépendante de𝑡, vérifiant

𝑇 + 𝐶 ⋅ (Φ(𝑡′) − Φ(𝑡)) = 𝑂(1)

(NB : on pourra remarquer queΦ(𝑡′) − Φ(𝑡) < 0lorsque 𝑡 ≠ 𝑡, sans quoi, il n’y aurait aucun espoir de montrer ce qui est demandé)

4. En déduire que le temps d’exécution𝑇 deinsere_besur𝑡vérifie 𝑇 + 𝐶 ⋅ (Φ(𝑡′) − Φ(𝑡)) = 𝑂(log |𝑡|)

Indication : on pourra commencer par montrer le résultat dans le cas où aucun appel àrebuild n’est effectué, montrer ensuite que dans le cas contraire le nombre d’appels récursifs avant l’appel à rebuild est un 𝑂(log |𝑡|), puis compter précisément les variations du potentiel de l’arbre dues à cette reconstruction et celles dues à l’insertion elle-même.

5. Étant donné une séquence de𝑛 insertions sur un arbre initial𝑡0, de coût en temps respectifs 𝑇1, …,𝑇u�, on note𝑡1,…,𝑡u� les arbres obtenus respectivement après la première, la deuxième,

…, la𝑛-ième insertion, montrer qu’on a

u�

u�=1

𝑇u�= 𝑂(∑u�

u�=1

log |𝑡u�|) + 𝐶 ⋅ (Φ(𝑡0) − Φ(𝑡u�))

En déduire en particulier que si le potentiel de𝑡0 est nul, on a

u�

u�=1

𝑇u�= 𝑂(∑u�

u�=1

log |𝑡u�|)

Autrement dit : en temps amorti, chacune des insertions à coûté un temps logarithmique en la taille de l’arbre.

Références

Documents relatifs

• Les étiquettes du sous arbre gauche d’un noeud sont plus petites que l’étiquete du noeud.. • Les étiquettes du sous arbre droit d’un noeud sont plus grande que l’étiquete

Judicaël Courant - 13 septembre 2017 1/2 Document sous licence Art Libre ( http://artlibre.org).. Écrire une fonction booléenne nommée est_divisible, qui prend en argument un

Écrire une fonction inverse(s) prenant en argument la représentation d’une

Écrire une fonction fusion(t1, t2) prenant en argument deux tableaux t1 et t2 de nombres supposés triés 1 de longueurs

— Il existe un moyen de convaincre quelqu’un qu’une formule est satisfiable en lui donnant un certificat (de taille polynomiale en la taille de la formule), qu’il peut vérifier

Le train à lévitation magnétique, ou Maglev, utilise les forces magnétiques pour assurer sa sustentation, son guidage et sa propulsion.. En régime de croisière, il n’y a pas de

Figure 2 – À gauche : Divers flux optiques (décalés verticalement pour une meilleure lisibilité) : le laser excitateur (F i ), partiellement réfléchi (F r ), pénètre dans

5 Il y a une erreur dans l’énoncé : il faut aussi le graphe en second argument de la fonction check_tags_predecessors qui a donc pour type task -&gt; graph