Arbres équilibrés Arbres AVL Adelson-Velsky et Landis Algorithmique avancée Hervé Blanchon Jérôme Goulian IUT Département Informatique Université Pierre Mendès France Georgy Maximovich Adelson-Velsky (russe, 9-) Yevgeniy Mikhailovich Landis (russe, 9-997) AVL en 9!! Les opérations de recherche, d'insertion et de suppression dans un arbre binaire de recherche se font en moyenne en O(log N) comparaisons, mais dans le pire des cas en O(N)!! Comment éviter cette dégradation au fil des insertions? "! Produire des arbres équilibrés (l'arbre binaire de recherche idéal a ses feuilles sur au plus niveaux), peu importe l ordre d insertion ; "! Rééquilibrer l arbre après chaque insertion ou quand temps libre! (par exemple en créant un nouvel arbre par ajout alterné aux feuilles et à la racine)!! Quel est le coût de cette réorganisation? "! O(N) dans le pire des cas!!! Si le nombre de recherches est bien plus grand que le nombre d insertions, alors cela vaut le coup de toutes façons! mais existe-t-il un moyen de préserver une insertion en log N tout en garantissant une recherche également en log N? "! Oui! Il faut abandonner l idée de produire des arbres parfaitement équilibrés au profit d arbres quasi-équilibrés. AVL : Arbres binaires de recherche (quasi) équilibrés!!un AVL est un ABR particulier "! c est un ABR (quasi) équilibré AVL : Arbres binaires de recherche (quasi) équilibrés!! Définir un AVL en considérant pour chaque nœud n de l arbre A (non vide) "! son facteur d équilibre!! bal(p) vérifie h(a d (p)) - h(a g (p))!!! bal(p) = -, ou + "! sa hauteur!! la hauteur des sous-arbre gauche et droit ne peut différer que de!! : "! Insertion aux feuilles comme dans un ABR "! puis rééquilibrage tout de suite 3
Implémentation des AVL!! Nœud d un arbre AVL private static class AvlNode<AnyType> // Constructors AvlNode( AnyType theelement ) this( theelement, null, null ); AvlNode( AnyType theelement, AvlNode<AnyType> lt, AvlNode<AnyType> rt ) element = theelement; left = lt; right = rt; height = + Math.max(left.height, right.height); AnyType element; // The data in the node AvlNode<AnyType> left; // Left child AvlNode<AnyType> right; // Right child int height; // Height!! Méthode de calcul de la hauteur d un nœud d un arbre AVL!! Lors d une insertion, seuls les nœuds situés entre le point d insertion et la racine peuvent avoir leur hauteur altérée!! Lorsque l on remonte du point d insertion vers la racine en mettant à jour les hauteurs, il se peut que l on se trouve sur un nœud dont les fils violent la contrainte de différence de hauteur "! il faut ré-équilibrer l arbre!! Soit " le nœud dont la hauteur des deux fils diffère de, cela peut se produire dans cas :.! insertion dans le sous-arbre gauche du fils gauche de ".! insertion dans le sous-arbre droit du fils gauche de " /** Return the height of node t, or -, if null. */ private int height( AvlNode<AnyType> t ) return t == null? : t.height; 5!! Illustration des cas &!! Lors d une insertion, seuls les nœuds situés entre le point d insertion et la racine peuvent avoir leur hauteur altérée!! Lorsque l on remonte du point d insertion vers la racine en mettant à jour les hauteurs, il se peut que l on se trouve sur un nœud dont les fils violent la contrainte de différence de hauteur "! il faut ré-équilibrer l arbre!! Soit " le nœud dont la hauteur des deux fils diffère de, cela peut se produire dans cas :.! insertion dans le sous-arbre gauche du fils gauche de ".! insertion dans le sous-arbre droit du fils gauche de " 3.! insertion dans le sous-arbre gauche du fils droit de ".! insertion dans le sous-arbre droit du fils droit de " 7 8
!! Illustration des cas 3 &!! Lors d une insertion, seuls les nœuds situés entre le point d insertion et la racine peuvent avoir leur hauteur altérée!! Lorsque l on remonte du point d insertion vers la racine en mettant à jour les hauteurs, il se peut que l on se trouve sur un nœud dont les fils violent la contrainte de différence de hauteur "! il faut ré-équilibrer l arbre!! Soit " le nœud dont la hauteur des deux fils diffère de, cela peut se produire dans cas :.! insertion dans le sous-arbre gauche du fils gauche de ".! insertion dans le sous-arbre droit du fils gauche de " 3.! insertion dans le sous-arbre gauche du fils droit de ".! insertion dans le sous-arbre droit du fils droit de "!! Les cas et sont symétriques par rapport à ", ainsi que les cas et 3 "! deux familles de cas 9!! Première famille de cas. insertion dans le sous-arbre gauche du fils gauche de!. insertion dans le sous-arbre droit du fils droit de! "! l insertion a lieu à l extérieur (gauche-gauche ou droite-droite)!! on fera une rotation simple Rotation simple (à droite)!! Premier cas (/) [situation.]!! Seconde famille de cas. insertion dans le sous-arbre droit du fils gauche de! 3. insertion dans le sous-arbre gauche du fils droit de! "! l insertion a lieu à l intérieur (droite-gauche ou gauche-droite)!! on fera une rotation double "! Le nœud k viole la contrainte car son sous-arbre gauche est plus haut de deux niveaux que son sous-arbre droit (seule situation à correspondre au cas.) "! L insertion s est faite sur X "! Y ne peut être au même niveau que X sinon il y aurait eu déséquilibre plus tôt "! Y ne peut être au même niveau que Z sinon k serait le premier nœud vers la racine qui était en violation d équilibre
Rotation simple (à droite)!! Premier cas (/) "! On voudrait monter X d un niveau et baisser Z d un niveau "! k devient la racine, X reste son fils gauche "! k devient la racine du sous-arbre droit de k "! Z reste le sous-arbre droit de k "! Y devient le sous-arbre gauche de k Rotation simple (à droite avec le fils gauche) /** * Rotate binary tree node with left child. * For AVL trees, this is a single rotation for case. * Update heights, then return new root. */ private AvlNode<AnyType> rotatewithleftchild( AvlNode<AnyType> k ) AvlNode<AnyType> k = k.left; k.left = k.right; k.right = k; k.height = Math.max( height( k.left ), height( k.right ) ) + ; k.height = Math.max( height( k.left ), k.height ) + ; return k;!! Rotation droite 3 Rotation simple (à gauche)!! Second cas (/) [situation.] "! Même analyse ; même remède Rotation simple (à gauche avec le fils droit) /** * Rotate binary tree node with right child. * For AVL trees, this is a single rotation for case. * Update heights, then return new root. */ private AvlNode<AnyType> rotatewithrightchild( AvlNode<AnyType> k ) AvlNode<AnyType> k = k.right; k.right = k.left; k.left = k; k.height = Math.max( height( k.left ), height( k.right ) ) + ; k.height = Math.max( height( k.right ), k.height ) + ; return k;!! Rotation gauche 5
Rotation double (gauche-droite)!! Premier cas (/3) [situation.] "! La rotation droite ne marche pas! Rotation double (gauche-droite)!! Premier cas (/3) [situation.] "! k3 ne peut pas être conservé comme racine "! l insertion a été faite dans Y ce qui garantit que l arbre n est pas vide, on peut assumer qu il a une racine et deux fils "! Une rotation entre k3 et K ne marche pas (déjà vu) "! k doit donc être la nouvelle racine!! k comme fils gauche et k3 comme fils droit "! Il faut deux rotations 7 8 Rotation double (gauche-droite)!! Premier cas (/3) [situation.] Rotation double (droite-gauche)!! Second cas (/) [situation 3.] "! k ne peut pas être conservé comme racine "! Une rotation entre k et K3 ne marche pas "! k doit donc être la nouvelle racine!! k comme fils gauche et k3 comme fils droit "! Il faut deux rotations /** Double rotate binary tree node: first left child * with its right child; then node k3 with new left child. * For AVL trees, this is a double rotation for case. * Update heights, then return new root. */ private AvlNode<AnyType> doublewithleftchild( AvlNode<AnyType> k3 ) k3.left = rotatewithrightchild( k3.left ); return rotatewithleftchild( k3 ); 9
Rotation double (droite-gauche)!! Second cas (/) [situation 3.] Exemples de rotations Ajouts successifs de, 3,,, 7, 5, dans l arbre vide /** Double rotate binary tree node: first right child * with its left child; then node k with new right child. * For AVL trees, this is a double rotation for case 3. * Update heights, then return new root. */ private AvlNode<AnyType> doublewithrightchild( AvlNode<AnyType> k ) k.right = rotatewithleftchild( k.right ); return rotatewithrightchild( k ); Exemples de rotations (facteur d équilibre) Ajouts successifs de, 3,,, 7, 5, dans l arbre vide Autre exemple () : basé sur la hauteur!! Séquence d insertion: "! 8 3 - [ROTD()] 3 3 [ROTG()] [DROTD(3)] - 3 [DROTG(3)] 3 3 5 7 5 7 7 3
Autre exemple () Autre exemple () 8 8 5 Autre exemple () Autre exemple () 8 8 Rotation simple 7 8
Autre exemple () Autre exemple () 8 8-9 3 Autre exemple () Autre exemple () 8 8-8 8 Rotation simple 3 3
Autre exemple () Autre exemple () 8 8-8 - 8 - - 33 3 Autre exemple () Autre exemple () 8 Voici un exemple où la rotation se fait loin du point d insertion - - - 8-8 - 9 7 Rotation double (droite-gauche) Noeud inséré 35 3
Autre exemple () Voici un exemple où la rotation se fait loin du point d insertion - 8 7 9 /** * Internal method to insert into a subtree. * @param x the item to insert. * @param t the node that roots the subtree. * @return the new root of the subtree. */ private AvlNode<AnyType> insert( AnyType x, AvlNode<AnyType> t ) Après rotation double 37 38 if( t == null ) return new AvlNode<AnyType>( x, null, null ); int compareresult = compare( x, t.element ); if( compareresult < ) t.left = insert( x, t.left ); if( height( t.left ) - height( t.right ) == ) if( compare( x, t.left.element ) < ) t = rotatewithleftchild( t ); else t = doublewithleftchild( t ); else if( compareresult > ) t.right = insert( x, t.right ); if( height( t.right ) - height( t.left ) == ) if( compare( x, t.right.element ) > ) t = rotatewithrightchild( t ); else t = doublewithrightchild( t ); else ; // Duplicate; do nothing t.height = Math.max( height( t.left ), height( t.right ) ) + ; return t;