Professional Documents
Culture Documents
03opće Strukture Podataka PDF
03opće Strukture Podataka PDF
Strukture podataka
Skup operacija nad apstraktnim tipovima objekata zvat emo jednim imenom apstraktni
tipovi podataka (ATP). Za razliku od njih, strukture podataka za pohranu, kao i njihovu
impelementaciju zvat emo strukture podataka za implementaciju. Kao primjer
moemo uzeti jedan niz apstraktnih tipova podataka, koji podrava manipulaciju nad
nekim skupom vrijednosti istog tipa, ukljuujui operacije za pristup lanovima tog niza,
kao i za njihovo modificiranje preko odreenog indeksa. Takoer, poznato je da programski
jezik C++, strukturu podataka za implementaciju kao to je niz brojeva, storira u
kontinualnim blokovima memorije, i koristi pokazivaku aritmetiku tako da zapravo sada
indeks pokazuje na adresu nekog bloka u memoriji raunara. Vano je napomenuti da
uvoenjem apstraktnih tipova podataka zapravo specificiramo o kojoj se strukturi radi,
koje operacije ista podupire, bez da ulazimo u detaljno funkcioniranje te struktura
podataka u smislu njene realizaciji u memoriji. Stoga, ukoliko se desi da dodje do pada
programa tokom njegovog izvravanja, programer se ne mora sputati na najnii nivo da
bi otklonio greke koje su se dogodile u memoriji raunara, ve e svoju panju fokusirati
na rad i implementaciju apstraktnih tipova podataka neophodnih za izvravanje programa.
Dakle, dobra je praksa da se rad algoritma odvoji od struktura podataka za njegovu
implementaciju, to je u literaturi poznato kao apstrakcija podataka (AP). Apstrakcija
podataka pravi razliku izmeu razliitih nivoa abstraktnog razmiljanja. Kao primjer moe
posluuti cjelobrojni tip podataka kao jedan vid apstraknog tipa podataka koji je
podran u skoro svim programskim jezicima. Kod ovog tipa, cjelobrojna aritmetika nam
dozvoljava da razmiljamo na nivou sabiranja, mnoenja, relacija uporeivanja, bez da
pretjerano razmiljamo o tome kako su brojevi predstavljeni u raunaru, kao i to kako se
vri proces mnoenja i sabiranja na niem nivou. Takoer, uvoenje apstraktnih tipova
podataka podstie modularan proces programiranja, na nain da se program razbija na
module sa dobro definiranim interfejsom. Ope je poznato da modularnost ima velike
prednosti, jer razliiti timovi programera mogu potpuno nezavisno od drugih timova pisati
i debagirati module, to nee uticati na ostatak programa. Sem toga, moduli se mogu
mijenjati drugim modulima, koji funkcioniraju slino ili ak bolje, koji su robusniji, i koji u
velikom broju sluajeva su napredniji u poreenju sa originalima.
Strukture podataka koje odreeni algoritam koristi, znaajno utiu na njegovu efikasnost,
kao i na jednostavnost njegovog razumijevanja i programiranja. Zbog ovoga je od
krucijalne vanosti koja e se struktura podataka koristiti tokom rjeavanja nekog
geometrijskog problema. Ope je poznato da objektno-orjentisani jezici, kao to su C++ i
Java, obezbjeuju odreen broj predifiniranih struktura podataka. U ovoj knjizi akcenat e
biti stavljen na C++, iako e se za rjeavanje nekih drugih geometrijskih problema koristiti
pored C++, Java i Python ija je brzina izvravanja za neke instance malo vea. Za veliki
broj problema komparativnom analizom bie pokazano da je u odnosu na CPU, Python
KOMPJUTERSKA GEOMETRIJA
superiorniji u odnosu na ostale ope poznate odnosno koritene programske jezike koji su
zastupljeni danas na tritu. Neke od predifiniranih struktura podataka su recimo cijeli
brojevi, zajedno sa operacijama za njihovo manipuliranje, kao to su aritmetiki operatori
za sabiranje, mnoenje, relacijski operatori, operatori dodjeljivanja, itd. Drugi primjeri
predefiniranih struktura su floating-point brojevi, karakteri, pokazivai, reference,
itd. Predefenirani tipovi podataka se mogu koristiti samostalno ili u kombinaciji sa drugim
strukturama podataka u cilju stavaranja kompleksnijih struktura podataka neophodnih za
rjeavanje odreenih tipova problema. U nastavku glave emo prezentirati neophodne
apstraktne tipove podataka, kao i njihove implementacije, a koje e biti osnova za rad sa
geometrijskim algoritmima. Dakle, prezentirat emo i implementirati ATP-a kao to su:
liste, stekovi i binarna stabla pretraivanja. Glavna motivacija zbog ega se uvode
ba ovi tipovi podataka lei u njihovoj efikasnosti tokom rjeavanja nekoliko geometrijskih
problema koji e biti detaljno istraeni i implementirani u ovoj knjizi. Bliskost sa
apstraktnim tipovima podataka doputa nam da opiemo problem na visokom i
apstraktnom nivou u pogledu apstraktnih operacija. Sem toga, poznavajui kako je neki
apstraktni tip podataka implementiran, dobijamo veu slobodu da se upustimo u analizu
nekih drugih struktura podataka za koje bi se moglo aproksimativno naslutiti da e biti od
koristi tokom rjeavanja nekih nepoznatih problema.
niz koji se sastoji od elemenata, i elimo da na -tom mjestu (0 < ) ubacimo neki
element , morali bismo da pomjerimo elemenata udesno. Dakle, ukoliko bismo trebali
postavi novi lan na nultom mjestu, morali bismo pomjeriti svaki od lanova za po jedno
mjesto udesno poevi od zadnjeg lana niza. Dakle, ova operacija nas kota u najgorem
sluaju () vremena. Dakle, koritenje jednostruko povezane liste nad nizovima ima
veliku prednost, posebno kada unaprijed ne znamo broj lanova neke strukture ili da li e
se taj broj poveavati ili smanjivati za vrijeme trajanja programa. Primjeuje se kod fiksnih
kontejnera kao to su nizovi, da se unaprijed treba alocirati prostor u memoriji, to je
sasvim nepotrebno, jer nekada se unaprijed ne zna koliko kontejnera je neophodno.
Ukoliko se desi da alociramo niz koji ima 100.000 lanova, za vrijeme trajanja programa,
niti jedan od lanova nee dopustiti memorijsku lokaciju nekom drugom procesu, bez
obzira da li se isti koristi ili ne. Ovo je veliki nedostatak upotrebe nizova, i preporuka ih je
izbjegavati kad god se za to ukae potreba zbog poveavanja performansi i utede
alocirane memorije.
Napomenimo jo da kod jednostruko povezane liste, svaki vor koji ukazuje odnosno
linkuje na sljedei vor se zove sljedbenik. Vano je istai da kod krune jednostruko
povezane liste, posljednji vor linkuje na prvi vor.
Kod dvostruko povezane liste, svaki vor je povezan i sa prethodnim vorom i sa sljedeim
vorom. Kod ove liste svaki vor koji linkuje na prethodni vor se zove prethodnik. Za
potrebe rjeavanja problema iz kompjuterske geometrije, najee je u upotrebi kruna
dvostruko povezana lista, ija je grafika ilustracija data na slici 3.13. U nastavku emo
koristiti krune dvostruko povezane liste, i zbog jednostavnosti emo ih naprosto zvati
listama. Ovakve liste najee emo koristiti za predstavljanje poligona, i vorova koji
ustvari predstavljaju vrhove poligona. U C++ implementaciji, vor emo predstaviti kao
instancu odnosno objekat klase Cvor, kao to se moe vidjeti iz koda koji prilaemo u
nastavku ovog odjeljka.
class Cvor
{
protected:
Cvor * sljedeciC; // pokazivac na sljedbenik
Cvor * prethodniC; // pokazivac na prethodnik
public:
Cvor();
virtual ~Cvor(){};
Cvor * sljedeci();
Cvor * prethodni();
Cvor * ubaci(Cvor *);
Cvor * ukloni();
void spoji(Cvor *);
};//kraj definicije klase Cvor
U gornjem kodu dali smo samo definiciju klase Cvor. U nastavku emo implementirati
konstruktor, destruktor i ostale metode ove klase. Tokom implementacije konstruktora,
trebamo natjerati da instance te klase upuuje na samu sebe. To emo postii tako to
emo inicijalizirate privatne lanice odnosno atribute sljedeciC i prethodniC na this.
Implementacija konstruktora data je ispod:
da po izlasku iz programa dealocira kreirane objekte ili da reagira prilikom poziva naredbe
delete novi. Konkretno, u gornjem sluaju, destrukor je zaduen da dealocira objekat
novi. Razlog zbog ega je stavljeno da destruktor za klasu Cvor bude virtuelni, lei u
injenici da isti treba osloboditi objekte odnosno instance klasa koje e naslijediti klasu
Cvor, kao npr. klasa Lista koja e biti definirana i impelementirana u nastavku odjeljka. Da
bismo skakali sa jednog vora na drugi, u nastavku dajemo dvije implementacije funkcija
lanica prethodni i sljedeci.
Da bismo implementirali metodu ubaci koja e biti u stanju da umetne novi vor u listu
(misli se na krunu dovstruko povezanu listu), posmatrajmo sliku ispod:
c->prethodniC = b;
return b;
}//kraj implementacije metode ubaci
Vano je primjetiti da ukoliko vor A prethodi voru B u povezanoj listi, onda operacija
spajanja utie na uklanjanje vora B. tavie, spajanje liste moe uticati da se vor B ubaci
nakon vora A. Na osnovu ovoga moe se zakljuiti da su metode za ubacivanje i uklanjanje
vorova iz liste samo specijalni sluajevi metode spoji. Dakle, ova metoda je generalizacija
spomenutih metoda. Takoer, ukoliko bismo probali pozvati metodu spoji tako da vor A
spoji sa samim sobom, vidjeli bismo da ne bismo dobili nikakav efekat.
definiciju generike klase (templejta) ListaCvorova koju emo javno naslijediti iz klase
Cvor.
template<class T>
class ListaCvorova : public Cvor
{
public:
T elementG; // genericki element
ListaCvorova(T element);
ListaCvorova();
friend class Lista<T>;
}//kraj implementacije metode ukloni
pri emu emo klasu Lista neto klasnije definirati i implementirati. Kao to se moe vidjeti
iz gornjeg C++ koda, stavili smo da je elementG javni, zbog jednostavnije pristupa njemu.
Bez umanjenja openitosti, mogli smo ga proglasiti privatnim, s tim da bi u tom sluaju
morali imati geter i seter metode. Takoer, proglasili smo da je klasa Lista prijatelj klasi
ListaCvorova, da bismo i u ovom sluaju izbjegli uvoenje dodatnih metoda za pristup
atributima. Sem ovoga, primjetimo da je T generiki tip podataka, to nam omoguava
da sada radimo sa raznim tipovima podataka tokom rjeavanja raznovrsnih geometrijskih
problema. U nastavku dajemo C++ implementaciju konstruktora sa i bez parametara.
template<class T>
ListaCvorova<T>::ListaCvorova(T element):elementG(element){}
template<class T>
ListaCvorova<T>::ListaCvorova(){}
template<class T>
class Lista
{
private:
ListaCvorova<T> *glavaL;
ListaCvorova<T> *trenutniC; // glava na trenutnom cvoru liste
int duzinaL; //duzina liste
public:
Lista();
T prviE();
T ukloniE();
~Lista();
T posljednjiE();
T ubaciE(T);
T prethodniE();
T sljedeciE();
bool jelPrviE();
bool jelPosljednjiE();
int vratiDuzinuL();
bool jelGlavaL();
T dodajNaPocetakE(T);
T dodajNaKrajE(T);
void postaviE(T);
T vratiE();
Lista *spojiL(Lista*);
};
Prije nego to implementiramo bilo koju od metoda klase Lista, usvojit emo da se lanovi
liste dodaju u smjeru kazaljke na satu (negativna orjentacija), dok se itaju u
suprotnom smjeru kazaljke na satu (pozitivna orjentacija). U nastavku emo bez
detaljnijeg objanjenja date imeplementacije konstruktora, destruktora, i ostalih metoda
klase Lista.
template<class T>
Lista<T> :: Lista(): duzinaL(0)
{
glavaL = new ListaCvorova<T>(0);
trenutniC = glavaL;
}
template<class T>
T Lista<T> :: prviE()
{
trenutniC = (ListaCvorova<T>*)glavaL->prethodni();
return trenutniC->elementG;
}
template<class T>
T Lista<T>::ukloniE()
{
if (trenutniC == glavaL) return 0;
void *el = trenutniC->elementG;
trenutniC = (ListaCvorova<T>*)trenutniC->prethodni();
delete (ListaCvorova<T>* )trenutniC->sljedeci()->ukloni();
--duzinaL;
return el;
}
template<class T>
Lista<T> :: ~Lista()
{
while (duzinaL > 0)
{
prviE();
ukloniE();
}
delete glavaL;
}
Implementacija metode posljednjiE() je data sa:
template<class T>
T Lista<T> :: posljednjiE()
{
trenutniC = (ListaCvorova<T>*)glavaL->sljedeci();
return trenutniC->elementG;
}
Implementacija metode ubaciE() je data sa:
template<class T>
T Lista<T> :: ubaciE(T el)
{
trenutniC->ubaci(new ListaCvorova<T>(el));
++duzinaL;
10 3. Strukture podataka | Doc. dr. Adis Alihodi
KOMPJUTERSKA GEOMETRIJA
return el;
}
template<class T>
T Lista<T> :: prethodniE()
{
trenutniC = (ListaCvorova<T>*)trenutniC->prethodni();
return trenutniC->elementG;
}
template<class T>
T Lista<T> :: sljedeciE()
{
trenutniC= (ListaCvorova<T>*)trenutniC->sljedeci();
return trenutniC->elementG;
}
template<class T>
bool Lista<T>::jelPrviE()
{
return ((trenutniC == glavaL->prethodni()) && (duzinaL > 0));
}
template<class T>
bool Lista<T> :: jelPosljednjiE()
{
return ((trenutniC == glavaL->sljedeci()) && (duzinaL > 0));
}
template<class T>
int Lista<T> :: vratiDuzinuL()
{
return duzinaL;
}
template<class T>
bool Lista<T> :: jelGlavaL()
{
return trenutniC == glavaL;
}
template<class T>
T Lista<T> :: dodajNaPocetakE(T el)
{
glavaL->prethodni()->ubaci(new ListaCvorova<T>(el));
++duzinaL;
return el;
}
template<class T>
T Lista<T> :: dodajNaKrajE(T el)
{
glavaL->ubaci(new ListaCvorova<T>(el));
++duzinaL;
return el;
}
template<class T>
template<class T>
void Lista<T> :: postaviE(T el)
{
if(trenutniC!=glavaL)trenutniC->elementG=el;
}
template<class T>
T Lista<T> :: vratiE()
{
return trenutniC->elementG;
}
//Implementacija metode
template<class T>
Lista<T> * Lista<T> :: spojiL(Lista<T> *l)
{
ListaCvorova<T> *a = (ListaCvorova<T>*)glavaL->prethodni();
a->spoji(l->glavaL);
duzinaL+=l->duzinaL;
l->glavaL->ukloni();
l->duzinaL= 0;
l->trenutniC=glavaL;
return this;
}
U ovom odjeljku emo dati par primjera koritenja definirane i implementirane klase Lista.
Pretpostavimo da smo definirali i implementirali jednu prostu klasu Tacka, kao ispod:
class Tacka
{
private:
double x,y;
public:
Tacka(){};
Tacka(double, double);
double dajX() const{return x;}
double dajY() const{return y;}
};
Tacka::Tacka(double x, double y)
{
this->x=x;
this->y=y;
}
while(!lista->jelPosljednjiE()){
Tacka * el=lista->sljedeciE();
cout<<el->dajX()<<" "<<el->dajY()<<endl;
}
for(int i=0;i<8;i++){
delete [] niz[i];
niz[i]=NULL;
}
delete [] niz;
niz=NULL;
delete lista;
Analogno se mogu iztestirati preostale metode. U nastavku emo dati testni primjer koji
e pomou metodu spojiL() spojiti dvije liste u jednu:
while(!lista3->jelPosljednjiE()){
Tacka * el=lista3->sljedeciE();
cout<<el->dajX()<<" "<<el->dajY()<<endl;
}
for(int i=0;i<5;i++){
delete [] niz1[i];
delete [] niz2[i];
niz1[i]=niz2[i]=NULL;
}
delete [] niz1;
delete [] niz2;
niz1=niz2=NULL;
delete lista1, lista2, lista3;
Pored ovih upotreba klase Lista, navest emo jo jedan primjer. Pretpostavimo da elimo
da elemente nekog niza konvertujemo u listu, koju emo nakon toga vratiti iz funkcije. To
moemo uraditi tako to emo prvo definirati prototip sljedee funkcije:
template<class T>
Lista<T> * nizUListu(T a[], int n);
template<class T>
Lista<T> * nizUListu(T a[], int n)
{
Lista<T> *s = new Lista<T>;
for (int i = 0; i < n; i++) s->dodajNaKrajL(a[i]);
return s;
}
Lista<double> * l;
double niz[5]={1,2,3,4,5};
l=nizUListu(niz,5);
while(!l->jelPosljednjiE()) cout<<l->sljedeciE()<<endl;
delete l;
Sljedea upotreba funkcija se odnosi na funkciju koja e vratiti najmanji element iz liste u
odnosu na rastui poredak poredanih elementa. Funkciju emo nazvati
najmanjiElement(), iji je prototip dat sa:
template<class T>
template<class T>
T najmanjiElement(Lista<T> & l, int (*fun) (T,T) )
{
if (l.vratiDuzinuL() == 0) return NULL;
T e = l.prviE();
for (l.prviE(); !l.jelGlavaL(); l.sljedeciE())
if (fun(l.vratiE(), e) < 0)
e = l.vratiE();
return e;
}
3.3 Stek
Ope je poznato da su liste neograniene strukture podataka, jer se moe bilo kojem
elementu iste proizvoljno pristupiti. Pored lista, postoje strukture podataka ijim
elementima se pristupa sa odreenim ogranienjem. Takve strukture podataka zovemo
stek. Naime, kod steka se prvi lan koji je dodat posljednji skida sa njega, dok se zadnji
dodat na steku, prvi skida. Zbog ovoga su poznati kao liste sa LIFO (eng. Last In First
Out) strukturom. Dvije osnovne operacije kod steka su: dodaj i skini. Jasno je da
operacija dodaj svaki put dodaje element na vrh steka, dok operacija skini jedino moe
skinuti element sa vrha steka. Stoga, jedini element koji je dostupan na steku je onaj koji
se nalazi na samom vrhu, tj. najvii (prvi za skidanje) element (eng. topmost element).
Pored ove dvije operacije, postoje jo operacije kao to su, jel prazan, koja vra tano
ukoliko je stek prazan, velicina steka, najvii element, do najvieg elementa, donji
element. Jasno je da jednostavna statika implementacija steka moe da se realizira
preko niza. Meutim, jedini problem kod ove implementacije jeste taj to bi duina niza
ograniavala duinu steka. Stoga emo umjesto niza kao fiksnog kontejnera koristiti liste
kao dinamike strukture podataka koje smo uveli u prethodnom odjeljku ove glave.
Koristei liste za realizaciju steka, posljednji element liste e predstavljati najvii element
steka, dok e prvi element liste predstavljati donji element steka odnosno prvi element
koji je dodat na steku. Zbog jednostavnosti implementacije steka, u nastavku emo dati
definicije i implementacije klase Stek.
16 3. Strukture podataka | Doc. dr. Adis Alihodi
KOMPJUTERSKA GEOMETRIJA
template<class T>
class Stek
{
private:
Lista<T> *s;
public:
Stek();
~Stek();
void dodaj(T);
T skini();
bool jelPrazan();
int velicinaS();
T najvisiE();
T doNajvisegE();
T donjiE();
};
template<class T>
Stek<T>::Stek(): s(new Lista<T>){}
template<class T>
Stek<T>::~Stek()
{
delete s;
}
template<class T>
void Stek<T>::dodaj(T el)
{
s->dodajNaKrajE(el);
}
template<class T>
T Stek<T>:: skini()
{
s->posljednjiE();
return s->ukloniE();
}
template<class T>
T Stek<T>:: najvisi()
{
return s->posljednjiE();
}
template<class T>
T Stek<T>:: donji()
{
return s->prviE();
}
template<class T>
T Stek<T>:: doNajviseg()
{
s->posljednjiE();
return s->prethodniE();
}
template<class T>
bool Stek<T>::jelPrazan()
{
return (s->vratiDuzinuL()==0);
}
template<class T>
int Stek<T>::dajVelicina()
{
return s->vratiDuzinuL();
}
Stek se moe koristi u sluaju kada se eli postii obrnut redoslijed lanova, kao to je
realizirano funkcijom ispod:
Jasno je da je Stek apstraktan tip podataka, pri emu je klasa Lista sakrivena unutar
privatnih lanica klase Stek. Stoga, klasi Lista se ne moe pristupiti izvana, niti se ista
moe modificirati. Dakle, Stek ovdje ima ulogu interfejsa. Funkcije koje koriste Stek,
nemaju potrebe da vode rauna kako je on implementiran odnosno da li je implementiran
preko liste ili obinog niza.
Inorder;
Postorder;
Preorder.
Kod Inorder-a prvo se posjeuje lijevo podstablo zatim vor pa desno podstablo, tj.
L--D.
Kod Postorder-a prvo se posjeuje lijevo podstablo zatim desno podstablo pa vor, tj.
L-D-.
Kod Preorder-a prvo se posjeuje vor zatim lijevo podstablo pa desno podstablo, tj.
-L-D. Vrlo je vano napomenuti, da emo binarno stablo koristiti za prikazivanje Beach
linija kod Voronoi dijagrama, pri emu e potomci binarnog stabla T (vidi grafiku
ilustraciju binarnog stabla T na slici 8.) odgovarati lukovima na Beach liniji.
19 3. Strukture podataka | Doc. dr. Adis Alihodi
KOMPJUTERSKA GEOMETRIJA