AVL stabla: rotacije, umetanje, brisanje sa C++ Primjer

โšก Pametni saลพetak

AVL stabla su samobalansirajuฤ‡a binarna stabla pretraลพivanja gdje razlika visine izmeฤ‘u lijevog i desnog podstabla svakog ฤvora ostaje unutar -1, 0 ili +1, ลกto jamฤi performanse pretraลพivanja O(log n).

  • ๐ŸŒฒ Definicija: Binarno stablo pretraลพivanja u kojem faktor ravnoteลพe svakog ฤvora leลพi u {-1, 0, +1}, nazvano po izumiteljima Adelson-Velskyju i Landisu.
  • โš–๏ธ Faktor ravnoteลพe: Izraฤunava se kao visina(lijevo) โˆ’ visina(desno); vrijednosti izvan {-1, 0, +1} pokreฤ‡u rotaciju radi vraฤ‡anja ravnoteลพe.
  • ๐Ÿ”„ Rotacije: ฤŒetiri sluฤaja - LL, RR, LR i RL - preusmjeravaju ฤvorove nakon neuravnoteลพenih umetanja ili brisanja kako bi se stablo odrลพalo logaritamskom visinom.
  • โž• Umetanje: Standardni BST umetnuti postupak nakon kojeg slijedi uzlazni hod koji ponovno izraฤunava faktore ravnoteลพe i izvodi najviลกe jednu jednostruku ili dvostruku rotaciju.
  • โž– Brisanje: Isto kao i BST brisanje, ali moลพe kaskadno proลกiriti viลกe rotacija uz stablo jer se visina podstabla moลพe smanjiti kod svakog pretka.
  • ๐Ÿš€ Primjena: Baze podataka, indeksi u memoriji, metapodaci datoteฤnog sustava i strukture umjetne inteligencije za pretraลพivanje koriste AVL stabla za brzo ureฤ‘eno pretraลพivanje.

AVL stabla

ล to su AVL stabla?

AVL stabla su binarna stabla pretraลพivanja u kojima je razlika visine izmeฤ‘u lijevog i desnog podstabla svakog ฤvora -1, 0 ili +1. To su samobalansirajuฤ‡a BST-a koja odrลพavaju logaritamsko vrijeme pretraลพivanja, nazvana po izumiteljima Adelson-Velskyju i Landisu (AVL).

Kako radi AVL stablo?

Da biste razumjeli zaลกto AVL stabla postoje, pogledajte ลกto ne ide po zlu s ravnicom Stablo binarnog pretraลพivanjaRazmotrite ove kljuฤeve umetnute zadanim redoslijedom:

AVL rad na stablu

Vizualizacija AVL stabla

Stablo raste linearno kada kljuฤevi stiลพu u rastuฤ‡em redoslijedu, degenerirajuฤ‡i pretragu na O(n). To poniลกtava svrhu BST-a - samo uravnoteลพeno stablo odrลพava pretragu logaritamskom. Sada pogledajte iste kljuฤeve umetnute u drugom redoslijedu.

AVL rad na stablu

Isti kljuฤevi, razliฤiti redoslijed umetanja proizvode pliฤ‡i oblik, pa se svako pretraลพivanje izvrลกava u O(log n). AVL stabla provode taj oblik promatrajuฤ‡i visinu pri svakom umetanju i ispravljajuฤ‡i neravnoteลพu bez naruลกavanja BST redoslijeda.

Faktor ravnoteลพe u AVL stablima

Faktor ravnoteลพe (BF) tracks visinu svakog ฤvora kako bi se stablo moglo samouravnoteลพiti u hodu.

Svojstva faktora ravnoteลพe

Faktor ravnoteลพe u AVL stablima

Faktor ravnoteลพe AVL stablo

  • Faktor ravnoteลพe je razlika izmeฤ‘u visine lijevog podstabla i visine desnog podstabla.
  • Balance factor(node) = height(node->left) โˆ’ height(node->right)
  • Jedine dopuลกtene vrijednosti su โˆ’1, 0 i +1.
  • Vrijednost -1 znaฤi da desno podstablo sadrลพi jednu dodatnu razinu - ฤvor ima preteลพak desni dio.
  • Vrijednost +1 znaฤi da lijevo podstablo sadrลพi jednu dodatnu razinu - ฤvor je preteลพak s lijeve strane.
  • Vrijednost 0 znaฤi da obje strane imaju jednaku visinu - ฤvor je savrลกeno uravnoteลพen.

