64
Strukture podataka i algoritmi – vježbe Skripta u nastajanju Zadnja promjena 16. siječnja 2011.

skripta SPA

  • Upload
    jo-cvit

  • View
    125

  • Download
    10

Embed Size (px)

Citation preview

Page 1: skripta SPA

Strukture podataka i algoritmi – vježbe

Skripta u nastajanju

Zadnja promjena 16. siječnja 2011.

Page 2: skripta SPA

Sažetak

Sadržaj ove skripte je većinom izravno pretipkan iz vježbi kolegija ”Strukture podatakai algoritmi” koje se u skoro nepromijenjenom obliku održavaju već nekoliko godina naMatematičkom odjelu Prirodoslovno-matematičkog fakulteta. Tekst vježbi nabavljen jeljubaznošću asistenta Zvonimira Bujanovića koji je izradio i ručno crtane skice.

Molim vas da bilo ispravke, komentare ili nejasnoće pošaljete kao elektronsku poštuna adresu [email protected]. Možda zajedno malo olakšamo budućim generaci-jama. Hvala vam,

Vaš pretipkač, Ilija.

Page 3: skripta SPA

Sadržaj

1 Uvod 21.1 Važnost modeliranja . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Pojmovi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Primjer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.3.1 Matematički model . . . . . . . . . . . . . . . . . . . . . . . . 41.3.2 Apstraktni tip podataka . . . . . . . . . . . . . . . . . . . . . . 51.3.3 Struktura podataka . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Liste 102.1 Općenita lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2 Stog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.3 Red . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3 Stabla 313.1 Općenito stablo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.2 Binarno stablo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4 Skupovi 484.1 Rječnik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514.2 Prioritetni red . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

1

Page 4: skripta SPA

Poglavlje 1

Uvod

Strukture podataka i algoritmi se može smatrati prijelaznim kolegijem. Na kolegiju C(Programiranje 2) učili smo sâm jezik C, to jest njegovu sintaksu. Na višim godinama,ako se odlučite za smjer računarstvo, naglasak će više biti namodeliranju problema. Ovajkolegij je prijelaz – znamo C, a učimo kako modelirati probleme, najčešće pristupe mo-deliranju i najčešće implementacije.

1.1 Važnost modeliranjaModeliranje ima veliku važnost u programiranju jer se naizgled ili stvarno teški problemidobrim modeliranjem mogu lakše ili brže riješiti.

Rješivost u nekoj formulaciji problem se može činiti nerješiv

Složenost razni modeli, strukture podataka i algortmi mogu povećati brzinu izvođenja(smanjiti vremensku složenost) ili smanjiti prostor potreban za izvođenje (smanjitiprostornu složenost)

1.2 PojmoviUvodimo neke pojmove koji se koriste na ovom kolegiju:

Model predstavlja formulaciju problema (koji se javlja u praksi, ”iz života”) u terminimamatematičkih objekata (niz, grafovi, stabla i slično)

Apstraktni tip podataka je apstraktno zadan tip podataka (obično složeni) zajedno saoperacijama. Implementacija operacija nije nužno zadana.

Struktura podataka je eksplicitna implementacija tipa podataka iz apstraktnog tipa po-dataka. To može biti skupina varijabli u nekom programskom jeziku zajedno savezama među varijablama.

Rješavanje problema je postupak postepenog pro njavanja u kojem prvo postavljamomatematički model i algoritam u neformalnom jeziku koji koristi matematičke pojmove,

2

Page 5: skripta SPA

zatim stvaramo apstraktni tip podataka i algoritam zapisujemo u pseudo-jeziku koji ko-risti operacije iz ATP, te konačno apstraktni tip podataka i algoritam u pseudokoduprevodimo u strukture podataka u stvarnom programskom jeziku i algoritam zapisanu stvarnom programskom jeziku, što nam predstavlja rješenje problema. Pri rješavanjućemo se koristiti slikovitim prikazima čestih struktura koji su vidljivi na slici 1.1. Postu-pak rješavanja postepenim pro njavanjem vidljiv je na skici 1.2.

...a).. 0.

b)..

1

..

2

.

...

.

...

..

n

..c)

....d)

..e)

... i

Slika 1.1: Slikoviti prikazi čestih struktura – a) ćelija, b) polje, c) zapis, d) pokazivač(pointer), e) kursor

..

Matematičkimodel

.neformalnoopisani al-goritam

.

Apstraktnitipovi po-dataka

.algoritamzapisan upseudo-jeziku (ko-riste seoperacijeiz ATP)

.

Strukturepodataka

.

algoritamzapisan uprogram-skom jeziku(operacijeiz ATP supretvoreneu potpro-grame)

.Problem . Rješenje problema

Slika 1.2: Skica postupka postepenog pro njavanja

Suvremeni, najčešće objektno orijentirani, jezici poput C++, Jave ili C# jezika uobi-čajeno dolaze sa standardnim bibliotekama koje implementiraju razne ATP (uključujućii sve koji se obrađuju na vježbama) koje tada možemo koristiti na nivou ATP-a, bez ula-ženja u implementaciju.

1.3 PrimjerPredstavimo gore opisani postupak na jednom primjeru. Raskršće izgleda kao na slici1.3. Putevi C i E su jednosmjerni, a ostali su dvosmjerni. Postoji 13 načina za prolazak

3

Page 6: skripta SPA

..B

.

C

.

D

.

E

.

A

Slika 1.3: Skica raskršća – ulice C i E su jednosmjerne

kroz raskršće. Neki parovi prolazaka (npr. AB i EC) mogu se obavljati istovremeno, noneki (npr. AD i EB) ne mogu jer im se putanje sijeku.

Na raskršće će se postavljati semafori. Potrebno je smisliti režim rada semafora. Tajrežim se sastoji od nekoliko faza. U svakoj fazi je dozvoljen neki prelazak. Zahtijeva se:

• Za vrijeme jedne faze ne smije biti dozvoljen prelazak dvama prelascima kojima seputanje sijeku

• Svaki od 13 prelazaka mora biti dozvoljen u barem jednoj fazi

• Broj faza treba biti što manji

Ovaj problem obrađujemo slijedeći skicu 1.2. Prvo formuliramo matematički model.

1.3.1 Matematički modelProblem modeliramo neorijentiranim grafom. Vrhovi predstavljaju ”prelaske”. Bridovipovezuju one prelaske koji se ne mogu obavljati istovremeno.

Problemi smeđusobnim sukobima u kojima je potrebnominimizirati korištenje dos-tupnih resursa često se mogu modelirati pomoću problema bojanja grafa (npr. problemirasporeda ili problem dodjeljivanja procesorskih registara). Potrebno je obojati vrhovegrafa sa štomanje boja tako da susjedni (međusobni incidentni vrhovi) ne smiju biti obo-janom istom bojom. Primjetimo da boje predstavljaju resurse (npr. termin, registar), aincidentnost međusobnu sukobljenost (oba ”vrha” istodobno zahtijevaju neki ograničeniresurs).

Neformalno opisani algoritam

Problem bojanja grafa je poznat u teoriji algoritama kao NP-težak. Ugrubo to znači nijeotkriven e kasni algoritam za njegovo rješavanje, i da se svi problemi koji su u takvojklasi mogu svesti na njega u razumnom vremenu. Detaljnije o tome na kolegijima Obli-kovanje i analiza algoritama i Složenost algoritama. Mi ćemo zato koristiti jedan pri-bližni, heuristički algoritam koji daje prilično dobro rješenje, no ne jamči da je ono ioptimalno. Algoritam spada u tzv. pohlepne (greedy) algoritme.

4

Page 7: skripta SPA

..

..AB ..AC ..AD

..BA ..BC ..BD

..DA ..DB ..DC

..EA ..EB ..EC ..ED

Slika 1.4: Skica problema raskršća u obliku grafa

void greedy(COLORED_GRAPH G){

boja novaBoja;radi{

novaBoja = bilo koja neupotrebljena boja;za svaki neobojeni vrh v grafa G{

ako v nije susjedan niti jednom vrhu obojenom sa novaBojaoboji v sa novaBoja

}} dok svi vrhovi od G nisu obojani

}

1.3.2 Apstraktni tip podatakaUvodimo apstraktni tip podataka COLORED_GRAPH kojim opisujemo matematičkipojam obojenog (neusmjerenog) grafa. Taj tip podataka treba uključivati sve operacijekoje se pojavljuju u neformalnomalgoritmukao što su provjera obojanosti i incidentnostiili bojanje vrha nekom bojom. ATP je zadan sljedećim popisom tipova i operacija.

vrh podaci ovog tipa identi ciraju vrhove

boja podaci ovog tipa označavaju boje. Uključena je i posebna boja ”prazno” koja oz-načava odsustvo boje

OBOJENI_GRAH podatak ovog tipa je (neorijentirani) graf sastavljen od različitih vrhovatipa vertex. Svaki vrh je obojan bojom tipa colortype

5

Page 8: skripta SPA

BOJA(v,G) funkcija koja vraća boju vrha v u grafu G

INCIDENTAN(v1,v2,G) logička funkcija koja testira jesu li vrhovi v1 i v2 u grafu Gsusjedni

POSTAVI_BOJU(c,v,G) procedura koja pridružuje boju c vrhu v u grafu G

Algoritam zapisan u pseudo-jeziku

