Arbres AVL : rotations, insertion, suppression avec C++ Exemple
โก Rรฉsumรฉ intelligent
Les arbres AVL sont des arbres de recherche binaires auto-รฉquilibrรฉs oรน la diffรฉrence de hauteur entre les sous-arbres gauche et droit de chaque nลud reste comprise entre -1, 0 ou +1, garantissant des performances de recherche O(log n).

Que sont les arbres AVL ?
Arbres AVL Ce sont des arbres binaires de recherche dans lesquels la diffรฉrence de hauteur entre le sous-arbre gauche et le sous-arbre droit de chaque nลud est de -1, 0 ou +1. Ce sont des arbres binaires de recherche auto-รฉquilibrรฉs qui maintiennent un temps de recherche logarithmique, nommรฉs d'aprรจs leurs inventeurs Adelson-Velsky et Landis (AVL).
Comment fonctionne lโarbre AVL ?
Pour comprendre l'utilitรฉ des arbres AVL, il faut examiner les problรจmes rencontrรฉs avec un arbre simple. Arbre de recherche binaireConsidรฉrons ces clรฉs insรฉrรฉes dans l'ordre indiquรฉ :
Visualisation de l'arborescence AVL
L'arbre croรฎt linรฉairement lorsque les clรฉs arrivent par ordre croissant, ce qui rรฉduit la complexitรฉ de la recherche ร O(n). Cela va ร l'encontre du principe d'un arbre binaire de recherche (ABR) : seul un arbre รฉquilibrรฉ garantit une complexitรฉ logarithmique. Considรฉrons maintenant les mรชmes clรฉs insรฉrรฉes dans un ordre diffรฉrent.
L'utilisation de clรฉs identiques dans un ordre d'insertion diffรฉrent produit une structure moins profonde, ce qui rรฉduit la complexitรฉ temporelle de chaque recherche (O(log n)). Les arbres AVL garantissent cette structure en contrรดlant la hauteur ร chaque insertion et en corrigeant les dรฉsรฉquilibres sans enfreindre l'ordre des arbres binaires de recherche.
Facteur dโรฉquilibre dans les arbres AVL
Le facteur d'รฉquilibre (BF) tracks la hauteur de chaque nลud afin que l'arbre puisse s'auto-รฉquilibrer ร la volรฉe.
Propriรฉtรฉs du facteur d'รฉquilibre
Arbre AVL du facteur d'รฉquilibre
- Le facteur d'รฉquilibre est la diffรฉrence entre la hauteur du sous-arbre gauche et la hauteur du sous-arbre droit.
Balance factor(node) = height(node->left) โ height(node->right)- Les seules valeurs autorisรฉes sont โ1, 0 et +1.
- Une valeur de โ1 signifie que le sous-arbre droit contient un niveau supplรฉmentaire โ le nลud est lourd ร droite.
- Une valeur de +1 signifie que le sous-arbre gauche contient un niveau supplรฉmentaire โ le nลud est lourd ร gauche.
- Une valeur de 0 signifie que les deux cรดtรฉs ont la mรชme hauteur โ le nลud est parfaitement รฉquilibrรฉ.
Rotations AVL
Les rotations sont dรฉclenchรฉes chaque fois qu'une insertion ou une suppression enfreint la rรจgle du facteur d'รฉquilibre. Les quatre cas sont LL, RR, LR et RL.
Gauche โ Rotation ร gauche
Cette rotation est effectuรฉe lorsqu'un nouveau nลud est insรฉrรฉ au niveau de l'enfant gauche du sous-arbre gauche.
Arbre AVL gauche โ Rotation gauche
Une seule rotation ร droite est effectuรฉe. Ce cas se produit lorsqu'un nลud a BF +2 et que son enfant gauche a BF +1.
Droite โ Rotation ร droite
Cette rotation est effectuรฉe lorsqu'un nouveau nลud est insรฉrรฉ au niveau de l'enfant droit du sous-arbre droit.
Une seule rotation ร gauche est effectuรฉe. Ce cas se produit lorsqu'un nลud a BF โ2 et que son enfant droit a BF โ1.
Rotation droite โ gauche
Cette rotation est effectuรฉe lorsqu'un nouveau nลud est insรฉrรฉ au niveau de l'enfant gauche du sous-arbre droit.
Se dรฉclenche lorsque BF(nลud) = โ2 et BF(enfant-droit) = +1. Faites pivoter l'enfant-droit vers la droite, puis faites pivoter le nลud vers la gauche.
Rotation gauche โ droite
Cette rotation est effectuรฉe lorsqu'un nouveau nลud est insรฉrรฉ au niveau de l'enfant droit du sous-arbre gauche.
Se dรฉclenche lorsque BF(nลud) = +2 et BF(enfant-gauche) = โ1. Faites pivoter l'enfant-gauche vers la gauche, puis faites pivoter le nลud vers la droite.
Insertion dans les arbres AVL
L'insertion est quasiment identique ร une insertion classique dans un arbre binaire de recherche. Aprรจs chaque insertion, l'arbre remonte et se rรฉรฉquilibre. L'insertion s'effectue en O(log n) dans le pire des cas.
Implรฉmentation de l'insertion d'arborescence AVL
รtape 1 : Insรฉrez le nลud en utilisant l'algorithme BST standard. Dans l'exemple ci-dessus, insรฉrez 160.
รtape 2 : Mettre ร jour le facteur d'รฉquilibre de chaque ancรชtre le long du chemin d'insertion.
รtape 3 : Si un ancรชtre quelconque dรฉpasse la plage du facteur d'รฉquilibre, effectuez la rotation correspondante. Dans l'exemple, le facteur d'รฉquilibre du nลud 350 est dรฉpassรฉ ; une rotation LL rรฉtablit donc l'รฉquilibre.
- If
BF(node) = +2etBF(left-child) = +1, effectuer une rotation LL. - If
BF(node) = โ2etBF(right-child) = โ1, effectuer une rotation RR. - If
BF(node) = โ2etBF(right-child) = +1, effectuer une rotation RL. - If
BF(node) = +2etBF(left-child) = โ1, effectuez une rotation LR.
Suppression dans les arborescences AVL
La suppression suit la mรชme logique qu'un arbre binaire de recherche classique et se rรฉรฉquilibre ensuite.
รtape 1 : Trouvez l'รฉlรฉment dans l'arborescence.
รtape 2 : Supprimez le nลud en utilisant la suppression standard d'un arbre binaire de recherche.
รtape 3 : Deux cas sont possibles.
1 cas: Suppression du sous-arbre droit.
- 1A. If
BF(node) = +2etBF(left-child) = +1, effectuer une rotation LL. - 1B. If
BF(node) = +2etBF(left-child) = โ1, effectuez une rotation LR. - 1C. If
BF(node) = +2etBF(left-child) = 0, effectuer une rotation LL.
2 cas: Suppression dans le sous-arbre gauche.
- 2A. If
BF(node) = โ2etBF(right-child) = โ1, effectuer une rotation RR. - 2B. If
BF(node) = โ2etBF(right-child) = +1, effectuer une rotation RL. - 2C. If
BF(node) = โ2etBF(right-child) = 0, effectuer une rotation RR.
C++ Exemple d'arbres AVL
Voici une C++ programme implรฉmentant les arbres AVL :
#include <iostream> #include <queue> #include <unordered_map> using namespace std; struct node { struct node *left; int data; int height; struct node *right; }; class AVL { public: struct node *root; AVL() { this->root = NULL; } int calheight(struct node *p) { if (p->left && p->right) { if (p->left->height < p->right->height) return p->right->height + 1; else return p->left->height + 1; } else if (p->left && p->right == NULL) { return p->left->height + 1; } else if (p->left == NULL && p->right) { return p->right->height + 1; } return 0; } int bf(struct node *n) { if (n->left && n->right) return n->left->height - n->right->height; else if (n->left && n->right == NULL) return n->left->height; else if (n->left == NULL && n->right) return -n->right->height; return 0; } struct node *llrotation(struct node *n) { struct node *p = n; struct node *tp = p->left; p->left = tp->right; tp->right = p; return tp; } struct node *rrrotation(struct node *n) { struct node *p = n; struct node *tp = p->right; p->right = tp->left; tp->left = p; return tp; } struct node *rlrotation(struct node *n) { struct node *p = n; struct node *tp = p->right; struct node *tp2 = p->right->left; p->right = tp2->left; tp->left = tp2->right; tp2->left = p; tp2->right = tp; return tp2; } struct node *lrrotation(struct node *n) { struct node *p = n; struct node *tp = p->left; struct node *tp2 = p->left->right; p->left = tp2->right; tp->right = tp2->left; tp2->right = p; tp2->left = tp; return tp2; } struct node *insert(struct node *r, int data) { if (r == NULL) { r = new struct node; r->data = data; r->left = r->right = NULL; r->height = 1; return r; } if (data < r->data) r->left = insert(r->left, data); else r->right = insert(r->right, data); r->height = calheight(r); if (bf(r) == 2 && bf(r->left) == 1) r = llrotation(r); else if (bf(r) == -2 && bf(r->right) == -1) r = rrrotation(r); else if (bf(r) == -2 && bf(r->right) == 1) r = rlrotation(r); else if (bf(r) == 2 && bf(r->left) == -1) r = lrrotation(r); return r; } void levelorder_newline() { if (this->root == NULL) { cout << "\nEmpty tree\n"; return; } levelorder_newline(this->root); } void levelorder_newline(struct node *v) { queue<struct node *> q; struct node *cur; q.push(v); q.push(NULL); while (!q.empty()) { cur = q.front(); q.pop(); if (cur == NULL && q.size() != 0) { cout << "\n"; q.push(NULL); continue; } if (cur != NULL) { cout << " " << cur->data; if (cur->left != NULL) q.push(cur->left); if (cur->right != NULL) q.push(cur->right); } } } struct node *deleteNode(struct node *p, int data) { if (p->left == NULL && p->right == NULL) { if (p == this->root) this->root = NULL; delete p; return NULL; } struct node *q; if (p->data < data) p->right = deleteNode(p->right, data); else if (p->data > data) p->left = deleteNode(p->left, data); else { if (p->left != NULL) { q = inpre(p->left); p->data = q->data; p->left = deleteNode(p->left, q->data); } else { q = insuc(p->right); p->data = q->data; p->right = deleteNode(p->right, q->data); } } if (bf(p) == 2 && bf(p->left) == 1) p = llrotation(p); else if (bf(p) == 2 && bf(p->left) == -1) p = lrrotation(p); else if (bf(p) == 2 && bf(p->left) == 0) p = llrotation(p); else if (bf(p) == -2 && bf(p->right) == -1) p = rrrotation(p); else if (bf(p) == -2 && bf(p->right) == 1) p = rlrotation(p); else if (bf(p) == -2 && bf(p->right) == 0) p = rrrotation(p); return p; } struct node *inpre(struct node *p) { while (p->right != NULL) p = p->right; return p; } struct node *insuc(struct node *p) { while (p->left != NULL) p = p->left; return p; } ~AVL() {} }; int main() { AVL b; int c, x; do { cout << "\n1.Display levelorder on newline"; cout << "\n2.Insert"; cout << "\n3.Delete\n"; cout << "\n0.Exit\n"; cout << "\nChoice: "; cin >> c; switch (c) { case 1: b.levelorder_newline(); break; case 2: cout << "\nEnter no. "; cin >> x; b.root = b.insert(b.root, x); break; case 3: cout << "\nWhat to delete? "; cin >> x; b.root = b.deleteNode(b.root, x); break; case 0: break; } } while (c != 0); }
Exemple d'exรฉcution du code ci-dessus :
- Copiez le code ci-dessus et enregistrez-le dans un fichier nommรฉ
avl.cpp. - Compilez le code :
g++ avl.cpp -o run
- Exรฉcutez le code.
./run
Avantages des arbres AVL
- La hauteur de l'arbre AVL est toujours รฉquilibrรฉe et ne dรฉpasse jamais la bรปche N.
- La recherche est plus rapide qu'avec un arbre binaire de recherche classique car l'arbre ne peut pas dรฉgรฉnรฉrer.
- L'รฉquilibrage automatique est intรฉgrรฉ ; aucune รฉtape de reconstruction n'est nรฉcessaire.
- Les performances dรฉterministes conviennent aux systรจmes temps rรฉel et aux index en mรฉmoire.











