Programmation Fonctionnelle Avancée – Master 1 TP 2 Mehdi Dogguy On appelle arbre AVL (Adelson-Velskii et Landis) un arbre binaire de recherche globalement équilibré, i.e. la différence entre la hauteur du sous-arbre gauche et droite est au plus égale à 1. En Haskell, une structure d’arbre dont les éléments sont de type a peut être définie de la manière suivante : data Tree a = Empty | Node a ( Tree a ) ( Tree a ) d e r i v i n g Show Exercice 1 : Un arbre binaire de recherche Implémentez les fonctions suivantes : 1. empty :: Tree a -> Bool qui renvoie vrai quand l’arbre donné en argument est vide, et faux sinon. 2. depth :: Tree a -> Int qui calcule la hauteur d’un arbre. 3. search :: Ord a => Tree a -> a -> Bool qui renvoie vrai quand l’élément de type a passé en argument apparaı̂t dans l’arbre. (⋆) Gardez en tête que tous les élément dans le sous-arbre gauche sont inférieurs à la racine, et ceux du sous-arbre droit sont supérieurs. 4. path :: Ord a => Tree a -> a -> [a] qui renvoie le chemin à suivre depuis la racine pour trouver l’élément recherché. 5. insert :: Ord a => Tree a -> a -> Tree a qui insère un nouvel élément dans l’arbre, et qui ne fait rien si l’élément y est déjà. Exercice 2 : Un arbre AVL Notez que la fonction d’insertion définie dans l’exercice précédent ne préserve pas l’équilibrage d’un arbre. Pour pouvoir fournir une implémentation d’insértion correcte pour les arbres AVL, il est utile de définir quelques rotations d’arbre. 1. Implémentez delta :: Tree a -> Tree a -> Int qui calcule la différence entre la hauteur des deux arbres passés en arguments. 2. Implémentez le prédicat balanced :: Tree a -> Bool qui renvoit vrai quand l’arbre donné en argument est équilibré, et faux sinon. Il existe deux rotations simples : la rotation gauche et la rotation droite. On effectue une rotation gauche quand le sous-arbre gauche est plus haut (i.e. le delta est égal à 2) que le sous-arbre droit. Ansi, la transformation effectuée peut être représentée par la figure suivante : se transforme en A B B BG C BG BD A BD C La rotation droite est l’opération inverse (et est définie de façon symétrique). 2. Définissez la fonction balanceL :: Tree a -> Tree a qui effectue la rotation gauche. 3. Définissez la fonction balanceR :: Tree a -> Tree a qui effectue la rotation droite. Il existe les double-rotations gauche-droite et droite-gauche. La transformation effectuée par une rotation gauche-droite est représentée ci-dessous. se transforme en A B BG BD C B A BD DG DD BG DG DD C 4. Définissez la fonction balanceLR :: Tree a -> Tree a qui effectue la rotation gauche-droite. 5. Déduisez la fonction balanceRL :: Tree a -> Tree a qui effectue la rotation droite-gauche. En utilisant ces rotations, implémentez les fonctions suivantes : 6. insert :: Ord a => Tree a -> a -> Tree a qui insère un élément de type a dans l’arbre, tout en préservant l’équilibrage. L’algorithme est assez simple : Suivant la valeur de l’élément e à insérer et celle de la racine, on sait où insérer e. Considérons l’arbre Tree r g d où e < r et g’ = insert g e. Si g’ est plus haut que d (i.e. delta g’ d > 2), alors un équilibrage est nécessaire : Si e est plus grand que la racine de g, alors on effectue une rotation gauche et sinon une rotation gauche-droite. L’autre cas (i.e. insertion à droite) est symétrique. 7. load :: Ord a => Tree a -> [a] -> Tree a qui construit un arbre à partir d’une liste d’éléments. 8. Déduisez une manière de trier une liste d’éléments et implémentez la fonction sort :: Ord a => [a] -> [a]