void greedy(COLORED_GRAPH *G){

bool zavrsetak, pronadeno;boja novaBoja;do {

zavrsetak = true;novaBoja = bilo koja još neiskorištena boja;for (svaki vrh v1 od G)

if (BOJA(v1,G) == blank) {zavrsetak = 0;pronadeno = 0;for (svaki vrh v2 od G){

if (INCIDENTAN(v1,v2,G) && COLOR(v2,G)==novaBoja)pronadeno = true;

}if (!pronadeno)

POSTAVI_BOJU(novaBoja,v1,G);} while (!finished);

}

1.3.3 Struktura podatakaOgraničavamo se na grafove sa ksnim, unaprijed zadanim brojem vrhova n. Tipoveboja, vrh kodiramo na sljedeći način:typedef int vrh;typedef int boja;

Vrijednost boje ”prazno” je 0. Naše vrhove-prijelaze AB, AC, AD,... preimenujemo u0, 1, . . . , n− 1. Nakon toga sâm obojeni graf (dakle podatak tipa OBOJENI_GRAF) mo-žemo prikazati jednom matricom susjedstva i jednim poljem.typedef struct obojenigraf {

bool susj[n][n];boja boje[n];

} OBOJENI_GRAF;

Umatrici susjedstva je incidentnost dva vrha vi i vj je zadana kao vrijednostsusj[i][j].Budući da je graf neusmjeren, matrica je nužno simetrična. i-ti element polja boje sa-drži boju pridruženu i-tom vrhu.

6

Page 9: skripta SPA

AB AC AD BA BC BD DA DB DC EA EB EC ED0 1 2 3 4 5 6 7 8 9 10 11 12

AB 0 1 1 1 1AC 1 1 1 1 1 1AD 2 1 1 1BA 3BC 4 1 1 1BD 5 1 1 1 1 1DA 6 1 1 1 1 1DB 7 1 1 1DC 8EA 9 1 1 1EB 10 1 1 1 1 1EC 11 1 1 1 1ED 12

Tablica 1.1: Matrica susjedstva susj[][] strukture OBOJANI_GRAF

..0.0 .0

.1

.

0

.

2

.

0

.

3

.

0

.

4

.

0

.

5

.

0

.

6

.

0

.

7

.

0

.

8

.

0

.

9

.

0

.

10

.

0

.

11

.

0

.

12

Tablica 1.2: Polje boje[], u kojem su zapisane boje u vrhovima

Za naš konkretni graf vrijednosti u početku izvođenja algoritma su prikazane u ta-blicama 1.1 i 1.2.

Da bi dobili kompletnu implementaciju ATP trebali bismo operacije iz ATP napisatikao potprograme. No za naše operacije je to trivijalno i svodi se na jedan logički uvjet ilijedno pridruživanje.

Algoritam zapisan u programskom jeziku

void greedy(OBOJENI_GRAF *G){

bool zavrsetak, pronadeno;boja novaBoja;

7

Page 10: skripta SPA

vrh i,j;novaBoja = 0;do {

zavrsetak = 0;novaBoja++;for (i=0; i<n; i++){

if (G->boja[i] == 0) {zavrsetak = 0;pronadeno = 0;for (j=0; j<n; j++)

if(G->susj[i][j] && G->boja[j] == novaBoja)pronadeno = 1;

if(!pronadeno)G->boja[i] = novaBoja;

}} while (!zavrsetak)

}

Ovaj potprogram trebalo bi još uklopiti u glavni program. Na početku glavnog programade nirala bi se konstanta n = 13, te svi potrebni tipovi. U tijelu glavnog programa bi seinicijalizirali podaci koji opisuju graf, a zatim bi se pozvala funkcija greedy. Na kraju bise interpretirao rezultat pohranjen u polju boje.

Rješenje koje opisani algoritam daje za naš konkretan problem raskršća zapisano jeu tablici 1.3:

Faza u radu semafora Dozvoljeni prolasci1 AB, AC, AD, BA, DC, ED2 BC, BD, EA3 DA, DB4 EB, EC

Tablica 1.3: Rješenje problema postavljanja semafora

Lako semože pokazati da je ovo i optimalno rješenje. Naime u grafu postoji tzv. klikareda 4: skup od 4 vrha koji su svi međusobno povezani (što se može vidjeti na slici 1.5).Očito nam treba barem 4 boje da obojimo ovu kliku (razmislite zašto). Postoje i drugaoptimalna rješenja, dakle i drugi načini da se zadani graf oboji sa 4 boje.

8

Page 11: skripta SPA

..

..AB ..AC ..AD

..BA ..BC ..BD

..DA ..DB ..DC

..EA ..EB ..EC ..ED

Slika 1.5: Klika reda 4 u grafu

9

Page 12: skripta SPA

Poglavlje 2

Liste

2.1 Općenita listaLista je konačan niz podataka istog tipa (a1, a2, . . . , an). Promatramo je kao sljedećiapstraktni tip podataka:

elementtype bilo koji tip (jednostavan ili složen)

LIST konačni niz podataka tipa elementtype

position podatak ovog tipa služi za identi ciranje elemenata u listi

FIRST(L) funkcija vraća poziciju na početku liste L

END(L) funkcija vraća poziciju na kraju liste L

NEXT(p,L) funkcija vraća iduću poziciju iza p u listi L

PREVIOUS(p,L) funkcija vraća prethodnu poziciju ispred p u listi L

RETRIEVE(p,L) funkcija vraća element na poziciji p u listi L

MAKE_NULL(&L) funkcija pretvara L u praznu listu i vraća END(L)

INSERT(x,p,&L) funkcija ubacuje element x na poziciju p u listi L

DELETE(p,&L) funkcija izbacuje element na poziciji p u listi L

Zadatak 2.1.1. Napišite potprogram oblika void PURGE (LIST *l) koji u listi L izba-cuje elemente-duplikate. Potprogram treba biti neovisan o implementaciji ATP LIST.

Rješenje: Ideja rješenja je počevši od prvog elementa uspoređivati sve elemente koji sli-jede trenutni s trenutnim. Kada izbacimo sve elemente koji su jednaki trenutnom, tre-nutni element postavimo na njemu sljedeći.

Pretpostavimo da nakon INSERT i DELETE pozicije svih neobrisanih elemenata os-taju očuvane.

10

Page 13: skripta SPA

void PURGE(LIST* l){

position p, q;p = FIRST(*l);while (p != END(*l)){

q = NEXT(p,*l);while (q != END(*l)){

if (RETRIEVE(p,*l) == RETRIEVE(q,*l)){

position temp = q;q = NEXT(q,*l);DELETE(temp,l);

}else

q = NEXT(q,*l);}p = NEXT(p,*l);

}}

Napomena: Objasnimo pretpostavku o očuvanju elemenata nakon operacija INSERT iDELETE. Pretpostavimo da imamo listu L = (6, 2, 9, 3) i neka su pozicije tih elemenataredom a, b, c i d. Također pretpostavimo da želimo ubaciti element 5 ispred pozicije c,dakle INSERT(5,c,&L). Lista će izgledati ovako: L = (6, 2, 5, 9, 3). Ali što će biti sapozicijama a, b, c, d?

Odgovor na ovo pitanje ovisi o implementaciji. Kao primjer, u implementaciji po-moću polja, a, b, c, d će biti pozicije od 6, 2, 5, 9 redom. Prikaz ove situacije može sevidjeti na slici 2.1. U implementaciji pomoću pokazivača kao u skripti, a, b, c, d će bitipozicije od 6, 2, 5, 3 redom.

..6.a .

2

.

b

.

9

.

c

.

3

.

d

..

a)

. 6. a.

2

.

b

..

c

.

9

.

d

.

3

.

b)

. 6. a.

2

.

b

.

5

.

c

.

9

.

d

.

3

.

c)

Slika 2.1: a) Početne pozicije u listi, b) Premještanje elemenata kako bi se stvorilo slo-bodno mjesto i c) Novi element ubačen na oslobođeno mjesto

Dakle – neće biti isto. De nicija liste ništa ne govori o tome što se događa sa po-zicijama. Ali ako kažemo da pozicije nakon svakog korištenja INSERT ili DELETE višenemaju nikakvo značenje, onda ATP LIST postaje vrlo nespretan za korištenje.

11

Page 14: skripta SPA