AVL rotacije

Rotacije se izvode kad god umetanje ili brisanje prekrลกi pravilo faktora ravnoteลพe. ฤŒetiri sluฤaja su LL, RR, LR i RL.

Lijevo โ€“ Lijeva rotacija

Ova rotacija se izvodi kada se novi ฤvor umetne u lijevo dijete lijevog podstabla.

AVL stablo lijevo โ€“ rotacija lijevo

AVL stablo lijevo โ€“ rotacija lijevo

Izvodi se jedna rotacija udesno. Ovaj sluฤaj se aktivira kada ฤvor ima BF +2, a njegov lijevi podreฤ‘eni ฤvor ima BF +1.

Desno โ€“ Desna rotacija

Ova rotacija se izvodi kada se novi ฤvor umetne u desno dijete desnog podstabla.

AVL stablo desno โ€“ desna rotacija

Izvodi se jedna rotacija ulijevo. Ovaj sluฤaj se aktivira kada ฤvor ima BF โˆ’2, a njegov desni potomak ima BF โˆ’1.

Rotacija desno โ€“ lijevo

Ova rotacija se izvodi kada se novi ฤvor umetne u lijevo dijete desnog podstabla.

Rotacija AVL stabla desno โ€“ lijevo

Okida se kada je BF(ฤvor) = โˆ’2 i BF(desno-dijete) = +1. Rotirajte desno desno dijete, a zatim rotirajte ฤvor lijevo.

Rotacija lijevo โ€“ desno

Ova rotacija se izvodi kada se novi ฤvor umetne u desno dijete lijevog podstabla.

Rotacija AVL stabla lijevo โ€“ desno

Okida se kada je BF(ฤvor) = +2 i BF(lijevo-dijete) = โˆ’1. Rotirajte lijevo dijete, a zatim ฤvor desno.

Umetanje u AVL stabla

Umetanje je gotovo identiฤno obiฤnom BST umetanju. Nakon svakog umetanja, stablo se podiลพe i ponovno uravnoteลพuje. Umetanje se izvrลกava u najgorem sluฤaju za O(log n).

Umetanje u AVL stabla

Implementacija umetanja AVL stabla

Korak 1: Umetnite ฤvor koristeฤ‡i standardni BST algoritam. U gornjem primjeru umetnite 160.

Korak 2: Aลพurirajte faktor ravnoteลพe svakog pretka duลพ putanje umetanja.

Korak 3: Ako bilo koji predak prekrลกi raspon faktora ravnoteลพe, izvrลกite rotaciju podudaranja. U primjeru, faktor ravnoteลพe ฤvora 350 je prekrลกen, pa rotacija LL vraฤ‡a ravnoteลพu.

  1. If BF(node) = +2 i BF(left-child) = +1, izvrลกite LL rotaciju.
  2. If BF(node) = โˆ’2 i BF(right-child) = โˆ’1, izvrลกite RR rotaciju.
  3. If BF(node) = โˆ’2 i BF(right-child) = +1, izvrลกite RL rotaciju.
  4. If BF(node) = +2 i BF(left-child) = โˆ’1, izvrลกite LR rotaciju.

Brisanje u AVL stablima

Brisanje slijedi istu logiku kao i obiฤni BST i naknadno se ponovno uravnoteลพuje.

Korak 1: Pronaฤ‘ite element u stablu.

Korak 2: Izbriลกite ฤvor standardnim BST brisanjem.

Korak 3: Moguฤ‡a su dva sluฤaja.

Sluฤaj 1: Brisanje iz desnog podstabla.

  • 1A. If BF(node) = +2 i BF(left-child) = +1, izvrลกite LL rotaciju.
  • 1B. If BF(node) = +2 i BF(left-child) = โˆ’1, izvrลกite LR rotaciju.
  • 1C. If BF(node) = +2 i BF(left-child) = 0, izvrลกite LL rotaciju.

Brisanje u AVL stablima

Sluฤaj 2: Brisanje iz lijevog podstabla.

  • 2A. If BF(node) = โˆ’2 i BF(right-child) = โˆ’1, izvrลกite RR rotaciju.
  • 2B. If BF(node) = โˆ’2 i BF(right-child) = +1, izvrลกite RL rotaciju.
  • 2C. If BF(node) = โˆ’2 i BF(right-child) = 0, izvrลกite RR rotaciju.

Brisanje u AVL stablima

C++ Primjer AVL stabala

Ispod je a C++ program koji implementira AVL stabla:

#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);
}

Primjer izvoฤ‘enja gornjeg koda:

  1. Kopirajte gornji kod i spremite ga u datoteku pod nazivom avl.cpp.
  2. Sastavite kod:
g++ avl.cpp -o run
  1. Pokrenite kod.
./run

C++ Primjer AVL stabala

Prednosti AVL stabala

  • Visina AVL stabla je uvijek uravnoteลพena i nikada ne raste iznad log N.
  • Pretraลพivanje je brลพe od obiฤnog binarnog stabla pretraลพivanja jer se stablo ne moลพe degenerirati.
  • Samobalansiranje je automatsko - nije potreban korak ponovne izgradnje.
  • Deterministiฤke performanse odgovaraju sustavima u stvarnom vremenu i indeksima u memoriji.

Pitanja i odgovori

AVL stablo je samobalansirajuฤ‡e binarno stablo pretraลพivanja gdje faktor ravnoteลพe svakog ฤvora ostaje u {-1, 0, +1}. Rotacije vraฤ‡aju ovu invarijantu pri svakom umetanju ili brisanju, zadrลพavajuฤ‡iping pretraลพivanje, umetanje i brisanje u O(log n).

Faktor ravnoteลพe ฤvora jednak je visini (lijevo podstablo) minus visini (desno podstablo). Vrijednosti moraju biti u rasponu {-1, 0, +1}. Faktor ravnoteลพe od +2 ili -2 signalizira da je umetanje ili brisanje poremetilo ravnoteลพu tog ฤvora i da je potrebna rotacija.

ฤŒetiri rotacije su LL, RR, LR i RL. LL koristi jednu desnu rotaciju, RR koristi jednu lijevu rotaciju, a LR i RL su dvostruke rotacije koje kombiniraju jednu rotaciju na podreฤ‘enom ฤvoru sa suprotnom rotacijom na ฤvoru.

Umetanje slijedi standardno BST pravilo, a zatim se stablo vraฤ‡a natrag prema gore aลพurirajuฤ‡i visine. Ako bilo koji predak prekrลกi pravilo ravnoteลพe, jedna jednostruka ili dvostruka rotacija vraฤ‡a ravnoteลพu. Potrebna je najviลกe jedna rotacija po umetanju.

AVL stabla su strogo uravnoteลพena s faktorom ravnoteลพe od najviลกe jedan, ลกto omoguฤ‡uje brลพe pretraลพivanje. Crveno-crna stabla omoguฤ‡uju labaviji balans, ลกto unos i brisanje ฤini jeftinijim, ali pretraลพivanje neลกto sporijim. Baze podataka preferiraju crveno-crna stabla za optereฤ‡enja s velikim brojem zapisa.

AVL stabla pokreฤ‡u indekse baza podataka u memoriji, metapodatke datoteฤnog sustava, redove prioriteta, pretraลพivanja telefonskog imenika, provjere pravopisa i bilo koje optereฤ‡enje koje zahtijeva deterministiฤko O(log n) pretraลพivanje plus prolazak po redoslijedu za upite raspona.

Da. AI sustavi koriste AVL stabla za tablice simbola, ureฤ‘ene pohrane znaฤajki, balansiranje kd stabla i pretraลพivanja najbliลพeg susjeda na strukturiranim podacima. Takoฤ‘er podupiru rangirane indekse pretraลพivanja u inteligentnim cjevovodima pretraลพivanja.

Da. GitHub Copilot i sliฤni AI asistenti scaffoldiraju rutine umetanja, brisanja i rotacije u C++, Java, ili Pythoni generirati jediniฤne testove koji provjeravaju invarijantnost faktora ravnoteลพe na svakoj operaciji.

Saลพmite ovu objavu uz: