38
Strukture podataka i algoritmi Sadržaj Osnove složenosti algoritama. Apstraktni tipovi podataka. Elementi apstraktnog tipa, pregled apstraktnih tipova (lista, stog, vektor, skup, stabla, prioritetni red, graf). Uređeni i neuređeni kontejneri. Najčešće strukture korištene za implementaciju apstraktnih tipova (polje, lista, hash, hrpa, stabla). Algoritmi za osnovne operacije nad apstraktnim tipovima. Opći pristupi rješavanja problema – rekurzivni algoritmi ("podijeli i vladaj", backtracking), pohlepni algoritmi. Složenije operacije nad apstraktnim tipovima (sortiranje, aritmetički izrazi, obilasci, najkraći put). Literatura: 1. Knuth, Donald E: "The Art of Computer Programming, Vol. 1: Fundamental Algorithms", 3rd edition, Addison-Wesley, 1997 2. Knuth, Donald E: "The Art of Computer Programming, Vol. 3: Sorting and Searching", 2nd edition, Addison-Wesley, 1998 3. Manber, Udi: Introduction To Algorithms - A Creative Approach, Addison-Wesley, 1989. 4. NIST: Dictionary of Algorithms and Data Structures, http://www.nist.gov/dads/ Definicija algoritma Algoritam je postupak (pravilo) za rješavanje određene klase problema. Sastoji se od niza instrukcija (naredbi). Izvođenje algoritma mora biti reproducibilno. Primjer: pečenje palačinki pripremi smjesu ulij ulje u tavu

Algoritmi i Strukture Podataka (FER)

Embed Size (px)

DESCRIPTION

algoritmi i strukture podataka fer

Citation preview

Page 1: Algoritmi i Strukture Podataka (FER)

Strukture podataka i algoritmi

Sadržaj

Osnove složenosti algoritama. Apstraktni tipovi podataka. Elementi apstraktnog tipa, pregled apstraktnih tipova (lista, stog, vektor, skup, stabla,

prioritetni red, graf). Uređeni i neuređeni kontejneri. Najčešće strukture korištene za implementaciju apstraktnih tipova (polje, lista, hash,

hrpa, stabla). Algoritmi za osnovne operacije nad apstraktnim tipovima. Opći pristupi rješavanja problema – rekurzivni algoritmi ("podijeli i vladaj",

backtracking), pohlepni algoritmi. Složenije operacije nad apstraktnim tipovima (sortiranje, aritmetički izrazi, obilasci,

najkraći put).

Literatura:

1. Knuth, Donald E: "The Art of Computer Programming, Vol. 1: Fundamental Algorithms", 3rd edition, Addison-Wesley, 19972. Knuth, Donald E: "The Art of Computer Programming, Vol. 3: Sorting and Searching", 2nd edition, Addison-Wesley, 19983. Manber, Udi: Introduction To Algorithms - A Creative Approach, Addison-Wesley, 1989.4. NIST: Dictionary of Algorithms and Data Structures, http://www.nist.gov/dads/

Definicija algoritma

Algoritam je postupak (pravilo) za rješavanje određene klase problema. Sastoji se od niza instrukcija (naredbi). Izvođenje algoritma mora biti reproducibilno.

Primjer: pečenje palačinki pripremi smjesu ulij ulje u tavu zagrij tavu dok ima smjese ponavljaj:

ulij pravu količinu smjese u tavučekaj dok se donja strana ispečeokreni palačinkučekaj dok se gornja strana ispečepremjesti palačinku na tanjurako je tava suha dolij malo ulja

Page 2: Algoritmi i Strukture Podataka (FER)

Ovakav opis je neprikladan za računalo. Neprecizan i nejasan. Npr. Što je smjesa? Koliko ulja? Koliko zagrijati? Kada je palačinka pečena?

Da bi algoritam bio prikladan za izvođenje, bilo na papiru, bilo na računalu mora udovoljavati nekim formalnim zahtjevima:

1. Postoji početna instrukcija.2. Po završetku neke instrukcije točno se zna koja se sljedeća izvršava

(determinizam).3. Postupak završava u konačno mnogo koraka za svaki problem iz klase koju

rješavamo (instrukcija se može izvršiti više puta).4. Postupak se sastoji od konačnog broja instrukcija.

Svojstva algoritma:1. Ulaz: Algoritam ima 0 ili više ulaznih (ali konačno mnogo) vrijednosti (objekata).2. Izlaz: Svaki algoritam mora imati barem 1 izlaz (rezultat, rješenje).3. Konačnost: Algoritam mora završiti u konačno mnogo koraka za svaki ulaz. Ako

je nužno, to se može postići i ograničavanjem skupa problema.4. Definiranost i nedvosmislenost: Svaka osnovna instrukcija mora biti

jednoznačno definirana za izvršitelja (računalo).5. Efikasnost: Mora imati "razumnu" složenost – mora završiti u "razumnom"

vremenu uz "razumni" utrošak prostora.

Analiza algoritma

Analiza algoritma ili analiza složenosti algoritma je izuzetno važna u dizajnu algoritama i programiranju. Često postoji više načina za rješavanje istog problema i moramo znati koji od njih je najefikasniji pod uvjetima koji nas zanimaju. Najčešće gledamo prostornu i vremensku složenost.

Vremenska složenost: Vrijeme potrebno za izvođenje algoritma. Mjeri se u nekim osnovnim jedinicama (strojne instrukcije, aritmetičke operacije). Jedinica se bira tako da uspoređujemo algoritme, a ne računala ili arhitekture računala.

Prostorna složenost: Memorija potrebna (obično maksimalna) za izvođenje algoritma. Jedinica mjere može biti bit, bajt, riječ, cijeli broj, broj sa pomičnim zarezom. Kod cijelih i floating-point brojeva se uzima da će konkretna implementacija zauzimati neki stalan broj bitova po svakom broju (32, 64 ...)

Prostorna složenost je uglavnom manje ograničavajuća jer algoritam istu memorijsku lokaciju često koristi više puta tokom izvođenja.

Primjer: Pečenje "n" palačinki.Podijelimo postupak u pripremu i pečenje.

Priprema: Koliko nam je vremena/posuđa potrebno za pripremu smjese? Koliko palačinki možemo napraviti od određene količine smjese?

