This shows you the differences between two versions of the page.
sd-ca:laboratoare:laborator-11 [2015/05/16 13:11] mihai.neacsu2901 [Laborator 11 - Heap-uri] Update responsabili |
sd-ca:laboratoare:laborator-11 [2015/05/21 05:57] (current) mihai.neacsu2901 [Aplicaţii] |
||
---|---|---|---|
Line 11: | Line 11: | ||
* folosească heap-ul pentru a implementa o metodă de sortare eficientă. | * folosească heap-ul pentru a implementa o metodă de sortare eficientă. | ||
===== Moduri de reprezentare a arborilor ===== | ===== Moduri de reprezentare a arborilor ===== | ||
- | În laboratorul precedent am considerat arborii binari ca fiind o înlănţuire de structuri, legate între ele prin pointeri la descendenţii stâng, respectiv drept. Această reprezentare are avantajul flexibilităţii şi a posibilităţii de a creşte sau micşora dimensiunea arborelui oricât de mult, cu un efort minim. Cu toate acestea, această metodă nu poate fi folosită atunci când este nevoie de o reprezentare compactă a arborelui în memorie (de exemplu pentru stocarea într-un fişier), pentru că acei pointeri nu sunt valizi decât în cadrul programului curent. | + | În laboratorul precedent am considerat arborii binari ca fiind o înlănţuire de structuri, legate între ele prin pointeri la descendenţii stâng, respectiv drept. Această reprezentare are avantajul flexibilităţii şi a posibilităţii de a creşte sau micşora dimensiunea arborelui oricât de mult, cu un efort minim. Cu toate acestea, metoda precedentă nu poate fi folosită atunci când este nevoie de o reprezentare compactă a arborelui în memorie (de exemplu pentru stocarea într-un fişier), pentru că acei pointeri nu sunt valizi decât în cadrul programului curent. |
Din acest motiv, există câteva moduri de a stoca arborii într-o structura liniară de date (vectori), dintre care: | Din acest motiv, există câteva moduri de a stoca arborii într-o structura liniară de date (vectori), dintre care: | ||
- | *Înlocuirea pointer-ilor din structurile asociate nodurilor cu întregi ce reprezintă indici într-un vector de astfel de structuri. Primul element din vector va fi rădăcina arborelui, şi va exista un contor curent (la nivelul întregului vector) care indică următoarea poziţie liberă din vector. Atunci când un nod trebuie adăugat în arbore, i se va asocia valoarea curentă a contorului, iar acesta va fi incrementat. Nodul părinte va conţine indicele nodului în vector, în locul adresei lui în memorie (practic acesta este un mic mecanism de alocare de memorie, pe care îl gestionăm noi). | + | *Înlocuirea pointer-ilor din structurile asociate nodurilor cu întregi ce reprezintă indici într-un vector de astfel de structuri. Primul element din vector va fi rădăcina arborelui, şi va exista un contor curent (la nivelul întregului vector) care indică următoarea poziţie liberă. Atunci când un nod trebuie adăugat în arbore, i se va asocia valoarea curentă a contorului, iar acesta va fi incrementat. În nodul părinte se va reţine indicele **în vector** al noului nod, în locul adresei lui în memorie (practic acesta este un mic mecanism de alocare de memorie, pe care îl gestionăm noi). |
*Eliminarea totală a informaţiei legate de predecesori, şi folosirea unei formule de calcul a părintelui si a descendenţilor unui nod pe baza indicelui acestuia în vector. | *Eliminarea totală a informaţiei legate de predecesori, şi folosirea unei formule de calcul a părintelui si a descendenţilor unui nod pe baza indicelui acestuia în vector. | ||
Pentru un arbore binar, cea de-a doua modalitate se implementează conform figurii de mai jos: | Pentru un arbore binar, cea de-a doua modalitate se implementează conform figurii de mai jos: | ||
+ | |||
{{:sd-ca:laboratoare:heap.jpg}} | {{:sd-ca:laboratoare:heap.jpg}} | ||
Line 24: | Line 25: | ||
Reprezentarea liniara (sub formă de vector) pentru un arbore binar complet devine: | Reprezentarea liniara (sub formă de vector) pentru un arbore binar complet devine: | ||
+ | |||
{{:sd-ca:laboratoare:2000px-binary_tree_in_array.svg.png?400}} | {{:sd-ca:laboratoare:2000px-binary_tree_in_array.svg.png?400}} | ||
Line 41: | Line 43: | ||
<code c>H[Parinte(x)] <= H[x]</code> | <code c>H[Parinte(x)] <= H[x]</code> | ||
- | unde H[x] reprezintă valoarea nodului x, din vectorul H asociat arborelui. | + | **H[x]** reprezintă valoarea nodului **x**, din vectorul H asociat arborelui. |
În mod similar, un max-heap are semnul inegalităţii inversat. Astfel, putem defini şi recursiv proprietatea de heap pentru orice (sub)arbore: | În mod similar, un max-heap are semnul inegalităţii inversat. Astfel, putem defini şi recursiv proprietatea de heap pentru orice (sub)arbore: | ||
Line 48: | Line 50: | ||
Pentru a implementa operaţiile de inserare, ştergere, etc. pentru un heap, vom avea nevoie mai întâi de două operaţii elementare: | Pentru a implementa operaţiile de inserare, ştergere, etc. pentru un heap, vom avea nevoie mai întâi de două operaţii elementare: | ||
- | *''pushDown'', care presupune că heap-ul a fost modificat într-un singur nod, şi noua valoare este mai mare decât cel puţin unul dintre descendenţi, şi astfel ea trebuie "cernută" către nivelurile de jos, până când heap-ul devine din nou valid. | + | *''pushDown'', care presupune că heap-ul a fost modificat într-un singur nod şi noua valoare este mai mare decât cel puţin unul dintre descendenţi, şi astfel ea trebuie "cernută" către nivelurile de jos, până când heap-ul devine din nou valid. |
- | *''pushUp'', care presupune că valoarea modificată (sau adăugată la sfârşitul vectorului, în acest caz) este mai mică decât părintele şi astfel ea propagă acea valoare spre rădăcina arborelui, până cand heap-ul devine valid. | + | *''pushUp'', care presupune că valoarea modificată (sau adăugată la sfârşitul vectorului, în acest caz) este mai mică decât părintele, şi astfel se propagă acea valoare spre rădăcina arborelui, până cand heap-ul devine valid. |
===== Operaţii uzuale asupra heap-ului ===== | ===== Operaţii uzuale asupra heap-ului ===== | ||
Având implementate cele două operaţii de bază, putem defini operaţiile uzuale de manipulare a heap-urilor: | Având implementate cele două operaţii de bază, putem defini operaţiile uzuale de manipulare a heap-urilor: | ||
Line 117: | Line 119: | ||
</code> | </code> | ||
===== Aplicaţii ===== | ===== Aplicaţii ===== | ||
- | Porniți exercițiile de la {{:sd-ca:laboratoare:sd_schelet_cod_lab10.zip | scheletul de cod}} oferit. | + | Porniți exercițiile de la {{:sd-ca:laboratoare:labheap-tasks.zip | scheletul de cod}} oferit. |
Modificați **main.cpp**, adăugând și testând toate operațiile din clasa Heap. | Modificați **main.cpp**, adăugând și testând toate operațiile din clasa Heap. | ||
Line 141: | Line 143: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | 2. [3p] Implementaţi operaţiile elementare de lucru cu heap-uri, prezentate în secţiunile anterioare: | ||
+ | |||
+ | *[1p] Implementati functiile de calcul ai parintelui si ai descendentilor. | ||
+ | |||
+ | <code c++> | ||
+ | template <typename T> | ||
+ | int Heap<T>::parent(int poz) | ||
+ | { | ||
+ | // TODO 2.1 | ||
+ | } | ||
+ | |||
+ | template <typename T> | ||
+ | int Heap<T>::leftSubtree(int poz) | ||
+ | { | ||
+ | // TODO 2.1 | ||
+ | } | ||
+ | |||
+ | template <typename T> | ||
+ | int Heap<T>::rightSubtree(int poz) | ||
+ | { | ||
+ | // TODO 2.1 | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Cele trei funcţii de mai sus vor întoarce -1 în cazul în care părintele, respectiv descendenţii nu există. | ||
+ | |||
+ | * [2p] Implementati pushUp si pushDown. | ||
+ | <code c++> | ||
+ | template <typename T> | ||
+ | void Heap<T>::pushUp(int poz) | ||
+ | { | ||
+ | // TODO 2.2 | ||
+ | | ||
+ | } | ||
+ | |||
+ | template <typename T> | ||
+ | void Heap<T>::pushDown(int poz) | ||
+ | { | ||
+ | // TODO 2.2 | ||
+ | | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | 3. [1p] Implementaţi operaţiile uzuale de lucru cu heap-uri: | ||
+ | |||
+ | <code c++> | ||
+ | template <typename T> | ||
+ | void Heap<T>::insert(T x) | ||
+ | { | ||
+ | // TODO 3 | ||
+ | } | ||
+ | |||
+ | template <typename T> | ||
+ | T Heap<T>::peek() | ||
+ | { | ||
+ | // TODO 3 | ||
+ | } | ||
+ | |||
+ | template <typename T> | ||
+ | T Heap<T>::extractMin() | ||
+ | { | ||
+ | // TODO 3 | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | 4. [2p] **p4.cpp** Implementaţi algoritmul de sortare folosind heap-uri, alegând una dintre cele două metode prezentate mai sus. Testați implementarea voastră a sortării rulând scriptul de testare ''test.sh''. | ||
+ | |||
+ | **Obs.:** | ||
+ | * Se va citi întâi numărul **n** de elemente, iar apoi **n** numere care trebuie sortate. | ||
+ | * Citirea se face de la **stdin**. Nu modificați afișarea! Afișarea are formatul folosit pentru script-ul de testing. | ||
+ | **BONUS!** [1p] Implementaţi şi celelalta metoda de sortare prin heap-uri, în afară de cea aleasă iniţial. Pentru testare se va modifica doar funcția ''heapSort'' și se va executa tot scriptul ''test.sh''. | ||
+ | |||
<hidden> | <hidden> | ||
2. [3p] Implementaţi operaţiile elementare de lucru cu heap-uri, prezentate în secţiunile anterioare: | 2. [3p] Implementaţi operaţiile elementare de lucru cu heap-uri, prezentate în secţiunile anterioare: |