You are on page 1of 4
Structuri de Date L A B O R A T O R 8a Heap binar si arbori Huffman Un minheap este un vector cu urmatoarele proprietati: - Valoarea fiecarui nod este mai mica sau egala cu valorile din nodurile fii, deci valoarea minima se afla in radacina arborelui. - Valoarea din pozitia k are fiul stanga in pozitia 2*k si fiul dreapta in pozitia 2*k+1. Parintele valorii din pozitia k este in pozitia k/2. Exemple de vectori minheap de lungime 4 construiti cu valorile 1,2,3,4 : 1,3,2,4 1 / \ 3 / 4 4 2 / 3 2 1,2,3,4 1 / \ 3 / 2 1,2,4,3 1 / \ 4 Adaugarea unui nou element la un vector Heap se face in prima pozitie libera (de la sfarsitul vectorului), dupa care se ridica acest element in vector astfel ca sa se respecte conditia de minheap. addPQ (x) { k=++n; // k este prima pozitie libera pune x in pozitia k repeta cat timp k>1 si a[k/2]>a[k] { // daca a[k] mai mic ca parintele sau schimba a[k] cu a[k/2] k=k/2 } } La extragerea si eliminarea valorii minime dintr-un minheap (primul element) se aduce apoi ultimul element in prima pozitie, dupa care se corecteaza vectorul pentru respectarea conditie de heap (prin propagarea in jos a valorii din prima pozitie, cat timp este necesar): delPQ { x=a[1] a[1]=a[n--] heapify(1) return x } // prima valoare e minima // se aduce ultima valoare la inceput // refacere heap Operatia "heapify" se poate exprima recursiv sau iterativ: se determina repetat valoarea maxima dintre elementele a[k], a[2*k] si a[2*k+1] si, daca e nevoie, se aduce acest maxim in pozitia k. Notand cu a vectorul si cu n dimensiunea sa, forma recursiva este: heapify(k) { // ajustare arbore cu radacina in k m=indmin (k) // indice minim dintre a[k],a[2k],a[2k+1] daca m != k atunci { schimba a[k] cu a[m] heapify(m) // ajustare subarbore cu radacina in m } } Varianta iterativa a functiei anterioare este: heapify(k) { repeta cat timp e necesar un interschimb { m=indmin (k) // indice maxim dintre a[k],a[2k],a[2k+1] daca m != k atunci{ schimba a[k] cu a[m] k=m } } } Observatie: inainte de a folosi indicii 2*k si 2*k+1 se verifica daca exista acesti indici in vector (daca sunt inferiori lui n). indmin (k) { st=2*k, dr=st+1 m=k // indice val. minima (initial k) daca st<=n si a[st] < a[m] atunci m=st // minim este a[st] daca dr<=n si a[dr] < a[m] atunci m=dr // minim este a[dr] return m } 2. Sa se defineasca o structura "PQ" cu vectorul heap si dimensiunea sa (vector cu dimensiune fixa). Sa se scrie si sa se verifice urmatoarele functii pentru un minheap de intregi: void initPQ (PQ & q,int n); // creare heap de capacitate n void addPQ (PQ & q, int x); // adauga x la minheap int delPQ (PQ & q); // scoate si sterge element minim din heap void printPQ (PQ q); // afisare heap ca vector int emptyPQ (PQ q); // daca vectorul heap este gol Sa se verifice cu programul urmator: int main () { PQ pq; int x[10]={3,8,2,9,7,1,5,4,6,0}; int i; initPQ (pq,10); for (i=0;i<9;i++){ addPQ (pq,x[i]); printPQ(pq); } while ( ! emptyPQ (pq)){ printPQ (pq); printf ("min=%d\n",delPQ(pq)); } getchar(); } Compresia Huffman Metoda de codificare Huffman a unui sir de caractere inlocuieste fiecare octet printr-un cod cu numar variabil de biti, astfel incat caracterele care apar mai frecvent sa aiba coduri mai scurte. Metoda Huffman are 2 etape: - Construirea unui arbore binar in care fiecare caracter apare ca o frunza; - Parcurgerea arborelui pentru generarea codurilor Huffman ale caracterelor. Arborele este construit treptat de jos in sus, folosindu-se o coada cu prioritati PQ ordonata crescator dupa numarul de aparitii, dupa algoritmul urmator: ch='0' // caractere pentru noduri nou create initializare coada pq repeta { citeste caracter si frecventa creare nod de arbore cu caracter si frecventa adauga la coada PQ adresa nod creat } repeta cat timp coada PQ contine cel putin doua elemente { scoate un element din coada PQ in "left" scoate un element din coada PQ in "right" f = suma frecvente din left si right; creare nod cu perechea (++ch,f) si fii left,right adauga la coada PQ adresa nod creat } scoate din coada PQ adresa r si afiseaza arbore cu radacina r In arborele creat caracterele de codificat se afla in frunze, iar calea de la radacina la o frunza (cu 0 pentru ramificare la stanga si 1 pentru ramificare la dreapta) reprezinta codul Huffman al caracterului respectiv. Calea la un nod frunza se poate determina recursiv, prin cautarea nodului ce contine un caracter dat (sau nerecursiv, folosind o legatura catre nodul parinte; pornind de la frunza catre radacina). 3. Sa se defineasca tipul structura Nod care contine un caracter, frecventa sa si pointeri catre cei doi fii (stanga,dreapta). Sa se scrie o functie de creare a unui nod de arbore cu fii cunoscuti: Nod* make ( char ch, short fr, Nod* left, Nod* right); Obs: Nodul creat nu are inca parinte, dar se modifica legaturile de la fii sai catre acest nod (daca exista acesti fii). Sa se modifice definitia tipului PQ pentru o coada cu prioritati realizata ca min-heap de pointeri Nod*, prioritatea fiind frecventa din Nod. Sa se modifice si functiile pentru operatii cu coada PQ ca sa lucreze cu poiteri Nod*. Sa se verifice operatiile cu coada folosind programul urmator: int main () { char ch[]={'a','b','c','d','e','f'}; short fr[]={36,14,12,10,20,8}; pq q; initPQ(q,f); // f=functie de comparare dupa frecventa for (int i=0;i<6;i++) addPQ(q,make (ch[i],fr[i],0,0)); // adauga la coada adresa nod while ( ! emptyPQ(q)){ Nod* p= delPQ(q); printf("%c=%d\n",p->ch,p->fr); } getchar(); } 4. Sa se scrie si sa se verifice o functie "build" pentru crearea unui arbore Huffman pe baza a doi vectori (de caractere si de frecvente): Nod* build (char c[], short f[], int n); // n=dimensiune vectori Arborele este construit treptat de jos in sus, folosindu-se o coada cu prioritati "pq" ordonata dupa numarul de aparitii, dupa algoritmul dat. Verificarea se va face prin afisarea prefixata cu indentare a arborelui: // afisare arbore void write (Nod* r,int ns) { if (t != 0) { printf ("%*c %c(%2d)\n", ns,' ',r->ch, r->fr); write (r->st,ns+3); write (r->dr,ns+3); } } Se va afisa continutul cozii la fiecare pas din ciclul principal. 5. Afisarea codurilor Huffman pe baza aborelui se va face cu functia urmatoar e: // proc. de generare si afisare coduri din arbore void afcod (Nod* r, int cod, int len) { // la primul apel cod=len=0 char s[10]; // aici se depune codul Huffman if (r==NULL) return; if ( r->st==NULL && r->dr==NULL) // daca nod frunza printf ("%c : %0*s \n", r->ch,len,itoa(cod,s,2)); else { afcod ( r->st, 2*cod+0,len+1); afcod ( r->dr, 2*cod+1,len+1); } }