Pečenje: Koliko nam posuđa treba tokom pečenja? Koliko traje pečenje jedne palačinke? n palačinki?

Page 3: Algoritmi i Strukture Podataka (FER)

Vidimo da se broj posuđa ne mijenja ovisno o broju palačinki koje moramo ispeći, dok se vrijeme povećava. Razumna pretpostavka je da pečenje svake palačinke traje jednako, dakle slijedi da vrijeme pečenja linearno ovisi o broju palačinki. Međutim što ako nestane smjese, a nismo dovoljno ispekli? Korekcija algoritma, nova analiza.

Primjer (Izvrednjavanje polinoma): Zadan je niz realnih brojeva an, an-1,..., a1, a0 i realni broj x. Izračunajte vrijednost polinoma Pn(x)= anxn + an-1xn-1 + ... + a1x + a0.

U problemu se pojavljuje n+2 brojeva. Pristupimo problemu preko indukcije – svodimo rješavanje zadanog problema na rješavanje manjeg problema. Pokušajmo ukloniti vodeći koeficijent an. Pretpostavimo da znamo izračunati Pn-1(x) = an-1xn-1 + an-2xn-2 + ... + a1x + a0. Baza indukcije je trivijalna: P0(x) = a0. Sada riješimo problem Pn(x) pomoću rješenja manjeg problema ( Pn-1(x) ). Korak indukcije je: Pn(x) = anxn + Pn-1(x).

Program:

for (P=0,i=0;i<=n;++i) {for (xi=1,j=0;j<i;++j)

xi *= x;P += a[i]*xi;

}

Ovaj algoritam nam očito daje točno rješenje, međutim nije efikasan. Izvršavanje zahtjeva n + (n – 1) + (n – 2) + ... + 1 = n·(n –1)/2 množenja i n zbrajanja. Bolje rješenje možemo dobiti ako uzmemo jaču pretpostavku indukcije: Znamo izračunati Pn-1(x) i znamo izračunati xn-1. Sada u koraku indukcije xn dobijemo iz xn-1 uz jedno množenje, a zatim anxn uz još jedno množenje. Pn(x) zahtjeva još jedno zbrajanje. Ukupno imamo 2·n množenja i n zbrajanja. Iako smo u koraku indukcije zahtjevali više, ukupan broj operacija je znatno manji.

Program:

for (P=0,xi=1,i=0;i<=n;++i,xi *= x) P += a[i]*xi;

Ovaj algoritam je jednostavan, efikasan i naoko optimalan. Međutim postoji i bolji algoritam. Pokušajmo sada ukloniti prvi koeficijent a0. Zapišimo naš polinom kao:

Pn(x) = (anxn-1 + an-1xn-2 + ... + a1)·x + a0 = P'n-1(x)·x + a0

Pretpostavka indukcije je sada: Znamo izračunati P'n-1(x) zadan brojevima an, an-1, ... , a1. Iz njega jednim množenjem i jednim zbrajanjem dobijamo Pn(x). Ako polinom raspišemo do kraja dobijemo:

Pn(x) = ((...((an·x + an-1)·x + an-2)·x + ..... )·x + a1)·x + a0.

Ovakav postupak izvrednjavanja polinoma zovemo Hornerovo pravilo (po W.G. Horneru). Vidimo da smo za cijeli postupak koristili samo n množenja i n zbrajanja, te jednu dodatnu memorijsku lokaciju.Program glasi:

Page 4: Algoritmi i Strukture Podataka (FER)

P = a[n];for (i=n-1;i>=0;--i)

P = x*P + a[i];

Primjer (Obris grada): Zadani su položaj i dimenzije n pravokutnih zgrada. Treba naći dvodimenzionalni obris tih zgrada uklanjanjem nevidljivih linija.

20191817161514131211109876543210 0 1 2 3 4 5 6 8 10 12 14 16 18 20 22 24 26 28 30

Ulaz za Skyline algoritam 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 8 10 12 14 16 18 20 22 24 26 28 30

Izlaz iz Skyline algoritma

Zgrade su nam predstavljene pravokutnicima, a zanima nas samo dvodimenzionalni prikaz. Sve zgrade imaju dno na istoj visini (0). Svaku zgradu B i tako možemo prikazati kao trojku (Li,Hi,Ri) – (Left, Height, Right). Li i Ri su horizontalne koordinate lijevog i desnog ruba zgrade, Hi je visina zgrade. Izlaz je lista točaka koje predstavljaju mjesta na kojima se visina obrisa mijenja. Uzmimo kao primjer listu (1,11,5), (2,6,7), (3,13,9), (12,7,16), (14,3,25), (19,18,22), (23,13,29), (24,4,28). Radi lakšeg snalaženja visine su podebljane. Obris za ove zgrade izgleda ovako (1,11,3,13,9,0,12,7,16,3,19,18,22,3,23,13,29,0).

Najjednostavniji pristup ovom problemu je dodavanje jedne po jedne zgrade u obris. Pretpostavka indukcije je da znamo riješiti problem za n-1 zgrada, zatim dodajemo ntu zgradu. Kad dodajemo jednu zgradu uzmemo postojeći obris i siječemo novu zgradu sa njim. Prvo pretražujemo obris da bi našli gdje ubacujemo lijevu koordinatu nove zgrade. Tada idemo nadesno po obrisu i korigiramo visinu na mjestima gdje je ona manja od

( 1, 11, 5)Left = 1, Height = 11, Right = 15,( 2, 6, 7)Left = 2, Height = 6, Right = 7,( 3, 13, 9) Left = 3, Height = 13, Right = 9,(12, 7, 16)Left = 12, Height = 7, Right = 16,(14, 3, 25)Left = 14, Height = 3, Right = 25,(19, 18, 22)Left = 19, Height = 18, Right = 22,(23, 13, 29)Left = 23, Height = 13, Right = 29,(24, 4, 28)Left = 24, Height = 4, Right = 28.

Page 5: Algoritmi i Strukture Podataka (FER)

