This is an old revision of the document!
Responsabili
În urma parcurgerii articolului, studentul va fi capabil să:
Diferență între 2 subarbori ai oricărui nod este maxim 1. Se definește:
Avantajul unui AVL Tree este faptul că produce cel mai echilibrat arbore în cazul cel mai defavorabil. Fiind perfect balansat, arborele AVL va scoate cel mai mic timp de căutare dintre toţi ceilalţi arbori. Dezavantajul său este numărul mai mare de rotaţii pe care îl efectuează.
La inserare se adaugă nodul astfel încât să aibă proprietatea de arbore binar de căutare, iar după se verifică factorul de balansare și se începe sau nu balansarea lui. Balansarea lui se face cu rotații duble.
Cele 4 tipuri de rotații (LL LR RL RR):
Puteți urmări încă un exemplu aici.
Structura de mai jos este cea folosită de noi pentru reprezentarea AVL-ului din scheletul de laborator.
/** * The AVL node struct definition */ typedef struct avl_node_t avl_node_t; struct avl_node_t { /* left child - smaller key */ avl_node_t *left; /* right child - bigger key */ avl_node_t *right; /* the key of the node which will also be used for sorting */ char *key; /* height of the node */ int height; }; /** * The AVL tree struct definition */ typedef struct avl_tree_t avl_tree_t; struct avl_tree_t { /* root of the tree */ avl_node_t *root; /* function used for sorting the keys */ int (*cmp)(const void *key1, const void *key2); };
Observăm absenţa pointer-ului către nodul părinte, fapt ce ne va cere să lucrăm cu (**node) pentru a putea efectua rotaţiile corespunzător.
Inserarea unui nod într-un AVL se realizează în 2 etape şi este asemănătoare inserării unui nod pentru un Treap:
Mai jos avem pseudocodul pentru operaţia de inserare:
void insert(nod, cheie) { if (nod == NULL) { node = create_node(cheie) } else if (nod->cheie > cheie) { insert(nod->left, cheie) } else { insert(nod->right, cheie) } nod->inaltime = 1 + max(intaltime(nod->left), inaltime(nod->right) aplicare_rotiri() }
Operaţia de ştergere pentru o cheie dintr-un arbore AVL se aseamănă cu ştergerea unei chei dintr-un BST. Vom elimina nodul pe care dorim să îl ştergem în momentul în care acesta ajunge la baza arborelui. Următorul pas este să reactualizăm înălţimile fiecărui nod şi să efectuăm rotiri atunci când este necesar. Astfel, putem rezuma stergerea unui nod dintr-un AVL în 3 etape:
1. Căutarea cheii pe care dorim să o ştergem.
2. Ştergerea nodului cu cheia respectivă. Dacă nodul are 2 succesori, îl vom înlocui cu cel mai mare nod din subarborele stâng (sau cel mai mic nod din subarborele drept). Altfel, nodul va fi înlocuit cu unul dintre succesorii săi nenuli (sau NULL, în cazul în care nodul este frunză).
3. Reactualizarea înălţimilor şi realizarea rotaţiilor necesare.
Mai jos avem pseudocodul pentru operaţia de ştergere:
void avl_delete(nod, cheie) { if (nod == NULL) { return } if (nod->cheie > cheie) { avl_delete(node->left, cheie) } else if (nod->cheie < cheie) { avl_delete(node->right, cheie) } else { if (node are 0 succesori) { stergere(node) return; } else if (node are 1 succesor) { node = succesor(node) } else { max_node = max_element(node->left); copy(node, max_node) avl_delete(node->left, max_node->cheie) } } nod->inaltime = 1 + max(intaltime(nod->left), inaltime(nod->right)) aplicare_rotiri() }
Un arbore roșu-negru este un arbore binar de căutare care are un bit suplimentar pentru memorarea fiecărui nod: culoarea acestuia, care poate fi roșu sau negru. Prin restrângerea modului în care se colorează nodurile pe orice drum de la rădăcină la o frunză, arborii roșu-negru garantează că nici un astfel de drum nu este mai lung decât dublul lungimii oricărui alt drum, deci că arborele este aproximativ echilibrat.
Un arbore binar de căutare este arbore roșu-negru dacă el îndeplineste următoarele proprietăți:
Mai multe detalii, aici:
Cu toate acestea, ambele tipuri de arbori discutate in acest articol sunt foarte populare si folosite.
Arborii Red-Black sunt foarte folosiți în cazuri generale, unde se descurcă binișor la toate capitolele, în timp ce arborii AVL sunt folosiți în domeniul bazelor de date, unde timpul pentru o căutare reprezintă un factor foarte important.
Cazuri concrete unde este utilizat Red-Black Tree:
enum COLOR { RED, BLACK }; /** * The Red-Black node struct definition */ typedef struct rb_node_t rb_node_t; struct rb_node_t { /* parent - RB_NODE_NULL for root */ rb_node_t *parent; /* left child - smaller key */ rb_node_t *left; /* right child - bigger key */ rb_node_t *right; /* the sorting is based on key */ void *key; /* data contained by the node */ void *data; /* color of the node */ enum COLOR color; }; /** * The Red-Black tree struct definition */ typedef struct rb_tree_t rb_tree_t; struct rb_tree_t { /* root of the tree */ rb_node_t *root; /* key size */ size_t key_size; /* data size */ size_t data_size; /* function used for sorting the keys */ int (*cmp)(const void *key1, const void *key2); };
1) [45p] Completați funcția de reechilibrare și implementați funcțiile de inserare, căutare și ștergere pentru AVL.
2) [45p] Completați părțile de cod care lipsesc din funcțiile din rb_tree.c.