Zbog toga ćemo u tekst zadatka (kad to bude potrebno) dodati rečnicu ”Pretposta-vite da nakon operacija INSERT i DELETE pozicije svih (neobrisanih) elemenata ostajuočuvane.” Svaka uobičajena implemenatacija ATP LIST (C++ STL, Java, C# Generics)ima ovakvu konvenciju. Implementacije iz skripte ne poštuju ovu konvenciju (ali one sapointerima i kursorima su jako blizu i mogu se izmijeniti tako da poštuju).

Uz ovupretpostavku, nakonINSERT(5,c,&L) vrijedi: a, b, c, d su pozicije od 6, 2, 9, 3.U šalabahteru piše INSERT ubacuje podatak x na poziciju p, ispravnije je reći da INSERTubacuje podatak x ispred podatka čija je pozicija p. Do pozicije novoubačenog elementamože se doći pomoću PREVIOUS(p,L), to je neka nova pozicija koja nije postojalau listi prije ubacivanja elementa. Nakon DELETE(c,&L) lista L = (6, 2, 9, 3) postajeL = (6, 2, 3), a, b, d su pozicije od 6, 2, 3; pozicija c više nema smisla.

Pretpostavimo da je na skupu koji sadrži elemente tipa elementtype de niran uređaj≤. Kažemo da je lista (a1, a2, . . . , an) sortirana ako vrijedi a1 ≤ a2 ≤ . . . ≤ an iliai ≤ ai+1,∀i. Drukčije rečeno lista L je sortirana ako vrijedi: RETRIEVE(p,L) ≤ RE-TRIEVE(NEXT(p,L),L) za sve pozicije p takve da su obje strane nejednakosti de nirane.

Zadatak 2.1.2. Napišite potprogram oblika SINSERT(elementtype x, LIST *L)kojim se u sortiranu listu L ubacuje element x tako da lista ostane sortirana. Potprogramtreba biti neovisan o implementaciji ATP LIST.

Rješenje: Ideja rješenja je pronaći ispravnomjesto za ubacivanje elementa. Kad naiđemona element koji je veći ili jednak od x, tada x možemo ubaciti ispred tog elementa.void SINSERT(elementtype x, LIST* l){

position p = FIRST(L);bool pronadeno = FALSE;while (!pronadeno && p!=END(l)){

if (x <= RETRIEVE(p,l))pronadeno = true;

elsep = NEXT(p,l);

}INSERT(x,p,l);

}

Napomena: Prethodni potprogram SINSERTmože poslužiti kao osnovni korak u algo-ritmu za sortiranje liste. Neka je L1 polazna nesortirana lista. Stvaramo novu sortiranulistu tako da redom elemente iz L1 ubacujemo u L2 pomoću SINSERT.

Zadatak 2.1.3 (Za vježbu). Napišite algoritam iz napomene kao potprogram neovisan oimplementaciji ATP LIST. Algoritam je u literaturi poznat pod nazivom insertion sort.Ukoliko je lista implementirana pomoću polja, tada se sortiranje može obaviti unutar listekorištenjem samo još jednog dodatnog elementa (in-place). Algoritam je vremenski kva-dratan, no približno sortiranu listu e kasno sortira. Zato se ponekad koristi za završetakrekurzivnog sortiranja (npr. u quicksort algoritmu).

12

Page 15: skripta SPA

..

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = ()

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (5)

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (3, 5)

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (3, 5, 7)

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (1, 3, 5, 7)

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (1, 3, 4, 5, 7)

..L1 = (5, 3, 7, 1, 4, 3) ..L2 = (1, 3, 3, 4, 5, 7)

.

SINSERT(5,L2)

.

SINSERT(3,L2)

. SINSERT(7,L2).

SINSERT(1,L2)

.

SINSERT(4,L2)

.

SINSERT(3,L2)

Slika 2.2: Primjer korištenja funkcije SINSERT za sortiranje liste L1 = (5, 3, 7, 1, 4, 3)

Zadatak 2.1.4. Napišite potprogram oblika void MERGE(LIST l1, LIST l2, LIST*l3) kojim se dvije sortirane liste L1 i L2 sažimlju u novu, također sortiranu listu L3.Potprogram mora biti neovisan o implementaciji ATP LIST.

Rješenje: Ideja rješenja je da uspoređujemo elemente lista L1 i L2, počevši od prve po-zicije. Ubacujemo manji element u treću listu, te se pomičemo u onoj listi čiji element jeubačen u treću listu.void MERGE(LIST l1, LIST l2, LIST* l3){

position p1, p2, p3;p1 = FIRST(l1);p2 = FIRST(l2);p3 = MAKE_NULL(l3);while (p1 != END(l1) && p2 != END(l2)){

if (RETRIEVE(p1,l1) <= RETRIEVE(p2,l2)){

INSERT(RETRIEVE(p1,l1),p3,l3);p1 = NEXT(p1,l1);

}else{

INSERT(RETRIEVE(p2,l2),p3,l3);p2 = NEXT(p2,l2);

}p3 = NEXT(p3,*l3);

}if (p1 == END(l1))

while (p2 != END(l2))

13

Page 16: skripta SPA

{INSERT(RETRIEVE(p2,l2),p3,l3);p2 = NEXT(p2,l2);p3 = NEXT(p3,*l3);

}else

while (p1 != END(l1)){

INSERT(RETRIEVE(p1,l1),p3,l3);p1 = NEXT(p1,l1);p3 = NEXT(p3,*l3);

}}

Napomena: Prethodni potprogrammože služiti kao osnovni korak u algoritmu za sorti-ranje. Polaznu nesortiranu listuL1 razbijemo na (što veće) podliste koje su već sortirane.Zatim redom sažimljemo dvije po dvije podliste, sve dok ne ostane samo jedna (sorti-rana) lista. Skica postupka vidljiva je na slici 2.3.

..(1, 3, 3, 4, 5, 7).

MERGE

.

(3, 5, 7),

.

(1, 3, 4)

.

MERGE

.

MERGE

.

(5),

.

(3, 7),

.

L →

.

(1, 4),

.

(3)

Slika 2.3: Primjer sortiranja liste L = (5, 3, 7, 1, 4, 3) korištenjem funkcije MERGE

Ovakav algoritam je poznat u literaturi pod nazivom merge sort. Bilo koji redosli-jed sažimanja daje ispravan rezultat (sortiranu listu). No, o redoslijedu izvršavanja ovisivrijeme izvršavanja.

U skripti Strukture podataka i algoritmi promatra se implementacija liste pomoću po-kazivača. Lista je prikazana kao vezana lista ćelija. Svaka ćelija sadrži jedan element ipokazivač (pointer) na iduću ćeliju. Također je dodana polazna ćelija (header), koja nesadrži element. Lista se poistovjećuje sa pokazivačem na header. Pozicija elementa a1je pokazivač na header, pozicija elementa a2 je pokazivač na ćeliju koja sadrži a1 i takodalje – pozicija elementa an je pokazivač na ćeliju koja sadrži an−1 ili pozicija elementaai je pokazivač na ćeliju koja sadrži pokazivač na element ai. Pozicija END(L) je po-kazivač na ćeliju koja sadrži an. Ova pomalo neprirodna de nicija služi za e kasnijuimplementaciju operacija INSERT() i DELETE().

Ova implementacija je izravno primjenjiva samo u programskim jezicima koji podr-žavaju pokazivače (Pascal, C, C++,...). Ukoliko programski jezik nema pokazivače (npr.

14

Page 17: skripta SPA

FORTRAN), implementaciju treba modi cirati tako da pokazivače zamijenimo sa kur-sorima.

Zadatak 2.1.5. Razradite implementaciju liste pomoću kursora, tako da u prethodno spo-menutoj implementaciji pomoću pokazivača zamijenite pokazivače kursorima.

Rješenje: Slobodanprostor umemoriji računala zauzmemo jednimvelikimpoljemoblika:struct {

elementtype element;int next;

} SPACE[maxlength];

Ovo polje predstavlja ”zalihu” ćelija od kojih će se graditi liste. Svaka lista je prikazanavezanom listom ćelija, s time da su veze među ćelijama uspostavljane kursorima a nepokazivačima. Vezana lista je građena isto kao i prije, dakle postoji header. Lista sepoistovjećuje s kursorom na header. Sve liste s kojima radimo ”troše” ćelije iz istog (je-dinstvenog) polja SPACE. Npr. za maxlength = 12 i za liste L = (a, b, c), M = (d, c),prikaz bi mogao izgledati kao na slici 2.4.

...0 .element

. 6.next

..1

.3

.

b

.

2

.

9

..

3

.

5

..

4

.

7

..

5

.

-1

.

d

.

6

.

10

.

a

.

7

.

2

..

8

.

11

.

c

.

9

.

-1

.

c

.

10

.

-1

..

11

.

1

.

SPACE

.

0

.

M

.

4

.

L

.

8

.

available

Slika 2.4: Prikaz implementacije vezane liste korištenjemkursora. Sivombojomoznačenisu headeri

Sve slobodne ćelije koje nisu dio ni jedne liste povezali smo u dodatnu vezanu listu(bez headera) koju zovemo available. Kad god nam treba nova ćelija, uzmemo je sa po-četka vezane liste available (simulacija korištenja new). Tipovi LIST i position iz ATPLIST de nirani su ovako:typedef int LIST;typedef int position;

Da bi dovršili implementaciju, treba još napisati potprograme koji realiziraju opera-cije iz ATP LIST. U tu svrhu je dovoljno prepisati potprograme iz implementacije po-moću pokazivača, tako da pokazivače zamijenimo kursorima. To ćemo napraviti zafunkciju INSERT, dok bi za preostale funkcije postupak bio sličan.

15

Page 18: skripta SPA

void INSERT(elementtype x, position p){

position temp;if (available == -1) printf(”Nema slobodnog mjesta.”);else{

temp = SPACE[p].next;SPACE[p].next = available;available = SPACE[available].next;SPACE[SPACE[p].next].element = x;SPACE[SPACE[p].next].next = temp;

}}

Ovaj zadatak ilustrira općenitu ideju kako se bilo koja implementacija temeljena na po-kazivačima može preraditi u implementaciju temeljenu na kursorima. U ovom kolegijućemo izložiti mnogo struktura podataka sa pokazivačima. Iako to ne naglašavamo, po-drazumijevamo da se iste strukture mogu ostvariti i kursorima.

2.2 StogStog je posebna, jednostavnija vrsta liste. Jedan kraj liste nazivamo vrh stoga. Osim funk-cije za pretvaranje stoga u prazni, od operacija nad listamadozvoljene su samoubacivanjei brisanje elemenata na vrhu stoga. Čitanje sadržaja elemenata stoga je također dozvo-ljeno samo na vrhu.

...

Stog

..

Stog

.

..Vrh stoga ..a6

..a5 ..a5

..a4 ..a4

..a3 ..a3

..a2 ..a2

..a1 ..a1

.

a6

..

Ubacivanje

.

Izbacivanje

Slika 2.5: Skica operacija ubacivanja i izbacivanja elementa u tipu podataka stog

Možemo zamisliti da imamo knjige naslagane jednu na drugu u nekoj kutiji – u sva-kom trenutku vidimo samo onu knjigu koja je na vrhu stoga i samo nju možemo uzetii maknuti van, te samo na nju možemo položiti novu knjigu. Stog se još naziva i LIFOstruktura (last-in- rst-out). Navodimo članove ATP STOG:

16

Page 19: skripta SPA

.....

S

..

1

.

S

..21

.

S

..

1

.

S

..31

.

S

.PUSH(1,&S)

.PUSH(2,&S)

.POP(&S)

.PUSH(3,&S)

Slika 2.6: Primjer korištenja funkcija PUSH i POP

elementtype bilo koji tip

STACK konačan niz podataka tipa elementtype

MAKE_NULL(STACK &S) pretvara S u prazni stog

EMPTY(STACK S) logička funkcija koja vraća istinu ako je stog prazan, a inače laž

PUSH(elementtype x, STACK &S) ubacuje x na vrh stoga S

POP(STACK &S) uklanja element na vrhu stoga

TOP(STACK S) vraća element sa vrha stoga (element ostaje i dalje na vrhu stoga)

Primjer jedne uporabe stoga je implementacija funkcijskih poziva u programskimjezicima. Ugrubo, kada se izvršava neki program, program izvršava naredbu po naredbukoristeći pritom programsko brojilo koje sadrži adrese naredbi i povećava se nakon svakeizvršene naredbe. Pri pozivu neke funkcije adresa trenutne naredbe i poslani parame-tri stavljaju se na stog – ako se unutar pozvane funkcije ponovno pozove neka drugafunkcija, podaci se ponovno stavljaju na stog. Funkcije se onda izvršavaju očekivanimredoslijedom (prvo posljednja pozvana, pa pretposljednja i tako dalje). Uporaba stoganam omogućava simulaciju svakog rekurzivnog programa nerekurzivnim.

Primjer: Neki kalkulatori omogućavaju unos u post x obliku (poznatom i kao obrnutipoljski zapis, reverse Polish notation ili RPN). U takvom zapisu unosi se potrebni brojoperanada, te potom operator. Time se izbjegava uporaba zagrada. Na primjer izraz(3+4)*2 se može zapisati kao 34+2*.

Zadatak 2.2.1. Opišite evaluaciju post x izraza na stogu. Prikažite sve korake na primjeruizračunavanja (A/(BC)) ∗D + E. Izraz prvo prebacite u post x.

Rješenje: Prvo prebacujemo izraz u post x zapis: (A/(BC)) ∗D + E ≡ ABC ↑ /D ∗E+. Potom zapisujemo algoritam za izračunavanje izraza:za svaki znak ch postfix izraza (redom){

ako je ch operand // tj. ako je ch brojstavi ch na stog

inače{

17

Page 20: skripta SPA

uzmi broj sa vrha stoga i spremi ga u b1uzmi broj sa vrha stoga i spremi ga u b2izvrši operaciju ch nad b2 i b1 // ch(b2,b1)rezultat stavi na stog

}}uzmi broj sa vrha stoga - to je rezultat

Postupak se može vidjeti na skici 2.7.Ostaje pitanje automatizacije postupka prevođenja izraza iz in x u post x zapis. Je-

dan takav postupak poznat je pod imenom Dijkstrin algoritam . Mogući problemi uprevođenju su:

Prioritet operatora Primjerice izraz 1 + 2 ∗ 3 nije 12 + 3∗ već 123 ∗+

Zagrade Njih možemo lako riješiti ”rekurzivno” (izraz unutar zagrada tretiramo kaopotpuno novi izraz koji rješavamo ispočetka i tako za svaku razinu ugnježđivanja)

Zapišimo pojednostavljenu inačicu Dijkstrinog algoritma:

1. učitaj sljedeći znak in x ulaza

2. ako je znak broj, ispiši ga

3. ako je znak operator tada:

(a) sve dok stog nije prazan i operator na vrhu stoga ima veći ili jednak prioritetod učitanog znaka, ispiši i ukloni operator na vrhu stoga (pretpostavimo ’(’ima najmanji prioritet)

(b) stavi znak na stog

4. ako je znak ’(’, stavi na stog

5. ako je znak ’)’, ispisuj i uklanjaj sve operatore sa vrha stoga sve dok ne naiđeš na ’(’;’(’ ukloni bez ispisivanja

6. ako preostaje još znakova na ulazu, vrati se na korak 1

7. ako nema više znakova na ulazu ispiši i uklanjaj ostatak stoga

Zadatak 2.2.2. Dijkstrinim algoritmom prebacite A+B*C+(D+E)*F u post x.

Rješenje: Kako bismo dobili izraz u post x obliku, provodimo opisani postupak. Pro-vođenje postupka vidljivo je na slici 2.8.

Napomena: U literaturi postoji više ”Dijkstrinih algoritama”. Gore opisani algoritamse naziva još i ”Shunting-yard” algoritam. Najuobičajenije se Dijsktrinim algoritmomnaziva onaj za pronalaženje minimalnog razapinjućeg stabla u grafu.

Edsger Wybe Dijkstra (1930—2002)

18

Page 21: skripta SPA

trenutna pozicija operacije koje radimo stog SABC↑/D*E+ PUSH(A,*S) A

ABC↑/D*E+ PUSH(B,*S) BA

ABC↑/D*E+ PUSH(C,*S)CBA

ABC↑/D*E+ t1 = TOP(S); POP(*S);t2 = TOP(S); POP(*S);α = t2 ↑ t1;PUSH(α,*S);

αA

ABC↑/D*E+ t1 = TOP(S); POP(*S);t2 = TOP(S); POP(*S);β = t2 / t1;PUSH(β,*S);

β

ABC↑/D*E+ PUSH(D,*S) Dβ

ABC↑/D*E+ t1 = TOP(S); POP(*S);t2 = TOP(S); POP(*S);γ = t2 ∗ t1;PUSH(γ,*S);

γ

ABC↑/D*E+ PUSH(E,*S) Eγ

ABC↑/D*E+ t1 = TOP(S); POP(*S);t2 = TOP(S); POP(*S);δ = t2 + t1;PUSH(δ,*S);

δ

Slika 2.7: Skica postupka računanja izraza u post x zapisu

19

Page 22: skripta SPA

A+B*C+(D+E)*F A je operand pa ga ispisujemoA+B*C+(D+E)*F + je operator pa ga stavljamo na stogA+B*C+(D+E)*F B je operand pa ga ispisujemoA+B*C+(D+E)*F * je operator većeg prioriteta od vrha stoga pa ga stavljamo na stogA+B*C+(D+E)*F C je operand pa ga ispisujemoA+B*C+(D+E)*F + je operator pa ispisujemo sve operatore većeg ili jednakog prioritetaA+B*C+(D+E)*F ( je najmanjeg prioriteta pa ju stavljamo na stogA+B*C+(D+E)*F D je operand pa ga ispisujemoA+B*C+(D+E)*F + je operator većeg prioriteta pa ga pišemo na stogA+B*C+(D+E)*F E je operand pa ga ispisujemoA+B*C+(D+E)*F ) je desna zagrada – ispisujemo i izbacujemo sa vrha stoga do (A+B*C+(D+E)*F * je operator, a stog je prazan – stavljamo na stogA+B*C+(D+E)*F F je operand, ispisujemo ga

ispisujemo i izbacujemo operatore sa vrha stoga

ulaz A + B * C + ( D + E ) * Fizlaz A B C *+ D E + F *+

stog+ +

*+

*+ +

(+

(+

+(+

+(+ +

*+

*+

Slika 2.8: Prikaz prevođenja izraza iz in x u post x oblik

Zadatak 2.2.3. Problem Hanojskih tornjeva glasi ovako: ”Dana su 3 štapa A, B i C. Naštap A je nanizano n diskova različitog promjera. Potrebno je prebaciti sve diskove sa štapaA na štap C koristeći B kao pomoć. Odjednom je moguće prebaciti samo 1 disk; ne smije sestavljati veći disk na manji.”

Nedostaje: skica Hanojski tornjevi, vidite npr. ovu sliku

Napišite rekurzivni program koji rješava ovaj problem. Neka move(x,y) označava pre-mještanje jednog diska sa štapa x na štap y.

Slika 2.9: Primjer Hanojskih tornjeva za n = 3

Rješenje: Što smo zapravo radili u primjeru sa n = 3? Premjestili smo gornja 2 diska(n− 1 diskova) sa štapa A na B koristeći C. Potom smo prebacili najveći disk sa štapa Ana štap C. Na kraju smo prebačena dva diska sa štapa B prebacili na štap C koristeći štapA. To nas navodi na ideju indukcije:

20

Page 23: skripta SPA

baza Za n = 1, rješenje je trivijalno – jednostavno prebacimo jedini disk na bilo kojištap

korak Zamislimo da znamo prebacivati sve hrpe od≤ n− 1 diskova. Tada za n diskovakonstruiramo algoritam:

• premjestimo gornjih n− 1 diskova sa A na B koristeći C• prebacimo najveći disk sa A na C• premjestimo n− 1 diskova sa B na C koristeći A

Zapišimo ovo u programskom kôdu:void HANOI (int n, char polazni, char pomocni, char zavrsni){

if (n == 1){

move(polazni,zavrsni);return;

}HANOI(n-1,polazni, zavrsni, pomocni);move(polazni,zavrsni);HANOI(n-1,pomocni,polazni,zavrsni);

}

Koliko koraka ovo traje? Za premještanje jednog diska trebamo jedan potez, pa je po-trebno vrijeme jedan. Za premještanje n diskova trebamo premjestiti hrpu od n − 1diskova, pa najveći disk, pa opet hrpu od n−1 diskova – ukupno 2 puta vrijeme od n−1premještanja i još jedan za najveći disk.

T (1) = 1

T (n) = 2 · T (n− 1) + 1

}T (n) = 2n − 1

O načinu računanja rekurzija trebali biste čuti na kolegiju ”Diskretna matematika”. Namreži su dostupni skenovi predavanja kolegija ”Složenost algoritama” koja detaljnije ras-pisuju ovo rješenje (Saša Singer: Uvod u složenost).

Zadatak 2.2.4. Napišite nerekurzivnu verziju funkcije HANOI

Rješenje: Rekurzivne pozive simuliramopomoću stoga (i to neovisno o implementaciji).U tu svrhu je potrebno na stog spremiti trenutni opis situacije prije ulaska u rekurziju -koliki je n, koji je štap početni, pomoćni,... Potrebno je znati i u kojoj smo fazi (1)-(5)– ovime zapravno simuliramo adresu instrukcije na koju se vraćamo nakon funkcijskogpoziva.

Potrebno je de nirati što je elementtype koji sprema trenutnu situaciju.typedef struct {

int n, faza; // ”faza” simulira adresu sljedeće po redu naredbechar pocetni, pomocni, zavrsni;

} elementtype;

21

Page 24: skripta SPA

Nakon toga možemo zapisati našu funkciju.void HANOI_STACK(int n, int pocetni, int pomocni, int zavrsni){

elementtype x;STACK S;MAKE_NULL(&S);f1: if(n == 1)

{move(pocetni,zavrsni);goto f5;

}f2: // spremamo trenutnu situaciju na stog

x.n = n;x.pocetni = pocetni; x.pomocni = pomocni; x.zavrsni = zavrsni;x.faza = 3;PUSH(x,&S);// za novi ”poziv” funkcijen = x.n - 1;pocetni = x.pocetni; pomocni = x.zavrsni; zavrsni = x.pomocni;goto f1; // simuliramo poziv

f3: move(pocetni,zavrsni);f4: // spremamo trenutnu situaciju na stog

x.n = n;x.pocetni = pocetni; x.pomocni = pomocni; x.zavrsni = zavrsni;x.faza = 5;PUSH(x,&S);n = x.n - 1;pocetni = x.pomocni; zavrsni = x.zavrsni; pomocni = x.pocetni;goto f1;

f5: if (EMPTY(S))return;

else{

/* gotovi smo s jednim pozivom - treba napraviti ”return”to znači vratiti prethodno stanje koje je na stogu */x = TOP(S); POP(&S);n = x.n;pocetni = x.pocetni; pomocni = x.pomocni; zavrsni = x.zavrsni;switch(x.faza){

case 3: goto f3; break;case 5: goto f5; break;

}}

}

Zadatak 2.2.5 (**). Učitajte brojeve n, n1, n2 i n3. n je ukupan broj diskova, n1 je brojdiskova na prvom štapu, n2 je broj diskova na drugom štapu, n3 na trećem štapu. Učitajte

22

Page 25: skripta SPA

n1 brojeva – veličine diskova na prvom štapu, a isto i za drugi i treći štap. Odredite kolikominimalno prebacivanja treba napraviti iz ove pozicije do pozicije u kojoj su svi diskovi nanekom (bilo kojem) štapu. Ispišite koji je to štap i koliko poteza treba (modulo 1000000).Neka je n ≤ 100000.

Zadatak 2.2.6. Razradite implementaciju stoga pomoću pokazivača. Ispišite potrebne de-nicije i funkcije.

Rješenje: Zapisujemo potrebne strukture i funkcije u programskom jeziku.typedef struct celija{

elementtype element;struct celija* next;

} celltype;

typedef celltype* STACK;

void MAKE_NULL(STACK* S){

*S = null;}bool EMPTY(STACK S){

if (S == null)return true;

elsereturn false;

}void PUSH(elementtype x, STACK *S){

celltype *temp;temp = (celltype*) malloc(sizeof(celltype));temp->element = x;temp->next = S;*S = temp;

}elementtype TOP(STACK S){

if(EMPTY(S)) printf(”Stog je prazan!\n”);else

return S->element;}void POP(STACK *S){

celltype *temp;if(EMPTY(*S)) printf(”Stog je prazan!\n”);else{

23

Page 26: skripta SPA

temp = S->next;free(S);S = temp;

}}

Napomena: Mogli smo iskoristiti ranije izvedenu implementaciju ATP LIST pomoćupokazivača i trivijalno napisati funkcije za ATP STACK. No to je nee kasno i komplici-rano jer je stog jednostavnija struktura.

2.3 RedRed (engl. queue) je specijalna vrsta liste. Od svih operacija nad listama dozvoljene susamo:

• Ubacivanje elemenata na jedan kraj liste (”začelje”, kraj liste)

• Uklanjanje elemenata sa drugog kraja liste (”čelo”, početak liste)

• Pregled sadržaja elementa samo na početku liste

Intuicija za ovaj apstraktni tip podataka je čekanje u bilo kakvom redu.

Primjer: Službenik u banci obrađuje klijente – ispred njega je red klijenata koji čekaju.U svakom trenutku službenik radi samo s klijentom koji je na početku reda; on odlazi izreda kada ga službenik obradi. Kad dolazi posve novi klijent, staje na kraj reda.

Računalni sustavi međusobno šalju poruke. Obrada vremena traje neko vrijeme.Ako u međuvremenu stignu druge poruke, stavljaju se u red čekanja.

Zbog svojih svojstava red još nazivamo i FIFO ( rst-in- rst-out) struktura. Navodimočlanove apstraktnog tipa podataka red.

elementtype bilo koji tip

QUEUE konačan niz podataka tipa elementtype

MAKE_NULL(QUEUE &Q) pretvara Q u prazni red

bool EMPTY(STACK S) logička funkcija koja vraća istinu ako je red prazan, a inačelaž

ENQUEUE(elementtype x, QUEUE &Q) ubacuje x na začelje reda Q

DEQUEUE(QUEUE &Q) uklanja element s čela reda Q; nije de nirano ako je red prazan(EMPTY(Q) == TRUE)

elementtype FRONT(QUEUE Q) vraća vrijednost elementa na čelu reda Q; nije de-nirano ako je red prazan

24

Page 27: skripta SPA

.... ..2 ..25 ..257 ..57.ENQ(2,&S) .ENQ(5,&S) . ENQ(7,&S). DEQ(&S)

Slika 2.10: Primjer korištenja funkcija ENQUEUE i DEQUEUE

Red se primjenjuje u takozvanim Breadth-First-Search algoritmima (BFS). Kod tih sealgoritama zadatak rješava razlaganjem na manje podzadatke, podzadaci na još manjepodzadatke, i tako dalje. Algoritam se sastoji od formiranja reda podzadataka koje joštreba rješiti. Program skida podzadatak sa čela i rješava ga; ako se jave novi podzadacioni se stavljaju na začelje reda. Problem je rješen kad se red isprazni.QUEUE Q;MAKE_NULL(&Q);ENQUEUE(Z,&Q); // Z označava početni zadatakwhile (!EMPTY(Q)){

elementtype P = FRONT(Q); DEQUEUE(&Q);za svaki podproblem PP od P radi

ENQUEUE(PP, &Q);}

Slika 2.11: Skica stabla podzadataka

Zadatak 2.3.1. Dan je orijentirani graf G čiji čvorovi su numerirani sa 0, 1, . . . , n− 1. Naprimjer:

Napišite funkciju koja ispisuje ”skup dostupnih vrhova” Si za dani čvor i, to jest svečvorove u koje se može doći iz čvora nekim putem u G. Pretpostavite da je graf zadanpoljem redova G koji predstavljaju popise susjeda pojedinih čvorova. Na primjer:

G[0] = (1, 2)

G[1] = (5)

G[2] = (1, 3)

G[3] = ()

...

25

Page 28: skripta SPA

..0.

2

.

5

. 1.

4

.

3

. 6

Slika 2.12: Skup dostupnih puteva za vrh 0 je S0 = 0, 1, 2, 3, 5

Rješenje: Ideja algoritma: čvorovi dostupni iz čvora i su svi susjedi od i, svi susjedi odsusjeda od i,... Si je znači sâm i, te unija skupova dostupnih od svih susjeda od i. Slije-dimo jednak postupak kao u općenitom BFS algoritmu:

• stavimo i u red Q

• sve dok se Q ne isprazni:

– uzmemo čvor s početka reda Q i ispišemo ga (ako već nije ispisan).– na Q stavimo sve susjede od tog čvora koji već nisu bili u redu Q

Stanje reda za gornji primjer

Slika 2.13: Skica stanja reda

#define N 1000void accessible (QUEUE G[], int pocetni){

bool posjeceno[N];int i;QUEUE Q;for (i = 0; i < N; i++)

posjeceno[i] = 0;ENQUEUE(pocetni, &Q); posjeceno[pocetni] = true;while (!EMPTY(Q)){

int vrh = FIRST(Q);printf(”%d”, vrh);while (!EMPTY(G[vrh])){

26

Page 29: skripta SPA

int susjed = FIRST(G[vrh]);DEQUEUE(&G[vrh]);if (!posjeceno[susjed]){

ENQUEUE(susjed,&Q);posjeceno[susjed] = true;

}}DEQUEUE(&Q);

}}

Napomena: Može li se ovaj zadatak riješiti pomoću stoga? Kojim se redoslijedom u tomslučaju obilaze vrhovi?

Zadatak 2.3.2. Napišite funkciju int distance(QUEUE G[], int start, intcilj) koja vraća udaljenost između vrhova start i cilj. Uputa: malo izmjenite rješenjeprethodnog zadatka.

Napomena: Napredavanjima smopromatrali implementaciju reda pomoću cirkularnogpolja.

Slika 2.14: Skica cirkularnog polja

• temeljena je na strukturi podataka koju čine (cirkularno) polje i kursori na čelo izačelje reda

• polje se ne smije do kraja napuniti jer u protivnomne bismomogli razlikovati punired od praznog

• alternativno, možemo dozvoliti da se red napuni do kraja, ali onda moramo imatijoš jednu boolean ćeliju koja će označavati je li red prazan

Zadatak 2.3.3. Razradite implementaciju reda pomoću cirkularnog polja, kursora na čeloi začelje, te ćelije koja označava je li red prazan ili ne.

Rješenje: Zapisujemo kôd rješenja:

27

Page 30: skripta SPA

#define maxlength ...

typedef struct {elementtype elements[maxlength];int front, rear;bool is_empty;

} QUEUE;

bool EMPTY(QUEUE Q){

return Q.is_empty;}elementtype FRONT(QUEUE Q){

if (EMPTY(Q))printf(”Red je prazan!\n”);

elsereturn Q.elements[Q.front];

}void MAKE_NULL(QUEUE *Q){

Q->front = 0; Q->rear = maxlength - 1;Q->is_empty = 1;

}int add_one(int x){

return (x+1)%maxlength;}void DEQUEUE(QUEUE *Q){

if (EMPTY((*Q))printf(”Red je prazan!\n”);

else{

Q->front = add_one(Q->front);if (add_one(Q->rear) == Q->front)

Q->is_empty = true;}

}void ENQUEUE(elementtype x, QUEUE *Q){

if(!EMPTY(*Q) && add_one(Q->rear) == Q->front)printf(”Red je pun!\n”);

else{

Q->rear = add_one(Q->rear);Q->elements[Q->rear] = x;Q->is_empty = 0;

28

Page 31: skripta SPA

}}

Napomena: Slanje tipa QUEUE bez pokazivača (to jest, slanje po vrijednosti) u funkci-jama EMPTY i FRONT izrazito je nee kasno zbog kopiranja cijelog polja.

Nedostaje: dodatne napomene

Zadatak 2.3.4 (zadaci za vježbu za prvi kolokvij, zadatak 11.). Napišite funkciju voidqueuesort(QUEUE *Q) koja prima red Q čiji su elementi cijeli brojevi. Funkcija trebasortirati elemente reda Q od najvećeg prema najmanjem. Na primjer, ako je na ulazuQ =3, 1, 4, 1, 2, 6, onda na izlazu treba biti Q = 6, 4, 3, 2, 1, 1.

Dozvoljeno je korištenje samo još jednog pomoćnog stoga. Nije dozvoljeno korištenjedrugih ATP ni polja. Funkcija treba biti napisana neovisno o implementaciji ATP STACKi QUEUE.

Rješenje:Nedostaje: objašnjenje ideje rješenja

void queuesort(QUEUE *Q){

STACK S;int N = 0, x,y,i,j;MAKE_NULL(&S);// prebroji koliko ima elemenata u Qwhile (!EMPTY(*Q)){

x = FIRST(*Q);DEQUEUE(Q);N++;PUSH(x,&S);

}// vrati elemente iz stoga u redfor (i = 0; i < N; i++){

x = TOP(S); POP(&S);ENQUEUE(x,Q);

}// N puta stavi najmanji element sa Q na Sfor(i = 0; i < N; i++){

x = FIRST(*Q); DEQUEUE(Q);PUSH(x, &S);

29

Page 32: skripta SPA

for(j = 0; j < N-i-1; j++){

x = FIRST( *Q ); DEQUEUE( Q );if (TOP(S) > x){

y = TOP(S); POP(&S);ENQUEUE(y, Q);PUSH(x, &S);

}else

ENQUEUE(x, Q);}

}// kopiraj natrag S na Qfor(i = 0; i < N; i++){

x = TOP(S); POP(&S);ENQUEUE(x,Q);

}}

30

Page 33: skripta SPA

Poglavlje 3

Stabla

3.1 (Uređeno) stablo

31

Page 34: skripta SPA

32

Page 35: skripta SPA

33

Page 36: skripta SPA

34

Page 37: skripta SPA

35

Page 38: skripta SPA

3.2 Binarno stabloDe nicija 3.2.1. Binarno stablo T je konačan skup podataka istog tipa (čvorova) tako davrijedi:

1. T je prazno ili

2. T ima istaknuti čvor (koji nazivamo korijen), a ostali čvorovi su podijeljeni u 2 pod-skupa TL i TR (koje nazivamo lijevo odnosno desno podstablo) koji opet imaju struk-turu binarnog stabla.

Terminologija je slična kao kod uređenih stabala. Razlika je u tome što kod uređenihstabala razlikujemo ”lijevo” i ”desno” dijete i u situacijama u kojima postoji čvor imasamo jedno dijete. Kod uređenih stabala važan je samo poredak djece nekog roditelja,a ne i pozicija pojedinog djeteta. Kod binarnog stabla razlikujemo lijevo i desno dijete,bez obzira na to je li jedino dijete nekog roditelja. Navodimo članove apstraktnog tipapodataka binarno stablo (ili BTREE):

node bilo koji tip

labeltype oznaka čvora koja nosi korisnu informaciju

LAMBDA oznaka za prazan čvor

BTREE binarno stablo

void MAKE_NULL(BTREE *T) pretvara T u prazno binarno stablo

bool EMPTY(BTREE T) logička funkcija koja vraća istinu ako je binarno stablo prazno,a inače laž

node CREATE(labeltype l, BTREE T_L, BTREE T_R, BTREE *T) uQ

LEFT_SUBTREE(BTREE T, BTREE *T_L) nakon poziva, parametar TL pokazuje nalijevo podstablo

RIGHT_SUBTREE(BTREE T, BTREE *T_R) nakon poziva, parametar TR pokazujena desno podstablo

node LEFT_CHILD(node i, BTREE T) vraća ime lijevog djeteta čvora i

node RIGHT_CHILD(node i, BTREE T) vraća ime desnog djeteta čvora i

node INSERT_LEFT_CHILD(labeltype l, node i, BTREE *T) dodaje novi čvorsa oznakom l tako da on bude lijevo dijete čvora i vraća ime čvora

node INSERT_RIGHT_CHILD(labeltype l, node i, BTREE *T) dodaje novičvor sa oznakom l tako da on bude desno dijete čvora i vraća ime čvora

DELETE(node i, BTREE *T) briše čvor i iz drva T

node ROOT (BTREE T) vraća ime korijena drveta T

36

Page 39: skripta SPA

node PARENT (node i, BTREE T) vraća roditelja čvora i u stablu T

labeltype LABEL(node i, BTREE T) vraća oznaku čvora i u binarnom stablu T

void CHANGE_LABEL(labeltype l, node i, BTREE *T) postavlja oznaku čvorai na l u stablu T

Zadatak 3.2.1. Napišite funkcijuint VISINA(BTREE T) koja vraća visinu binarnog sta-bla T. Pokušajte napisati funkciju sa i bez korištenja funkcija LEFT_SUBTREE i RIGHT_-SUBTREE.

Rješenje: Prvo ćemo zapisati rješenje korištenjem LEFT_SUBTREE i RIGHT_SUBTREEfunkcija, a potom bez korištenja.

pomoću SUBTREE funkcija Uovom slučaju rekurzivno prolazimo kroz svemanja stablai vraćamo visinu uvećanu za 1.int VISINA(BTREE T){

if(EMPTY(T))return -1;

else{

BTREE TL, TR;

LEFT_SUBTREE( T, &TL );RIGHT_SUBTREE( T, &TR );

return 1 + max(VISINA(TL), VISINA(TR)); // uz pret-postavku da smo je funkcija max(a,b) definirana

}}

bez korištenja SUBTREE funkcija Kakobismoosigurali rekurzivnost po čvorovimaumjestopo stablima, početni poziv samo poziva rekurzivnu funkciju koja kao argumentprima čvor.int VISINA(BTREE T){

return vis(ROOT(T), T);}

int vis(node n, BTREE T) // vraća visinu podstabla čiji jekorijen čvor n{

if (n == LAMBDA)return -1;

else{

int vL, vR;

37

Page 40: skripta SPA

vL = vis(LEFT_CHILD(n, T), T);vR = vis(RIGHT_CHILD(n, T), T);

return 1 + max(vL, vR);}

}

Zadatak 3.2.2. Napišite funkciju int BRAT(BTREE B) koja vraća broj čvorova koji nisulistovi i čiji brat ima 1 dijete, te koji imaju 5 ili više potomaka.

Rješenje: Pokušajmo si pomoći razbijanjem traženih uvjeta na više manjih. Za takvemanje uvjete pišemo manje funkcije:int jeList(node n, BTREE B) // pretpostavimo da n nije LAMBDA{

if(LEFT_CHILD(n, B) == LAMBDA && RIGHT_CHILD(n, B) == LAMBDA)return 1;

elsereturn 0;

}

int bratOK(node n, BTREE B){

node roditelj, brat;

if(n == ROOT(B)) // korijen nema braćereturn 0;

roditelj = PARENT(n, B);if( n == LEFT_CHILD(n, roditelj))

brat = RIGHT_CHILD(n, roditelj);else

brat = LEFT_CHILD(n, roditelj);

if(brat != LAMBDA){

int brojDjece = (LEFT_CHILD(brat, B) != LAMBDA) +(RIGHT_CHILD(brat, B) != LAMBDA);

return brojDjece == 1;}else

return 0;}

int brojPotomaka(node n, BTREE B){

int lijevo = 0, desno = 0;

38

Page 41: skripta SPA

if(n == LAMBDA) return 0;

if(LEFT_CHILD(n, B) != LAMBDA)lijevo = 1 + brojPotomaka(LEFT_CHILD(n, B), B);

if(RIGHT_CHILD(n, B) != LAMBDA)desno = 1 + brojPotomaka(RIGHT_CHILD(n, B), B);

return lijevo + desno;}

Kao i u prošlom zadatku jer prototip funkcije zahtijeva binarno stablo kao argument,pišemo rekurzivnu funkciju koja umjesto kopija cijelog stabla koristi čvorove za prola-zak po stablu. Stoga u početnu funkciju dodajemo samo poziv na takvu novostvorenufunkciju.int BRAT(BTREE B){

return prebroji(ROOT(B), B);}

int prebroji(node n, BTREE B){

int nJeTakav = 0;

if(n == LAMBDA) return 0;

if(!jeList(n, B) && bratOK(n, B) && brojPotomaka(n, B) >= 5)nJeTakav = 1;

return nJeTakav + prebroji(LEFT_CHILD(n, B), B) +prebroji(RIGHT_CHILD(n, B), B);

}

Zadatak 3.2.3. Napišite funkciju LABEL MAXBRAT(BTREE B) koja vraća najveći labelmeđu svim čvorovima iz prethodne funkcije.

Rješenje: Za rješenje ovog zadatka iskoristit ćemo prethodno napisane funkcije. Novostje korištenje vanjskih spremišta max i nadjen kojim može pristupiti svaki rekurzivnipoziv funkcije jer se u pozive prosljeđuje adresa spremišta.labeltype MAXBRAT(BTREE B){

labeltype maxi;int nadjen = 0;obidjiStablo( ROOT(B), B, &max, &nadjen);

if(!nadjen)printf(”Nema takvog...”);

39

Page 42: skripta SPA

return max;}

void obidjiStablo(node n, BTREE B, labeltype *maxi, int *nadjen){

if(n == LAMBDA) return;

if(!jeList(n, B) && bratOK(n, B) && brojPotomaka(n, B) >= 5)if(*nadjen == 0 || *max < LABEL(n, B)){

*nadjen = 1;*max = LABEL(n, B);

}

obidjiStablo(LEFT_CHILD(n, B), B, max, nadjen);obidjiStablo(RIGHT_CHILD(n, B), B, max, nadjen);

}

Zadatak 3.2.4 (Svojstva binarnih stabala). Dokažite sljedeće tvrdnje:

1. Maksimalni broj čvorova na nivou i binarnog stabla je 2i

2. Maksimalni ukupan broj čvorova u binarnom stablu visine v je 2v+1 − 1

3. Neka je n0 broj listova, a n2 broj čvorova s 2 djece. Dokažite da je n0 = n2 + 1

4. Visina binarnog stabla sa n čvorova je veća ili jednaka od ⌈log2(n+ 1)− 1⌉

5. Visina binarnog stabla sa n0 listova je veća ili jednaka od ⌈log2 n0⌉

Rješenje: Dokazujemo tvrdnje redom:

1. Prvu tvrdnjumožemo dokazati indukcijom po nivou binarnog stabla i. Na nultomnivou nalazi se samo korijen, a za i = 0 imamo 2i = 20 = 1.Pretpostavimo da na nivou i − 1 imamo najviše 2i−1 čvorova. Jer u binarnomstablu svaki čvormože imati maksimalno 2 djeteta, na nivou i imamomaksimalno2i−1 · 2 = 2i čvorova.

2. Prema 1, maksimalni broj čvorova na nivou i je 2i. Ukupan broj čvorova u binar-nom stablu je zbroj čvorova po svim nivoima stabla, pa je u binarnom stablu visinev maksimalni broj čvorova 20 + 21 + . . .+ 2v =

∑i=vi=0 2

i = 2k+1 − 1.

3. Označimo sa n1 broj čvorova sa 1 djetetom. Čvorovi mogu biti listovi (bez djece),imati jedno ili dva djeteta. Ukupan broj čvorova u binarnom stablu je zato n =n0 + n1 + n2.Do svakog čvora osim korijena vodi točno jedan brid od roditelja, pa je broj bri-dova je b = n − 1 = n0 + n1 + n2 − 1. S druge strane, iz čvorova izlazi onolikobridova koliko imaju djece, pa je broj bridova b = n1 + 2n2.Konačno, iz ovih identiteta dobivamo n0+n1+n2−1 = n1+2n2 ⇒ n0 = n2+1.

40

Page 43: skripta SPA

.

Slika 3.1: Dodavanje čvorova kako bi popunili stablo

4. Neka je v visina nekog binarnog stabla Prema 2 je n ≤ 2v+1 − 1. Slijedimo niznejednakosti:

n ≤ 2v+1 − 1

n+ 1 ≤ 2v+1/ log2log2(n+ 1) ≤ v + 1

⇒ v ≥ log2(n+ 1)− 1

Na kraju, jer je v cijeli broj vrijedi v ≥ ⌈log2(n+ 1)⌉ − 1.

5. Neka je T binarno stablo sa n0 listova visine vT . Ako u T postoji neki list (ili čvorsa samo jednim djetetom) na nivou manjem od vT , stvorimo novo stablo T1 takoda tom listu dodamo dva djeteta odnosno preostalo dijete, kao na slici 3.1.Postupak ponavljamo sve dokmožemo. Timedobivamokonačni niz stabalaT, T1, . . . , Tk.Visina zadnjeg (potpunog) stabla je jednaka visini polaznoga, vTk

= vT , jer nismododavali djecu u posljednjem nivou. Također, jer smo u svakom koraku samo do-davali djecu vrijedi n0(Tk) ≥ n0(T ).Stablo Tk je potpuno pa posebno ima sve listove na posljednjem nivou vT . Prema1 vrijedi:

n0(T ) ≤ n0(Tk) ≤ 2vTk = 2vT / log2⇒ vt ≥ log2(n0)

Ponovno, jer je vT cijeli broj slijedi

vt ≥ ⌈log2(n0)⌉

Zadatak 3.2.5. Binarno stablo na slici 3.2 opisuje građu aritmetičkog izraza. Ispišite inor-der, preorder i postorder obilaske sa stabla na slici.

Rješenje: Traženi ispisi obilazaka prikazani su u tablici 3.1. Što primjećujete pri postor-

Preorder: + * / 32 ↑ 2 4 3 5Inorder: 32 / 2 ↑ 4 * 3 + 5

Postorder: 32 2 4 ↑ / 3 * 5 +

Tablica 3.1: Ispisi obilazaka stabla iz zadatka 3.2.5

der obilasku? Postorder obilazak stabla aritmetičkog izraza daje zapis izraza u post xobliku.

Zadatak 3.2.6. Dokažite da je binarno stablo u kojem svi čvorovi čuvaju međusobno raz-ličite informacije jednoznačno određeno preorder i inorder obilascima.

41

Page 44: skripta SPA

..+.

.

/

.

32

.

.

2

.

4

.

3

.

5

Slika 3.2: Stablo aritmetičkog izraza

Rješenje: Opisat ćemo algoritam (rekurzivni, induktivni) iz kojega će ovo biti očito. Pri-sjetimo se prvo redoslijeda obilaženja pri traženim obilascima: preorder obilazak rekur-zivno posjećuje korijen, pa lijevo stablo L i na kraju desno stablo D. Inorder obilazakobilazi redom L korijen D.

Ideja rješenja je pokazati da možemo jednoznačno odrediti koji čvor je korijen, tekoja su preostala podstabla L i D. Potom ponavljamo postupak određivanja korijena dokne odredimo cijelo stablo. Naime, zapis preorder obilaska nam jednoznačno određuješto je korijen stabla – prvi element u obilasku. Nakon što smo odredili korijen, jer susvi čvorovi međusobno različiti po pretpostavci, možemo ga pronaći i inorder obilasku.Time smo odredili preostala stabla, jer korijen razdvaja lijevo i desno podstablo u zapisuinorder obilaska. Zapišimo ovu ideju u obliku matematičke indukcije po broju čvorovau stablu:

baza Kada je u stablu samo n = 1 čvor, oba zapisa su jednaka i oba predstavljaju korijen.

pretpostavka Pretpostavimo da su sva binarna stabla koja imaju manje od n čvorovajednoznačno određena inorder i preorder obilaskom.

korak Neka je zadano neko stablo T sa n čvorova, te zapisi preorder obilaska a1, . . . , ani inorder obilaska b1, . . . , bn. Prvi član u zapisu preorder obilaska a1 je korijenstabla. Svi čvorovi čuvaju međusobno različite informacije pa možemo pronaćii takav da vrijedi bi = a1. Prema de niciji inorder obilaska, b1, . . . , bi−1 je zapis

..a1. a2 . . . ai+1.L

. ai+2 . . . an.D

.

b1 . . . bi−1

.

L

.

bi

.

bi+1 . . . bn

.

D

Slika 3.3: Skica razdvajanja stabala na korijen, lijevo i desno podstablo

lijevog podstabla, a bi+1, . . . , bn je zapis desnog podstabla (skica ovog korakamože

42

Page 45: skripta SPA

se vidjeti na slici 3.3). Primjetimoda su ti zapisi opet u inorder obliku. Time smo i upreorder zapisu odredili lijevo i desno podstablo. Određena podstabla imajumanjeod n čvorova jer ne sadržavaju korijen, pa su po pretpostavci indukcije jedinstvenoodređena.

Zadatak 3.2.7. Razmislite i dokažite možemo li jednoznačno odrediti binarna stabla akoimamo zadane druge obilaske?

• preorder i postorder

• inorder i postorder

Zadatak 3.2.8. Reproducirajte binarno stablo iz zapisa obilazaka:

preorder ABDEFHJKLCGI

inorder EDFKJLHBAGIC

Rješenje: Na slici 3.4 vidljiv je postupak rješavanja i konačno rješenje.

Slika 3.4: Skica rješavanja

De nicija 3.2.2. Potpuno binarno stablo je binarno stablo čijim se čvorovima mogu datiimena 0, 1, . . . , N tako da vrijedi:

1. lijevo dijete čvora i ima ima 2i + 1 (osim u slučaju 2i + 1 > N – u tom slučaju ganema)

2. desno dijete čvora i ima ime 2i+ 2 (ako 2i+ 2 > N , onda nema)

Napomena: Postoji jednostavna implementacija za binarno stablo. U polju dimenzije2n − 1 možemo prikazati sva potpuna binarna stabla sa visinom manjom ili jednakomn. Ovaj prikaz može poslužiti i za ”obična” (nepotpuna) binarna stabla, no puno indeksamože ostati neiskorišteno.

Zadatak 3.2.9. Razradite implementaciju binarnog stabla pomoću polja i prikažite ga upolju.

43

Page 46: skripta SPA

Rješenje: Dopunimo oznake ”lažnim” čvorovima u koje napišemo primjerice ”-” i pri-kažemo stablo kao u de niciji potpunog binarnog stabla. U tu svrhu prvo de niramočlanove ATP BTREE.#define maxlength ...typedef int node;typedef struct{

labeltype labels[maxlength];} BTREE;