visine nove zgrade. To radimo dok ne dođemo do točke u obrisu koja je desnije od kraja nove zgrade.Npr. na početku (1,11,5,0) -> (1,11,5,7,7,0) -> (1,11,3,13,9,0). Prolaz za k-tu zgradu traje "otprilike" k koraka. To nam daje ukupno 1 + 2 + ... + n-1 + n = (n+1)n/2 koraka.Da bi poboljšali algoritam koristiti ćemo se čestom i poznatom tehnikom "podijeli i vladaj" (divide and conquer). Više o ovoj tehnici saznat ćemo kasnije. Za sada pokušajmo samo umjesto proširivanja rješenja za n-1 zgradu na n, proširiti rješenje za n/2 zgrada na rješenje za n. Kod podijeli i vladaj strategije problem se dijeli u manje potprobleme koji se rješavaju rekurzivno. Rješenja manjih problema se zatim spajaju u konačno rješenje. Općenito je efikasnije podijeliti problem na potprobleme jednake veličine osim ako priroda algoritma nije takva da ćemo imati problema pri spajanju rješenja (prevelika složenost). Pokušajmo dakle spojiti rješenja dva potproblema, u našem slučaju dva obrisa. Primjenjujemo gotovo isti postupak kao i kod dodavanja jedne zgrade. Pretražujemo oba obrisa slijeva nadesno tražimo susjedne horizontalne točke i postavljamo visinu obrisa na veću od dvije postojeće visine. Spajanje traje n/2+n/2 = n koraka.Ako sada idemo izraziti složenost algoritma dobijamo T(n) = 2T(n/2) +cn. Ovdje nam je c neka konstanta koju ne moramo točno odrediti jer nam ne utječe bitnije na rješenje relacije. O ovakvom zanemarivanju konstanti ćemo više govoriti na kraju ovog uvoda u složenost algoritama (vidi O-notacija). Rješenje gornje relacije je T(n) = c1nlog(n) + c2, gdje su c1 i c2 neke druge konstante. Usporedimo li to sa brojem koraka potrebnim za izvršavanje prvog algoritma vidimo da je složenost drugog algoritma primjetno manja.

Grafička i tablična usporedba broja koraka za navedene algoritme

Možemo općenito zapamtiti da ako podijelimo problem u dva problema jednake veličine, a zatim rješenja kombiniramo u linearnom broju koraka imati ćemo složenost nlog n. Ključna činjenica u poboljšanju ovog algoritma je da dodavanje jedne zgrade u obris traje jednako kao spajanje dva upola kraća obrisa. Ako u algoritmu imamo korak koji je općenitiji nego što nam se čini potrebnim, pokušajmo ga primjeniti na složeniji dio problema.

Kako mjeriti složenost? U gornjim primjerima smo nekako prirodno uzimali broj "ulaznih elemenata". Međutim pokušati ćemo tu mjeru malo formalizirati. Zadaća je konkretni problem iz skupa koji rješavamo. Ako algoritam nalazi namanji broj u nizu cijelih brojeva, primjer zadaće je: "Nađi najmanji broj u nizu 1, 5, 4".

Imamo npr. 2 algoritma za isti problem (isti ulaz). Kako ih usporediti? Najkorektnije – izmjeriti složenost za svaku zadaću. Vrlo nepraktično – treba izvršiti oba algoritma za svaku zadaću. Skup zadaća je u najboljem slučaju veoma velik, a najčešće je

n n(n-1)/2 nlog n2 1 25 10 910 45 24

100 4950 461

2 4 6 8 10 12 14

20

40

60

80

100

Page 6: Algoritmi i Strukture Podataka (FER)

beskonačan. Moramo nekako podijeliti skup zadaća na manje dijelove i elemente unutar tih dijelova proglasiti ekvivalentnim. Npr. u gornjem primjeru skup zadaća je beskonačan skup X = { "pečenje 0 palačinki", "pečenje 1 palačinke", ..., "pečenje n palačinki", ... }.

Uvodimo složenost kao funkciju veličine zadaće koju rješavamo. Veličina zadaće nije jednoznačna, ovisit će o problemu. To je mjera količine podataka na ulazu u algoritam. Ako je zadaća označena sa x, tada njenu veličinu označavamo sa |x|. Formalno to je broj bitova potreban za reprezentaciju zadaće x na ulazu u algoritam, uz korištenje jednoznačnog i razumno kratkog načina kodiranja.

Kada se govori o cijelim brojevima (integer) u kontekstu algoritama trebamo biti oprezni jer nekad će se taj izraz nekad koristiti uz podrazumijevanje zapisa fiksne duljine (fixed integer), a nekad uz "duge brojeve" koji su posebna struktura podataka. Kod fiksne duljine unaprijed znamo koliko prostora svaki broj zauzima i tu smo skup cijelih brojeva ograničili na vrijednosti koje možemo prikazati u zadanom broju bitova (232, 264...). "Dugi brojevi" su način prikazivanja velikih brojeva u računalu. Tu nam je broj prikazan kao niz manjih brojeva – najčešće gorespomenutih. Sve operacije nad dugim brojevima se zatim izvode preko osnovnih operacija (nad fiksnim integerima) koje nam pruža računalo. Ovo moramo uzeti u obzir i kod analize ulaza u algoritam, kao i kod analize njegove složenosti jer "elementarne operacije" nad njima više nisu elementarne i moramo uzeti u obzir i njihovo trajanje i zauzeće prostora.

Primjer: Nalaženje najmanjeg elementa u polju cijelih brojeva. Ulaz je polje cijelih brojeva duljine n. Uzmimo da se radi o integerima fiksne duljine. Označimo ulazno polje sa A. Njegovi elementi su A1, A2, ..., An. Neka je duljina jednog broja k bitova, tada je njihova ukupna duljina nk bitova. Pošto je duljina fiksna uzeti ćemo kao jedinicu za mjeru riječ duljine k bitova. Ulazno polje je tada duljine n.

Primjer: Određivanje da li je broj prost. Ulaz je prirodan broj n. Što je razumno kodiranje za ovaj slučaj? Ne promatramo niti jedan konkretan algoritam za testiranje broja, promatramo samo vrstu ulaza! Uzmimo pozicioni prikaz u sustavu sa bazom b, b2.Prikažimo ulazni broj n kao:

n = akbk + ... + a1b + a0, 0 ai < b, za i = 0..k-1, 0 < ak <b

za i = 0..k, ai,su znamenke broja.

k nam je mjera duljine zapisa. Ulaz u algoritam je niz znamenki, duljina ulaza je broj znamenki.Za gornji primjer, za zadaću s ulazom n, x = {n}; duljinu zadaće u bazi b označimo sa |x|b, što je u ovom slučaju: |x|b = k+1 = logb n + 1

