Arbre binaire de Recherche
Le but de ce TP est de manipuler des Arbres Binaires de Recherche. Rappel : ce sont des arbres binaires dont les noeuds contiennent des valeurs telles que pour chaque noeud de valeur :
les valeurs du sous arbres gauche de sont inférieures ou égales à
les valeurs du sous arbre droit de sont strictement supérieures à
Rappel: Papier et crayon sont des outils souvent indispensables à l'informaticien·ne.
Structure
On utilisera la classe suivante qui donne une implatantion minimale des arbres binaires.
Voilà par exemple la création d'un arbre binaire de racine 3, avec un fils gauche de valeur 1 et un fils droit de valeur 4.
En voici un autre, cette fois, 4 a aussi un fil droit de valeur 5.
On peut accéder aux différents champs de cette façon :
Exercice : créer l'ABR où l'on a rajouté 2 comme fils droit du sous-arbre enraciné en 1 de l'exemple précédent.
Insertion
On rappelle l'algorithme d'insertion dans un arbre binaire de recherche :
l'insertion dans un noeud vide renvoie l'arbre contenant le noeud à insérer,
sinon, on compare la valeur à insérer à la valeur du noeud, on insère à gauche si la valeur est plus petite ou égale et à droite sinon
On vous donne la fonction suivante qui permet de récupérer les éléments de l'arbre en ordre infixe et de les mettre dans une liste.
Techniquement on utilise le fantastique système de générateurs en Python. On utilise l'instruction yield qui permet d'énumérer l'ensemble à générer. L'instruction yield agit un peu comme un return qui n'arête pas l'exécution du programme, mais le met en pause jusqu'à son prochain appel.
On peut maintenant vérifier que le tri par ABR... trie bien les valeurs comme escompté!
Rotations
Reprenez les algorithmes (vus en TD) de rotation droite et rotation gauche et implantez les.
Equilibre
On dit qu'un ABR est équilibré si la différence de hauteur à gauche et à droite de tous les noeuds est strictement ingérieure à 2 (donc -1, 0 ou 1).
Le but de cette section est d'écrire un algorithme d'insertion dans les arbres équilibrés, qui préserve cet propriété. Pour cela, on va sauvegarder au niveau de chaque noeud la hauteur du sous-arbre issu du noeud. Cela permetra de connaitre en temps constant l'état d'équilibre d'un arbre.
Dans la littérature, on appelle AVL cette classe d'ABR. Le nom AVL provient du nom des deux auteurs de l'algorithme: Georgii Adelson-Velsky et Evguenii Landis.
On crée une nouvelle classe AVL pour les arbres avec paramètres de hauteur.
Il faut maintenant modifier légèrement notre algorithme d'insertion pour prendre en compte la mise à jour du paramètre de hauteur. On effectue une insertion classique puis on calcule le nouveau paramètre en fonction de celui des fils.
On va avoir besoin des fonctions de rotation, cependant, il faut faire attention à ce que les rotation modifient les hauteurs des différents noeuds. On écrit donc une nouvelle fonction qui, après l'opération de rotation, met à jour les hauteurs de x et de y.
On a maintenant tous les outils pour écrire une vraie insertion AVL, c'est-à-dire, une insertion qui conserve des arbres équilibrés. Le principe est le suivant : on suppose que l'on est sur un noeud et l'insertion a modifié sa valeur d'équilibre pour l'amener à 2 ou -2, on supose que les sous-arbres ont déjà été rééquilibrés. Plusieurs cas sont possibles :
l'équilibre de est de 2 et l'équilibre de son fils gauche est de 1 : on applique une rotation droite sur
l'équilibre de est de 2 et l'équilibre de son fils gauche est de -1 : on applique une rotation gauche sur le fils gauche puis une rotation droite sur
l'équilibre de est de -2 et l'équilibre de son fils droit est de -1 : on applique une rotation gauche sur
l'équilibre de est de -2 et l'équilibre de son fils droit est de 1 : on applique une rotation droite sur le fils droit puis une rotation gauche sur .
Il est possible de montrer que ce sont les seules possibilités, vous pouvez essayer de voir vous-même pourquoi (un excellent exercice). Remarquez que lorsqu'on insère, les sous-arbres sont rééquilibrés avant la racine. Donc l'algorithme d'insertion dans un arbre non vide est le suivant :
on insère à gauche ou à droite en fonction de la valeur à insérer,
on met à jour la valeur de hauteur du noeud
si l'arbre obtenu est déséquilibré, on le rééquilibre avec l'algorithme décrit ci-dessus.