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).

  • ๐ŸŒฒ Dรฉfinition: Un arbre de recherche binaire dans lequel le facteur d'รฉquilibre de chaque nล“ud se trouve dans {-1, 0, +1}, nommรฉ d'aprรจs ses inventeurs Adelson-Velsky et Landis.
  • ๏ธ Facteur d'รฉquilibre : Calculรฉ comme hauteur(gauche) โˆ’ hauteur(droite) ; les valeurs en dehors de {-1, 0, +1} dรฉclenchent une rotation pour rรฉtablir l'รฉquilibre.
  • (I.e. Rotation : Quatre cas โ€” LL, RR, LR et RL โ€” rรฉalignent les nล“uds aprรจs des insertions ou des suppressions dรฉsรฉquilibrรฉes afin de maintenir la hauteur logarithmique de l'arbre.
  • โž• Insertion: Insertion BST standard suivie d'une marche ascendante qui recalcule les facteurs d'รฉquilibre et effectue au maximum une ou deux rotations.
  • โž– Effacement: Identique ร  la suppression d'un BST, mais peut entraรฎner plusieurs rotations en cascade dans l'arbre car la hauteur du sous-arbre peut diminuer ร  chaque ancรชtre.
  • ๐Ÿš€ Applications : Les bases de donnรฉes, les index en mรฉmoire, les mรฉtadonnรฉes du systรจme de fichiers et les structures de recherche d'IA utilisent les arbres AVL pour des recherches ordonnรฉes rapides.

Arbres AVL

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รฉ :

AVL Arboriculture

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.

AVL Arboriculture

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

Facteur dโ€™รฉquilibre dans les arbres AVL

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

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.

Arbre AVL ร  droite โ€“ Rotation ร  droite

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.

Arbre AVL Droite โ€“ Rotation Gauche

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.

Arbre AVL Rotation gauche โ€“ droite

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.

Insertion dans les arbres AVL

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.

  1. If BF(node) = +2 et BF(left-child) = +1, effectuer une rotation LL.
  2. If BF(node) = โˆ’2 et BF(right-child) = โˆ’1, effectuer une rotation RR.
  3. If BF(node) = โˆ’2 et BF(right-child) = +1, effectuer une rotation RL.
  4. If BF(node) = +2 et BF(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) = +2 et BF(left-child) = +1, effectuer une rotation LL.
  • 1B. If BF(node) = +2 et BF(left-child) = โˆ’1, effectuez une rotation LR.
  • 1C. If BF(node) = +2 et BF(left-child) = 0, effectuer une rotation LL.

Suppression dans les arborescences AVL

2 cas: Suppression dans le sous-arbre gauche.

  • 2A. If BF(node) = โˆ’2 et BF(right-child) = โˆ’1, effectuer une rotation RR.
  • 2B. If BF(node) = โˆ’2 et BF(right-child) = +1, effectuer une rotation RL.
  • 2C. If BF(node) = โˆ’2 et BF(right-child) = 0, effectuer une rotation RR.

Suppression dans les arborescences AVL

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 :

  1. Copiez le code ci-dessus et enregistrez-le dans un fichier nommรฉ avl.cpp.
  2. Compilez le code :
g++ avl.cpp -o run
  1. Exรฉcutez le code.
./run

C++ Exemple d'arbres AVL

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.

FAQ

Un arbre AVL est un arbre binaire de recherche auto-รฉquilibrรฉ oรน le facteur d'รฉquilibre de chaque nล“ud reste dans l'intervalle {-1, 0, +1}. Les rotations rรฉtablissent cet invariant lors de chaque insertion ou suppression.ping rechercher, insรฉrer et supprimer en O(log n).

Le facteur d'รฉquilibre d'un nล“ud est รฉgal ร  la hauteur de son sous-arbre gauche moins sa hauteur de son sous-arbre droit. Les valeurs doivent รชtre comprises entre -1 et +1. Un facteur d'รฉquilibre de +2 ou -2 indique qu'une insertion ou une suppression a dรฉsรฉquilibrรฉ ce nล“ud et qu'une rotation est nรฉcessaire.

Les quatre rotations sont LL, RR, LR et RL. LL utilise une simple rotation vers la droite, RR utilise une simple rotation vers la gauche, et LR et RL sont des rotations doubles qui combinent une rotation sur l'enfant avec une rotation opposรฉe sur le nล“ud.

L'insertion suit la rรจgle standard des arbres binaires de recherche, puis l'arbre remonte en mettant ร  jour les hauteurs. Si un ancรชtre enfreint la rรจgle d'รฉquilibre, une ou deux rotations suffisent ร  le rรฉtablir. Au maximum une rotation est nรฉcessaire par insertion.

Les arbres AVL sont strictement รฉquilibrรฉs avec un facteur d'รฉquilibrage maximal de un, ce qui accรฉlรจre les recherches. Les arbres rouge-noir permettent un รฉquilibrage moins strict, ce qui rend les insertions et les suppressions plus rapides, mais lรฉgรจrement plus lentes pour les recherches. Les bases de donnรฉes privilรฉgient les arbres rouge-noir pour les charges d'รฉcriture importantes.

Les arbres AVL alimentent les index de bases de donnรฉes en mรฉmoire, les mรฉtadonnรฉes du systรจme de fichiers, les files d'attente prioritaires, les recherches dans l'annuaire tรฉlรฉphonique, les correcteurs orthographiques et toute charge de travail nรฉcessitant une recherche dรฉterministe O(log n) plus un parcours en ordre pour les requรชtes de plage.

Oui. Les systรจmes d'IA utilisent les arbres AVL pour les tables de symboles, les bases de donnรฉes de fonctionnalitรฉs ordonnรฉes, l'รฉquilibrage des arbres kd et la recherche des plus proches voisins dans les donnรฉes structurรฉes. Ils sous-tendent รฉgalement les index de recherche classรฉs dans les pipelines de recherche intelligents.

Oui. GitHub Copilot et les assistants IA similaires gรฉnรจrent des routines d'insertion, de suppression et de rotation dans C++, Java, Pythonet gรฉnรฉrer des tests unitaires qui vรฉrifient l'invariant du facteur d'รฉquilibre sur chaque opรฉration.

Rรฉsumez cet article avec :