Ako uspoređujemo zapise u bazama b i c, zbog relacije:

logbn = logcn/logcb,

Page 7: Algoritmi i Strukture Podataka (FER)

Imati ćemo da su zapisi u te dvije baze "gotovo proporcionalni" (logcb je konstanta). Za prikaz u bazi 2 imamo |x|2 = log2 n + 1, log2 ćemo ubuduće označavati kao lg.

Duljina prikaza broja u bazi 2

U prethodnom slučaju cijeli broj nam je za našu analizu bio "nedjeljiva cjelina", dok smo ovom slučaju prirodni broj razbijali na manje jedinice – znamenke, čiji broj nismo unaprijed odredili. Prednost je mogućnost prikaza velikih brojeva, ali jedan ulazni broj više ne možemo računati kao 1 jedinicu prostora.

Prikaz u "bazi 1": Broj n napišemo kao n znakova. 1 = x, 5 = xxxxx. Duljina ovakvog zapisa je |x|1 = n. Ovo je bitno dulji zapis od bilo koje druge baze – nije "razumno kompaktan".

O-notacija

Već smo spominjali da kod usporedbe algoritama često tražimo okvirnu efikasnost. Ovdje ćemo opisati najčešći način takve aproksimacije. Ova metoda se koncentrira na ponašanje algoritama kod veoma velikog broja ulaznih podataka. Uzmimo na primjer da nam je ulaz neka matrica sa n elemenata. Neka prvom algoritmu treba 100·n koraka, a drugome 2·n2+50. Kod prvog algoritma zanemarimo konstantu 100 i kažemo da je njegovo trajanje n, kod drugog zanemarimo 2 i 50, te je njegovo trajanje n2. Kažemo da je drugi algoritam sporiji, makar npr. za n=5 prvome treba 500 koraka, a drugome 100. Naša aproksimacija vrijedi za dovoljno veliki n, konkretno n≥50. Ako bi prvi algoritam trebao npr. 100·n1.8 on je po ovoj metodi opet brži od drugoga, makar n mora biti oko 300,000,000 da bi 100·n1.8 bilo manje od 2·n2+50. Ipak ovakvi primjeri nisu česti, jer večina algoritama ima male konstante.

Ovdje koristimo već definiranu duljinu zadaće. Za istu duljinu postoji velik broj različitih zadaća i algoritam se ne mora ponašati isto za sve njih. Za ovu metodu moramo odabrati nekog predstavnika tih zadaća iste duljine. Najčešće će to biti najgori slučaj, odnosno zadaća za koju moramo izvršiti najveći broj koraka. Najbolji slučaj nam nije baš reprezentativan, jer obično postoji neka zadaća za koju je rješenje trivijano. Prosječan slučaj je opet dosta teško definirati i izmjeriti. Ako idemo varirati neke ulazne parametre, pa računamo prosječnu složenost može nam se dogoditi da odaberemo slučajeve koji se jako rijetko pojavljuju u praksi – te nam mjerenje neće odgovarati stvarnim

20 40 60 80 100

2

4

6

Page 8: Algoritmi i Strukture Podataka (FER)

rezultatima. Za analizu prosječnog slučaja potrebno je najčešće jednako posla kao i za potpunu analizu algoritma.

Uvedimo sada neke oznake za ovakve usporedbe.Funkcija g(n) je O(f(n)) ako postoje konstante c i N takve da za svaki n>N vrijedi g(n)≤c·f(n). Oznaka se čita "o" ili "veliko o". Drugim riječima, za dovoljno velike n naša funkcija g nije veća od f puta konstanta, kao što smo i najavljivali u uvodu. Vrijednosti g(n) mogu biti manje, čak i bitno manje of c·f(n), O notacija nam daje samo gornju ogradu.

Sada možemo pisati npr. 2·n2 + 10 = O(n2), 5·n + 3 = O(n). Konvencija je da je kod jednakosti sa O oznakama O uvijek na desnoj strani (nije prava jednakost). Primjetimo da zbog toga što je O gornja ograda vrijedi i slijedeće: 2·n+5 = O(n) = O(n2) ... Kod ocjene algoritma moramo paziti da ne damo previsoku gornju ogradu.

Nije greška napisati npr O(3·n+5) ali to je identično sa O(n). Slično pišemo i O(log n) bez baze logaritma, jer promjena baze mijenja samo konstantni faktor. O(1) nam označava konstantu. Ponekad želimo malo preciznije opisati složenost pa možemo pisati npr. T(n) = 2·n2 +O(n).

Za svaku monotono rastuću1 funkciju f i konstante c>0 i a>1 vrijedi (f(n))c = O(af(n)). Riječima, eksponencijalna funkcija raste brže od bilo kojeg polinoma. Ako za f(n) uzmemo n imamo da je nc = O(an). Ako za f(n) uzmemo loga n, dobijemo (loga n)c = O(aloga n) = O(n).

Slijedeća pravila nam pokazuju kako zbrajamo i množimo u O-notaciji.1. Ako je f(n)=O(s(n)) i g(n)=O(r(n)) tada je f(n) + g(n) = O(s(n) + r(n)).2. Ako je f(n)=O(s(n)) i g(n)=O(r(n)) tada je f(n) · g(n) = O(s(n) · r(n)).

Za oduzimanje i dijeljenje ne možemo primjeniti slična pravila. O-notacija nam u grubo odgovara "≤" relaciji.

Evo još nekih jednostavnih relacija koje vrijede za O notaciju:

f(n) = O(f(n))c·O(f(n)) = O(f(n)), za konstantu c>0

O(f(n)) + O(f(n)) = O(f(n))O(O(f(n))) = O(f(n))

O(f(n))·O(g(n)) = O(f(n)·g(n))O(f(n)·g(n)) = f(n)·O(g(n))

O notacija nam daje gornju ogradu za trajanje nekog algoritma. Međutim to nam nije dovoljno. Većina algoritama koje ćemo spominjati ima O(2n), tj. ne treba im više od eksponencijalnog vremena. To je međutim dosta gruba ocjena. Većina ih je mnogo brža od toga. Zato nas zanima i donja ograda na algoritam. Gornja granica nam je opet lakša za računati jer ona govori da postoji neki algoritam koji ne treba više vremena od