Potrebno je implementirati i funkcije iz ATP. Zapisujemo samo skicu koda. Potrebno jejoš dodati provjere da su nam indeksi unutar maxlength i ako u polju piše ”-”.node ROOT (BTREE T) {

return 0;}

node LEFT_CHILD(node i, BTREE T) {return 2*i+1;

}

node RIGHT_CHILD(node i, BTREE T) {return 2*i+2;

}node PARENT(node i, BTREE T) {

return (i-1)/2;}labeltype LABEL(node i, BTREE T) {

return T.labels[i];}CHANGE_LABEL(labeltype l, node i, BTREE *T) {

T->labels[i] = l;}INSERT_LEFT_CHILD(labeltype l, node i, BTREE *T) {

T->labels[2*i+1] = l; // ili iskoristimo funkciju CHANGE_LABEL}INSERT_RIGHT_CHILD(labeltype l, node i, BTREE *T) {

T->labels[2*i+2] = l;}DELETE(node i, BTREE *T) {

T->labels[i] = ”-”;}

Napomena: U poglavlju sa listama napravili smo funkciju MERGE za spajanje dvije sor-tirane vezane liste u jednu (zadatak 2.1.4). U napomeni nakon tog zadatka spomenut jei problem određivanja redoslijeda spajanja više sortiranih lista u jednu. Ovdje ćemo datiopćenitiji pristup.

