Purpose: Laboratory Module 1 Movile Binomiale (Heap-uri binomiale) Familiarizarea cu notiunile de arbore binomial si movila binomiala Prezentarea operatiilor specifice movilelor binomiale Intelegerea modului in care se manipuleaza informatia retinuta intr-o astfel de structura 1 Consideratii teoretice Mulţimea, ca noţiune fundamenatală, este la fel de importantă în informatică ca şi în matematică. În timp ce mulţimile matematice sunt nemodificabile, mulţimile manipulate de algoritmi pot creşte, descreşte sau, în restul cazurilor, se pot modifica în timp. Astfel de mulţimi se numesc dinamice. În funcţie de algoritmi, sunt necesare diferite timpuri de operaţii pentru a fi executate asupra mulţimilor. De exemplu, mulţi algoritmi au nevoie doar de posibilitatea de a insera elemente într-o mulţime, de a şterge elemente dintr-o mulţime sau de a testa apartenenţa la o mulţime. Alţi algoritmi necesită operaţii mai complicate. De exemplu, cozile de prioritate suportă operaţiile de inserare a unui element şi extragere a celui mai mic element dintr-o mulţime. Deci nu e surprinzător faptul că cea mai bună modalitate de a implementa o mulţime dinamică depinde de operaţiile pe care trebuie să le ofere. Operaţiile pe o mulţime dinamică pot fi împărţite în două categorii: interogări, care returnează informaţii despre mulţime sau operaţii de modificare, care modifică mulţimea. Există structuri de date simple ca: stive, cozi, liste înlănţuite şi arbori cu rădăcină, tabele de dispersie (hash). O altă structură de date importantă este ansamblul (heap-ul). Heapsort introduce o tehnică nouă de proiectare a algoritmilor bazată pe utilizarea unei structuri de date, numită heap. Structura de date heap este utilă nu doar pentru algoritmul heapsort, ea poate fi la fel de utilă şi în tratarea eficientă a unei cozi de prioritate. Termenul heap a fost introdus şi utilizat iniţial în contextul algoritmului heapsort, dar acesta se foloseşte şi în legătură cu alocarea dinamică, respectiv în tratarea memoriei bazate pe colectarea reziduurilor, de exemplu, în limbajele de tip Lisp. Prezentăm în continuare sructurile de date avansate: heap-uri binomiale şi operaţiile care se execută pe heap-uri: creare, găsirea cheii minime, reuniunea a două heap-uri binomiale, inserarea unui nod, extragerea nodului având cheia minimă şi descreşterea unei chei. 1.1 Arbori binomiali Deoarece un heap binomial este o colecţie de arbori binomiali, vom prezenta mai intâi arborii binomiali şi demonstra unele proprietăţi esenţiale ale acestora. Arborele binomial B k este un arbore ordonat definit recursiv. După cum se vede în figura de mai jos, arborele binomial B 0 constă dintr-un singur nod. Arborele binomial B k constă din doi arbori binomiali B k-1 care sunt înlănţuiţi: rădăcina unui dintre ei este fiul situat cel mai în stânga rădăcinii celuilalt arbore. Figura prezintă arborii binomiali de la B 0 la B 4. Lema următoare conţine câteva proprietăţi ale arborilor binomiali. Arborele binomial B k are: 1. 2 k noduri, 2. înălţimea k, 3. exact 2 i noduri, la adâncimea i, pentru i=0,1,,k. 4. rădăcina de grad k, grad mai mare decât al oricărui alt nod; mai mult, dacă fii rădăcinii sunt numerotaţi de la stânga spre dreapta prin k-1, k-2,,0 atunci fiul i este rădăcina subarborelui B i. 1
Reprezentarea logica a arborilor binomiali Definitia recursiva a arborelui binomial B k. Triunghiurile reprezinta subarborii radacinii. Arborii binomiali B 0 pana la B 4. Corolar Gradul maxim al nodurilor unui arbore binomial având n noduri este lg n. Structura unui nod dintr-un arbore binomial typedef struct BinomialNode int inf,rank; BinomialNode *llink,*rlink,*down; *pbinomialnode; Reprezentarea fizica a arborilor binomiali 2
1.2 Heap-uri binomiale Un heap binomial H este format dintr-o mulţime de arbori binomiali care satisfac următoarele proprietăţi de tip binomial. 1. Fiecare arbore binomial din H satisface proprietatea de ordonare a unui heap: cheia unui nod este mai mare sau egală decât cheia părintelui său. 2. Există cel mult un arbore binomial în H a cărui rădăcină are un grad dat. Conform primei proprietăţi, rădăcina unui arbore cu proprietatea de heap ordonat, conţine cea mai mică cheie din arbore. Proprietatea a doua implică faptul că un heap binomial H având n noduri conţine cel mult [lg n]+1 arbori binomiali. Pentru o justificare a acestei afirmaţii se observă că reprezentarea binară a lui n are [lg n]+1 biţi, fie aceştia <b [lg n], b [lg n]-1,,b 0 >, astfel încât =. Din proprietatea 1 a lemei de mai i 0 sus rezultă că arborele binomial B i apare în H dacă şi numai dacă bitul b i =1. Astfel heap-ul H conţine cel mult [lg n]+1 arbori binomiali. În figura de mai jos este prezentat un heap binomial H având 13 noduri. Reprezeantarea binară a numărului 13 este 1101, iar H conţine arborii binomiali cu proprietatea de heap B 3, B 2 şi B 0 având 8, 4 şi respectiv un nod, în total fiind 13 noduri. Reprezentarea fizica a heap-urilor binomiale n = [lgn] b i i 2 După cum ilustrează şi figura de mai sus, fiecarea arbore binomial al unui heap binomial este memorat conform reprezentării stânga-fiu, dreapta-frate. Fiecare nod are un câmp cheie plus alte informaţii specifice aplicaţiei care foloseşte heap-ul. În plus, fiecare nod x conţine pointerii p[x] spre părintele lui, fiu[x] spre fiul situat cel mai în stânga şi frate[x] spre fratele lui x, situat imediat în dreapta. Dacă nodul x este o rădăcină atunci p[x]=nil. Dacă nodul x nu are fii atunci fiu[x]=nil, iar dacă x este fiu situat cel mai în dreapta, atunci frate[x]=nil. Fiecare nod conţine de asemenea câmpul grad[x], care reprezintă numărul fiilor lui x. Rezultă din figură că rădăcinile arborilor binomiali conţinuţi de un heap binomial sunt păstrate într-o listă înlănţuită pe care o vom numi în continuare listă de rădăcini. La o traversare a listei de rădăcini gradul rădăcinilor formează un şir strict crescător. Din a doua proprietate de heap binomial, rezultă că gradele rădăcinilor unui heap binomial având n noduri formează o submulţime a mulţimii 0,1,,[lg n]. Câmpul frate are semnificaţii diferite după cum nodurile sunt rădăcini sau nu. Dacă x este rădăcină atunci frate[x] referă rădăcina următoare în lista de rădăcini. (dacă x este ultima rădăcină din listă atunci, ca de obicei, frate[x]=nil.) Un heap binomial dat H este referit prin pointerul cap[h] spre prima rădăcină din lista de rădăcini a lui H. Dacă heap-ul binomial H nu are elemente, atunci cap[h]=nil. 3
Structura unei movile binomiale: typedef struct BinomialHeap int indmin,rank; pbinomialnode refvectheap[max_arb]; *pbinomialheap; 1.3 Operaţii pe heap-uri binomiale Heap-urile binomiale fac parte din structurile de date cunoscute sub numele de heap-uri interclasabile, caracterizate de următoarele operaţii: - CREEAZĂ-HEAP(H) creează şi returnează un heap nou care nu conţine elemente. - INSEREAZĂ(H,x) inserează nodul x a cărui cheie a fost iniţializată în heap-ul H. - MINIMUM(H,y) returnează un pointer la nodul cu cea mai mică cheie din H - EXTRAGE-MIN(H) şterge nodul cu cheia minimă din H şi returnează un pointer la acest nod. - REUNEŞTE(H 1,H 2,H) creează şi returnează un heap nou care conţine toate nodurile heap-urilor H 1 şi H 2. Heap-urile H 1 şi H 2 sunt distruse în urma acestei operaţii. - DESCREŞTE-CHEIE(H,x,k) atribuie nodului x din heap-ul H valoarea k pentru cheie, valoare presupusă a nu fi mai mare decât valoarea curentă a cheii. - ŞTERGE(H,x) şterge nodul x din heap-ul H. Tabelul de mai jos arată că heap-urile binare obişnuite, folosite, de exemplu, în heapsort, suportă bine aceste operaţii, cu excepţia operaţiei REUNEŞTE. În cazul heap-urilor binare, pentru fiecare operaţie, exceptând REUNEŞTE, tipul de execuţie în cazul cel mai defavorabil este O(lg n) (sau mai bun). Dacă totuşi un heap binar trebuie să execute operaţia REUNEŞTE, aceasta va fi lentă. Prin concatenarea celor două tablouri care memorează heap-urile binare care se interclasează şi apoi aplicarea operaţiei RECONSTITUIE-HEAP, timpul de execuţie, în cazul cel mai defavorabil, pentru operaţia REUNEŞTE este θ(n). procedură heap binar (cazul cel mai defavorabil) heap binomial (cazul cel mai defavorabil) CREEAZĂ-HEAP θ(1) θ(1) INSEREAZĂ θ(lg n) O(lg n) MINIMUM θ(1) O(lg n) EXTRAGE-MIN θ(lg n) θ(lg n) REUNEŞTE θ(n) O(lg n) În tabel sunt prezentaţi timpii de execuţie ai operaţiilor pentru trei implementări ale heap-urilor interclasabile. Numărul elementelor din heap-urile folosite de o operaţie este notat cu n. În continuare vom analiza heap-urile binomiale a căror margini pentru timpii de execuţie, în cazurile cele mai defavorabile, sunt prezentate în tabelul de mai sus. În particular, operaţia reuneşte pentru interclasarea a două heap-uri binomiale cu n elemente va fi de complexitate O(lg n). Vor fi ignorate operaţiile de alocare a nodurilor înainte de o inserare şi de eliberare a nodurilor după o ştergere. Presupunem că de aceste detalii este responsabil codul care apelează operaţiile heap-ului. Heap-urile binare, binomiale şi Fibonacci sunt ineficiente în raport cu operaţia de CĂUTARE; pentru găsirea unui nod care conţine o anumită valoare nu se poate stabili o cale de căutare directă în aceste structuri. Crearea unui heap binomial nou Pentru a crea un heap binomial vid, procedura CREEAZĂ-HEAP-BINOMIAL va aloca şi returna un obiect H, pentru care cap[h]=nil. Timpul de execuţie este θ(1). 4
Găsirea cheii minime Procedura HEAP-BINOMIAL-MIN returnează un pointer y la nodul cu cea mai mică cheie dintr-un heap binomial H având n noduri. Această implementare presupune că nu există chei cu valoarea. HEAP-BINOMIAL-MIN(H,y) 1: y=nil 2: x=cap[h] 3: min= 4: cât timp x NIL execută 5: dacă cheie[x]<min atunci 6: min=cheie[x] 7: y=x 8: sfârşit daca 9: y=frate[x] 10: sfârşit cât timp 11: return Cheia minimă a unui heap binomial se află într-o rădăcină deoarece este un heap ordonat. Procedura HEAP-BINOMIAL-MIN verifică toate rădăcinile (în număr de cel mult [lg n]+1) şi reţine minimul curent în min, respectiv un pointer la acest minim în y. Apelată pentru heap-ul binomial din figura 3, procedura va returna un pointer la nodul care conţine cheia 1. Timpul de execuţie al procedurii HEAP-BINOMIAL-MIN este O(lg n) deoarece există cel mult [lg n]+1 rădăcini verificate. Reuniunea a două heap-uri binomiale Operaţia de reuniune a două heap-uri binomiale este folosită de aproape toate celelalte operaţii rămase. Procedura HEAP-BINOMIAL-REUNEŞTE înlănţuie repetat arborii binomiali care au rădăcini de acelaşi grad. Procedura următoare leagă arborele B k-1 având nodul rădăcină y la arborele B k-1 având nodul rădăcină z; mai precis, z va fi părintele lui y. Nodul z devine astfel rădăcina unui arbore B k. BINOMIAL-LEGĂTURĂ(y,z) 1: p[y]=z 2: frate[y]=fiu[z] 3: fiu[z]=y 4: grad[z]=grad[z]+1 5: return Procedura BINOMIAL-LEGĂTURĂ plasează nodul y în capul listei înlănţuite care conţine fiii nodului z într-un timp O(1). Reprezentarea stânga-fiu, dreapta-frate a fiecărui arbore binomial asigură succesul acestei proceduri deoarece fiecare arbore binomial are proprietatea de ordonare a arborelui: fiu cel mai din stânga al rădăcinii unui arbore B k este rădăcina unui arbore B k-1. Procedura HEAP-BINOMIAL-REUNEŞTE uneşte două heap-uri binomiale H 1 şi H 2 şi returnează heap-ul rezultat. Pe parcursul efectuării operaţiei, reprezentările heap-urilor H 1 şi H 2 sunt distruse. Procedura foloseşte pe lângă procedura BINOMIAL-LEGĂTURĂ încă o procedură auxiliară ANSAMBLU-BINOMIAL-INTERCLASEAZĂ, care interclasează listele de rădăcini ale heap-urilor H 1 şi H 2 într-o singură listă simplu înlănţuită ordonată crescător după gradul nodurilor. 5
Pas 1. Se uneste B 0 din H 1 cu B 0 din H 2 si rezulta un arbore B 1 Pas 2. Se uneste B 1 din H 1 cu B 1 din H 2 si rezulta un arbore B 2 Pas 3. Se uneste B 2 din H 1 cu B 2 rezultat anterior si se obtine un B 3. 6
Movila rezultata in urma reuninunii movilelor intiale H 1 si H 2 este: Procedura HEAP-BINOMIAL-REUNEŞTE se desfăşoară în două faze. În prima fază se interclasează (prin apelul HEAP-BINOMIAL-INTERCLASEAZĂ) listele de rădăcini ale heap-urilor binomiale H 1 şi H 2 într-o listă simplu înlănţuită H care este ordonată crescător în raport cu gradul nodurilor rădăcină. Lista formată poate conţine cel mult două rădăcini cu acelaşi grad. Astfel, în faza a doua sunt unite toate rădăcinile care au acelaşi grad, astfel încât să nu existe două noduri cu acelaşi grad. Deoarece lista înlănţuită H este ordonată după grad, operaţiile de înlănţuire din faza a doua sunt efectuate rapid. Detaliem cele două fraze ale procedurii. Liniile 1-3 încep prin interclasarea celor două liste ale heap-urilor binomiale H 1 şi H 2 într-o singură listă de rădăcini H. Listele de rădăcini ale lui H 1 şi H 2 sunt ordonate strict crescător după grad, iar HEAP-BINOMIAL-INTERCLASEAZĂ returnează o listă de rădăcini H, ordonată crescător după grad. Dacă listele H 1 şi H 2 au împreună m noduri, atunci timpul de execuţie pentru HEAP-BINOMIAL-INTERCLASEAZĂ este O(m), datorat examinării repetate a rădăcinilor din capul listelor şi adăugării rădăcinii având gradul mai mic în lista de rădăcini rezultat, eliminând această rădăcină din lista dată la intrare. HEAP-BINOMIAL-REUNEŞTE(H 1,H 2,H) 1: Cheama CREEAZĂ-HEAP-BINOMIAL(H ) 2: Cheama HEAP-BINOMIAL-INTERCLASEAZĂ(H 1,H 2,H) 3: eliberează obiectele H 1,H 2, dar nu şi listele referite de ele 4: dacă cap[h]=nil atunci 5: return 6: sfârşit dacă 7: prec-x=nil 8: x=cap[h] 9: urm-x=frate[x] 10: cât timp urm-x NIL 11: dacă (grad[x] grad[urm-x]) sau (frate[urm-x] NIL şi grad[frate[urm-x]]=grad[x]) atunci 11: prec-x=x cazurile 1 si 2 12: x=urm-x 13: altfel 14: dacă cheie[x] cheie[urm-x] atunci 15: frate[x]=frate[urm-x] cazul 3 16: BINOMIAL-LEGĂTURĂ(urm-x,x) cazul 3 17: altfel 18: dacă prec-x=nil atunci 19: cap[h]=urm-x cazul 4 20: altfel 7
21: frate[prec-x]=urm-x cazul 4 22: sfârşit dacă 23: BINOMIAL-LEGĂTURA(x,urm-x) 24: x=urm-x 25: sfârşit dacă 26: urm-x=frate[x] 27: sfârşit dacă 28: sfârşit dacă 29: sfârşit cât timp 30: return În continuare procedura HEAP-BINOMIAL-REUNEŞTE iniţializează câţiva pointeri în lista de rădăcini H. Dacă heap-urile binomiale date la intrare sunt vide, atunci în liniile 4-5 se iese din procedură. Începând cu linia 6 ne situăm în cazul în care H conţine cel puţin o rădăcină. Din acest punct se păstrează 3 pointeri în lista de rădăcini: - x indică rădăcina curentă examinată, - prec-x indică rădăcina precedentă lui x în lista de rădăcini: frate[prec-x]=x, - urm-x indică rădăcina următoare lui x în listă: frate[x]=urm-x. H poate conţine iniţial cel mult două rădăcini cu un grad dat: deoarece H 1 şi H 2 sunt heap-uri binomiale, ele nu au două rădăcini având acelaşi grad. Mai mult, procedura HEAP-BINOMIAL- INTERCLASEAZĂ ne garantează că dacă H conţine două rădăcini având acelaşi grad, atunci ele sunt adiacente în lista de rădăcini. În timpul execuţiei procedurii HEAP-BINOMIAL-REUNEŞTE, de fapt, pot exista 3 rădăcini având acelaşi grad. Vom vedea când se produce această situaţie. La fiecare iteraţie a ciclului cât timp din liniile 9-24 se decide dacă se poate lega x şi urm-x în funcţie de gradul lor şi de gradul lui frate[urm-x]. Un invariant al acestui ciclu este faptul că la fiecare reluare a corpului ciclului atât x cât şi urm-x sunt diferiţi de NIL. Cazul 1, se produce atunci când grad[x] grad[urm-x], adică x este rădăcina unui arbore B k şi urmx este rădăcina unui arbore B l pentru un l>k. Această situaţie este tratată în liniile 11-12. Deoarece nu trebuie să legăm x şi urm-x, nu rămâne decât să deplasăm pointerii în listă. Actualizarea pointerului urm-x pentru a referi nodul ce urmează noului nod x este efectuată în linia 24, deoarece aceasta este comună tuturor cazurilor. Cazul 2, are loc atunci când x este prima rădăcină din cele 3 care au acelaşi grad, adică atunci când grad[x]=grad[urm-x]=grad[frate[urm-x]] Acest caz este tratat similar cu cazul 1: efectuăm doar o deplasare a pointerilor în listă. Testul din linia 10 este comun cazurilor 1 şi 2, la fel cum liniile 11-12 tratează amândouă cazurile. Cazurile 3 şi 4 se produc atunci când x este prima rădăcină din 2 rădăcini succesive având acelaşi grad, adică grad[x]=grad[urm-x] grad[frate[urm-x]]. Aceste cazuri apar la iteraţia următoare după fiecare caz, dar unul din ele urmează imediat după cazul 2. În cazurile 3 şi 4 vom înlănţui x şi urm-x. Aceste cazuri diferă între ele după cum x sau urm-x au cheia mai mică, fapt ce determină care din noduri va fi rădăcină în procesul de legare a lor. În cazul 3, cheie[x] cheie[urm-x], astfel că urm-x va fi legat la linia x. Linia 15 şterge urm-x din lista de rădăcini, iar în linia 16 urm-x devine fiul situat cel mai în stânga lui x. În cazul 4, cheia mai mică o are urm-x, deci este legat la urm-x. Liniile 17-21 şterg x din lista de rădăcini. Există două subcazuri, după cum x este (linia 19) sau nu (linia 21) prima rădăcină din listă. În linia 22, x devine fiul situat cel mai în stânga lui urm-x, iar linia 23 actualizează x pentru iteraţia următoare. Pregătirea iteraţiei următoare a ciclului cât timp este aceeaşi pentru ambele cazuri 3 şi 4. x referă un arbore B k+1 obţinut prin negarea a doi arbori B k. După operaţia HEAP-BINOMIAL-INTERCLASEAZĂ în lista de rădăcini existau zero, unu sau doi arbori B k+1, deci x este acum prima rădăcină din lista de rădăcini pentru un număr de unu, doi sau trei arbori B k+1. În cazul existenţei unui singur arbore (x referindu-l pe acesta), la iteraţia următoare se va produce cazul 1: grad[x] grad[urm-x]. Dacă x referă primul arbore din doi existenţi atunci la iteraţia următoare are loc unul din cazurile 3 sau 4. În sfârşit, dacă x referă primul arbore din trei existenţi atunci la iteraţia următoare are loc cazul 2. Timpul de execuţie pentru HEAP-BINOMIAL-REUNEŞTE este O(lg n), unde n este numărul total de noduri din heap-urile binomiale H 1 şi H 2. Justificăm acest rezultat după cum urmează: fie n 1 şi n 2 numărul nodurilor heap-urilor H 1 şi respectiv H 2 astfel încât n=n 1 +n 2. Atunci numărul maxim de rădăcini 8
conţinute de H 1 şi H 2 este [lg n 1 ]+1, respectiv [lg n 2 ]+1. Astfel imediat după apelul HEAP-BINOMIAL-INTERCLASEAZĂ, H conţine cel mult [lg n 1 ]+[lg n 2 ]+2 2[lg n]+2=o(lg n) rădăcini. Rezultă că timpul de execuţie pentru HEAP-BINOMIAL-INTERCLASEAZĂ este O(lg n). Fiecare iteraţie a ciclului cât timp se execută într-un timp O(1) şi pot exista cel mult [lg n 1 ]+[lg n 2 ]+2 iteraţii deoarece de la fiecare iteraţie fie pointerii avansează cu o poziţie în lista H, fie se elimină o rădăcină din lista de rădăcini. Astfel, timpul total de execuţie este O(lg n). Inserarea unui nod Procedura următoare inserează nodul x în heap-ul binomial H. Se presupune că nodul x este creat şi câmpul cheie[x] este iniţializat. HEAP-BINOMIAL-INSEREAZĂ(H,x) 1: Cheama CREEAZĂ-HEAP-BINOMIAL(H ) 2: p[x]=nil 3: fiu[x]=nil 4: frate[x]=nil 5: grad[x]=0 6: cap[h ]=x 7:Cheama HEAP-BINOMIAL-REUNEŞTE(H,H,H) 8: return Procedura creează un heap binomial H cu un nod într-un timp O(1) pe care îl reuneşte apoi cu heap-ul binomial H având n noduri într-un timp O(lg n). Procedura HEAP-BINOMIAL-REUNEŞTE eliberează spaţiul alocat heap-ului binomial temporar H. Extragerea nodului având cheia minimă Procedura următoare extrage nodul având cheia minimă din heap-ul binomial H şi returnează un pointer la nodul extras. HEAP-BINOMIAL-EXTRAGE-MIN(H) 1: caută rădăcina x cu cheia minimă în lista de rădăcini şi şterge x din lista de rădăcini a lui H 2: Cheama CREEAZĂ-HEAP-BINOMIAL(H ) 3: inversează ordinea memorării fiilor lui x în lista înlănţuită asociată şi atribuie lui cap[h ] capul listei rezultate 4: Cheama HEAP-BINOMIAL-REUNEŞTE(H,H,H) 5: return 9
Modul de funcţionare al procedurii este ilustrat în figura de mai sus.se prezintă situaţia obţinută după linia 1: rădăcina x având cheia minimă a fost eliminată din lista de rădăcini a lui H. Dacă x este rădăcina unui arbore B k, atunci fiii lui x de la stânga la dreapta, sunt rădăcinile unor arbori B k-1, B k-2,, B 0. Se ilustrează faptul că inversând lista fiilor lui x (în linia 3) obţinem un heap binomial H care conţine toate nodurile din arborele corespunzător lui x, exceptându-l pe x. Deoarece în linia 1 arborele lui x este şters din H, heap-ul binomial rezultat prin reunirea în linia 4 a lui H şi H, va conţine toate nodurile care existau iniţial în H, exceptându-l desigur pe x. În final, în linia 5 se returnează x. HEAP-BINOMIAL-EXTRAGE-MIN se execută într-un timp O(lg n) deoarece fiecare din liniile 1-4 se execută într-un timp O(lg n). 10
Sample Coding #include <conio.h> #include <stdio.h> #define MAX_ARB 10 typedef struct BinomialNode int m_inf, m_rank; BinomialNode *m_prlink,*m_pllink,*m_pdown; *pbinomialnode; typedef struct BinomialHeap int m_indmin,m_rank; pbinomialnode refvectheap[max_arb]; *pbinomialheap; void CombineBinomialTrees(pBinomialNode *arb1,pbinomialnode *arb2); void MELD(pBinomialHeap h1,pbinomialheap h2,pbinomialheap *heaprez); void CreateBinomialTree(int rank, pbinomialnode *rad); void CreateBinomialHeap(pBinomialHeap *H); void DisplayBinomialTree(pBinomialNode r, int level); void DisplayBinomialHeap(pBinomialHeap H); void DeleteMin(pBinomialHeap h, pbinomialheap *Hrez); int main() pbinomialheap H1,H2,Hrez; printf("introduceti prima movila: \n"); CreateBinomialHeap(&H1); DisplayBinomialHeap(H1); printf("introduceti a doua movila: \n"); CreateBinomialHeap(&H2); DisplayBinomialHeap(H2); printf("\n"); MELD(H1,H2,&Hrez); DisplayBinomialHeap(Hrez); printf("\n"); DeleteMin(H1,&Hrez); printf("\n"); DisplayBinomialHeap(Hrez); getch(); void CombineBinomialTrees(pBinomialNode *arb1,pbinomialnode *arb2) pbinomialnode t,p1; 11
if((*arb1)->m_rank!=(*arb2)->m_rank) return; if((*arb1)->m_inf > (*arb2)->m_inf) t=*arb1; *arb1=*arb2; *arb2=t; p1=(*arb1)->m_pdown; if(p1) p1->m_pllink->m_prlink=*arb2; (*arb2)->m_pllink=p1->m_pllink; (*arb2)->m_prlink=p1; p1->m_pllink=*arb2; else (*arb1)->m_pdown=*arb2; (*arb1)->m_rank=(*arb2)->m_rank+1; return; void MELD(pBinomialHeap h1,pbinomialheap h2,pbinomialheap *heaprez) int maxrank,first,i,j,min,indmin; pbinomialnode transport,vm[max_arb]; maxrank=h1->m_rank > h2->m_rank? h1->m_rank : h2->m_rank; transport=null; first=1; (*heaprez)=new BinomialHeap; for(i=0;i<maxrank;i++) j=0; if(h1->refvectheap[i]) VM[j++]=h1->refVectHeap[i]; if(h2->refvectheap[i]) VM[j++]=h2->refVectHeap[i]; if(transport) VM[j++]=transport; transport=null; if(j>1) CombineBinomialTrees(&VM[0],&VM[1]); transport=vm[0]; if(j==3) VM[0]=VM[2]; j=1; else j=0; if(j) (*heaprez)->refvectheap[i]=vm[0]; 12
else (*heaprez)->refvectheap[i]=null; if((*heaprez)->refvectheap[i]) if((*heaprez)->refvectheap[i]->m_inf < min first) indmin=i; first=0; if(transport) (*heaprez)->refvectheap[i]=transport; (*heaprez)->m_rank=i+1; else(*heaprez)->m_rank=i; (*heaprez)->m_indmin=indmin; return; void CreateBinomialTree(int rank, pbinomialnode *rad) int i; pbinomialnode radlocal,temp1,temp2; radlocal=new BinomialNode; radlocal->m_rank=rank; scanf("%d", &radlocal->m_inf); radlocal->m_pdown=null; radlocal->m_prlink=radlocal->m_pllink=radlocal; for(i=0;i<rank;i++) CreateBinomialTree(rank-1,&temp1); if(radlocal->m_pdown==null) radlocal->m_pdown=temp1; else temp2=radlocal; temp2->m_prlink->m_pllink=temp1; temp1->m_prlink=temp2->m_prlink; temp1->m_pllink=temp2; temp2->m_prlink=temp1; *rad=radlocal; return; void CreateBinomialHeap(pBinomialHeap *H) int i, prim,pozmin,min,resp; pbinomialheap h; h= new BinomialHeap; 13
h->refvectheap[i]=null; printf("rangul = "); scanf("%d", &h->m_rank); printf("\n"); for(i=0;i<h->m_rank;i++) printf("exista arborele de rang [0/1]\n",i); scanf("%d", &resp); if(resp) CreateBinomialTree(i,&(h->refVectHeap[i])); prim=1; for(i=0;i<h->m_rank;i++) if(h->refvectheap[i]) if(h->refvectheap[i]->m_inf < min prim) prim=0; min=h->refvectheap[i]->m_inf; PozMin=i; h->m_indmin=pozmin; *H=h; return; void DisplayBinomialTree(pBinomialNode r, int level) pbinomialnode p1,p2; int i, prim; for(i=0;i<level;i++) printf(" "); printf("%d\n", r->m_inf); prim=1; for(p1=p2=r->m_pdown;(p1!=p2 prim) && (p1!=null); p1=p1->m_prlink) prim=0; DisplayBinomialTree(p1,level+1); void DisplayBinomialHeap(pBinomialHeap H) int i; for(i=0;i<h->m_rank;i++) if(h->refvectheap[i]!=null) DisplayBinomialTree(H->refVectHeap[i],0); printf("pozitia elementului minim este : %d\n", H->m_IndMin); return; 14
void DeleteMin(pBinomialHeap h, pbinomialheap *Hrez) pbinomialheap htemp, hlocal; pbinomialnode p, p1,p2; int i, prim, PozMin, min; p=h->refvectheap[h->m_indmin]; h->refvectheap[h->m_indmin]=null; prim=1; min=0; for(i=0;i<h->m_rank;i++) if(h->refvectheap[i]) if(h->refvectheap[i]->m_inf < min prim) prim=0; PozMin=i; min=h->refvectheap[i]->m_inf; h->m_indmin=pozmin; htemp = new BinomialHeap; for(i=0;i<max_arb;i++) htemp->refvectheap[i]=null; prim=1; for(p1=p2=p->m_pdown,i=0;(p1!=null) && (prim p1!=p2); p1=p1->m_prlink,i++) prim=0; htemp->refvectheap[i]=p1; htemp->m_rank = p->m_rank? p->m_rank : 0; prim=0; for(i=0;i<htemp->m_rank;i++) if(htemp->refvectheap[i]) if(htemp->refvectheap[i]->m_inf < min prim) prim=0; PozMin=i; min=htemp->refvectheap[i]->m_inf; htemp->m_indmin=pozmin; MELD(h,hTemp,Hrez); return; 15