1 n1 ≥ n2 => f(n1) ≥ f(n2)

Page 9: Algoritmi i Strukture Podataka (FER)

prikazanog. Donja ograda znači da nijedan algoritam ne može imati bolju ogradu za taj problem.

Ako postoje konstante c i N, takve da za svaki n≥N broj koraka T(n) za rješavanje problema veličine n je barem c·g(n) tada kažemo da je T(n)=Ω(g(n)). Npr. n2 = Ω(n2-100), n=Ω(n0.9). Ω notacija nam odgovara "≥" relaciji. Ako f(n) zadovoljava f(n)=O(g(n)) i f(n)=Ω(g(n)), tada kažemo da je f(n)=Θ(g(n)). Npr. 5·n·log2n-10=Θ(n·log n). Konstante koje se javljaju za O i Ω ne moraju biti iste.

Sada imamo O, Ω i Θ koji nam otprilike odgovaraju "≤","≥" i "=".

Za relacije koje odgovaraju "<" i ">" uvodimo i novu oznaku. Kažemo da f(n)=o(g(n)) (čitamo "malo o") ako vrijedi:

Npr. n/log2n=o(n), ali n/10≠o(n).

Slično pišemo da je f(n)=ω(g(n)), ako je g(n)=o(f(n)).

Za svaku monotono rastuću funkciju f i konstante c>0 i a>1 vrijedi (f(n))c = o(af(n)). Riječima, eksponencijalna funkcija raste brže od bilo kojeg polinoma.

Page 10: Algoritmi i Strukture Podataka (FER)

Strukture podataka

Apstraktni tipovi podataka

Apstraktni tip podataka (ATP) je definiran modelom podataka i operacija nad njima. Model podataka i operacija definira što su podaci sa kojima radimo, te što operacije rade nad podacima. Njime nije definirano kako se podaci predstavljaju u memoriji niti kako operacije izvršavaju svoju zadaću.

Sučelje ATP-a je specifikacija modela u nekom programskom jeziku. Implementacija ATP-a je realizacija modela u nekom programskom jeziku. Isti ATP često može imati više rezličitih implementacija.

Operacije su jednoznačno definirane bez obzira na implementaciju. Korištenje ATP-ova nam omogućava skrivanje interne strukture podataka i odvajanje kôda koji upravlja internim strukturama od aplikacijskog kôda.

Pristupanje internim podacima vrši se isključivo preko strogo definiranih operacija (metoda). Tako se smanjuje mogućnost greške, ali i omogućava se zamjena implementacije apstraktnog tipa bez potrebe za mijenjanjem cjelokupnog kôda.

Apstraktni tipovi podataka u C-u

Kod implementacije apstraktnih tipova podataka u C-u, osnove značajke jezika koje koristimo su nam anonimni pointeri (forward deklarirani) i dinamičko alociranje memorije.

struct nepoznata_struktura *p;

Anonimni pointer se ponaša vrlo slično kao void pointer. Dok je struktura definirana samo unaprijed, ne možemo pristupiti njenoj "unutrašnjosti'', tj. nijednom njenom članu - ona je neprozirna. Pokazivač na strukturu koristimo samo kao handle kojim pristupamo našem tipu.

U C-u će nam obično header file sadržavati deklaracije za naš neprozirni pokazivač, te prototipove funkcija koje će izvršavati operacije nad našim tipom.

Page 11: Algoritmi i Strukture Podataka (FER)

ATP STACK

Apstrakcija stoga (složaja) - kolekcije podataka sa LIFO (Last In - First Out) pristupom

Tipovi:- STACK- element

Operacije:

STACK new() stvara novi prazni stogdelete (STACK s) uništava stogpush(STACK s, element e) ubacuje element na vrh stogapop(STACK s) uklanja element s vrha stogaelement top(STACK s) vraća element na vrhu stogabool is_empty(STACK s) vraća logičku vrijednost koja govori da li

je stog prazan

Implementacija pomoću niza.Implementacija pomoću vezane liste.

Prikaz operacija nad ATP STACK

Ubacivanje elementa - push(STACK s, element e);

Gornji element - element top(STACK s);

3

310

10

5

310

35

10

5

5

5

Page 12: Algoritmi i Strukture Podataka (FER)

Uklanjanje s vrha - pop(STACK s);

Sučelje za ATP STACK

typedef int stack_element_t;struct stack_tag;typedef struct stack_tag *stack_t;

stack_t stack_new();void stack_delete (stack_t S);void stack_push(stack_t S, stack_element_t e);void stack_pop(stack_t S);stack_element_t stack_top(stack_t S);bool stack_is_empty(stack_t S);

ATP QUEUE

Apstrakcija reda - kolekcije podataka sa FIFO (First In - First Out) pristupom

Tipovi:- QUEUE- element

Operacije:

QUEUE new() stvara novi (prazni) reddelete (QUEUE q) uništava redenqueue(QUEUE q, element e) ubacuje element na kraj redadequeue(QUEUE q) uklanja element s početka redaelement front(QUEUE q) vraća element na početku redabool is_empty(QUEUE q) vraća logičku vrijednost koja govori da

li je red prazan

Implementacija pomoću cirkularnog niza.Implementacija pomoću vezane liste.

5

10

3103

Page 13: Algoritmi i Strukture Podataka (FER)

Prikaz operacija nad ATP QUEUE

Ubacivanje na kraj - enqueue(QUEUE q, element e);

Element sa početka - element front(QUEUE q);

D

Izbacivanje sa početka - dequeue(QUEUE q);

Sučelje za ATP QUEUE

typedef char queue_element_t;struct queue_tag;typedef struct queue_tag *queue_t;

queue_t queue_new();void queue_delete(queue_t Q);void queue_enqueue(queue_t Q, queue_element_t e);void queue_dequeue(queue_t Q);queue_element_t queue_front(queue_t Q);bool queue_is_empty(queue_t Q);

B C D

A B C D

A B C D

A B C D

A B C D

A B C

A

Page 14: Algoritmi i Strukture Podataka (FER)

ATP SET

Apstrakcija skupa.

Tipovi:- SET- element

Operacije:SET new() stvara novi prazan skupdelete(SET s) uništava skupinsert(SET s, element e) ubacuje element u skupremove(SET s, element e) uklanja element iz skupabool is_member(SET s, element e) vraća logičku vrijednost koja