Redoslijed sažimanja opisujemo binarnim stablom kojem su listovi veličine polaznihliste koje želimo spojiti. Unutrašnji čvorovi su rezultati pojedinih sažimanja, a korijenpredstavlja ukupno vrijeme sažimanja liste.

44

Page 47: skripta SPA

Uočimo jednu posebnost ovog stabla – svaki čvor ima točno 0 ili 2 djeteta. Stablo saovakvim svojstvom nazivamo 2-stablo. Cilj nam je pronaći raspored (2-stablo) za kojeće ukupno vrijeme sažimanja biti minimalno.

Primjer: Kao primjer navedimo dva moguća sažimanja liste od 110 članova. U oba slu-čaja počinjemo sa 3 podliste: L1 veličine 30, L2 veličine 20 i L3 veličine 10.

Slika 3.5: Prikaz redoslijeda sažimanja listi u obliku stabla

Uočimo da članovi pojedine liste sudjeluju u onoliko operacija MERGE koliko je dug putod pripadnog lista do korijena stabla (tj. koliko ja razina lista). Ukupno vrijeme sažima-nja liste prikazanog nekim 2-stablom je suma vremena sažimanja pojedinih listova posvim listovima stabla,

ukupno vrijeme sažimanja =∑list Li

wi · pi︸ ︷︷ ︸ponderirana duljina

