Laboratorul 7: Heap si tabele de dispersie
Obiective
În urma parcurgerii acestui laborator studentul va fi capabil:
Structura laboratorului se gaseste in acest link.
1. Heap binar
Proprietați:
valoarea unui nod este mai mare (max heap)/mică (min heap) decât valorile tuturor copiilor săi
toate frunzele se găsesc pe nivelul h sau h-1, unde h este înălțimea arborelui
un heap binar este un arbore complet
Structura Heap:
- structura_heap.c
typedef struct
{
int *vec;
int size;
int capacity;
int type; // Tipul heap-ului, camp optional
}Heap;
Operații de bază:
1.1 Crearea unei structuri de tip Heap
- creare_heap.c
Heap* create(int capacitate)
{
Heap *h = (Heap *) malloc(sizeof(Heap));
if (h == NULL)
{
printf("Nu s-a putut aloca spatiu");
return;
}
h->size = 0; // Initial heap-ul este gol
h->capacity = capacitate;
h->vec = (int*) calloc(capacitate, sizeof(int));
if (h->vec == NULL)
{
printf("Nu s-a putut aloca spatiu");
return;
}
return h;
}
1.2 Popularea unui Heap
- populare_heap.c
void populateHeap(Heap *h, int *buf, int n)
{
if(h == NULL || buf == NULL || n ==0)
return;
while(n > h->capacity)
resize(h);
int i = 0;
for( i = 0; i < n; i++)
h->vec[i] = buf[i];
h->size = n;
for(i = (n-1)/2; i >= 0; i--)
heapify(h, i);
}
Etape:
se crează un heap cu o capacitate suficient de mare pentru stocarea elementelor
se mută elementele vectorului in spațiul de stocare al heap-ului
se aplică heapify pe toate nodurile interne
1.3 Adăugarea unui element
- adaugare_element.c
void insert(Heap *h, int x)
{
int i;
if(h->capacity == h->size)
resize(h);
i = h->size;
h->size++;
while(i >= 0 && x > h->vec[parent(h,i)])
{
h->vec[i] = h->vec[parent(h,i)];
i = parent(h,i);
}
h->vec[i] = x;
}
Etape:
se verifică dacă avem suficient spațiu in heap, iar dacă nu avem se face o realocare
se adaugă elementul la finalul heap-ului și se compară cu parintele său
dacă este mai mare se interschimbă și se reia etapa 2
1.4 Ștergerea unui Element
Etape:
se mută ultimul element pe poziția elementului de șters
se aplică algoritmul heapify down pe elementul modificat
1.5 Heapsort
Etape:
se redimensionează heap-ul corespunzător
se populează heap-ul cu valorile vectorului pe care dorim să-l sortăm
se aplică heapify pe toate nodurile interne
se interschimbă primul element cu ultimul
se scade dimensiunea heap-ului
se aplică heapify pe poziția 0, apoi se reia până când epuizăm toate elementele
Probleme propuse
Implementați ștergerea unui element dintr-un heap.
Implementați metoda de sortare heapsort.
Aflați eficient cele mai mari/mici k elemente dintr-un vector folosind un heap.
Metoda I: Folositi un min heap de dimensiune k pentru a stoca primele k elemente din vectorul primit ca parametru, apoi parcurgeti restul elementelor din vector. Daca elementul curent este mai mare decat varful heap-ului adaugati elementul in heap in locul varfului. Atentie: Asigurati-va ca dupa ce adaugati elementul refaceti proprietatea de min heap!
Metoda II: Metoda II: Creati un min heap folosind valorile opuse ale elementelor din vectorul primit ca parametru, eliminati pe rand varful heap-ului de k ori, numerele rezultate vor fi numerele cu semn schimbat ale solutiei.
Fie un vector k-sortat de dimensiune n, sortați eficient vectorul k-sortat. Un vector se numeste k-sortat daca fiecăre element se află la maxim k poziții distanță de poziția sa din cadrul vectorului sortat.
Hint: Creati un min-heap de dimensiune k+1, populati-l cu primele k+1 elemente, apoi parcurgeti restul elementelor, la fiecare pas stocati varful in vectorul solutie si adaugati urmatorul element din vector in heap. Nu uitati ca la final sa adaugati si elementele ramase in heap in vectorul solutie
Fie M vectori sortați de diferite dimensiuni. Interclasați vectorii eficient folosind un heap.
Metoda I: Implementati un min heap in care fiecare element este o pereche (valoarea, index_vector). Creati un min heap de acest tip de dimensiune M, introduceti primul element din fiecare vector. La fiecare pas din cadrul algoritmului eliminati radacina din heap, introduceti valoarea in vectorul solutie si adaugati in heap urmatorul element din vectorul corespunzator elementului abia eliminat.
Metoda II: Implementati un min heap (vezi curs), folosind elemente de tip min heap implementati un heap in care fiecare element este un heap de sine statator. Pentru simplitate vom numii heap-ul de heap-uri heap global, iar heap-urile din acesta heap-uri interne. Creati un heap global de dimensiune M si populati fiecare heap intern cu cate unul dintre vectorii sortati, aplicati heapify pe toate nodurile interne ale heap-ului global. Cat timp heap-urile interne inca mai au elemente faceti urmatoarele operatii:
Extrageti radacina radacinii heap-ului global si stocati valoarea in vectorul solutie
Aplicati heapify pe radacina heap-ului global
Aplicati heapify pe heap-ul global
2. Hashtable
O tabelă de dispersie (hashtable) este o structură de date ce realizează o mapare de forma cheie - valoare. Această formă de stocare facilitează accesul rapid la date.
2.1 Tratarea Coliziunilor
Dispersie deschisă(Chaining)
Dispersie închisă(Open addressing)
2.1.1 Dispersia deschisă
În cazul dispersiei deschise se folosesc liste înlănțuite pentru stocarea elementelor cu aceiași cheie. În cel mai nefavorabil caz toate elementele sunt puse la același index rezultând o complexitate O(n) pentru operațiile efectuate.
Mai jos aveți un exemplu de tabelă de dispersie în care coliziunile se tratează prin înlănțuire, iar funcția hash este f(x) = x % 7:
În cazul unei dispersii deschise este foarte important factorul de încărcare a = N/M unde:
2.1.2 Dispersia închisă
În cazul dispersiei închise toate elementele se memorează în tablea T, iar la producerea coliziunilor se verifică alte celule, cel mai simplu mod de rezolvare al coliziunilor este cel în care se caută prima celula liberă pentru inserare, dar se pot utiliza si alte metode.
Mai jos aveți un exemplu de tabelă de dispersie în care coliziunile se tratează liniar, iar funcția hash este f(x) = x % 7:
Probleme propuse
Fie structura din josul paginii. Considerați că trebuie stocate pentru căutări/adăugări/ștergeri cât mai rapide 10.000 de înregistrări de tip Student.Implementați folosind:
Tabelă de dispersie cu înlănțuire
Tabelă de dispersie cu adresare închisă
Găsiți toate perechile de mulțimi simetrice dintr-un vector de mulțimi de 2 elemente. Două mulțimi sunt simetrice dacă capătul din stanga al primei mulțimi este egal cu cel din dreapta a celei de-a doua, iar capătul din dreapta al primei este egal cu cel din stanga al celei de-a doua.
Exemplu: { [10, 20], [20, 10], [1, 11], [1, 10], [10, 1]} ⇒ {[10, 20], [1, 10]}
Hint: Se folosește o singură tabelă de dispersie.
- Student.c
typedef struct
{
int cod; // unic
char *nume, *prenume, *adresa;
int medie;
int an_studii;
}Student;