govori da li je element u skupubool is_subset(SET s1, SET s2) vraća logičku vrijednost koja

govori da li je skup s1 podskup skupa s2

SET union(SET s1, SET s2) vraća uniju skupova s1 i s2SET intersection(SET s1, SET s2) vraća presjek skupova s1 i s2- SET difference(SET s1, SET s2) vraća razliku skupova s1 i s2

Implementacija pomoću bit-vektora.Implementacija pomoću sortirane vezane liste.

Bit - vektor reprezentacija za ATP SET

A B C D E F G H I J K L M N O P

Sučelje za ATP SET

typedef char set_element_t;struct set_tag;typedef struct set_tag *set_t;

set_t set_new();void set_delete(set_t S);void set_insert(set_t S, set_element_t e);void set_remove(set_t S, set_element_t e);

0 0 1 0 0 1 0 0 0 1 1 1 0 0 0 0

Page 15: Algoritmi i Strukture Podataka (FER)

bool set_is_member(set_t S, set_element_t e);bool set_is_subset(set_t S1, set_t S2);set_t set_union(set_t S1, set_t S2);set_t set_intersection(set_t S1, set_t S2);set_t set_difference(set_t S1, set_t S2);

ATP DICTIONARY

Apstrakcija rječnika -(skupa za kojeg nisu definirani pojmovi podskup, unija, presjek, razlika)-.

Tipovi:- DICTIONARY- element

Operacije:DICTIONARY new() stvara novi prazan rječnikdelete(DICTIONARY d) uništava rječnikinsert(DICTIONARY d, element e) ubacuje element u rječnikremove(DICTIONARY d, element e) uklanja element iz rječnikabool is_member(DICTIONARY d, element e)

vraća logičku vrijednost koja govori da li je element u rječniku

bool is_empty(DICTIONARY d) vraća logičku vrijednost koja govori da li je rječnik prazan

Implementacija pomoću hash tablice.Implementacija pomoću binarnog stabla traženja.Proširenje na asocijativnu kolekciju.

Sučelje za ATP DICTIONARY

typedef int dictionary_element_t;struct dictionary_tag;typedef struct dictionary_tag *dictionary_t;

dictionary_t dictionary_new();void dictionary_delete(dictionary_t D);void dictionary_insert(dictionary_t D, dictionary_element_t e);void dictionary_remove(dictionary_t D, dictionary_element_t e);bool dictionary_is_member(dictionary_t D, dictionary_element_t e);bool dictionary_is_empty(dictionary_t D);

Jednostruko povezana vezana lista

Page 16: Algoritmi i Strukture Podataka (FER)

Dvostruko povezana vezana lista

Jednostruko povezana vezana lista sa zaglavljem

Dvostruko povezana vezana lista sa zaglavljem

Prednost zaglavlja je uklanjanje specijalnih slučajeva za ubacivanje i brisanjeelemenata. Mana je to što moramo definirati da li je to regularan ili poseban čvor.

ATP LIST

Apstrakcija liste -(kolekcije/kontejner podataka s definiranim poretkom)-.

Tipovi:- LIST- element

Operacije:

LIST_new() stvara novu praznu listudelete(LIST l) uništava listuinsert(LIST l, element e) ubacuje element na trenutnu poziciju u listiremove(LIST l) uklanja element sa trenutne pozicije iz listeelement_get(LIST l) vraća element na trenutnoj poziciji u listifirst(LIST l) postavlja listu na prvu pozicijulast(LIST l) postavlja listu na zadnju pozicijubool next(LIST l) postavlja listu na sljedeću poziciju

A B C D

CA DB

A B C

A B

Page 17: Algoritmi i Strukture Podataka (FER)

bool previous(LIST l) postavlja listu na prethodnu poziciju

Implementacija pomoću niza.Implementacija pomoću vezane liste.

Mane ovakvog pristupa:- kako manipulirati sa dvije različite pozicije u listi,- kako implementirati npr. Bubble Sort?

ATP LIST + POSITION

Tipovi:- LIST- position- element

Operacije:

LIST_new() stvara novu praznu listudelete(LISt l) uništava listuinsert(LIST l, position p, element e)

ubacuje element na zadanu poziciju u listi

remove(LIST l, position p) uklanja element sa zadane pozicije iz liste

element_get(LIST l, position p) vraća element na zadanoj poziciji u listi

position first(LIST l) vraća prvu poziciju u listiposition end(LIST l) vraća poziciju nakon zadnje u listiposition next(LIST l, position p) vraća sljedeću poziciju u listiposition previous(LIST l, position p) vraća prethodnu poziciju u listi

Bolje rješenje, ali u sučelju liste prejudiciramo da je implementacija dvostruko vezana lista ili polje.

ATP LIST + ITERATOR

Tipovi:- LIST- ITERATOR - predstavlja poziciju- element

Operacije

Page 18: Algoritmi i Strukture Podataka (FER)

LIST_new() stvara novu praznu listudelete(LIST l) uništava listuinsert(LIST l, ITERATOR i, element e)

ubacuje element na zadanu poziciju u listi

remove(LIST l, ITERATOR i) uklanja element sa zadane pozicije iz liste

ITERATOR first(LIST l) vraća prvu poziciju u listiITERATOR end(LIST l) vraća poziciju nakon zadnje u listi

Operacije na ATP ITERATOR:

delete(ITERETOR i) uništava iteratorelement_get(ITERATOR i) vraća element na zadanoj poziciji u

listinext(ITERATOR i) postavlja iterator na sljedeću pozicijubool is_valid(ITERATOR i) pokazuje li iterator na ispravnu

pozicijubool is_equal(ITERATOR i1, ITERATOR i2)

pokazuju li oba iteratora na istu poziciju

.

Kolekcija Klijent Iterator

Odvajamo sučelje liste od sučelja za prolazak (iteriranje) po listi. U sučelju liste ne odajemo kako se može prolaziti po njoj (možda postoji više načina). Još malo bolje odvajanje sučelja i implementacije nego kod liste sa pozicijama.

Gornje sučelje nam zadaje forward only iterator.

Operacije za BIDIRECTIONAL ITERATOR:- sve kao i za forward only,- previous(ITERATOR i) - postavlja iterator na prethodnu poziciju.

