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

ล to su AVL stabla?

AVL stabla su binarna stabla pretraลพivanja u kojima je razlika izmeฤ‘u visine lijevog i desnog podstabla ili -1, 0 ili +1.

AVL stabla se takoฤ‘er nazivaju samobalansirajuฤ‡im binarnim stablom pretraลพivanja. Ova stabla pomaลพu u odrลพavanju logaritamskog vremena pretraลพivanja. Ime je dobio po svojim izumiteljima (AVL) Adelsonu, Velskyju i Landisu.

Kako radi AVL stablo?

Kako bismo bolje razumjeli potrebu za AVL stablima, pogledajmo neke nedostatke jednostavnih stabala binarnog pretraลพivanja.

Razmotrite sljedeฤ‡e kljuฤeve umetnute danim redoslijedom u stablo binarnog pretraลพivanja.


 AVL rad na stablu
Vizualizacija AVL stabla

Visina stabla linearno raste kada umetnemo kljuฤeve rastuฤ‡im redoslijedom njihove vrijednosti. Dakle, operacija pretraลพivanja, u najgorem sluฤaju, traje O(n).

Za traลพenje elementa potrebno je linearno vrijeme; stoga nema koristi od koriลกtenja Stablo binarnog pretraลพivanja struktura. S druge strane, ako je visina stabla uravnoteลพena, dobivamo bolje vrijeme traลพenja.

Pogledajmo sada iste kljuฤeve, ali umetnute drugaฤijim redoslijedom.

AVL rad na stablu

Ovdje su kljuฤevi isti, ali buduฤ‡i da su umetnuti drugaฤijim redoslijedom, zauzimaju razliฤite poloลพaje, a visina stabla ostaje uravnoteลพena. Stoga pretraลพivanje neฤ‡e trajati viลกe od O(log n) za bilo koji element stabla. Sada je oฤito da ako se umetanje pravilno izvede, visina stabla moลพe biti uravnoteลพena.

U AVL stablima provjeravamo visinu stabla tijekom operacije umetanja. Izmjene su napravljene kako bi se odrลพala uravnoteลพena visina bez naruลกavanja osnovnih svojstava stabla binarnog pretraลพivanja.

Faktor ravnoteลพe u AVL stablima

Faktor ravnoteลพe (BF) temeljni je atribut svakog ฤvora u AVL stablima koji pomaลพe u praฤ‡enju visine stabla.

Svojstva faktora ravnoteลพe


Faktor ravnoteลพe u AVL stablima

Faktor ravnoteลพe AVL stablo
  • Faktor ravnoteลพe poznat je kao razlika izmeฤ‘u visine lijevog podstabla i desnog podstabla.
  • Faktor ravnoteลพe (ฤvor) = visina (ฤvor->lijevo) โ€“ visina (ฤvor->desno)
  • Dopuลกtene vrijednosti BF su โ€“1, 0 i +1.
  • Vrijednost โ€“1 oznaฤava da desno pod-stablo sadrลพi jedno dodatno, tj. da je stablo desno teลกko.
  • Vrijednost +1 oznaฤava da lijevo podstablo sadrลพi jedno dodatno, tj. da je stablo lijevo teลกko.
  • Vrijednost 0 pokazuje da stablo ukljuฤuje jednake ฤvorove sa svake strane, tj. da je stablo savrลกeno uravnoteลพeno.

AVL rotacije

Kako bi se AVL stablo samo uravnoteลพilo, prilikom umetanja ili brisanja ฤvora iz stabla, izvode se rotacije.

Izvodimo sljedeฤ‡u LL rotaciju, RR rotaciju, LR rotaciju i RL rotaciju.

  • Lijevo โ€“ Lijeva rotacija
  • Desno โ€“ Desna rotacija
  • Rotacija desno โ€“ lijevo
  • Rotacija lijevo โ€“ desno

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 desna rotacija. Ova vrsta rotacije prepoznaje se kada ฤvor ima faktor ravnoteลพe +2, a njegov lijevi potomak ima faktor ravnoteลพe +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. Ova vrsta rotacije identificira se kada ฤvor ima faktor ravnoteลพe -2, a njegov desni potomak ima faktor ravnoteลพe -1.

Rotacija desno โ€“ lijevo

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

Rotacija AVL stabla desno โ€“ lijevo

Ova se rotacija izvodi kada ฤvor ima faktor ravnoteลพe kao โ€“2, a njegov desni dijete ima faktor ravnoteลพe kao +1.

Rotacija lijevo โ€“ desno

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

Rotacija AVL stabla lijevo โ€“ desno

Ova rotacija se izvodi kada ฤvor ima faktor ravnoteลพe kao +2, a njegov lijevi dijete ima faktor ravnoteลพe kao -1.

Umetanje u AVL stabla

Operacija umetanja gotovo je ista kao kod jednostavnih stabala binarnog pretraลพivanja. Nakon svakog umetanja balansiramo visinu stabla. Operacija umetanja traje O(log n) najgore vremenske sloลพenosti.


Umetanje u AVL stabla

Implementacija umetanja AVL stabla

Korak 1: Umetnite ฤvor u AVL stablo koristeฤ‡i isti algoritam umetanja kao BST. U gornjem primjeru umetnite 160.

Korak 2: Nakon dodavanja ฤvora, aลพurira se faktor ravnoteลพe svakog ฤvora. Nakon umetanja 160, faktor ravnoteลพe svakog ฤvora se aลพurira.

