Arbres AVL Arbres AVL 1 Arbres AVL Les
Arbres AVL • Arbres AVL 1
Arbres AVL • Les arbres AVL sont balancés. • Un arbre AVL est un arbre de recherche binaire T tel que pour chaque nœud interne v de T, les hauteurs des enfants de v peuvent différer au plus de 1. Exemple d’arbre AVL où la hauteur est indiquée à côté de chaque nœud. 2
Hauteur d’un arbre AVL • Proposition: La hauteur d’un arbre AVL T contenant n clés est O(log n). • Justification: La façon la plus simple d’aborder ce problème est de trouver n(h): le nombre minimum de nœuds internes d’un arbre AVL de hauteur h. • On voit que n(1) = 1 et n(2) = 2 • Pour n ≥ 3, un abre AVL de hauteur h contient le nœud racine, un sous -arbre AVL de hauteur n-1 et un autre sous-arbre AVL de hauteur n-2. • i. e. n(h) = 1 + n(h-1) + n(h-2) 3
Hauteur d’un arbre AVL (suite) • Connaissant n(h-1) > n(h-2), on obtient n(h) > 2 n(h-2) n(h) > 4 n(h-4) … n(h) > 2 in(h-2 i) • On résout pour le cas de base et on obtient: n(h) ≥ 2 h/2 -1 • On prend les logarithmes: h < 2 log n(h) +2 • Ainsi, la hauteur d‘un arbre AVL est O(log n) 4
Insertion • Un arbre de recherche binaire T est dit balancé si pour chaque nœud v, la hauteur des enfants de v diffère par au plus un. • L’insertion d’un nœud dans un arbre consiste à effectuer un expand. External(w) sur T, ce qui change la hauteur de certains nœuds de T. • Si une insertion fait que T devient débalancé, on grimpe dans l’arbre à partir du nœud nouvellement créé jusqu’à ce qu’on trouve le premier nœud x tel que son grand-parent z est un nœud débalancé. • Puisque z est devenu débalancé par une insertion dans le sousarbre dont la racine est son enfant y, hauteur(y) = hauteur(frère(y)) + 2 • Pour rebalancer. . . 5
Insertion: rebalancement • Pour rebalancer le sous-arbre de racine z, on doit effectuer une restructuration • On renomme x, y, et z en a, b, et c selon l’ordre de la traversée des nœuds dans une traverse en-ordre. • z est remplacé par b, dont les enfants sont maintenant a et c, dont les enfants, à leur tour, consistent dans les quatre autres sous-arbres qui étaient auparavant les enfants de x, y, et z. 6
Insertion (suite) Non balancé. . . balancé 7
Restructuration • Les quatre façons d’effectuer la rotation des nœuds d’un arbre AVL, représentées graphiquement. -Rotations simples : a=z b=y a=z c=x T 0 b=y rotation simple T 1 T 3 T 2 c=z T 0 rotation simple b=y c=x T 1 T 2 T 3 b=y a=x c=z a=x T 0 T 1 T 2 T 3 8
Restructuration (suite) • Rotations doubles : rotation double a=z c=y b=x T 0 T 3 T 2 T 1 T 0 T 1 rotation double c=z a=y T 2 T 3 b=x a=y c=z b=x T 0 T 1 T 3 T 2 T 0 T 1 T 2 T 3 9
L’algorithme restructure( ) Algorithme restructure(x) : Entrée: Un nœud x d’un arbre de recherche binaire T qui possède un parent y et un grand-parent z Sortie: L’arbre T restructuré par une rotation (simple ou double impliquant les nœuds x, y, et z. 1: Soit (a, b, c) une liste en-ordre des nœuds x, y, et z, et soit (T 0, T 1, T 2, T 3) une liste en-ordre des quatre sous-arbres de x, y, et z, n’ayant pas leur racine à x, y, ou z. 2. Remplacer le sous-arbre ayant sa racine à z avec un nouveau sousarbre dont la racine est à b. 3. Soit a l’enfant gauche de b et soient T 0, T 1 les sous-arbres gauche et droit de a, respectivement. 4. Soit c l’enfant droit de b et soient T 2, T 3 les sous-arbres gauche et 10 droit de c, respectivement.
Algorithm de restructuration cut/link (suite) • Voici les détails de cet algorithme. . . • Tout arbre qui a besoin d’être rebalancé peut être découpé en 7 parties : x, y, z, et les 4 sous-arbres ancrés aux enfants de ces nœuds (T 0 à T 3) z 11
Algorithm de restructuration cut/link (suite) z • Faire un nouvel arbre qui est balancé et placer les 7 parties de l’ancien arbre dans le nouvel arbre de façon à ce que la numérotation soit encore correcte quand on traverse le nouvel arbre en-ordre. • Ceci fonctionne quelle que soit la façon dont l’ancien arbre était débalancé. • Voyons comment cela fonctionne ! 12
Algorithm de restructuration cut/link (suite) • Numéroter les 7 parties en traversant l’arbre en-ordre (noter que x, y et z sont maintenant renommés selon leur ordre dans la traversée) 13
Algorithm de restructuration cut/link (suite) • Créez un tableau, numéroté 1 à 7 (on peut ignorer le 0 ième élément avec une perte d’espace minime) 1 2 3 4 5 6 7 • Coupez (cut()) les 4 arbres T et placez les selon leur rang en-ordre dans le tableau. 1 2 3 4 5 6 7 14
Algorithm de restructuration cut/link (suite) • Effectuez cut x, y, et z dans cet ordre (enfant, parent, grand-parent) et placez les dans leur rang en-ordre dans le tableau. 1 2 3 4 5 6 7 • Ensuite on peut re-linker ces sous-arbres à l’arbre principal. • Linkez d’abord le rang 4 (b) à l’endroit où se trouvait la racine du sous-arbre auparavant. 15
Algorithm de restructuration cut/link (suite) • Linkez les rangs 2 (a) et 6 (c) comme enfants de 4. 16
Algorithm de restructuration cut/link (suite) • Finalement, linkez les rangs 1, 3, 5, et 7 comme enfants de 2 et de 6. • Et vous avez un arbre balancé! 17
Algorithm de restructuration cut/link (suite) • Cet algorithme de restructuration a exactement le même effet que l’un des 4 cas de rotation discutés auparavant. • Avantages: pas d’analyse de cas, plus élégant • Désavantage: un peu plus de code à écrire • Même ordre de temps d’exécution 18
Suppression • On voit facilement qu’effectuer remove. Above. External(w) peut débalancer T. • Soit z le premier nœud débalancé rencontré en remontant l’arbre à partir de w. De plus, soit y l’enfant de z ayant la plus grande hauteur, et soit x l’enfant de y ayant la plus grande hauteur. • On peut effectuer l’opération restructure(x) pour rebalancer le sous -arbre de racine z. • Comme cette restructuration peut débalancer un autre nœud plus haut dans l’arbre, on doit continuer à vérifier l’état de balancement jusqu’à la racine de T. 19
Suppression (suite) • Exemple de suppression dans un arbre AVL: 44 4 y 1 17 62 2 1 T 0 50 48 1 3 x 78 0 Oh non, débalancé! 2 1 54 88 T 2 32 T 1 T 3 Ouf, rebalancé! 20
Suppression (suite) • Exemple de suppression dans un arbre AVL : Oh non, débalancé! Ouf, rebalancé! 21
Implémentation • Pour implanter un arbre AVL en Java, on utilise la structure de nœud suivante : public class AVLItem extends Item { int height; AVLItem(Object k, Object e, int h) { super(k, e); height = h; } public int height () { return height; } public int set. Height(int h) { int old. Height = height; height = h; return old. Height; } 22 }
Implémentation (suite) public class Simple. AVLTree extends Simple. Binary. Search. Tree implements Dictionary { public Simple. AVLTree(Comparator c) { super(c); T = new Restructurable. Node. Binary. Tree(); } private int height(Position p) { if (T. is. External(p)) return 0; else return ((AVLItem) p. element()). height(); } private void set. Height(Position p) { // called only if p is internal ((AVLItem) p. element()). set. Height (1+Math. max(height(T. left. Child(p)), height(T. right. Child(p)))); } 23
Implémentation (suite) private boolean is. Balanced(Position p) { // test whether node p has balance factor // between -1 and 1 int bf = height(T. left. Child(p)) - height(T. right. Child(p)); return ((-1 <= bf) && (bf <= 1)); } private Position taller. Child(Position p) { // return a child of p with height no // smaller than that of the other child if(height(T. left. Child(p)) >= height(T. right. Child(p)); return T. left. Child(p); else return T. left. Child(p); } 24
Implémentation (suite) private void rebalance(Position z. Pos) { //traverse the path of T from z. Pos to the root; for each node encountered //recompute its height and perform a rotation if it is unbalanced while (! T. is. Root(z. Pos)) { z. Pos = T. parent(z. Pos); set. Height(z. Pos); if (!is. Balanced(z. Pos)) { // perform a rotation Position x. Pos = taller. Child(z. Pos)); z. Pos = ((Restructurable. Node. Binary. Tree)T). restructure(x. Pos); set. Height(T. left. Child(z. Pos)); set. Height(T. right. Child(z. Pos)); set. Height(z. Pos); }}} 25
Implémentation (suite) public void insert. Item(Object key, Object element) throws Invalid. Key. Exception { super. insert. Item(key, element); // may throw an Invalid. Key. Exception Position z. Pos = action. Pos; // start at the insertion position T. replace(z. Pos, new AVLItem(key, element, 1)); rebalance(z. Pos); } public Object remove(Object key) throws Invalid. Key. Exception { Object to. Return = super. remove(key); // may throw an Invalid. Key. Exception if (to. Return != NO_SUCH_KEY) { Position z. Pos = action. Pos; // start at the removal position rebalance(z. Pos); } return to. Return; }} 26
- Slides: 26