Operacije za RANDOM ACCESS ITERATOR:- sve isto kao i za bidirectional,- seek(ITERATOR i, index n) - postavlja iterator na n-tu poziciju.

Novi tip indeks - pozitivni cijeli broj.

Moramo paziti na brisanje iteratora koji se više ne koriste.

Iteratore možemo dijeliti i po vrsti pristupa koji omogućuju elementu - read only, read / write.

Page 19: Algoritmi i Strukture Podataka (FER)

Operacije za read / write iterator:- sve kao i za odgovarajući read only,- put(ITERATOR i, element e) - zamjenjuje element na tekućoj

poziciji novom vrijednošću.

ATP MAP

Apstrakcija proizvoljnog (korisnički definiranog) preslikavanja.

Tipovi:- MAP- ITERATOR- key- value- element = (key, value)

Operacije:MAP_new() stvara novo prazno preslikavanjedelete (MAP m) uništava preslikavanjeinsert(MAP m, element e) ubacuje element sa ključem e.key i

vrijednošću e.valueremove(MAP m, key k) uklanja element sa ključem k iz mapeITERATOR find(MAP m, key k)

pronalazi vrijednost zadanu ključem k

ITERATOR first(MAP m) daje iterator na "prvi" elementITERATOR end(MAP m) daje iterator na kraj mape (iza zadnjeg

elementa)

.

MAP ITERATOR:

delete(ITERETOR i) uništava iteratorelement_get(ITERATOR i) vraća element na zadanoj poziciji u listinext(ITERATOR i) postavlja iterator na sljedeću pozicijubool is_valid(ITERATOR i) pokazuje li iterator na ispravnu pozicijubool is_equal(ITERATOR i1, ITERATOR i2)

pokazuju li oba iteratora na istu poziciju

Implementacija pomoću hash tablice ili binarnog stabla pretraživanja.Implementacije traže dodatnu funkcionalnost nad ključem.

Page 20: Algoritmi i Strukture Podataka (FER)

Hash

¤ Uglavnom za ubacivanje i pretraživanje.¤ Ključevi od 1 do n → polje od n elemenata. Za male n OK.¤ JMBG kao ključ?¤ Imamo n ključeva iz skupa U, |U| = M. Imamo polje od m elemenata. Uvodimo hash funkciju h: U → [1…m].

1 m

¤ Svodimo veliki skup (M mogućih) na m pretinaca. Rezultat funkcije je indeks u polju.¤ Više ključeva završi u istom pretincu - sudar (collision). 1. Hash funkcija koja minimizira sudare, 2. Obrada sudara.¤ U idealnom slučaju ubacivanje i pretraga O(1).

Hash funkcije:¤ Treba raspoređivati elemente što slučajnije i što uniformnije,¤ h(x) = x mod m, za prosti m,- nekad ne možemo prilagoditi veličinu tablice na prosti broj,¤ h(x) = (x mod p) mod m, za prosti p, m < p << M,- kp + r?

0 p Raspršivanje elemenata mod p

0 m Fizičko polje (raspršivanje po mod m)

¤ h(x) = ((ax + b) mod p) mod m; a, b slučajni; a, b < p, a ≠ 0,- u prosjeku vrlo dobra za sve ulaze,¤ Možemo slučajno odabirati i p.

Obrada sudara¤ Ulančavanje:- pretraživanje: prolaz kroz listu,- ubacivanje: test na duplikate,- dinamička alokacija, dodatna memorija za pokazivače,- radi za bilo koji broj ključeva.

Page 21: Algoritmi i Strukture Podataka (FER)

1 m2

¤ Re-hashing:- otvoreno adresiranje (koristimo samo primarnu tablicu),- veličina tablice fiksna,- kod sudara na neki način tražimo alternativnu poziciju.

¤ Prelijevanje:- dvije tablice - primarna i preljevna,- stvaramo veze iz primarne u preljevnu (slično ulančavanju).

1 kPrimarna tablica

Preljevna tablica

- ako se ponovi ista vrijednost ključa element stavljamo u prvi slobodan pretinac u preljevnoj tablici.

Re-hashing¤ Linearno ispitivanje- kod sudara pokušamo sa sljedećim praznim mjestom

(h(x) + 1, h(x) + 2, …),- lako dolazi do sekundarnih sudara, stvaranje grozdova (clustering),

pretraga se može pretvoriti u linearnu.

1 k k+1

¤ Kvadratno ispitivanje

• • •

Page 22: Algoritmi i Strukture Podataka (FER)

- h2(x) = h(x) + c * i2, i je redni broj re-hasha,- ključevi sa istim primarnim hash-em slijede isti niz pozicija (sekundarni

grozdovi).

1 k k+1 k+2 k+4 m

0 1 2 4

¤ Dvostruko hash-iranje- h(x) + h2(x), h(x) + 2 * h2(x), …- h2(x) i n (broj pretinaca) relativno prosti.

k = h(x);a = h2(x);

1 k k+a k+2a m

a a

Organizacija Prednosti Maneulančavanje

•neograničen broj elemenata•neograničen broj sudara

•gubici veliki za relativno prazne tablice

re-hashing•brzo re-hashiranje

•brz pristup (primarna tablica)

•unaprijed zadan maksimalan broj elemenata

•višestruki sudari•brisanje

preljevanje•brz pristup

•sudari ne zapunjavaju primarnu tablicu

•teža procjena veličine primarne i preljevne tablice

Page 23: Algoritmi i Strukture Podataka (FER)

Stabla

¤ Formalna definicija: stablo je 1. Prazno, 2. Posjeduje korijen i 0 ili više podstabala.¤ Hijerarhijska struktura,¤ Skup čvorova (verteksa) i bridova koji ih povezuju¤ Čvor je povezan i sa roditeljem (parent) i sa djecom (child)¤ Poseban čvor - korijen stabla (root) nema roditelja (nivo 0)¤ Čvor koji nema djece - list (leaf)¤ Djeca istog čvora međusobno su siblinzi, redoslijed djece je bitan.

Root

Parent

Child

Leaf

Stupanj stabla je najveći broj djece koje posjeduje neki čvor u stablu.Stablo stupnja 2 zovemo binarno stablo, djecu označavamo sa lijevo i desno.

kod binarnog stabla ako imamo samo jedno dijete bitno je da li je lijevo ili desno

