This shows you the differences between two versions of the page.
sda-ab:laboratoare:07 [2020/12/24 13:03] ruben_gilian.udroiu |
sda-ab:laboratoare:07 [2021/04/04 14:45] (current) leonard.necula [1. Obiectivele laboratorului] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 07: BST si Tries ===== | + | ===== Laboratorul 6: BST si AVL ===== |
+ | |||
+ | ====== 1. Obiectivele laboratorului ====== | ||
+ | |||
+ | *Intelegerea notiunii de arbore binar de cautare (BST) | ||
+ | *Intelegerea notiunii de arbore binar de cautare echilibrat (AVL) | ||
+ | |||
+ | |||
+ | Structura laboratorului se gaseste in **[[https://github.com/sda-ab/lab-06-tasks|acest link.]]** | ||
+ | |||
+ | ====== 2. BST (Binary Search Tree) ====== | ||
+ | |||
+ | BST este un arbore binar cu o structură specială adaptată căutărilor eficiente, având următoarele proprietăți: | ||
+ | *se dă un nod rădăcină (numit si **root**) | ||
+ | *în subarborele său stâng sunt stocate valori **mai mici** decât cea din rădăcină | ||
+ | *în subarborele său drept sunt stocate valori **mai mari** decât cea din rădăcină | ||
+ | |||
+ | <note warning>Într-un BST **NU** apar valori duplicat, iar criteriul/valoarea în funcție de care se face sortarea se mai numește și **cheie**(key).Cheile sunt alese în așa fel încât proprietatea BST sa se verifice la fiecare nivel.</note> | ||
+ | |||
+ | **Operații specifice BST** | ||
+ | *Inserarea/Căutarea/Ștergerea unui element | ||
+ | *Găsirea predecesorului/succesorului unui element | ||
+ | *Afișarea datelor stocate in forma sortată | ||
+ | *Găsirea elementului min/max/al k-lea ca valoare | ||
+ | |||
+ | {{ :sda-ab:laboratoare:binary-search-tree-example.png?400 |}} | ||
+ | <note>Se poate observa că parcurgerea în **inordine(SRD)** a unui BST va furniza un șir de numere ordonat crescător.</note> | ||
+ | |||
+ | === 2.1. Inserarea unui element într-un BST === | ||
+ | |||
+ | Operația de inserarea a unui element într-un BST adaugă elementul ca frunza a arborelui, iar mai apoi nodul cu cheia data va fi inserat ca fiu stâng sau drept in așa fel încât sa se respecte proprietatea BST. | ||
+ | <code c> | ||
+ | Node* newNode(Data data) | ||
+ | { Node* node = (Node*)malloc(sizeof(Node)); | ||
+ | node->val = data; | ||
+ | node->left = node->right = NULL; | ||
+ | return node; | ||
+ | } | ||
+ | |||
+ | Node* insert(Node* node, int key) | ||
+ | { // daca (sub)arborele e gol – se creeaza un nod | ||
+ | //si se returneaza adresa | ||
+ | if (node == NULL) return newNode(key); | ||
+ | |||
+ | //altfel se coboara la stanga sau dreapta in arbore | ||
+ | if (key < node->val) node->left = insert(node->left, key); | ||
+ | else if (key > node->val) | ||
+ | node->right = insert(node->right, key); | ||
+ | //pointerul node se intoarce nemodificat pe acest return | ||
+ | return node; | ||
+ | } //nu se adauga elemente egale | ||
+ | </code> | ||
+ | |||
+ | {{ :sda-ab:laboratoare:binary-search-tree-insertion-animation.gif?400 |}} | ||
+ | |||
+ | === 2.2. Căutarea unui element într-un BST === | ||
+ | |||
+ | *se începe cu rădăcina, daca valoarea căutata este chiar rădăcina - return | ||
+ | *daca valoarea cautata < cea stocata in rădăcină – se caută in subarborele stâng, altfel in cel drept. | ||
+ | *cautarea are loc pana la gasirea elementului sau pana se ajunge la NULL. | ||
+ | <code c> | ||
+ | Node* search(Node* root, int key) { | ||
+ | //daca radacina e null sau s-a gasit elementul | ||
+ | if (root == NULL || root->val == key) | ||
+ | return root; | ||
+ | //daca valoarea stocata in radacina e mai mica decat cheia | ||
+ | if (root->val < key) return search(root->right, key); | ||
+ | // daca valoarea stocata in radacina e mai mare decat cheia | ||
+ | return search(root->left, key); | ||
+ | } //daca nu se gaseste nod cu aceasta cheie se returneaza NULL | ||
+ | |||
+ | </code> | ||
+ | |||
+ | === 2.3. Ștergerea unui element într-un BST === | ||
+ | |||
+ | La partea de ștergere, trebuie avut în vedere: | ||
+ | *Dacă nodul este **frunză** - se elibereaza spatiul ocupat si se seteaza legatura parintelui catre acel nod cu NULL | ||
+ | {{ :sda-ab:laboratoare:stergere_bst_1.png?400 |}} | ||
+ | *Dacă nodul are doar un subarbore - nodul e scos din arbore și singurul sau copil (subarbore) e legat direct la parintele său (**OBS**- trebuie actualizat pointerul la stânga sau dreapta din parintele nodului de șters ca să pointeza catre subarborele nodului de șters) | ||
+ | {{ :sda-ab:laboratoare:stergere_bst_2.png?400 |}} | ||
+ | *Dacă nodul are doi subarbori - se bazeaza pe ideea că același set de valori poate fi reprezentat folosind 2 BST diferiți în așa fel încât să ne reducem la unul dintre cele două cazuri de mai sus. | ||
+ | {{ :sda-ab:laboratoare:stergere_bst_3_1.png?400 |}} | ||
+ | <note tip>Idee : Se alege valoarea minimă din subarborele drept, se interschimbă cu valoarea rădăcinii, iar apoi se reface arborele pentru a păstra proprietatea BST</note> | ||
+ | {{ :sda-ab:laboratoare:stergere_bst_3_3.png?400 |}} | ||
+ | <note tip>Pentru a șterge nodul cu valoarea 12, se caută valoarea minimă in subarborele drept(prin inordine(SRD)), se înlocuieste cu această valoarea,iar apoi valoarea duplicat este ștearsă, ajungându-se la cazul II, în care nodul cu valoarea actualizată 19 are un singur subarbore.</note> | ||
+ | |||
+ | ====== 3. AVL (Adelson-Velsky and Landis tree) ====== | ||
+ | |||
+ | Un **AVL** este un arbore binar de căutare echilibrat.(Proprietatea de echilibru trebuie respectata pentru fiecare nod - înălțimea | ||
+ | subarborelui stâng al nodului diferă de inaltimea subarborelui drept al nodului prin cel mult o unitate). De asemenea, trebuie păstrată si condiția de BST. | ||
+ | <note> | ||
+ | Arbore binar **perfect** - fiecare nod are exact 2 copii si toate frunzele sunt la acelasi nivel. | ||
+ | |||
+ | Arbore binar **complet** - un arbore binar care este complet umplut cu posibila exceptie a ultimului nivel care este umplut de la stanga la dreapta | ||
+ | |||
+ | **OBS!** - arborii compleți sau perfecți sunt implicit echilibrați | ||
+ | |||
+ | </note> | ||
+ | === 3.1 Reprezentarea unui nod AVL === | ||
+ | |||
+ | <code c> | ||
+ | typedef int Data; | ||
+ | typedef struct N Node; | ||
+ | |||
+ | struct N { | ||
+ | int height; //inaltimea | ||
+ | Data val; // datele efective memorate | ||
+ | struct N *left,*right; // legatura catre subarbori | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | **OBS** - înălțimea unui AVL este floor(log N), iar orice **cautare/adaugare/stergere/gasire min/max de element** se va realiza cu complexitatea **O(log N)** - cost amortizat pe mai multe operații. | ||
+ | <note warning>**Adăugarea/Ștergerea** unui nod poate conduce la modificarea conditiei de echilibru, de aceea se folosesc tehnici de **rotație** pentru a reface proprietatea AVL</note> | ||
+ | |||
+ | === 3.2 Inserarea unui nod in AVL === | ||
+ | |||
+ | Operația constă în inserția unui nod într-un BST, urmată de reechilibrarea arborelui. | ||
+ | Există doua tipuri de rotații (care pastrează proprietatea BST): | ||
+ | *rotație la dreapta | ||
+ | *rotație la stânga | ||
+ | {{ :sda-ab:laboratoare:tree_rotation.gif?300 |}} | ||
+ | |||
+ | === 3.2.1 Rotații simple === | ||
+ | |||
+ | ***RR** - x copilul drept al lui y, y copilul drept al lui z | ||
+ | {{ :sda-ab:laboratoare:avl_rr.png?300 |}} | ||
+ | ***LL** - x este copilul stâng al lui y, y copilul stâng al lui z | ||
+ | {{ :sda-ab:laboratoare:avl_ll.png?300 |}} | ||
+ | |||
+ | === 3.2.1 Rotații duble === | ||
+ | ***LR** - y e copilul stang al lui z si x e copilul drept al lui y - rotație la stanga + rotație la dreapta | ||
+ | {{ :sda-ab:laboratoare:avl_lr.png?400 |}} | ||
+ | ***RL** - y e copilul drept al lui z si x e copilul stâng al lui y – rotașie dreapta +rotație stânga | ||
+ | {{ :sda-ab:laboratoare:avl_rl.png?400 |}} | ||
+ | |||
+ | <note>**OBS** Ștergerea unui nod se bazeaza pe cea pentru BST, cu updatarea înălțimii și rebalansarea făcută exact ca in cazul inserării unui nod </note> | ||
+ | |||