gdjewi označava vrijeme potrebno za sažimanje listaLi, a pi označava razinu odLi. Našpočetni problem za nalaženje optimalnog rasporeda sažimanja listi se sada može iskazatiovako:

Za zadane w1, . . . , wn pronađite 2-stablo sa n listova koje ima minimalnuponderiranu duljinu.

Ovaj problem rješava jedna od inačica Huffmanovog algoritma .Zadatak 3.2.10. Nađite optimalni plan sažimanja za 6 sortiranih listi sa duljinama w1 =2, w2 = 3, w3 = 5, w4 = 7, w5 = 0, w6 = 13.

Rješenje: Rješenje nalazimokorištenjemHuffmanovog algoritma. Uočimoda suuovomprimjeru polazne liste već sortirane po veličini, što općenito ne mora biti slučaj. Sasta-vimo listu koja sadrži naše liste koje shvatimo kao čvorove. U svakom koraku spajamopo dvije liste kao podstabla u novo stablo čiji je korijen kao oznaku ima sumu oznakakorijena dviju lista, dok ne dođemo do liste koja sadrži samo jedno stablo. Postupak semože vidjeti na slici 3.6.

David Albert Huffman (1925—1999), jedan od pionira računarstva

45

Page 48: skripta SPA

Algoritam 1: Huffmanov algoritamUlaz:L, lista težina sortiranih lista (w1, . . . , wn);Izlaz:T , optimalni plan sažimanja lista w1, . . . , wn u obliku binarnog stabladok lista L više od jednog elementa radi

odaberi 2 stabla T1 i T2 iz L čiji korijeni imaju najmanje oznake;izbaci stabla T1 i T2 iz liste L;stvoro novo 2-stablo Tnovo u čijem korijenu piše zbroj oznaka korijena stabalaT1 i T2, kojemu je lijevo podstablo T1 i desno podstablo T2;ubaci Tnovo u listu;

vrati jedino preostalo stablo u listi L

Zadatak 3.2.11. Pokažite da Huffmanov algoritam daje optimalni plan sažimanja sorti-ranih listi

Rješenje: Tvrdnju dokazujemo indukcijom po broju listi koje treba sažeti.baza Za n = 1 imamo samo jednu listu pa je ne treba sažimati

pretpostavka Pretpostavimo da Huffmanov algoritam daje optimalni plan sažimanjan− 1 listi sa težinama w1, . . . , wn−1.

korak Neka je T neko optimalno stablo sažimanja (ono nemora biti Huffmanovo stablo,važno je samo da je optimalno). Tvrdimo da je tada su tada čvorovi w1 i w2 nanajnižoj razini stabla, to jest p1 = p2 = v, gdje je v visina stabla T .Pretpostavimo suprotno: tada postoji neki čvor wk, k ̸= 1, 2 koji jest na najnižemnivou. Zamijenimo liw1 iliw2 sawk dobivamo stablo sa manjim ukupnim vreme-nom sažimanja od T , što je kontradikcija s optimalnošću stabla T . Naime vrijemesažimanja stabla T ′ nastalog iz stabla T je:

v(T ′) = v(T ) −pkwk − p1w1︸ ︷︷ ︸uklonimo čvorove iz stabla

+p1wk + pkw1︸ ︷︷ ︸vratimo čvorove

na svakom nivou razlike se zbraja wk − w1 puta manje u ukupnu težinu, pa tomožemo zapisati i ovako:

= v(T ) + (p1 − pk)︸ ︷︷ ︸<0

(wk − w1)︸ ︷︷ ︸≥0

< v(T )

Uočimo da se promjenom listova na istomnivou nemijenja ukupno vrijeme. Stogapostoji optimalno stablo u kojem su spojeni čvorovi s oznakama w1 i w2, pa ihsmijemo spojiti. Time dobivamo listu sa čvorovima težina (w1 + w2, w3, . . . , wn)koja sadržin−1 listi, a za nju po pretpostavciHuffmanov algoritamdaje optimalnorješenje.

46

Page 49: skripta SPA

..

..2 ..3 ..5 ..7 ..9 ..13

..5.

2

.

3

..5 ..7 ..9 ..13

..10.

5

.

2

.

3

.

5

..7 ..9 ..13

..10.

5

.

2

.

3

.

5

..16.

7

.

9

..13

..23.

10

.

5

.

2

.

3

.

5

.

13

..16.

7

.

9

..39.

23

.

10

.

5

.

2

.

3

.

5

.

13

.

16

.

7

.

9

Slika 3.6: Skica izgradnje optimalnog stabla sažimanja korištenjem Huffmanovog algo-ritma

47

Page 50: skripta SPA

Poglavlje 4

Skupovi

Apstraktni tip podataka skup ili set sastoji se od ovih elemenata:elementtype bilo koji tip sa de niranim totalnim uređajem ≤ – to su podaci koje

čuvamo u skupu

SET skup podataka tipa elementtype

void MAKE_NULL (SET* A) funkcija pretvara skupa A u prazni skup ∅

void INSERT(elementtype x, SET* A) ubacuje element x u skup A

void DELETE(elementtype x, SET* A) izbacuje element x iz skupa A (nije de-nirana ako x /∈ A

bool MEMBER(elementtype x, SET* A) vraća true ako vrijedix ∈ A, a false inače

elementtype MIN(SET A) funkcija vraća najmanji element u skupuA (najmanji pouređaju ≤)

elementtype MAX(SET A) funkcija vraća najveći element u skupuA (najveći po ure-đaju ≤)

bool SUBSET(SET A, SET B) funkcija vraća true ako vrijedi A ⊆ B, a false inače

void UNION(SET A, SET B, SET* C) rezultat funkcije je C = A ∪B

void INTERSECTION(SET A, SET B, SET* C) C = A ∩B

void DIFFERENCE(SET A, SET B, SET *C) C = A−B

Na predavanjima se obrađuju razne implementacije ATPa SET kao što su implementa-cije pomoću bit-polja i sortirane vezane liste. To su prilično jednostavne implementacijekoje su brze kod nekih, a spore kod drugih operacija (kao što je često slučaj kod ATPa).Sortirana vezana lista tako trebaO(N) za funkciju MEMBER, bit-polje ima veću složenostza funkcije MIN i MAX, sortirano polje za INSERT. Postoje i nešto kompliciranije imple-mentacije koje omogućavaju dobre složenosti svih funkcija (O(logN)). Primjer takveimplementacije je crveno-crno stablo (red-black tree)

Radi se o binarnom stablu traženja koje ”pazi” da uvijek bude balansirano. Primjenjuje se za raspore-đivanja zadataka procesorima u novijim Linux kernelima.

48

Page 51: skripta SPA

Zadatak 4.0.12. Napišite funkciju kojom se implementira UNION pod pretpostavkom da jeskup dan sortiranom listom svojih elemenata. Implementacija mora biti neovisna o ATPLIST

Rješenje: Ideja je slična kao kod MERGE, no ovaj put moramo paziti da isti element neubacujemo više putavoid UNION(LIST A, LIST B, LIST* C){

position pA, pB, pC;pA = FIRST(A), pB = FIRST(B), pC = MAKE_NULL(C);while((pA != END(A)) && (pB != END(B)){

if (RETRIEVE(pA,A) == RETRIEVE(pB, B)){

INSERT(RETRIEVE(pA,A), pC, C);pA = NEXT(pA, A); pB = NEXT(pB, B);

}else if (RETRIEVE(pA, A) < RETRIEVE(pB, B)){

INSERT(RETRIEVE(pA, A), pC, C);pA = NEXT(pA, A);

}else{

INSERT(RETRIEVE(pB, B), pC, C);pB = NEXT(pB, B);

}pC = NEXT(pC, *C);

}while (pA != END(A)){

INSERT(RETRIEVE(pA,A), pC, C);pA = NEXT(pA, A); pC = NEXT(pC, *C)m

}while (pB != END(B)){

INSERT(RETRIEVE(pB, B), pC, C);pB = NEXT(pB, B); pC = NEXT(pC, *C);

}}

49

Page 52: skripta SPA

50

Page 53: skripta SPA

4.1 Rječnik

51

Page 54: skripta SPA

52

Page 55: skripta SPA

53

Page 56: skripta SPA

54

Page 57: skripta SPA

55

Page 58: skripta SPA

56

Page 59: skripta SPA

57

Page 60: skripta SPA

58

Page 61: skripta SPA

4.2 Prioritetni red

59

Page 62: skripta SPA

60

Page 63: skripta SPA

61

Page 64: skripta SPA

Nedostaje: Ostatak gradiva može se pronaći na stranici kolegija u obliku prezentacija

62