Visina stabla je najveća udaljenost između korijena i lista.¤ čvor sadrži ključ i podatke. Ključ mora biti iz potpuno uređenog skupa.- (x1 ≠ x2 => x1 < x2 ili x2 < x1)- najčešće cijeli brojevi ili brojevi sa pomičnim zarezom.

Reprezentacija stabla¤ eksplicitna - čvor sa k djece sadrži polje od k pokazivača- ponekad se čuva i pokazivač na roditelja- stablo bilo kojeg stupnja može se prikazati kao binarno

stablo - preko pokazivača na prvo dijete i pokazivača na svog siblinga. Siblinzi formiraju vezanu listu.

Page 24: Algoritmi i Strukture Podataka (FER)

¤ implicitna - koristi se polje, veze su implicirane pozicijom u polju.- za binarno stablo najčešće: korijen je na A[1],- lijevo dijete čvora na A[i] je A[2i], desno na A[2i + 1],- ako je stablo duboko, a nije balansirano - puno praznog prostora,- ako je dobro balansirano, štedimo na pokazivačima,- dimenzije moramo znati unaprijed.

~ balansirano binarno stablo.

~ duboko, nebalansirano stablo (velika dubina za mali broj čvorova).

Obilazak stabla - procedura u kojoj obiđemo svaki čvor točno jednom.

(A(B(D, E, F), C(G, H)))

B

FE

A

D G

C

A

B

CD

E

H G

H

F

Page 25: Algoritmi i Strukture Podataka (FER)

AB

D ¤ ispis obilaska stabla:

E - preorder => DEFBGHCA,

F - postorder => ABDEFCGH,

C - inorder => DBEAGCH (ignorirali smo treće dijete, slova F nema).

GH

Obilasci stabla:¤ preorder - prvo obiđemo redom svu djecu, a zatim trenutni čvor,¤ postorder - prvo obiđemo trenutni čvor, a zatim svu djecu,¤ inorder - obiđemo lijevo dijete, zatim trenutni čvor, zatim desno dijete. Ima

smisla samo za binarno stablo.

Prikaz aritmetičkih izraza

(5 * 3) + (6 - 2) - inorder,5 3 * 6 2 - + - preorder,+ * 5 3 - 6 2 - postorder.

(5 + 2) * 3 - inorder,5 2 + 3 * - preorder,* + 5 2 3 - postorder.

((16 + 4) * 5) / (9 - 7) - inorder,

16 4 + 5 * 9 7 - / - preorder.

/ * + 16 4 5 - 9 7 - postorder.

*

35 6 2

+

* -

5+

9 7

16

4

/

5

5

3

*

2

+

Page 26: Algoritmi i Strukture Podataka (FER)

Binarna stabla pretraživanja

(Binary Search Tree)

Efikasno implementiraju: traženje, ubacivanje i brisanje.

Svi potomci lijevo od čvora imaju manju vrijednost ključa, a svi desno od njega veću.

Stablo je konzistentno ako svi ključevi zadovoljavaju uvjet.

Zbog jednostavnosti ne dozvoljavamo jednake ključeve.

Operacije:¤ Pretraživanje1. Usporedimo traženu vrijednost (x) sa vrijednošću u korijenu (root).2. x = r - pronašli smo, x < r - traži u lijevom podstablu, x > r - traži u desnom podstablu.

- u najgorem slučaju pretraga traje do zadnjeg lista (leaf) u stablu (dubina stabla).Npr. Da nam je tražena vrijednost x = 8.

¤ Ubacivanje1. Pretražimo stablo za novu vrijednost (x)2. Ako ga pronađemo kraj, inače (pretraga završi na nekom listu)

umetnemo kao lijevo ili desno dijete.

¤ Brisanje- brisanje lista jednostavno,- brisanje čvora sa jednim djetetom slično,- brisanje čvore sa oba djeteta. Neka je B taj čvor: 1. Zamijenimo ključ od B sa ključem čvora X takvim da:

a. X ima najviše jedno dijete,b. Brisanje čvora X ostavlja stablo konzistentnim.

2. Brišemo X - ima samo jedno dijete.- čvor X mora biti velik barem kao ključevi u lijevom podstablu od B i manji od svih ključeva u desnom podstablu od B (najveći

8

6

14

4

7

12

20

10

Page 27: Algoritmi i Strukture Podataka (FER)

ključ u lijevom podstablu od B). X se naziva prethodnik od B i sigurno nema desno dijete. Zašto?

Složenost

Složenost svih operacija nad stablom ovisi o obliku stabla.U najgorem slučaju pretražujemo do dna stabla.

Ostale operacije su u konstantnom vremenu (prespajanja, zamjene).

Sve operacije ovise o pretrazi. Ako je stablo balansirano očekujemo da je visina lg (n + 1), gdje je n broj čvorova. Ubacivanje u slučajnom poretku nam daje visinu O(log n), preciznije 2ln n.

Najgori slučaj - stablo degenerira u listu. Npr. ubacivanje sortiranih brojeva. Brisanja također mogu debalansirati stablo - uvijek uzimamo prethodnika.

Slučajna ubacivanja i brisanja nam daju stablo visine

AVL stabla

Adel' son-Vel' skii i Landis (1962).

Prva struktura koja garantira O(log n) u najgorem slučaju za sve operacije.Trošimo dodatno vrijeme kod ubacivanja i brisanja da bi balansirali stablo.Vrijeme za balansiranje ne smije prijeći O(log n).

AVL stablo je binarno stablo pretraživanja takvo da je za svaki čvor razlika u visini lijevog i desnog podstabla najviše 1.

Visina AVL stabla sa n unutrašnjih čvorova je:

lg(n + 1) < h < 1.4404 lg(n + 2) - 0.3277

10

6

13

4

7

12

20

8

6

14

4

7

12

20

13

8

10

Page 28: Algoritmi i Strukture Podataka (FER)

Složenost za pretraživanje O(log n).

Razlikujemo optimalno stablo - svi listovi na istoj dubini.

Čvor dodatno čuva razliku dubina, oznake +, •, -.

Ubacivanje:1. Ubacimo x na dno kao kod BST,2. Ako je stablo ostalo AVL stablo gotovo inače rebalansiramo.

Page 29: Algoritmi i Strukture Podataka (FER)