Korak 3: Sada provjerite krลกi li bilo koji ฤvor raspon faktora ravnoteลพe ako je faktor ravnoteลพe naruลกen, zatim izvedite rotacije koristeฤ‡i donji sluฤaj. U gornjem primjeru, faktor ravnoteลพe od 350 je naruลกen i tu postaje primjenjiv sluฤaj 1, izvodimo LL rotaciju i stablo je ponovno uravnoteลพeno.

  1. Ako je BF(ฤvor) = +2 i BF(ฤvor -> lijevo dijete) = +1, izvrลกite LL rotaciju.
  2. Ako je BF(ฤvor) = -2 i BF(ฤvor -> desno dijete) = 1, izvedite RR rotaciju.
  3. Ako je BF(ฤvor) = -2 i BF(ฤvor -> desno dijete) = +1, izvrลกite RL rotaciju.
  4. Ako je BF(ฤvor) = +2 i BF(ฤvor -> lijevo dijete) = -1, izvrลกite LR rotaciju.

Brisanje u AVL stablima

Brisanje je takoฤ‘er vrlo jednostavno. Briลกemo koristeฤ‡i istu logiku kao u jednostavnim stablima binarnog pretraลพivanja. Nakon brisanja, restrukturiramo stablo, ako je potrebno, kako bismo zadrลพali njegovu uravnoteลพenu visinu.

Korak 1: Pronaฤ‘ite element u stablu.

Korak 2: Izbriลกite ฤvor u skladu s BST brisanjem.

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

Sluฤaj 1: Brisanje iz desnog podstabla.

  • 1A. Ako je BF(ฤvor) = +2 i BF(ฤvor -> lijevo dijete) = +1, izvrลกite LL rotaciju.
  • 1B. Ako je BF(ฤvor) = +2 i BF(ฤvor -> lijevo dijete) = -1, izvrลกite LR rotaciju.
  • 1C. Ako je BF(ฤvor) = +2 i BF(ฤvor -> lijevo dijete) = 0, izvedite LL rotaciju.

Brisanje u AVL stablima

Sluฤaj 2: Brisanje iz lijevog podstabla.

  • 2A. Ako je BF(ฤvor) = -2 i BF(ฤvor -> desno dijete) = -1, izvrลกite RR rotaciju.
  • 2B. Ako je BF(ฤvor) = -2 i BF(ฤvor -> desno dijete) = +1, izvrลกite RL rotaciju.
  • 2C. Ako je BF(ฤvor) = -2 i BF(ฤvor -> desno dijete) = 0, izvedite RR rotaciju.

Brisanje u AVL stablima

C++ Primjer AVL stabala

Ispod je C++ 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
{
private:
    
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;
            }
    }

    struct node * llrotation(struct node *n){
        struct node *p;
        struct node *tp;
        p = n;
        tp = p->left;

        p->left = tp->right;
        tp->right = p;

        return tp; 
    }


    struct node * rrrotation(struct node *n){
        struct node *p;
        struct node *tp;
        p = n;
        tp = p->right;

        p->right = tp->left;
        tp->left = p;

        return tp; 
    }


    struct node * rlrotation(struct node *n){
        struct node *p;
        struct node *tp;
        struct node *tp2;
        p = n;
        tp = p->right;
        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;
        struct node *tp;
        struct node *tp2;
        p = n;
        tp = p->left;
        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){
            struct node *n;
            n = new struct node;
            n->data = data;
            r = n;
            r->left = r->right = NULL;
            r->height = 1; 
            return r;             
        }
        else{
            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<<"\n"<<"Empty 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 *t;
        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 = llrotation(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();
            // to print the tree in level order
            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);
  
}

Izvrลกeni primjer gornjeg koda:

  1. Kopirajte gornji kod i zalijepite u โ€œavl.cppโ€.
  2. Sastavite kod:
g++ avl.cpp -o run
  1. Pokrenite kod.
./run

C++ Primjer AVL stabala

Prednosti AVL stabala

  • Visina AVL stabla uvijek je uravnoteลพena. Visina nikada ne prelazi log N, gdje je N ukupan broj ฤvorova u stablu.
  • Omoguฤ‡uje bolju sloลพenost vremena pretraลพivanja u usporedbi s jednostavnim stablima binarnog pretraลพivanja.
  • AVL stabla imaju sposobnost samobalansiranja.

Rezime

  • AVL stabla su binarna stabla pretraลพivanja koja se sama balansiraju.
  • Faktor ravnoteลพe temeljni je atribut AVL stabala
  • Faktor ravnoteลพe ฤvora definiran je kao razlika izmeฤ‘u visine lijevog i desnog podstabla tog ฤvora.
  • Vaลพeฤ‡e vrijednosti faktora ravnoteลพe su -1, 0 i +1.
  • Operacije umetanja i brisanja zahtijevaju izvoฤ‘enje rotacija nakon naruลกavanja faktora ravnoteลพe.
  • Vremenska sloลพenost operacije umetanja, brisanja i pretraลพivanja je O(log N).
  • AVL stabla slijede sva svojstva stabala binarnog pretraลพivanja.
  • Lijevo podstablo ima ฤvorove koji su manji od korijenskog ฤvora. Desno podstablo ima ฤvorove koji su uvijek veฤ‡i od korijenskog ฤvora.
  • AVL stabla se koriste tamo gdje je operacija pretraลพivanja ฤeลกฤ‡a u usporedbi s operacijama umetanja i brisanja.

Saลพmite ovu objavu uz: