Académique Documents
Professionnel Documents
Culture Documents
Avni Rexhepi
Prishtin 2014
1
Avni Rexhepi
Parathnie
Ky libr u dedikohet studentve t Fakultetit t Inxhinieris Elektrike dhe
Kompjuterike, t Universitetit t Prishtins, mirpo natyrisht se mund t prdoret edhe
nga t gjith t interesuarit pr kt lmi. Ky sht botimi i par dhe vrejtjet e
sygjerimet e lexuesve jan t mirseardhura. T gjith shembujt n libr, jan marr m
shum pr qllime shkollore, pr t shrbyer si udhzime n realizimin e detyrave t
caktuara, e jo si projekt i gatshm pr prdorim apo pjes t ndonj projekti. Emrat e
prdorur si shembuj jan t rastit dhe prjashtohet mundsia e keqprdorimit t
qllimshm.
Pr vrejtjet dhe sygjerimet mund t na kontaktoni prmes posts elektronike, n
adresn: avni.rexhepi@uni-pr.edu.
Avni Rexhepi
Hyrje
Algoritmet dhe strukturat e t dhnave jan veglat/pajisjet e programerve pr
kryerjen e punve. Ato definohen dhe prdoren (n programe) pr t realizuar
llogaritjet e nevoshme pr zgjidhjen e problemeve nga jeta reale, prmes
prdorimit t programeve dhe kompjuterve. Algoritmet na mundsojn
kryerjen e operacioneve/veprimeve llogaritse n nj mnyr t caktuar. Kto
llogaritje i bjn me t dhnat e thjeshta ose me strukturat e t dhnave t cilat
na shrbejn q t krijojm objektet abstrakte n programe, t cilat pasqyrojn
n mnyrn m t mir t mundshme objektet konkrete (reale, fizike) nga bota
reale dhe jeta e prditshme.
Algoritmet
ka sht algoritmi? Algoritmi sht procedur hap pas hapi, pr zgjidhjen e
problemit. Algoritmi sht proceudra e kryerjes s ndonj detyre t caktuar.
Algoritmi sht idea prapa cilitdo program kompjuterik. Kto do t ishin disa
prej definicioneve m t thjeshta lidhur me at se ka sht algoritmi.
Prndryshe ekzistojn edhe shum definicione t tjera, t cilat n mnyra t
ndryshme e japin shpjegimin ose mundohen ta sqarojn se ka sht algoritmi.
Algoritmi definohet edhe si: Algoritmi sht bashksi e rregullave pr kryerjen e
llogaritjeve me dor ose me ndonj pajisje. Algoritmi sht nj procedur e
prcaktuar hap pas hapi pr arritjen e nj rezultati t caktuar. Algoritmi sht nj
varg i hapave llogarits q e transformojn hyrjen n dalje. Algoritmi sht nj
varg i operacioneve t kryera n t dhnat q duhet t jen t organizuara n
struktura t t dhnave. Algoritmi sht nj abstraksion i programit q duhet t
ekzekutohet n nj makin fizike (modeli i llogaritjes), etj. Algoritmi m i
njohur n histori daton q nga koha e Greqis antike: ky sht Algoritmi i
Euklidit pr llogaritjen e pjestuesit m t madh t prbashkt t dy numrave t
plot.
Avni Rexhepi
Termi algoritm konsiderohet t ket ardhur nga emri i dijetarit islam, matematikanit
arab Ab Jafar Abdallh Muammad ibn Ms al-Khwrizm, i cili jetoi n vitet
780-850 n Bagdad. Ai ishte nj matematikan q shkroi pr numrat indo-arab dhe
ishte ndr t part q e prdori zeron si pozicion n notacionin baz t
pozicioneve pr numrat. Nga punimi i tij Hisab al-jabr wal-muqabala, q
konsiderohet si libri i par i shkruar pr algjebrn, e ka prejardhjen termi algjebr.
Al-Khwarizmi, i prkthyer n latinisht si Algoritmi ose Algaurizin, ishte
matematikan, astronom dhe gjeograf gjat perandorise Abaside (Kalifati Abasid,
ishte kalifati i tret islam q pasoi Profetin Muhamed) dhe ishte dijetar, studiues dhe
shkenctar n Shtpin e dituris/urtsis (Dr al-ikma), n Bagdad. N
shekullin e dymbdhjet, prkthimet e puns s tij n latinishte pr numrat indian
prezentuan sistemin numerik pozicional decimal n botn prndimore. Libri i tij
Prmbledhje e llogaritjeve me kompletim dhe balansim prezentoi zgjidhjen e par
sistematike t ekuacioneve lineare dhe kuadratike. N kohn e renesanss
evropiane, ai konsiderohej si zbuluesi origjinal i algjebrs, edhe pse tash dihet se
puna e tij bazohej n burime m t vjetra indiane dhe t greqis antike.
Edhe fjalt e mbetura prej punimeve t tij flasin pr kontributin e tij n matematik.
Fjala algjebr, q rrjedh prej fjals al-jabr, q ishte njri prej dy operacioneve q
ai prdori pr t zgjidhur ekuacionet kuadratike. Poashtu, termi Algorism dhe
Algorithm, buron prej forms latine t emrit t tij. Punimi i tij n latinisht ishte
quajtur Algoritmi de numero indorum.
N hyrje t librit t tij, ai kishte shkruar:
Dashuria
pr
shkenc
dashamirsia dhe prfillja t ciln
Zoti e tregon pr t diturit, ajo
prpikmri me t ciln ai i mbron
dhe prkrah ata n sqarimin e
paqartsive dhe eleminimi e
vshtirsive, m ka inkurajuar q t
prpiloj nj punim t shkurtr pr
llogaritjen me al-jabr dhe almuqabala, duke u kufizuar n at
q sht m e lehta dhe m e
dobishmja n aritmetik.
(al-jabr do t thot "kthim, restaurim",
duke iu referuar procesit t largimit t
pjess s zbritur n ann tjetr t
ekuacionit;
al-muqabala
sht
"krahasimi"
dhe
i
referohet
zbritjes/thjeshtimit t vlerave t njjta
n t dy ant e ekuacionit).
Avni Rexhepi
zgjidhur, menaxhimin e kompleksitetit dhe dekompozimin (zbrthimin) n
nnprobleme t vogla t cilat mund t implementohen me lehtsi. Shpeshher,
shum prej algoritmeve, jan t lehta pr tu implementuar pas dekompozimit.
Mirpo, n shumicn e rasteve, jan disa algoritme, zgjedhja e t cilave sht
kritike, sepse shumica e resurseve t sistemit do t shpenzohet n ekzekutimin e
tyre. Pra, sht me rndsi t studiohen algoritmet themelore t cilat jan t
dobishme pr zgjidhjen e problemeve n nj spektr t gjer t sferave t
aplikimeve.
Shum gjuh programuese tani kan librarit e implementimeve t shum
algoritmeve themelore, si p.sh. STL (Standard Template Library) e C++-it,
mirpo ne do t mirremi me implementimin e versioneve t thjeshta t
algoritmeve themelore, prmes s cilave ato kuptohen m mir dhe pastaj m
leht prdoren pr t akorduar (prmirsar n detaje) versionet e gatshme nga
librarit. sht m e rndsishme, mundsia e reimplementimit t algoritmeve
bazike paraqitesh shum shpesh. Arsyeja primar pr t vepruar kshtu qsht se
shum shpesh ballafaqohemi me ambient trsisht t ri hardverik dhe softverik,
me veti t cilat implementimet e vjetra nuk mund ti prdorin pr t prfituar sa
m shum. Me fjal tjera, shpeshher implementojm algoritmet bazike t
qepura pr problemin ton, sesa t varemi nga nj rutin (nnprogram)
sistemor, pr ti br zgjidhjet m portabile dhe m afatgjata. Nj arsye tjetr e
shpesht pr t reimplementuar algoritmet bazike sht se prkundr
avantazheve t inkorporuara n C++, mekanizmat q prdoren pr
bashkprdorim (sharing) t softverit nuk jan gjithmon mjaft t fuqishme pr
t na lejuar q t prshtasim programet e librarive q t performojn efektivisht
n detyra specifike.
Programet kompjuterike jan shpeshher t tejoptimizuara (angl.
overoptimized). Mund t mos ia vlen q t mirret mundimi pr tu siguruar q
nj implementim i nj algoritmi t caktuar sht m efikasi i mundshm, prveq
nse ai algoritm do t prdoret pr detyra jashtzakonisht t mdha ose do t
prdoret shum her. Prndrsyhe, nj implementim relativisht i thjesht, i
zgjedhur me kujdes, do t mjaftoj. Mund t presim q ai do t punoj dhe me
gjas do t jet pes apo dhjet her m i ngadalshm sesa versioni m i mir i
mundshm, por kjo do t thot se do t marr disa sekonda koh shtes pr
ekzekutim. Pr kontrast, zgjidhja e duhur e algoritmit n vend t par, mund t
bj ndryshimin pr faktor 100 ose 1000 apo m shum her, gj q mund t
prkthehet n minuta, or ose edhe m shum koh pr ekzekutim. Kryesisht do
t koncentrohemi n implementimet m t thjeshta t arsyeshme t algoritmeve
m t mira.
Zgjedhja e algoritmit m t mir pr ndonj detyr t caktuar mund t jet
proces i komplikuar, ndoshta duke krkuar analiz matematikore t sofistikuar.
8
Strukturat e t dhnave
Organizimi i t dhnave pr prpunim (angl. processing- prpunim, procesim,
shqyrtim), sht detyr thelbsore n zhvillimin e programeve kompjuterike.
Shum algoritme krkojn prdorimin e reprezentimit t duhur t t dhnave pr
t qen efektive. Ky reprezentim i t dhnave dhe operacionet prcjellse pr to,
njihen si struktura t t dhnave. Secila struktur e t dhnave mundson
insertimin arbitrar por dallojn n at se si mundsojn qasjen n antart e
grupit. Disa struktura t t dhnave lejojn qasjen dhe fshirjen arbitrare, gjersa t
tjerat imponojn kufizime, si lejimi i qasjes vetm n elementin e fundit t
insertuar ose vetm n elementin e par t insertuar n grup.
Struktura e t dhnave mundson arritjen e nj prej qllimeve t programimit t
orientuar n objekte: riprdorimi i komponenteve. Secila strkutur e t dhnave
e implementuar nj her, mund t riprdoret prsri n aplikacione t ndryshme.
Struktura e t dhnave pra sht reprezentimi i t dhnave dhe operacioneve n
ato t dhna. Shum struktura t t dhnave ruajn nj koleksion t objekteve
dhe pastaj ofrojn metodat pr t shtuar objekte, pr t larguar objektet
ekzistuese ose pr t ju qasur objekteve t koleksionit.
Standardi i C++-it krkon q t gjitha implementimet t ofrojn librarit
prkrahse t njohura si Standard Template Library (Libraria Standarde e
Shablloneve, shkurt STL). STL ofron koleksionin e strukturave t t dhnave
dhe ofron disa algoritme themelore, si p.sh., sortimi. Si tregon edhe vet emri,
STL prdor me t madhe shabllonet.
Pr shum aplikacione, zgjedhja e strukturs s duhur t t dhnave sht
vendimi i vetm i rndsishm i prfshir n implementim: kur t jet br
zgjedhja, algoritmet e nevojshme jan t thjeshta. Pr t njjtat t dhna, ndonj
9
Avni Rexhepi
struktur e t dhnave mund t krkoj m shum ose m pak hapsir sesa t
tjerat; pr ndonj operacion (veprim) me t dhnat, disa struktura mund t ojn
n algoritme m efikase ose m pak efikase, se t tjerat. Zgjedhja e algoritmit
dhe e strukturs s t dhnave jan t ndrlidhura ngusht dhe vazhdimisht
krkojm mnyra pr t kursyer kohn ose hapsirn, duke br zgjedhjen e
duhur.
Struktura e t dhnave nuk sht objekt pasiv. Ne duhet t marrim n
konsiderim edhe operacionet t cilat do t kryhen n t (dhe algoritmin e
prdorur pr kto operacione). Ky koncept sht i formalizuar me nocionin: tipi
i t dhnave (angl. data type). Interesimi primar sht n implementimin konkret
t qasjeve themelor t cilat prdoren pr strukturimin e t dhnave. Shqyrtojm
metodat themelore t organizimit dhe metodat pr manipulimin e t dhnave,
prmes shembujve specifik t cilt ilustrojn prfitimet pr secilin dhe shtjet e
ndrlidhura, si mengaxhimi i memories. Gjithashtu, do t diskutohen tipet
abstrakte t t dhnave (ADT-Abstract Data Types), ku ndahen definicionet e
tipeve t t dhnave prej implementimeve.
Do t diskutohen tiparet e vargjeve, listave t lidhura dhe stringjeve. Kto
struktura klasike t t dhnave kan prdorim t gjer. P.sh., tek pemt
(struktura e t dhnave, n form peme), ato praktikisht formojn bazn pr
pothuajse t gjitha algoritmet. Do t shqyrtohen edhe operacionet e ndryshme
primitive pr manipulimin e ktyre strukturave t t dhnave, pr t zhvilluar nj
bashksi themelore (angl. basic set) t veglave t cilat mund t prdoren pr
zhvillimin e algoritmeve t sofistikuara pr problemet e vshtira.
Studimi i ruajtjes s t dhnave si objekte me madhsi t ndryshueshme (angl.
variable-size objects) dhe n strukturat e lidhura t t dhnave krkon njohuri
pr mnyrn se si sistemi e menaxhon hapsirn e ruajtjes (hapsirn memorike
- angl. storage) t ciln ua alokon (ndan) programeve pr t dhnat e tyre. N
fakt, diskutohet qasja e menaxhimit t hapsirs dhe disa mekanizmave
themelor t prgjithshm, sepse shum elemente jan t varura nga vet sistemet
dhe pajisjet q prdoren. Do t shohim mnyrat specifike pr t cilat prdoren
mekanizmat e C++-it pr alokim t hapsirs.
Poashtu, do t shqyrtohen disa shembuj t strukturave t prbra, si vargjet e
listave t lidhura dhe vargjet e vargjeve. Nocioni i ndrtimit t mekanizmave
abstrakt t rritjes s kompleksitetit nga nivelet e ulta sht tem q prsritet.
Shembujt, pastaj mund t shrbejn si baz pr algoritme m t avansuara.
Kto struktura t t dhnave jan t blloqe ndrtimi t rndsishme (angl.
building blocks) t cilat mund t prdoren n mnyr natyrale n C++ dhe n
shum gjuh t tjera programuese. Vargjet, stringjet, listat e lidhura dhe pemt,
jan elementet themelore t ndrtimit t shum algoritmeve. Reprezentimi
10
Avni Rexhepi
t ardhshm, zakonisht quhet Nyje (angl. Node). Pra, nyja prmban vlern
(angl. value) ose t dhnat (angl. data) dhe pointerin ose pointert, q e
ndrlidhin at me nyjen e ardhshme dhe at t prparshme, ashtu q edhe pse
fizikisht t vendosur n lokacione t ndryshme t memories, logjikisht antart
prsri krijojn nj list me antar t njpasnjshm.
Ky organizim i t dhnave t renditura, ku antart e njpasnjshm jan n
lokacione t shprndara t memories, por jan t lidhur mes vete prmes
pointerve quhet list e lidhur. Pra, lidhjen prej nj antari (lokacioni t
memories) deri tek antari tjetr (lokacioni tjetr n memorie), e realizojm
prmes pointerve, t cilt tregojn pozitn e antarit t ardhshm ose atij t
prparshm (Rikujtojm se pointeri ruan adresa, kshtu q pra tregon adresn se
ku ndodhet antari prkats). Nse struktura e t dhnave, pr secilin antar
(nyje) definon vetm vlern dhe pointerin pr n pozitn e ardhshme, themi se
kemi t bjm m listn e lidhur njfish, pasi q ldhja sht vetm nnjrin
kah (drejtim). Nse struktura pr secilin antar t vetin, ka vlern dhe dy
pointer, njri pr antarin e prparshm dhe tjetrin pr antarin e ardhshm n
list, ather kemi t bjm me listn e lidhur dyfish.
...
...
...
...
...
...
hapsirs
memorie,
Statike, dhe
Dinamike.
Avni Rexhepi
Kur deklarimi i vargut/lists bhet n kohn e ekzekutimit, prmes prmes
operatorit new(i cili prcakton lokacionin n memorie dhe pointerin pr at
lokacion) dhe gjat ekzekutimit shtohen ose largohen antart e lists, ather
themi se kemi t bjm m struktur dinamike.
Krijimi i tipit abstrakt t t dhnave (angl. Abstract Data Type ADT) na
mundson q t krijojm struktura logjike, t cilat i prshtaten nevojave t
programit dhe realitetit nga jeta e prditshme, kurse realizimi fizik i tyre ( n
prapavi) prsri mbetet i bazuar n at q sht e realizueshme fizikisht, si
bashksi e lokacioneve t njpasnjshme ose t atyre t shprndara n memorie.
Nse lokacionet jan t krijuara dinamikisht (gjat ekezekutimit) dhe rezervohen
n pozita t ndryshme n memorie, ather prmes pointerve t tyre, i
prcjellim lokacionit e t dhnave, si n figurn vijuese.
Avni Rexhepi
nj klas mund t trashgoj vetit e nj klase tjetr. Klasa ekzistuese quhet
klas baz, ndrsa klasa e re quhet klas trashguese. Trashgimia prdoret pr
t redukuar kodin burimor n programimin e orientuar n objekte. Pa prdorim
t trashgimis, secila klas do t duhet t definoj t gjitha karakteristikat e
veta n mnyr eksplicite. (angl. explicit i caktuar, i hollsishm, i qart, i
sakt; implicit-i nnkuptuar, i padyshimt).
Klasa baz i prmbledh elementet e prbashkta pr nj grup t klasave
trashguese. Klasat trashguese prveq q i ekzekuton elementet e prbashkta
q i trashgon, i ekzekuton gjithashtu edhe ato q i ka karakteristike t vetat.
Grupimi i karakteristikave t prbashkta dhe vendosja e tyre n nj vend, n
vend t prsritjes s tyre n t gjitha vendet ku ato ndodhin, n nj mnyr e
redukon madhsin e programeve.
Riprdorimi
Kur klasa t jet shkruar, krijuar dhe debug-uar (debaguar), ajo mund t
shprndahet edhe tek programert e tjer pr prdorim n programet e tyre. Kjo
veti referohet si riprdorshmri (angl. reusability; nga use-prdorim dhe abilitymundsi, aftsi, pra aftsi e t qenit e riprdorshme). Programert mund t
marrin nj klas ekzistuese dhe pa e modifikuar at, ti shtojn karakteristika
dhe aftsi plotsuese. Kjo veti referohet si extensibility zgjerueshmri (angl.
extensibility-zgjerueshmri, zgjatshmri). Kjo bhet duke derivuar (trashguar)
nj klas t re nga nj klas ekzistuese. Klasa e re do t trashgoj tiparet e
vjetrs dhe poashtu do t shtoj tipare t veta t reja.
Enkapsulimi
Enkapsulimi ose enkapsulimi i t dhnave (angl. Data encapsulation) sht nj
prej vetive m t rndsishme t klasave. N programimin e orientuar n
objekte, nj objekt krijohet duke prfshir t dhnat dhe funksionet pr lexim
(hyrje) dhe shtypje (dalje) t t dhnave. Objekti prkrah enkapsulimin.
Enkapsulimi sht procesi i kombinimit t funksioneve antare t klass dhe t
dhnave (vlerave) antare t klass, si dhe mbajtjes s tyre t sigurta nga
interferencat (ndrhyrjet) nga jasht. T dhnat nuk jan t qasshme nga jasht
dhe vetm funksionet t cilat jan brenda klass mund t ju qasen atyre. Izolimi i
t dhnave nga qasja direkte, nga programert quhet fshehje e t dhnave
(angl. data hiding).
Abstraksioni
Abstraksioni ose abstraksioni i t dhnave (angl. Data abstraction) sht
mundsia e krijimit t tipeve t t dhnave t shfrytzuesit pr t modeluar
objektet e bots reale, duke prdorur tipet e brenshme t t dhnave.
Abstraksioni i t dhnave ndihmon pr t ju qasjur t dhnave dhe funksioneve
s bashku, gj q definon tip t ri t t dhnave t quajtur tip abstrakt i t
16
17
Avni Rexhepi
3. Vetia e fshehjes s t dhnave i mundson programerit q t dizajnoj
dhe t zhvilloj programe t sigurta t cilat nuk e rregullojn kodin n
pjest tjera t programeve.
4. Vetia e enkapsulimit u lejon programerve q t definojn klasn me
shum funksione dhe karakteristika, ndrsa vetm disa funksione i
ekspozohen shfrytzuesit.
5. T gjitha gjuht programuese t orientuara n objekte mund t krijojn
pjes t zgjeruara dhe t riprdorshme t programeve.
6. POO zgjeron procesin e t menduarit t programerve duke drguar n
zhvillimin e shpejt t softverit t ri n koh m t shkurt.
18
Lista e lidhur
Lista e lidhur sht koleksion linear i elementeve t t dhnave t quajtura nyje,
ku renditja lineare realizohet prmes pointerve. Secila nyje sht e ndar n dy
ose m shum pjes, t quajtura fushat e informacionit dhe fushat e adresave.
Fusha e informacionit ose fusha e t dhnave (vlerave) prdoret pr t ruajtur
informacionin ose elementin e t dhns, ndrsa fusha e adresave prdoret pr t
ruajtur adresn e ndonj nyjes tjetr n list. Secila nyje do t ket adres unike.
Nse nyja prmban nj fush t t adress dhe nj ose m shum fusha t t
dhnave thuhet se kemi list t lidhur njfish (angl. single linked list) ose
zingjir n nj kahje. Nse nyja prmban dy fusha adresash dhe nj ose m
19
Avni Rexhepi
shum fusha t dhnash, thuhet se kemi t bjm me list t lidhur dyfish (angl.
double linked list) ose zingjir dy-kahsh.
Info
Lidhja
Lidhja_m
a. Nyja me nj lidhje
Info
Lidhja_d
b. Nyja me dy lidhje
Figura 4 Nyjet
12
18
...
Fillimi
22
21
Avni Rexhepi
10
20
30
40
Fundi
Fillimi
Steku
Pr vargjet/listat, bazuar n idet nga jeta reale, kemi listat ku antart jan sikur
nj grumbull i librave, njri mbi tjetrin ose sikur fishekt n karikator. I fundit i
vendosur, sht i pari n rend pr qasje. Kto struktura njihen si strukturat LIFO
(Last In, First Out I fundit brenda, i pari jasht) (ose n renditjen e kundrt
FILO (First In, Last Out I pari brenda, i fundit jasht). Lista LIFO sht e
njohur si Stack (Stek) ose PushDown Stack (Steku shtyje posht) (angl.
Stack grumbull, mullar, gyp, raft, etj). Steku mund t imagjinohet edhe si nj
gyp i mbyllur, n t cilin antart futen nj nga nj me radh dhe mund t
shtyhen deri n fund t stekut, por qasje kemi vetm n elementin n krye (t
fundit t futur n stek), q quhet top (kreu) i stekut. Insertimi dhe trheqja e
antarve bhet vetm n nj pozit (n nj skaj t gypit).
22
Figura 7 - Steku
23
Avni Rexhepi
Stack - Steku
...
push
pop
push
Deque Rreshti dy-kahor
...
popFront pushFront
pushBack popBack
B
D
G
F
H
F
G
Avni Rexhepi
problemit t interpretohet dhe t zbrthehet ashtu q t mund t paraqitet prmes
ktyre strukturave themelore, pr t cilat jan krijuar dhe standardizuar
algoritmet dhe funksionet pr kryerjen e operacioneve t ndryshme dhe
prpunimin e t t dhnave.
Nse bhet nja paraqitje e kategorizuar e strukturave t t dhnave, mund t
bhet nj ndarje si n figurn 10.
Strukturat e t dhnave
T brendshme
(Built-in)
Numrat e plot
(Integer)
Numrat jo t
plot (Float)
Karakteret
(Char)
Pointer
Vargjet
Listat
Listat lineare
Steku
Queue
26
Fajllat
Listat jo-lineare
Pemt
Grafet
27
Avni Rexhepi
Bus-i (zbrarra, lidhja) e lidh procesorin dhe memorien kryesore. Bus-i sht
nj bashksi (set) e fijeve prquese t gravuara n pllakn kryesore t
kompjuterit (angl. motherboard pllaka am) e ngjashme me autostradn dhe
transporton instruksionet dhe t dhnat ndrmjet procesorit dhe memories dhe
pajisjeve t tjera t lidhura n kompjuter.
CPU Central Processing Unit (Njsia
qndrore procesuese)
Main memory memoria kryesore
Data bus bus-i i t dhnave
Address bus bus-i i adresave
Control bus bus-i i kontrolls
Input/Output
devices
pajisjet
hyrse/dalse
(Ekrani,
tastiera,
memoria
e
qndrueshme/disku, lidhjet e rrjets, etj)
Switch 2
0
Decimal Value
0
29
Avni Rexhepi
Tani fillon ngatrrimi. T dy numrat duket se kan shifrat e njjta: 10. Mirpo,
pr numrin decimal ky sht reprezentimi i vlers 10, kurse pr sistemin binar,
shifrat 10 nuk paraqesin vlern decimale 10, por vlrn binare 2.
Shifrat n sistemin numerik binar reprezentojn gjendjen e ndrprersit.
Kompjuteri kryen aritmetikn duke prdorur sistemin numerik binar pr t
ndryshuar gjendjen e bashksive t ndrprersave (transistorve).
Rezervimi i memories
Njsia e memories mund t mbaj nj bajt, ndrsa t dhnat n program mund t
jen m t mdha sesa bajti dhe krkojn 2, 4 ose 8 bajta pr tu ruajtur
nmemorie. Para se ndonj e dhn t ruhet n memorie, duhet treguar
30
Madhsia n
Rangu i vlerave
bita
8
128 to 127
short 16
16
32,768 to 32,767
Grupi
Integers
Integers
31
Avni Rexhepi
Tabela 1-2: Tipet e thjeshta t t dhnave
Tipi
Madhsia n
bita
Rangu i vlerave
Grupi
int 32
32
2,147,483,648
2,147,483,647
Integers
long 64
64
9,223,372,036,854,775,808
9,223,372,036,854,775,807
Integers
char
Characters
float 32
32
3.4e-038 to 3.4e+038
Floating-point
double 64
64
1.7e-308 to 1.7e+308
Floating-point
boolean 1
0 or 1
Boolean
16 (Unicode)
32
Integer
Grupi ADT integer prbhet nga katr tipe abstrakte t t dhnave t prdorura
pr t rezevuar memorie pr ruajtje t numrave t plot: byte , short , int , dhe
long , si sht prshkruar n tabeln Tabeln 1-2.
Varsisht nga natyra e t dhnave, ndonjher velra e plot duhet t ruhet duke
prdorur edhe shenjn pozitive ose negative, si +10 ose -5. Ndonj her tjetr
nj numr i plot supozohet t jet pozitiv, ashtu q nuk sht i nevojshm
prdorimi i shenjs. Numri i ruajtur me shenj, quhet signed number (numr
me shenj) ndrsa ai q nuk ruhet me shenj quhet unsigned number (numr
pa shenj).
Problemi sht q shenja e z 1 bit t memories, e cila prndryshe do t mund t
prdorej pr t reprezentuar vlern. Pr shembull, bajti i ka 8 bita dhe t gjith
ata mund t prdoren pr t ruajtur numrat pa shenj prej 0 deri n 255. Nurmi
me shenj mund t ruhet n rangun -128 deri n +127.
C dhe C++ prkrahin numrat me shenj, ndrjsa Java jo. Nj numr unsigned
integer sht vler q sht e nnkuptuar se sht pozitive. Shenja plus nuk
ruhet n memorie. Ndrsa n Java, t gjith numrat paraqiten me shenj. Zero
ruhet si numr pozitiv.
Bajti - byte
Tipi abstrakt i t dhnave byte (bajt) sht m i vogli n grupin integer dhe
deklarohet prmes fjals s rezervuar byte, Fig. 1.2. Programert zakonisht e
prdorin tipin byte pr drgimin ose pranimin e t dhnave nga fajllat ose npr
rrjet. Tipi byte poashtu zakonisht prdoret kur punohet me t dhna binare t
cilat mund t mos jen kompatibile (t pajtueshme) me tip tjetr abstrakt t t
dhanve. Zgjedhni tipin byte sa her q keni nevoj pr t lvizur t dhnat n
dhe nga fajlli ose npr rrjet.
33
Avni Rexhepi
short
Tipi short (angl. short-i shkurtr) sht ideal pr prdorim n programet q
ekzekutohen n kompjutert 16-bitsh, Fig. 1.3. Mirpo, shumica e
kompjuterve t till sot jan n mbeturina dhe jan zvendsuar me 32 ose 64bitsh. Prandaj, short sht tipi m s paku i prdorur. Zgjedheni kt tip nse
programi do t ekzeutohet n kompjuter t vjetr.
Floating-Point
Grupi floating-point prdoret pr t ruajtur numrat real, n memorie. Numri
real prmban vlern decimale. Jan dy lloje t tipeve t t dhanve floating
point: float dhe double (shiko tabeln 1-2). Tipi float sht numr me
precizitet t njfisht dhe double sht me precizitet t dyfisht. Preciziteti i
numrit sht numri i vendeve pas presjes decimale, q prmban vlern e sakt.
34
35
Avni Rexhepi
double
Tipi double (Figura 1.7) prdoret pr t ruajtur numrat real t cilt jan shum t
mdhenj ose shum t vegjl dhe krkojn sasi t dyfisht t memories e cila
rezervohet pr tipin float. Zgjedhni tipin double, sa her q duhet ruajtur vler
decimale me saktsi m shum se 7 shifra pas piks decimale.
Character
Tipi character (karakter Figura 1.8) reprezentohet si nj vler integer e cila
i prgjigjet bashksis (setit) t karaktereve. Seti karakter ia ndan nj vler
integer secilit karakter, simbol dhe shenj piksimi t gjuhs.
36
Boolean
ADT bool-ean (Figura 1.9) rezervon memorie pr t ruajtur nj vler booleane, e cila sht e sakt ose e pasakt (e vrtet ose fals) dhe reprezentohet si
zero ose nj. Zgjedhni tipin bool-ean sa her q duhet ruajtur nj prej dy
mundsive n memorie.
Adresat e memories
Imagjinoni memorien kryesore si nj seri n dukje t pakufijshme t fushave
(katrorve) t organizuar n grupe me nga tet. Secilit grup prej tet fushave (1
bajti) i ndahet nj numr unik i quajtur adres e memories (angl. memory
address, si n Figurn 1.10). Kjo sht shum me rndsi gjat msimit t
strukturave t t dhnave, prndryshe mund t shkaktohet konfuzion.
Avni Rexhepi
Avni Rexhepi
shfrytzuesit, definon kolonat/fushat (tipet primitive t t dhnave) t cilat
prbjn rreshtin/rekordin (tipin e t dhnave t definuar prej shfrytzuesit).
Mnyra apo forma e prdorur pr t definuar t dhnat e definuara prej
shfrytzuesit ndryshon varsisht prej gjuhs programuese q prdoret pr t
shkruar programin. Disa gjuh programuese. Disa gjuh programuese, si Java,
nuk i prkrahin fare tipet e definuara prej shftytzuesit. N vend t ksaj,
prdoren atributet e klasave, pr t grupuar tipet primitive t t dhnave.
N gjuht programuese C dhe C++, definimi i tipit t definuar prej shftytzusit
bhet prmes definimit t structure (strukturs). Paramendojeni strukturn si
nj shabllon, p.sh., shablloni pr shkronjn A. Shablloni nuk sht shkronja A,
por ai e definon si duket shkronja A. Nse ju duhet shkronja A, e vendosni
shabllonin mbi nj letr dhe e vizatoni shkronjn A. Nse duhet edhe nj
shkronj A, e prdorni shabllonin e njjt dhe e prsritni procesin e njjt. Pra,
duke e prdorur shabllonin mund t bni sa t doni shkronja A.
E njjta vlen edhe pr strukturn. Kur ju duhet nj grup i t dhnave primtive,
q prfaqsohen nga nj struktur, ju e krijoni nj instance (nj instanc, nj
rast, nj shembull) t strukturs. Instanca sht njsoj si shkronja A q paraqitet
n letr, pasi t largoni shabllonin. Secila instanc prmban t njjtat t dhna
primitive t cilat jan definuar n struktur, edhe pse secila instanc ka kopjen e
vet t ktyr t dhnave primitive.
X;
ather shohim se, njsoj sikur q tipi i variabls X sht tipi primitiv int,
ather tipi i variabls studenti1 sht StudentRecord, tipi i definuar prej
41
Avni Rexhepi
shfrytzuesit. Ose thn ndryshe, X sht variabl e tipit int, kurse studenti1
sht i tipit StudentRecord.
Madhsia e X-it sht sa madhsia e tipit primitiv int, kurse madhsia e
studenti1, sht sa shuma e tipeve primitive int dhe char, t definuara n
strukturn StudentRecord.
Madhsia e tipit t definuar prej shfytzuesit StudentRecord sht sa shuma e
madhsive t nj numri t plot (integer) dhe t nj karakteri (char). Rikujtojm
se madhsia e tipit primitiv t t dhnave matet n bita. Numri i bitave t tipit
primitiv t t dhnave varej nga gjuha programuese. Prandaj, programert duhet
ti referohen emrit t tipit primitiv, n vend se numrit t bitave. Kompjuteri e di
se sa bita ti rezervoj pr secilin tip primitiv t t dhnave.
43
Avni Rexhepi
44
Pointert
Pointeri (treguesi) sht nj objekt i cili mund t prdoret pr t ju qasur nj
objekti tjetr. Pointeri ofron qasje indirekte n nj objekt. Njerzit i prdorin
pointert n jetn e prdithshme gjat tr kohs. Pr shembull:
-
45
Avni Rexhepi
-
Avni Rexhepi
funksionojn dhe t mund t prdoren. Kshtu string dhe vector jan t
implementuara duke ofruar interfejsin i cili i fsheh sjelljet e klasit t dyt t
tipeve t brendshme.
Prdorimi i vector-it
Pr t prdorur vector-in standard, programi duhet t prfshij header
file-in e libraris, prmes: #include <vector>. Njsoj siq duhet deklaruar
variabla para prdorimit t saj n ndonj shprehje dhe t inicializohet para se t
prdoret vlera e saj, edhe vargu duhet gjithashtu. vector deklarohet duke i
dhn nj emr, n pajtim me rregullat e zakonshme t identifikatorve dhe duke
i treguar kompajlerit se t far tipi jan elementet e tij. Mund ti prcaktohet
edhe madhsia, mirpo nse nuk sht br kjo, madhsia sht zero, por
vector-it duhet ti ndryshohet madhsia m von.
Secili objekt n koleksionin e objekteve q e paraqet vargu, mund t qaset
prmes prdorimit t [ ] - array indexing operator (operatorit t indeksimit t
vargut). Thuhet se operatori [ ] e indekson vargun, q nnkupton se e specifikon
cili prej objekteve do t qaset.
N C++ vargjet gjithmon indeksohen duke filluar prej zeros. Prandaj,
deklarimi:
vector<int> a(3); // 3 int objekte: a[O], a[l], dhe a[2]
rezervon hapsirn pr ruajtjen e tre integer-ave, d.m.th., a[0], a[1] dhe a[2]. N
vector-in e STL-it nuk kryehet/bhet verifikimi i rangut t indeksave, kshtu
q qasja jasht kufijve t indeksave t vargut nuk kapet (nuk hetohet, nuk
vrehet) nga kompajleri, prandaj duhet kujdes i veant nga programert. (N
rastin e mparshm, indeksa legal jan vetm 0, 1 dhe 2). Kontrollimi i kufijve
t rangut (angl. range checking) mund t bhet prmes prdorimit t
funksionit/operatorit at, kshtu q; a.at(i), sht njsoj si a[i], mirpo
n kt rast sinjalizohet gabimi (error), nse indeksi i sht jasht kufijve.
Madhsia e vector-it mund t fitohet prmes funksionit size (angl. sizemadhsia). Pr kodin e mparshm, fragmenti a.size( ), do t kthente 3.
Vreni sintaksn: dot operatori prdoret pr t thirrur funksionin size, t
vector-it.
Madhsia e vector-it mund t ndryshohet duke thirrur funksionin resize
(angl. resize - ricakto madhsin). Kshtu, si alternative e deklarimit fillestar,
do t ishte:
48
Avni Rexhepi
t ishte memorie e ndar pr ndonj objekt tjetr. N kt rast do t paraqiteshin
rezultate jokorrekte, varsisht prej detajeve t implementimit t vector-it. (mund
t ndodh q n ndonj platform programi t funksionoj mir, por n t tjerat
do t jepte rezultate t gabuara). Duhet pasur kujdes me kufijt, sepse gabimet
pr nj pozit, jan t zakonshme dhe shum t vshtira pr tu detektuar.
Pjesa tjetr e programit sht relativisht e thjesht. Rutina rand, e deklaruar n
stdlib.h jep numr t rastit (angl. random number). Manipulimi n rreshtin 25,
e vendos at n kufijt 1 deri 100. Rezultatet jepen n dalje n rreshtat 28-30.
C++ standar specifikon se fushveprimi (angl. scope-horizont, brezi, fush,
shtrirje, etj) i i n rreshtin 20 prfundon me unazn for. (Me fjal tjera, i
nuk duhet t jet e dukshme n rreshtin 24). Prandaj, prdoren emra t ndryshm
pr numratort e unazave.
50
(a)
arr
original
(b)
arr
original
(c)
arr
(d)
original
Avni Rexhepi
her m i madh. N kt mnyr, kur zgjerojm vargun pre N elementeve n 2N
elemente, kostoja e N kopjimeve mund t shprndahet proporcionalisht prgjat
N elementve t ardhshme t cilat mund t insertohen n var pa pasur nevoj pr
zgjerim. Si rezutat, ky zgjerim dinamik sht vetm pak m i shtrenjt sesa
fillimi me nj madhsi fikse, por sht shum m fleksibil.
N programin 1.2, paraqitet programi i cili lexon nj numr t palimituar t
integjerave prej hyrjes standarde (tastiers) dhe i ruan rezultatet n vargun
dinamik (q zgjerohet dinamikisht). Deklarimi i funksionit getInts tregon se
vector sht parametr. Shenja & n deklarimin e funksionit para array,
specifikon se kjo sht referenc pr parametrin aktual, e jo kopje e tij. Pr kt
arsye, t gjitha ndryshimet n parametrin formal reflektohen edhe n argumentin
aktual (Referencat do t sqarohen n vazhdim).
1
#include <iostream>
2
#include <vector>
3
using namespace std;
4
5
// Lexo nje numer te pakufizuar te int-ev pa perpjekje per
rregullim gabimesh
6
// mbushni parametrat e vektorit me te dhena; madhesia e tij
7
// pas kthimit (return) tregon se sa elemente u lexuan
8
void getInts( vector<int> & array )
9
{
10
int itemsRead = 0;
11
int inputVal;
12
13
cout<<"Jepni numrin e cfaredoshem te integjerave: ";
14
while( cin >> inputVal )
15
{
16
if( itemsRead == array.size( ))
17
array.resize( array.size( ) * 2 + 1 );
18
array[ itemsRead++ ] = inputVal;
19
}
20
array.resize( itemsRead );
21 }
22
23 int main( )
24 {
25
vector<int> array;
26
27
getInts( array );
28
for( int i = 0; i < array.size( ); i++ )
29
cout << array[ i ] << endl;
52
53
Avni Rexhepi
7//mbush parametrat e vektorit me te dhena; madhesia e tij
8//pas kthimit (return) tregon se sa elemente u lexuan
9
void getInts( vector<int> & array )
10 {
11
int inputVal ;
12
13
array.resize( 0 );
14
cout << "Enter any number of integers: ";
15
while( cin >> inputVal )
16
array.push_back( inputVal );
17 }
Avni Rexhepi
Thirrja sipas vlers sht e prshtatshme pr objektet e vogla t cilat nuk duhet
t ndryshohen nga funksioni.
Thirrja sipas referenc konstante sht e prshtatshme pr objektet e mdha t
cilat nuk duhet t ndryshohen nga funksioni.
N disa raste m komplekse, thirrja sipas vlert duhet t evitohet. Programi
mund t dshtoj t kompajlohet nse jan br zgjedhjet e gabuara. Pasi q
string dhe vector, reprezentojn objekte t mdha, thirrja sipas vlers sht n
prgjithsi e paprshtatshme, e pavend. N vend t ksaj, kur kto objekte jan
parametra t funksionit, ato zakonisht prcillen sipas referencs ose referencs
konstante, varsisht prej asaj se a pritet q funksioni ti ndryshoj vlerat e
parametrave apo jo.
Pasi q string sht objekt i klasit t par, leximi (hyrja), shtypja (dalja),
kopjimi dhe karhasimi funksionojn ashtu si pritet. Prandaj, str1==str2 sht
true nse dhe vetm nse stringjet jan t njjta.
Secili karakter i string-ut mund t qaset duke prdorur operatorin e indeksimit
t vargut (si zakonisht, indeksat fillojn prej zeros). string ofron shum
funksione t dobishme.
Nse s sht string, ather s.length() kthen gjatsin e tij (d.m.th., numrin e
karaktereve), ndrsa s.c_str() kthen stringun primitiv. Stringu primitiv
nevojitet nganjher pr t ndrvepruar me pjest tjera t librarive. Pr
shembull, pr t hapur fajllin, duhet t prcillet stringu primitiv. S fundi,
operatort + dhe += pr string jan t definuar q t bjn bashkimin e
stringjeve (njri string lidhet n fundin e tjetrit). Kto operacione jan ilustruar
n kodin n Programin 1.4.
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 int main( )
6
{
7
string a = "hello";
8
string b = "world";
9
string c; // duhet te jete " "
10
11
c = a + // duhet te jete "hello "
12
c += b;
// duhet te jete "hello world"
13
14
// Shtype c ne menyren e lehte.
15
cout << "c eshte: " << c << endl;
16
17
// Shtype c ne menyren primitive.
18
cout << "c eshte: " << c.c_str() << endl;
19
57
Avni Rexhepi
20
// Shtype c karakter pas karakteri.
21
cout << "c eshte: ";
22
for( int i = 0; i < c.length(); i++
23
cout << c[i];
24
cout << endl;
25
26
return 0;
27 }
Programi 1.4 Ilustrim i funksioneve t string.
(&x) 1000
x=5
(&y) 1004
y=7
5
ptr
(&ptr) 1200
1000
(b)
(a)
(&x) 1000
x = 10
(&y) 1004
y=7
10
ptr
(&ptr) 1200
7
y
(b)
59
Avni Rexhepi
Deklarimi thot q x sht nj int i inicializuar n 5, y sht nj int i
inicializuar n 7 dhe ptr sht pointer n vler t tipit int dhe sht inicializuar
q t pointoj n x. L t shikojm se ku mund t kemi gabuar. Sekuenca vijuese
e deklarimit sht jokorrekte:
int *ptr = &x; // ILLEGAL: x nuk eshte deklaruar akoma
int x = 5;
int y = 7;
Ktu jemi duke prdorur x-in para se t jet deklaruar, kshtu q kompajleri do
t ankohet.
Ja edhe nj gabim i zakonshm (i shpesht):
int x = 5;
int y = 7;
int *ptr = x; // ILLEGAL: x nuk eshte adres
N kt rast, jemi duke tentuar q pointeri ptr t pointoj n x, por kemi harruar
se pointeri mban nj adres. Prandaj, duhet t kemi adres n ann e djaht t
urdhrit. Kompajleri do t lajmroj gabim pasi sht harruar operatori i adress
& para x-it.
Duke vazhduar me shembullin e njjt, supozojm se kemi deklarimin korrekt
por me pointerin ptr t painicializuar:
int x = 5;
int y = 7;
int *ptr; // LEGAL por ptr i painizializuar
Cila sht vlera e ptr? Si paraqitet n Figurn 1.15, vlera sht e padefinuar,
pasi q nuk ka qen e inicializuar asnjher. Prandaj, vlera e *ptr sht poashtu e
padefinuar. Pointeri duhet t jet duke pointuar diku para se t dereferencohet.
Sidoqoft, prdorimi i *ptr kur ptr sht i padefinuar sht edhe m i keq sepse
ptr mund t ket ndonj adres q nuk ka kuptim fare, e kshtu duke shkaktuar
q programi t bllokohet nse pointeri dereferencohet. Edhe m keq, ptr mund t
jet duke pointuar n nj adres e cila sht e qasshme: n t cilin rast,
programi nuk do t bllokohet menjher, por do t jet i gabueshm dhe jep
rezultate t pasakta. Nse *ptr sht cak i ndonj prcaktimi t vlers, ather ai
do t ndryshoj aksidentalisht ndonj t dhn tjetr, e cila mund t rezultoj n
bllokim t mvonshm t programit. Ky sht lloj i vshtir pr tu detektuar i
gabimit sepse shkaku dhe simptomet e gabimit mund t ken distanc t madhe
kohore mes veti.
M hert sht paraqitur sintaksa korrekte pr ndarjen (prcaktimin) e vlers:
ptr = &x; // LEGAL
Avni Rexhepi
Pr shembull, dy urdhrat vijues jan shum t ndryshm.
*ptr += 1;
*ptr++;
5
ptr1
ptr1
y
(a)
ptr1
7
ptr2
ptr2
y
(b)
7
ptr2
y
(c)
Figura 1.16 (a) gjendja fillestare; (b) ptr1=ptr2 duke filluar nga gjendja
fillestare;
(c) *ptr1=*ptr2 duke filluar nga gjendja fillestare.
N shembujt paraprak, pas urdhrit, *ptr1 dhe *ptr2 jan t dy 7. Ngajshm,
shprehja:
ptr1==ptr2;
#include <iostream>
#include <string>
using namespace std;
int main( )
{
63
Avni Rexhepi
7
string *strPtr;
8
9 strPtr = new string( "hello" );
10 cout << "Stringu eshte: " << *strPtr << endl;
11 cout << "Gjatesia e tij: "<<(*strPtr).length( )<<endl;
12
*strPtr += " world";
13
cout << "Tani stringu eshte " << *strPtr << endl;
14
15
delete strPtr;
16
17 return 0;
18 }
Programi 1.5 Ilustrim i alokimit dinamik t memories
64
Askush nuk do ti shkruante qllimisht kta tre urdhra njri pas tjetrit, mirpo
supozoni situatn kur ata jan t shprndar n nj kod t gjat e n nj funksion
kompleks. Para thirrjes s delete, kemi nj objekt t alokuar n mnyr dinamike
i cili ka dy pointer q pointojn n t.
Pas thirrjes s delete, vlerat e s dhe t (d.m.th., ku ata pointojn) nuk kan
ndryshuar. Mirpo, si sht ilustruar n Fig. 1.17, ata tani jan stale pointer.
(angl. stale-bajat, i ndenjur, etj.). Stale pointer sht pointeri vlera e t cilit m
nuk i referohet nj objekti valid. Pra, sht fshir objekti ku pointon pointeri.
Dereferencimi i s dhe t mund t drgoj n rezultate t paparashikueshme. Ajo
q i bn kto gjra veanrisht t vshtira sht se, edhe pse sht e qart q t
sht stale pointer, fakti q edhe s sht i till sht m pak i dukshm, sidomos
nse keni parasysh supozimin se kto urdhra mund t jen t shprndar n
ndonj funksion kompleks. Pr m tepr, n disa situata, memoria q ishte e
zn nga objekti sht e pandryshuar deri n nj thirrje t mvonshme t new
pr krkes t memories, gj q mund t jep iluzionin se nuk ka ndonj problem.
s
Hello
Fig. 1.17 Stale pointert: pas urdhrit delete t, pointert s dhe t tani pointojn
n nj objekt q nuk ekzisto m; urdhri delet s do t ishte fshirje e dyfisht
ilegale
Problem tjetr sht i ashtuquajturi double-delete (fshirja e dyfisht). Ky
problem ndodh kur tentohet t fshihet i njjti objekt m shum se nj her. Kjo
do t ndodhte nse do t jepej n vazhdim urdhri:
delete s;
// fshirje e dyfisht
sepse s sht stale dhe objekti n t cilin pointon nuk sht valid (nuk
ekziston). sht mundia shum e madhe q do t paraqiten probleme t kohs
s ekzekutimit (angl. run-time error).
Kto jan rreziqet e alokimit dinamik t memories. Duhet t jemi t sigurt q
kurr t mos e thrrasim urdhrin delete m shum se nj her pr nj objekt dhe
at vetm pasi t mos jet i nevojshm. Nse nuk thirret delete fare, edhe pse
objekti m nuk sht i nevojshm, ather do t ket rrjedhje t memories.
Gjithashtu, nse kemi variabl pointer dhe synojm ta fshijm me delete, duhet
t jemi t sigurt q objekti n t cilin pointohet ka qen i krijuar me urdhrin
65
Avni Rexhepi
new. Kur kemi thirrje t funksionit prej funksionit, prcjellja e t gjitha
elementeve bhet m e vshtir.
S fundi, pointert mund t bhen stale pointer edhe nse nuk sht br
alokimi dinamik. Shqyrtoni kodin n programin 1.6.
Pr ndonj arsye me kuptim (prveq pr ilustrim t gabimit), kemi funksionin
stupit i cili kthen pointerin n string. Nse funksioni stupit e thrret new pr t
krijuar stringun, ather thirrsi do t jet prgjegjs pr thirrje t delete. N
vend se t ngarkohet thirrsi, gabimisht kemi vendosur q funksioni stupid t
prdor nj string automatik dhe t kthej adresn e tij. Programi kompajlohet
por mund t mos funksionoj, sepse prmban gabim. Problemi sht se vlera t
ciln e kthen funksioni stupid sht pointer. Por pointeri sht duke pointuar n
s, e cila m nuk ekziston, sepse sht variabl automatike dhe funksioni stupid
(angl. stupid-torollak), ve ka kthyer me return (ka prfunduar punn). Kur t
kthehet vlera pointer, t jeni t sigurt q keni dika n t ciln pointoni dhe se
ajo ekziston edhe pasi kthimi (return) t jet kompletuar.
1
2
3
4
5
6
7
8
9
10
11
string *stupid( )
{
string s = "stupid";
return &s;
}
int main( )
{
cout << *stupid( ) << endl;
return 0;
}
Programi 1.6 - Stale pointer: i pointuari, s, nuk ekziston pasi funksioni stupid
kthen rezultatin.
1.5 Referencat
Prve tipit pointer, n C++ ekziston edhe tipi reference (referenc). Referenca
sht nj alias (pseudonim, nofk) pr nj objekt tjetr dhe mund t shihet edhe
si pointer konstant q dereferencohet gjithmon n mnyr implicite. Pr
shembull, n kodin vijues, cnt bhet sinonim pr nj variabl me nj emr
shum t gjat dhe t vshtir pr tu shkruar:
int emeriGjateiVariables = 0;
int & cnt = emeriGjateiVariables;
cnt += 3:
66
#include <iostream>
using namespace std;
// Nuk funksionon.
void swapGabim ( int a, int b )
{
int tmp = a;
a = b;
67
Avni Rexhepi
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
b = tmp;
}
// C Style duke perdorur pointeret.
void swapPtr( int *a, int *b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
// C++ Style duke perodrur referencat.
void swapRef( int &a, int &b )
{
int tmp = a:
a = b;
b = tmp;
}
// Programi per testim te funksioneve swap (shkmbe)
int main( )
{
int x = 5;
int y = 7;
swapGabim ( x, y );
cout << "x=" << x << " y=" << y << endl;
swapPtr ( &x, &y ) ;
cout << "x=" << x << " y=" << y << endl;
swapRef ( x, y );
cout << "x=" << x << " y=" << y << endl;
return 0;
}
68
Avni Rexhepi
Figura 1.18. ilustron se struktura Student prbhet prej katr objekteve t
ndryshme. Nse kemi deklarimin,
Student s;
70
i cili kthen true nse Studenti i par (lhs: left-hand side i ans s majt) sht
m i vogl se studenti i dyt (rhs: right-hand side i ans s djatht), bazuar n
ndonj kriter t definuar prbrenda funksionit, nga ana e shfrytzuesit.
(Shkurtesat lhs dhe rhs, pr left-hand side dhe right-hand side do t prdroren
npr shembuj, respektivisht). Prdorimi i mekanizmit t klasave, mundson q
ky funksion t prfshihet si antar i strukturs, ngjashm me antart e t
dhnave.
Operatori i ndarjes s vlers me kopjimi = dhe operatori i barazis == gjithashtu
mund t definohen, por nse nuk bjm asgj, definicioni standard prdoret pr
kopjim dhe krahasimi i barazis bhet ilegal. N mnyr specifike, n mnyr
standarde, kopjimi i strukturs implementohet si kopje antar pr antar. Me
fjal tjera, secili antar kopjohet nga njra strukutur n tjetrn, e jo n nivel t
strukturs, si trsi.
Problemi me kt mekanizm sht ilustruar n deklarimin vijues:
struct Profesor
(
71
Avni Rexhepi
string *emri;
string "mbiemri;
int IDPunetori;
} ;
Supozojm se kemi:
Profesor s, t;
Nse supozojm se t sht inicializuar, ather s=t sht kopjim antar pr
antar. Mirpo, dy antart e par t strukturs jan thjesht pointer, kshtu q
vetm adresat kopjohen. Prandaj rezultati sht q s.emri tani sht duke e
bashkndar memorien (angl. sharing memory) me t.emri, dhe kto nuk jan
kopje t pavarura t stringut. Nse m von jepet urdhri:
delete t.emri;
emri
s
mbiemri
12345
IDPunetori
12345
Shala
73
Avni Rexhepi
themelor sht treguar n figurn 1.20. Struktura rezultuese sht lista e lidhur
klasike, e cila i ruan t dhnat me nj kosto (mim) t nj pointeri pr element.
Definicioni i strukturs sht:
//Node=Nyje; item=elementi; next=tjetri,i/e ardhshme;
struct Node
{
Object item; // elementi
Node *next;
}l;
A1
First
(Fillimi, i/e par-i/a)
A3
A2
Last
(Fundi, i/e fundit)
1.7.1 Kontejnert
Kontejneri sht struktur e t dhnave e cila mban disa objekte t cilat
zakonisht jan t tipit t njjt (angl. contain-prmbaj, z, prfshij; angl.
container en, kuti). Tipet e ndryshme t kontejnerve organizojn objektet
prbrenda tyre n mnyra t ndryshme. Edhe pse numri i organizimeve t
ndryshme teoritikisht sht i pakufizuar, vetm nj numr i kufizuar i tyre ka
rndsi praktike dhe ato q prdoren m s shpeshti jan t prfshira n STL.
STL i prmban kontejnert vijues: deque, list, map, multimap, set, multiset,
stack, queue, priority_queue dhe vector.
Kontejnert e STL-it jan t implementuar si klasa shabllone (template classes)
t cilat prfshijn nj numr funksionesh t cilat specifikojn se cilat operacione
mund t kryhen n elementet e ruajtura n strukturn e t dhnave t specifikuar
prej kontejnerit ose n vet strukturn e t dhnave. Disa operacione mund t
gjinden n t gjith kontejnert, edhe pse ato mund t jen t implementuar
ndryshe. Funksionet e zakonshme t t gjith kontejnerve prfshijn
konstruktorin e zakonshm, konstruktorin e kopjimit (copy constructor),
destruktorin, empty() (zbraze), max_size() (madhsia maksimale), size()
(madhsia), sap() (shkmbe), operatorin = dhe prveq priority_queue gjasht
operatort relacional t mbingarkuar. Pr m tepr, funksionet e zakonshme n
t gjith kontejnert, prveq stack, queue dhe priority_queue, prfshijn edhe
funksionet: begin() (fillimi), end() (fundi), rbegin(), rend(), erase() (fshije) dhe
clear() (pastro).
Elementet e ruajtura n kontejner mund t jen t fardo tipi dhe ato duhet t
ofrojn s paku konstruktorin e zakonshm, destruktorin dhe operatorin e
ndarjes s vler (=). Kjo sht posaqrisht e rndsishem pr tipet e definuara
prej shfrytzuesit. Disa kompajler mund t krkojn mbingarkimin e disa
operatorve (s paku = = dhe <, por ndoshta edhe != dhe > poashtu) edhe
pse programi nuk i prdor ato. Gjithashtu, copy construcor-i dhe operatori i
funksionit = duhet t ofrohen nse t dhnat jan pointer, sepse operacionet e
insertimit prdorin kopjen e nj elementi q sht duke u insertuar, e jo vet
elementin.
75
Avni Rexhepi
1.7.2 Iteratort
Iteratori (angl. interate prsris), sht nj objekt q prdoret pr t ju referuar
nj elementi t ruajtur n kontejner. Prandaj, iteratori sht nj prgjithsim i
pointerit. Nj iterator mundson qasjen n informacionin e prmbajtur n
kontejner ashtu q opercioni i dshiruar t mund t kryhet n kto elemente.
Si prgjithsim i pointerve, iteratort mbajn notacionin e njjt t
dereferencimit. Pr shembull, *i sht nj element i referencuar nga iteratori
i. Poashtu, aritmetika e iteratorve sht e ngjashme me at t pointerve, edhe
pse t gjitha operacionet n iterator nuk lejohen n t gjith kontejnert.
Pr kontejnert: stack, queue dhe priority_queue nuk prkrahet asnj iterator.
Operacionet e iteratorve pr klasat list, map, multimap, set dhe multiset, jan si
vijon (i1 dhe i2 jan iterator, n sht numr):
i1++, ++i1, i1--, --i1
i1=i2
i1 == i2, i1 != i2,
*i1
Aritmetika e pointerve
Pointert prdoren pr t kaluar npr memorie sekuencialisht duke prdorur
aritmetikn e pointerve dhe operatorin e inkrementimit (++) dhe operatorin e
dekrementimit (--). Operatori i inkrememntimit e rrit vlern e variabls pr 1,
ndrsa operatori i dekrementimit e zovlon vlern e variabls pr 1.
N shembullin vijues, vlera e variabls numriStudentit rritet pr 1, duke br q
vlera finale t jet 1235:
int numriStudentit = 1234;
numriStudentit++;
77
Avni Rexhepi
ptNumriStudentit2 = &numriStudentit2;
a[0] a[1]
ptr
Edhe pse mbledhja apo zbritja e tipit integer prej tipit pointerit ka kuptim,
mbledhja e dy pointerve nuk ka kuptim. Mirpo, zbritja e dy pointerve
funksionon: y-x do t vlersohej 4 (n shembullin e mparshm, lart sepse
zbritja sht operacioni invers i mbledhjes). Prandaj, pointert mund t zbriten,
por jo t mblidhen.
Pr dy pointer, x dhe y, x<y sht e sakt nse objekti n t cilin pointon
pointeri x sht n adres m t ult sesa objekti n t cilin pointon pointeri y.
Nse supozojm se asnjri nuk pointon n NULL, kjo shprehje sht pothuajse e
pakuptim prve nse t dy pointojn n elementet e vargut t njjt. N kt
rast, x<y do t jet e sakt nse x pointon n elementin me indeks m t ult se
y, sepse si sht treguar, elementet e vargut sht e garantuar se ruhen n
lokacione t njpasnjshme dhe n rritje t memories. Krahasimi i vlerave t
pointerve q pointojn n t njjtin varg sht i vetmi prdorim legjitim i
operatorve t krahasimit n rastine pointerve. Prdorimet tjera duhet evituar.
Mos prdorni operatort relacional (t krahasimit) n pointer, prve nse t dy
pointert pointojn n pjest e vargut t njjt.
79
Avni Rexhepi
-
Pointert n pointer
Imagjinoni rastin kur kemi nj list me 1 milion student me notat e tyre dhe
numrat e tyre, dhe kkohet q t sortohet lista sipas emrit, mbiemrit dhe numrit
t studentit. Intuitivisht, mund t mendohet pr krijimin e dy kopjeve t lists,
secila me nj renditje t sortimit. Mirpo, kjo shkakton humbje t panevojshme
t memories. Ka nj qasje m t mir pr t sortuar listat: prdorimi i pointerve
t pointerve.
Dihet se pointeri sht variabl e cila prmban adresn e memories s nj
variable tjetr. Pointeri i pointerit, gjithashtu sht variabl q prmban adresn
e memories, por n kt rast adresn e memories s nj pointeri tjetr.
Nse ju duket e koklavitur, nuk jeni i vetmi! Koncepti i pointerit n pointer nuk
sht mjaft intuitiv. Mirpo, kjo sqarohet duke deklaruar variablat dhe duke
ruajtur vlerat n memorie.
Le t fillojm me deklarimin e katr variablave dhe inicializimin e tyre me
shkronja t alfabetit. Kjo sht treguar n urdhrin e par t shembullit n vijim.
Urdhri i dyt deklaron pointerin e quajtur ptInitial dhe pointerin n pointer t
quajtur ptPtInitial. Pointeri deklarohet duke prdorur shenjn asterisk (*).
Pointeri n pointer deklarohet duke prdorur dy asterisk-a (**).
char inital1='D', inital2='A', inital3='C', inital4='B';
char *ptInitial, **ptPtInitial;
ptInitial = &inital1;
ptPtInitial = &ptInitial;
80
Avni Rexhepi
Vargjet primitive
Sikur variablat e zakonshme, q duhet t deklarohet para se t prdoret n
ndonj shprehje dhe t inicializohet pra se t prdoret vlera e saj, ashtu duhet
edhe vargu. Vargu deklarohet duke i dhn emrin dhe duke i treguar kompajlerit
tipi e elementeve t tij. Nse definohet vargu, duhet t jepet edhe madhsia e tij.
Madhsia mund t anashkalohet nse bhet inicializimi direkt i vargut, e
kompajleri pastaj i numron vlerat e inicializuara dhe at e merr si madhsi t
vargut. Operatori i indeksimit t vargut [ ] ofron qasjen n elementet e vargut.
Secili objekt i bashkesis s objekteve q e tregon vargu, mund t qaset prmes
operatorit t indeksimit t vargut. Thuhet se operatori [ ] e indekson vargun, q
do t thot se specifikon cili element i vargut qaset.
Vargjet n C++ gjithmon indeksohen duke filluar nga zeroja. Mirpo, C++, nuk
bn kontrollim t kufijve, prandaj duhet kujdes sepse qasja me indeks jasht
kufijve nuk hetohet nga kompajleri. Nuk gjenerohet as gabim eksplicit i kohs
s ekzekutimit, mirpo ndodhin sjellje dhe velra t uditshme t programit.
Pr m tepr, nse vargu prcilet se argument aktual n funksion, athere
funksioni nuk ka ide pr madhsin e vargut, prve nse i prcilllet edhe nj
parametr plotsues. Vargjet, nuk mund t kompjohen me operatorin =, bazuar
n tiparet themelore t gjuhs pr vargjet dhe pointer dhe kufizimet pr to.
(1000)
a[0]
&a[1]
(1004)
a[1]
&a[2]
(1008)
a[2]
&i
(1012)
i
. . .
konstantet lokale
a=1000
Avni Rexhepi
for (i=1; i<=3; i++)
a[i]=0;
// thirrja e funksionit
//deklarimi i funksionit
Avni Rexhepi
shkruar n nj urdhr if ose n unaz. (Kujdes!. sht e zakonshme t harrohet
shenja \ para zeros, duke ln vetm 0, q n fakt sht karakteri pr shifrn 0).
Prandaj, nj varg prej 6 karaktereve N, i, n, a, dhe \0, prfaqson
stringun Nina, pa marr parasysh se ka ka n karakterin e gjasht.
Pra, problemi sht se C++ nuk ofron absolutisht asgj si prkrahje pr stringjet.
Pr m tepr, nuk ofron drejtprdrejt disa gjra t gjuhs, si n rastin kur
deklarojm dy stringje str1 dhe str2, si n vijimL
char strl[10]; // gjatesia Max eshte 9
char str2[10]; // gjatesia Max eshte 9
//Gabim!
// Gabim !
Ky dshtim vjen drejtprdrejt prej faktit se str2 dhe str2 jan vargje dhe ndarja e
vlers s vargjeve dhe krahasimi i vargjeve nuk sht i prkrahur nga gjuha. N
fakt, e tr prkrahja ofrohet nga libraria e C++, e cila specifikon funksionet t
cilat punojn me stringjet null-terminated. Prototipet pr kto funksione jan
dhn n fajllin q duhet prfshir me direktivn #include <string.h> (ose
<string>). Ky fajll sht i replikuar n <cstring>. Disa funksione t rndsishme
jan paraqitur n figurn n vijim.
1
2
3
4
size-t
char *
char *
int
strlen(
strcpy(
strcat(
strcmp(
const char
char *lhs,
char *lhs,
const char
*str );
const char *rhs );
const char *rhs ) ;
*lhs, const char *rhs 1;
86
87
Avni Rexhepi
strcpy do t mund t rezultonte n gabim hardveri. Vreni se gjihmon mund t
drgojm string jokonstant n parametrin q pret string konstant. Prandaj, kemi:
strcpy( name2, "Mark" ); // LEGALE
strcpy( "Mark", name2 ); // ILEGALE!
strcpy( name2, name1 ) ; // LEGALE
por kjo nuk sht njsoj si deklarimi i nj vargu pr t ruajtur kopjen e stringut
aktual; pr m tepr, name1[3]=e leht prcaktohet nga kompajleri se sht
ilegal n kt rast. Shembull i shpesht ku mund t prdoret deklarimi const char
* sht:
const char *message = "Welcome to FIEK!";
Ktu programeri pret q t kopjoj name n name2, por sht mashtruar sepse
deklarimi i strcpy tregon se duhet t prcillen dy pointer. Thirrja e till e
funksionit dshton sepse name2 sht vetm pointer, e jo pointer n lokacion t
mjaftueshm t memories, pr t mbajtur kopjen e name. nse name2 sht
NULL pointer, pointon n konstanten string t ruajtur n memorie vetm pr
lexim (angl read-only memory), ose pointon n nj lokacion ilegal t
zakonshm, strcpy sht e sigurt se do t tentoj ta dereferencoj at, duke
gjeneruar gabim. Nse name2 pointon n varg t modifikueshm (p.sh.,
ekzekutohet urdhri name2=name), ather nuk ka problem.
Edhe pse kto procedura duken shum restriktive dhe t ngatrruara, C++-i
ofron tipin <string> dhe e bn t duket njsoj sikur nj tip i predefinuar, si
sht tipi int. Rrjedhimisht, nuk kemi nevoj t brengosemi pr kufizimet e
detyruara n C++, sepse ato jan t fshehura prbrenda string-ut.
Prparsit
89
Avni Rexhepi
T metat
Lidhja me stringjet
Marrim n konsiderim null-terminated strings (stringjet e krijuara prej
karaktereve dhe t prmbyllura me karakterin null). Stringjet jan t
ngjashme me vargjet dinamike, por madhsia e tyre logjike tregohet prmes
karakterit null. Prandaj, kapacitety i tyre sht gjithmon nj element m
shume sesa madhesia logjike maksimale. Madhsia logjike e stringut njihet si
length (gjatsia).
Shembull. ASCII stringu "Hello!", i prfaqsuar n brendi t kompjuterit:
H e l l o ! \0
72 101 108 108 111 33 0
Pjes kodi
Programi n vijim e gjene minimumin e vlerave t insertuara.
#include <iostream>
using namespace std;
int main() {
// vargu static
int vargu1[15];
int n = 0;
int vlera = 0;
cout << "Jepni vlerat. Shtypni \"-1\" pr fund: ";
while (n < 15 && vlera != -1) {
cin >> vlera;
if (vlera != -1) {
vargu1[n] = vlera;
n++;
91
Avni Rexhepi
}
}
if (n == 0) {
cout << "Nuk keni dhn asnje vler!";
} else {
int minimumi = vargu1[0];
for (int i = 1; i < n; i++) {
if (vargu1[i] < minimumi)
minimumi = vargu1[i];
}
cout << "Vlera minimale sht " << minimumi;
}
return 0;
}
92
a1
a2
93
Avni Rexhepi
Memoria e alokuar me new nuk reciklohet automatikisht. Dshtimi pr t recikluar at,
shkakton rrjedhje t memories (angl. memory leak).
Problemi ndodh kur a1 sht variabl lokale. Kur funksioni n t cilin sht
deklaruar kthen rezultatin me return (d.m.th. kur a1 del prej fushveprimit
(angl. scope)), memoria e shoqruar me vargun restaurohet automatikisht nga
ana e sistemit; a1 del prej fushveprimit kur blloku (funksioni) n t cilin sht
deklaruar prfundon. Pr shembull, n Fig. D.3 a1 sht variabl lokale n
funksionin f. Kur f kthen (me return), e tr prmbajtja e a1, duke prfshir
memorien e ndar pr vargun, lirohet. N kontrast, kur a2 del prej
fushveprimit, vetm memoria e shoqruar me pointerin, lirohet. Memoria e
alokuar me new tani sht e pareferencuar dhe nuk prdoret pr ndonj urdhr
tjetr. Situata sht paraqitur grafikisht n Fig. 1.25.
Pr t recikluar memorien, duhet t prdoret operatori delete. Sintaksa sht:
delete [ ] a2;
Operatori delete, reciklon memorien e alokuar dinamikisht, e q nuk sht m e
nevojshme.
94
shume
Vargjet dinamike
Nj prej problemeve q ndodhin gjat puns me strukturn e vargut, sht fakti
q madhsia e vargut nuk mund t ndryshohet gjat ekzekutimit t programit.
Nuk ekziston ndonj zgjidhje e drejtprdrejt, por mund t enkapsulohet
menagjimi i kapacitetit.
Pjes kodi
//DynamicArray=VarguDinamik, size=madhesia, capacity=kapaciteti
//storage=depoja=vendiiRuajtjes,
class DynamicArray
{
private:
int size;
int capacity;
int *storage;
public:
DynamicArray() {
capacity = 10;
95
Avni Rexhepi
size = 0;
storage = new int[capacity];
}
DynamicArray(int capacity) {
this->capacity = capacity;
size = 0;
storage = new int[capacity];
}
~DynamicArray() {
delete[] storage;
}
};
Sigurimi i kapaciteit
Para se t shtohet nj ose m shum vlera, duhet t sigurohemi q kemi kapacitet
t mjaftueshm pr ruajtjen e tyre. Realizoni hapat vijues:
96
Paketimi
Kur largohen(fshihen) elementet, sasia e hapsirs s lir rritet. Nse ka shum
pak vlera n vargun dinamik, hapsira e pashftyrzuar bhet shpenzim i kot.
Pr t ruajtur hapsirn, zhvillojm mekanizmin e zvoglimit t kapacitetit, kur
ai sht i teprt.
97
Avni Rexhepi
98
Verifikimi i kufijve
Algoritmi pr verifikim t kufinjve, verifikon nse indeksi sht prbrenda
kufijve: 0...madhsia-1 dhe nse jo, e lajmron problemin (n programim, angl.
thros exception hedh kundrshtimin).
Funksioni InsertAt
Ky operacion mund t krkoj zgjerimin e vargut, ashtu q algoritmi s pari e
thrret metodn pr sigurim t kapacitetit, e cila duhet t siguroj kapacitetin
minimal madhesia+1. Pastaj shiftoni (zhvendosni) pr nj element (pozit) n t
djatht, t gjitha elementet prej i deri te madhesia-1, ku i sht pozita e
insertimit. Vreni se nse elementi i ri insertohet pas elementit t fundit n varg,
99
Avni Rexhepi
ather nuk ka nevoj pr shiftim. Pas shiftimit, vendose vlern n elementin e it dhe rrite madhsin pr 1.
Funksioni RemoveAt
Shifto t gjitha elementet prej i deri te madhesia-1, ku i sht pika e largimit,
pr nj element (pozit) n t majt. Pastaj zvoglo madhsin pr 1 dhe thirre
operacionin e paketimit. Paketimi bhet nse ka shum pak elemente t mbetura
pas largimit (fshirjes).
100
Pjes kodi
//DynamicArray=varguDinamik, rangeCheck=verifikimiiRangut
//set=cakto,get=merr,removeAt=levizeN, moveCount=numriLevizjeve
#include <cstring>
#include <exception>
void DynamicArray::rangeCheck(int index)
{
if (index < 0 || index >= size)
throw " Indeksi jasht kufijve!";
}
void DynamicArray::set(int index, int value)
{
rangeCheck(index);
storage[index] = value;
}
int DynamicArray::get(int index)
{
rangeCheck(index);
return storage[index];
}
101
Avni Rexhepi
void DynamicArray::removeAt(int index)
{
rangeCheck(index);
int moveCount = size - index - 1;
if (moveCount > 0)
memmove(storage + index, storage
sizeof(int) * moveCount);
size--;
pack();
}
(index
102
1),
index,
2. Analiza e algoritmeve
N prgjithsi, ne e prdorim kompjuterin sepse kemi nevoj t prpunojm sasi
t mdha t t dhnave. Kur ekzekutojm nj program pr sasi t mdha t
vlerave hyrse, ne duhet t jemi t sigurt q programi prfundon prbrenda nj
kohe t arsyeshme. Kohzgjatja e ekzekutimit sht pothuajse gjithmon e
pavarur prej gjus programuese ose edhe prej metodologjis s prdorur (p.sh.,
procedurale kundrejt asaj t orientuar n objekte).
Algoritmi sht nj grup i sepcifikuar qart i urdhrave t cilt i prcjell
kompjuteri, pr t zgjidhur nj problem. Kur t jet definuar algoritmi pr nj
problem t caktuar dhe t jet vrtetuar se sht korrekt, hapi tjetr sht q t
prcaktohet sasia e resurseve, si koha dhe hapsira, t ciln do ta krkoj
algoritmi. Ky hap quhet analiz e algoritmit. Algoritmi i cili krkon disa
gigabajt t memories kryesore nuk sht i dobishm n shumicn e
kompjuterve aktual, edhe nse sht trsisht korrekt.
Pra, t shohim:
- Si t llogaritet/vlersohet koha e nevojshme pr nj algoritm
- Si t prdoren teknikat t cilat e zvoglojn n mas t madhe kohn e
ekzekutimit t nj algoritmi,
- Si t prdoret prdoret korniza matematike pr prshkrimin m rigoroz t
kohs s ekzekutimit t nj algoritmi
- Si t shkruhet nj funksion i thjesht pr krkim binar
Pr t qen me interes, algoritmi duhet t zgjidh problemin e prgjithshm t
specifikuar mir. Nj problem algoritmik sht i specifikuar duke prshkruar
setin komlet t instancave q duhet ti punoj dhe cilat tipare duhet ti ket dalja
(rezultati n dalje) si rezultat i ekzekutimit t ndonjrs prej ktyre instancave.
Ky dallim ndrmjet problemit dhe nj instance t problemit sht fundamental.
Pr shembull, problemi algoritmik i njohur si sortim sht i definuar si vijon:
Hyrja: nj sekuenc e n vlerave (elsave), a1, a2, ... an.
Dalja: Permutacioni (rirenditja) e sekuencs hyrse ashtu q: a1<a2<...<an.
Nj instanc e sortimit mund t jet nj varg i numrave ose nj list e emrave.
Prcaktimi nse kemi n fakt nj problem t prgjithshm me t cilin duhet t
mirremi, kundrejt nj instance t problemit, sht hapi i par drejt zgjidhjes s
tij. Kjo sht e vrtet pr algoritmet ashtu si edhe n jetn reale.
103
Avni Rexhepi
Algoritmi sht procedura e cila merr cilndo prej instancave t mundshme
hyrse dhe e transformon at n daljen e dshiruar. Ka shum algoritme t
ndryshme pr zgjidhjen e problemit t sortimit. Pr shembull, nj prej metodave
t sortimit fillon me nj element t vetm (prandaj duke formuar kshtu n
mnyr triviale nj list t sortuar prej nj antari) dhe pastaj n mnyr
inkrementuese (rritse) inserton elementet e mbetura ashtu q lista qndron e
sortuar. Ky algoritm, i njohur si sorti i insertimit (anlg. insertion sort), sht
prshkruar n vijim:
InsertionSort(A)
for i = 1 to n-1 do
for j = i+1 to 2 do
if (A[j] < A[j-1]) shkmbe vendet (A[j],A[j-1])
Avni Rexhepi
Situata e njjt sht e vrtet edhe me RAM modelin e llogaritjes. Ne bjm nj
abstraksion q n prgjithsi sht shum i dobishm. sht plotsisht e vshtir
q t dizajnohet nj algoritm i till q RAM modeli t jep rezultate
substancialisht t gabuara, duke performuar ose shum m mir ose shum m
keq n praktik sesa q modeli sygjeron. Fuqia (angl. robustness) e RAM na
mundson q t analizojm algoritmet n modelin e pavarur nga makina.
Kompleksiteti i rastit m t mir, rastit mesatar dhe rastit m t keq
Duke prdorur RAM modelin e llogaritjes, mund t numrojm se sa hapa do t
merr algoritmi i jon n cilndo instanc t hyrjes, thjesht duke e ekzekutuar
at n nj hyrje t dhn. Sidoqoft, pr t kuptuar me t vrtet se sa i mir apo
i keq sht nj algoritm, duhet ta dijm se si punon ai prgjat t gjitha
instancave.
Pr t kuptuar nocionet e komplesitetit m t mir, mesatar dhe m t keq, njeriu
duhet t mendoj lidhur me ekzekutimin e algoritmit n t gjitha instancat e
mundshme t t dhnave t cilat mund ti jepen atij. Pr problemin e srotimit,
seti i instancave t mundshme hyrse t t gjitha aranzhimeve t mundshme t
numrave t elsave. Ne mund t reprezentojm seciln instanc hyrse si nj
pik n graf, ku boshti x sht madhsia e problemit (pr sortimin, numri i
elementeve q sortohen) dhe n boshtin y sht numri i hapave t ndrmarr nga
algoritmi n kt instanc. Ktu ne supozojm, tresisht me arsye, se nuk sht
me rndsi se far jan vlerat e elsave, por vetm sa jan dhe si jan t
renditura. Pr shembull, nuk do t duhej t merr m shum koh sortimi i 1,000
emrave anglez sesa sortimi i 1,000 emrave shqip.
1. Kompleksiteti i rastit m t keq t algoritmit sht funksioni i definuar
nga numri maksimal i hapave t ndrmarr n cilndo instanc t
madhsis n.
2. Kompleksiteti i rastit mesatar t nj algoritmi sht funksioni i definuar
nga numri mesatar i hapave q ndrmirren n cilndo instanc t
madhsis n.
3. Kompleksiteti i rastit m t mir t nj algoritmi sht funksioni i
definuar ng anumri minimal i hapave q ndrmirren n cilndo instanc
t madhsis n.
N praktik, m e dobishmja prej ktyre tri matjeve vrtetohet t jet
kompleksiteti i rastit m t keq, t cilin shum njerz e konsiderojn kundrintuitive. Pr t ilustruar se prse analiza e rastit m t keq sht e rndsishme,
shqyrtoni tentimin e projektimit t asaj q do t ndodh me ju nse shkoni n
kazino (bastore) pr t vn bast me n euro.
106
107
Avni Rexhepi
-
Kompleksiteti kohor
Kompleksiteti kohor sht mnyr pr t reprezentuar sasin e krkuar t kohs
q programi t ekzekutohet deri n kompletim (prfundim).
Llogaritja e kompleksitetit kohor
Llogaritja e kompleksitetit kohor bhet zakonisht me metrikn e zakonshme t
njohur si Big O notation (Notacioni O e madhe), i cili i largon t gjith
faktort konstant, ashtu q koha e ekzekutimit t mund t prafrohet n relacion
me N (numrin e vlerave hyrse), gjersa N i afrohet infinitit. N prgjithsi, mune
ta konsideroni si n vijim.
urdhri;
N kt rast, kemi nj urdhr t vetm dhe kompleksiteti kohor i tij do t jet
konstant. koha e tij e ekzekutimit nuk do t ndryshoj n realcion me N.
for (i=1; i<N; i++)
{
urdhri;
}
109
Avni Rexhepi
lart t rritjes, n krahasim me funksionet lineare dhe ato logaritmike, ndrsa ato
eksponenciale, edhe shum shum m t lart.
N
1
2
5
10
15
20
30
40
50
60
70
80
90
100
lg n
0.0
1.0
2.3
3.3
3.9
4.3
4.9
5.3
5.6
5.9
6.1
6.3
6.5
6.6
n lgn
1.0
2.0
5.0
10.0
15.0
20.0
30.0
40.0
50.0
60.0
70.0
80.0
90.0
100.0
0.0
2.0
11.6
33.2
58.6
86.4
147.2
212.9
282.2
354.4
429.0
505.8
584.3
664.4
1.0
4.0
25.0
100.0
225.0
400.0
900.0
1600.0
2500.0
3600.0
4900.0
6400.0
8100.0
10000.0
1.0
8.0
125.0
1000.0
3375.0
8000.0
27000.0
64000.0
125000.0
216000.0
343000.0
512000.0
729000.0
1000000.0
2.0
4.0
32.0
1024.0
32768.0
1048576.0
1073741824.0
1099511627776.0
1125899906842620.0
1152921504606850000.0
1180591620717410000000.0
1208925819614630000000000.0
1237940039285380000000000000.0
1267650600228230000000000000000.0
Klasifikimi i rritjes
Pasi q shkalla/shpejtsia e rritjes s nj algoritmi sht e rndsishme dhe pasi
q kemi par q shkalla/shpejtsia e rritjes sht e dominuar nga termi m i
madh n nj ekuacion, ne do ti hedhim posht termat q rriten m ngadal. Kur
i largojm t gjitha kto gjra, mbesim me at q e quajm rendi i funksionit ose
algoritmit t ndrlidhur. Pastaj mund ti grupojm algoritmet s bashku n baz
t rendit t tyre. Ne i grupojm ato n tri kategori ato q rriten s paku aq
shpejt sa disa funksione, ato q rriten me shkall/shpejtsi t njjt dhe ato q
rriten m shpejt.
Kompleksiteti kohor i nj algoritmi ka t bj me prcaktimin e nj shprehjeje
me numrin e hapave t nevojshm si funksion i madhsis s problemit. Pasi q
masa e numrit t hapave sht e disi e vrazhd, nuk synohet t nxirret nj
numrator i sakt i hapave. N vend t ksaj, tentohet q vetm t gjinden
kufinjt asimptotik n numrin e hapave. Analiza asimptotike prdor notacionet
si O notacionin, q njihet si Big Oh notation (Big Oh notacioni). Dy
konstrukte tjera notacionale t prdorur nga shkenctart e kompjuteriks n
analizn e algoritmeve jan: notacioni (Big Theta notacioni) dhe notacioni
(Big Omega notacioni).
Vlersimi i performanss s nj algoritmi fitohet duke mbledhur totalin e
ngjarjeve (paraqitjeve) t do operacioni gjat ekzekutimit t algoritmit.
Performansa e nj algoritmi vlersohet si funksion i madhsis s hyrjes n dhe
duhet t konsiderohet si modul i konstants multiplikative.
Notacionet vijuese jan notacionet e prdorura zakonisht n analizn e
performansave dhe t prdorur pr t karakterizuar kompleksitetin e nj
algoritmi.
Avni Rexhepi
t djatht t n0, vlera e f(n) gjithmon shtrihet nn cg(n). Pra, big O sht kufiri i
eprm pr t gjitha funksionet q i takojn ksaj klase.
cg(n)
f(n)
n0
f(n)=O(g(n))
Fig. 2.2 Big O
n0
f(n)=q (g(n))
112
f(n)
cg(n)
n0
f(n)=W (g(n))
Fig. 2.4 Big
Rastet e analizave
Kompleksiteti i nj algoritmi sht funksioni g(n) i cili jep kufirin e eprm t
numrit t operacioneve (ose kohs s ekzekutimit) t kryera nga nj algoritm
kur madhsia e hyrjes sht n. Ekzistojn dy interpretime t kufirit t eprm
Avni Rexhepi
n pozitn e par t vargut, ose kompleksiteti i algoritmit t sortimit, kur n
fillim jepet vargu i sortuar.
Pasi q sht shum e vshtir q t vlersohet sjellja statistike e hyrjes, t
shumtn e herave ne knaqemi m sjelljen e rastit m t keq. T shumtn e
kohs, kompleksiteti i g(n) prafrohet me familjen e tij O(f(n)) ku f(n) sht nj
prej funksioneve vijuese: n (kompleksiteti linear), log n (kompleksiteti
logaritmik), na ku a2 (kompleksiteti polinomial), an (kompleksiteti
eksponencial).
Optimaliteti
Kur nj her t jet vlersuar kompleksiteti i nj algoritmi, parashtrohet pyetja a
sht ky algoritm optimal. Nj algoritm pr problemin e dhn sht optimal
nse kompleksiteti i tij arrin kufirin e poshtm prgjat t gjitha algoritmeve q
e zgjidhin kt problem. P.sh., cilido algoritm q e zgjidh problemin e
pikprerjes s n segmenteve do t ekzekutoj s paku n2 operacione n rastin
m t keq edhe nse ai nuk bn asgj prveq shtypjes s rezultati (daljes). Kjo
shkurtohet duke thn se problemi ka kompleksitet (n2). Nse gjindet nj
algoritm O(n2) i cili e zgjidh kt problem, ai do t jet optimal dhe i
kompleksitetit (n2).
Reduktimi
Nj teknik tjetr pr vlersimin e kompleksitetit t problemit do t jet
transformimi i problemi, i quajtur gjithashtu edhe reduktim i problemit. P.sh, t
supozojm se e dijm kufirin e poshtm t nj problemi A dhe ne do t
dshironim t vlersojm kufirin e poshtm t problemit B. Nse mund t
transformojm problemin A n problemin B me disa hapa transformimi, mimi i
t cilve sht m i vogl se ai pr zgjidhjen e problemit A, ather B ka kufi t
njjt si A.
Algoritmika
sht e qart se temat e algoritmeve dhe strukturave t t dhnave nuk mund t
ndahen pasi q jan t ndrlidhura pazgjidhshm. Kshtu, para se t flasim pr
strukturat e t dhnave, duhet t fillojm me nj rishikim t shpejt t
algoritmeve themelor dhe n veanti, si t matet efikasiteti relativ i
algoritmeve. shtja themelore n studimin e efikasitetit t algoritmeve sht
sasia e resurseve q ato i shfrytzojn, zakonisht e matur n koh ose hapsir.
Zakonisht jan dy mnyra pr t matur kto madhsi. Njra sht analiza
matematike e algoritmit t prgjithshm t prdorur, e quajtur analiza
114
Rishikim i asimptots
Ka nj mori lojrash t cilat i prdorin analizuesit e algoritmeve, pr t studiuar
kohn e ekzekutimit t algoritmit, por elementi i par dhe themelor sht nocioni
115
Avni Rexhepi
asimptotik. Supozojm se sht br analiza e algoritmit dhe sht zbuluar se
koha e rastit m t keq sht:
T(n) = 13n3 + 42n2 + 2n log n +3 n
Nse nuk prcaktohet ndryshe, supozojm se algoritmet jan marr me baz 2.
Kur vlera e n sht e vogl, ne nuk brengosemi shum pr kt funksion, sepse
ai nuk do t jet shum i madhe, por gjersa n rritet, do t duhet t brengosemi
pr kohn e ekzekutimit. Sa m shum q rritet n, madhsia n3 sht shum
m e madhe se n2, q sht shum m e madhe sesa n log n (vreni se 0 < log
n < n, sa her q n > 1), e q sht m i madh se n . Prandaj, termi
n3dominon vlera t mdha t n. Gjithashtu, vreni se faktori i par 13 sht
konstante. Faktort e till konstant mund t ndikohen nga shpejtsia e procesorit
ose kompajleri, kshtu q mund t injorohen (pr sa koh q jan realitivisht t
vegjl). Pra, kt funksion do ta prgjithsonim n form t ngjeshur duke thn
se koha e tij e ekzekutimit sht afrsisht e rendit n3 dhe kjo shkruhet si T(n)
O(n3).
Joformalisht, shprehja T(n) O(n3) do t thot q, kur injorohen faktort
shumzuese konstant dhe kur shqyrtohet termi me rritjen m t shpejt, fitohet
n3. Kjo sht intuita m standarde q prdoret dhe m e lehta pr tu aplikuar
pr shumicn e rasteve.
Sipas definicionit, T(n) O(f(n)), nse limn-> T(n)/f(n) sht ose zero ose
konstant (por jo ). Pr shembull, ne tham q funksioni i mparshm ishte T(n)
O(n3). Duke prdorur definicionin, do t kemi:
T ( n)
13n 3 42n 2 2n log n 3 n
lim
n f ( n )
n
n3
lim
lim (13
n
42 2 log n
3
2.5 )
n
n
n
= 13.
116
O(1)
Algoritmi i cili gjithmon ekzekutohet njsoj, pa marr parasysh hyrjen. Pr
shembull, algoritmi i cili gjithmon kthen nj vler t njjt, pa marr parasysh
hyrjen mund t konsiderohet algoritm me efikasitet O(1). Qasja direkte e rastit,
n cilindo element t vargut, sht poashtu O(1).
O(logn)
Algoriteme e bazuar n pemt binare jan zakonisht t rendit O(log2 n). Kjo pr
arsye se pema e balansuar binare ka (log2 n) shtresa dhe krkimi i cilitdo
element n pemn binare krkon prshkimin e vetm nj nyjeje n seciln
shtres.
117
Avni Rexhepi
Algoritmi i krkimit binar sht poashtu shembull i rendit O(log n). N krkimin
binar, krkohet vlera n vargun e sortuar dhe duke filluar n mes, mundsohet
q n hapin tjetr, t krkohet vetm gjysma e eprme ose e poshtme. Kshtu
vazhdohet, n do hap pasues, me pjesn e mbetur. Vargu mund t ndahet n
gjysma, vetm log n her, para se t arrihet n nj element t vetm, i cili n fakt
do t jet elementi q krkohej (me kusht q t jet n varg).
O(n)
Algoritmet me efikasitet n krkojn vetm nj kalim npr tr hyrjen. Pr
shembull, algoritmi i krkimit linear, i cili krkon nj vler n vargun e
pasortuar, duke verifikuar secilin element me radh, sht O(n). shpesh, qasja n
elementet e lists s lidhur sht O(n), sepse lista e lidhur nuk prkrah qasjen e
rastit.
O(nlogn)
Shpeshher, algoritmet e mira t sortimit jan t rendit O(n log n). shembull i
algoritmit me kt efikasitet sht merge sort (sorti i bashkimit), i cili e ndan
vargun n dy pjes, i sorton dy gjysmat me thirrje rekursive, duke e thirrur
vetveten n to dhe pastaj duke i bashkuar rezultatet prapa n nj varg t vetm.
Pasi q e ndan vargun n gjysm, seciln her, unaza e jashtme ka efikasitetin
log n, dhe pr do nivel t vargut q sht ndar (kur vargu sht n dy gjysma,
pastaj n qerek, pastaj n t tetn, e kshtu me radh), ai do t duhet t bashkoj
t gjith elementet, gj q paraqet nj operacion t rendit n. Pra, n her log n.
O(n2)
Efikasitet deri diku i arsyeshm, akoma n rangun e kohs polinomiale, sht
tipik pr shembujt e disa algoritmeve t sortimit, si p.sh., algoritmi i sortit me
selektim.
O(2n)
Efikasiteti m i rndsishm jo-polinomial sht kjo rritja eksponenciale kohore.
Shum probleme t rndsishme mund t zgjidhen vetm me algoritme t ksaj
shkalle t efikasitetit (ose edhe m t keqe). Nj shembull do t ishte faktorizimi
i numrave t mdhenj, t shprehur n form binare. Mnyra e vetme sht
prova/gabimi dhe qasja naive do t prfshinte ndarjen/pjestimin e secilit numr
m pak sesa numri q faktorizohet n at numr, deri sa t gjendet plotpjestimi.
Pr seciln rritje t nj shifre, kjo do t krkonte dyfish testime.
118
Parakushtet matematike
Baza e mir matematike sht nj vegl q do t nevojitet pr t arsyetuar lidhur
me strukturat e t dhnave dhe algoritmet me t cilat punohet. T kuptuarit e
matematiks ndihmon n aftsin e dizajnimit t strukturave t mira t t
dhnave, pasi q prmes matematiks sht e mundur q t fitohet pasqyr m e
qart e natyrs s strukturave t t dhnave dhe ndjenja e prgjithshme pr
efikasitetin e tyre kohor dhe hapsinor. Le t shohim disa nocione preliminare t
shumave dhe provave prmes induksionit.
Shumat: shumat jan t rndsishme n analizn e programeve t cilat operojn
me prsritje (iterativisht). Pr shembull, fragmenti vijues i kodit:
for (i=1; i<n ; i++) { ... };
ku trupi i unazs ({...}) merr kohn f(i) pr t kaluar kohn totale t ekzekutimit,
jepet prmes shums:
n 1
T(n) =
f (i) .
i 0
Nse kemi unaza t ndrthurura (unaz brenda unazs), ather kemi edhe
shuma t ndrthurura (shum brenda shums). Zgjidhja e shumave ndahet n dy
hapa themelor. S pari duhet thjeshtuar shumn sa m shum q t jet e
mundur, duke larguar termat konstant (vini re, ktu konstante do t thot do gj
q nuk varet nga variabla e unazs, i) dhe duke ndar termat individuale n
shuma individuale. Pastaj, secila prej shumave t mbetura t thjeshtuara mund t
zgjidhet pavarur. Disa prej shumave t rndsishme pr tu ditur jane:
n
1 n (Serit konstante)
i 1
n
i
i 1
n(n 1)
(Serit aritmetike)
2
i 1
c n 1 1
c 1 (Serit gjeometrike)
c
c 1
i 0
n
119
Avni Rexhepi
3
i 0
3 h 1 1
3h
3 1
120
f (i)
i a
xa
f ( x)dx
Rekurrenca
Konstrukt tjetr matematik i cili paraqitet gjat studimit t algoritmeve rekurzive
sht ai i rekurrencs. Rekurrenca sht formula matematike e cila definohet
rekurzivisht. Pr shembull, le t kthehemi tek shembulli i pems 3-are, me
lartsi h. ka edhe nj mnyr tjetr pr t prshkruar numrin e nyjeve n pemn
3-are. Nse h=0 ather pema prbhet prej nj nyjeje t vetme (rrnjs).
Prndryshme, ajo pem prbhet prej rrnjs dhe 3 kopjeve t pems 3-are me
lartsi h-1. Kjo sygjeron rekurrencn vijuese q definon numrin e nyjeve N(h)
n pemn 3-are me lartsi h:
N(0) = 1
N(h) = 3N(h1) + 1 if h _ 1.
Edhe pse definicioni duket t jet cirkular, sht i bazuar mir pasi q
prfundimisht do t redukojm n:
N(0).
N(1) = 3N(0) + 1 = 3 _ 1 + 1 = 4
N(2) = 3N(1) + 1 = 3 _ 4 + 1 = 13
N(3) = 3N(2) + 1 = 3 _ 13 + 1 = 40
e kshtu me radh.
Jan dy metoda themelore pr zgjidhjen e rekurrencave. Njra (e cila funksionon
mir pr rekurrencat e thjeshta t rregullta) sht q t zgjerohet definicioni i
rekurrencs me prsritje, duke e redukuar prfundimisht n nj shum dhe
tjetra sht q thjesht t hamendsohet prgjigja dhe t prdoret induksioni. Ja
nj shembull i tekniks s par:
N(h) = 3N(h1) + 1
121
Avni Rexhepi
= 3(3N(h 2) + 1) + 1 = 9N(h 2) + 3 + 1
= 9(3N(h 3) + 1) + 3 + 1 = 27N(h3) + 9 + 3 + 1
...
= 3kN(hk) + (3k1 + ... + 9 + 3 + 1)
Kur prfundon e gjith kjo? Ne e dijm se N(0)=1, kshtu q le t caktojm k=h
duke implikuar q
N(h) = 3hN(0) + (3h1 + ... + 3 + 1) = 3h + 3h1 + ... + 3 + 1 =
i 0
Pra, kjo sht e njjta gj si m par, por e derivuar (nxjerrur) n mnyr tjetr.
Prova prmes induksionit: teknika tjetr e rndsishme matematike sht ajo e
provs (vrtetimit) prmes induksionit. Vrtetimet (provimet) me induksion jan
kritike pr t gjitha aspektet e shkencave kompjuterike dhe strukturave t t
dhnave, e jo vetm prova t efikasitetit. N veanti, virtualisht t gjitha
argumentet e korrektsis jan t bazuara n induksion. Prej msimeve nga
matematika diskrete keni msuar qasjen prmes induksionit. Keni nj teorem t
ciln doni ta vrtetoni dhe sht e forms: Pr t gjitn nurmat e plot n 1,
vlen se ... kur shprehja e teorems prfshin n njfar forme n-in. Idea sht
q n hapin e par, t njohur si rasti baz, t vrtetohet se teorema vlen pr
nj bashksi themelore t vlerave n, (p.sh., n=1 n kt rast). Pastaj tregohet se
nse teorema vlen pr rastin baz, ather hapi i dyt, i njohur si hapi induktiv,
sht q t vrtetohet q shprehja e dhn pr cilindo numr natyral n,
implikon q shprehja e dhn vlen edhe pr cilindo numr natyral t ardhshm,
n+1. Prej ktyre dy hapave, induksioni matematik sht rregulla pr t ciln
konkludojm se shprehja e dhn vlen pr t gjith numrat natyral.
N strukturat e t dhnave, posaqrisht kur punohet me pemt, ky tip i
induksionit nuk sht shum i dobishm. N vend t ksaj, prdoret versioni i
ngjashm,i quajtur induksioni i fort, q duket t jet m relevant. Idea sht q
t supozohet se nse teorema vlen pr t gjitha vlerat e n t cilat jan n
mnyr strikte m t vogla sesa n, ather ajo vlen edhe pr n.
Le ti kthehemi rastit t mparshm dhe ta vrtetojm me induksion.
Teorem: le t jet T pema komplete 3-are me n-1 nyje. Le t tregoj H(n)
lartsin e ksaj peme. Ather:
H(n) = (log3(2n + 1)) 1
Rasti baz: marrim vlern m t vogl legale pr n, n=1 n kt rast. Pema me
nj nyje t vetme ka lartsin zero, kshtu q H(1)=0. Duke vendosur n=1 n
122
T(n)= i
i 0
Avni Rexhepi
lokalizoni
vrtetimin vijues?)
gabimin
125
Avni Rexhepi
N3 Ngjashm, algoritmi q proceson treshe t t dhnave (ndoshta n
unaz t trefisht), ka koh kubike t ekzekutimit, q sht praktike
vetm pr prdorim n probleme t vogla. Kur N sht 100, koha e
ekzekutimit sht 1 milion. Sa her q N dyfishohet, koha e
ekzekutimit tetfishohet.
2N Shum pak prej algoritmeve me koh eksponenciale t ekzekutimit do
t jen t prshtatshm pr prdorim praktik, edhe pse algoritmet e
tilla paraqiten natyrisht si zgjidhje brute-force t problemeve. Kur N
sht 20, koha e ekzekutimit sht 1 milion. Sa her q N dyfishohet,
koha e ekzekutimit ngritet n katror.
Koha e ekzekutimit t nj programi t veant zakonisht sht ndonj konstante
e shumzuar me nj nga kto terma (termi kryesor) plus ndonj term m i vogl.
Vlera e koeficientit konstant dhe termave t prfshir varen nga rezultatet e
analizs dhe detajet e implementimit. N terma t vrazhdt, koeficionti i termit
kryesor (t par) ka t bj me numrin e instruksioneve n unazn e brendshme:
n cilindo nivel t dizajnit t algoritmit, sht menquri t limitohet numri i
instruksioneve t tilla. Pr N t madh, efekti i termit kryesor dominon; pr N t
vogl ose pr algoritme t ndrtuara me kujdes, mund t kontribuojn m shum
terma dhe krahasimet e algoritmeve jan m t vshtira. N shumicn e rasteve,
kohve t ekzekutimit t programeve thjesht ju referohemi si lineare,
kaudratike, kubike, N log N, etj.
Prfundimisht, pr t zvogluar kohn e ekzekutimit t programit, fokusohemi
n minimizimin e instruksioneve n unazn e brendshme. Secili instruksion
shkon n vzhgim t kujdesshm: a sht m t vrtet i nevojshm? A ka
mnyr m efikase pr t prmbushur detyrn e njjt? Disa programer besojn
se pajisjet automatike t ofruara nga kompajlert modern mund t prodhojn
kodin m t mir t makins; t tjert besojn se rruga m e mir q t
prpunohen me kujdes dhe t kodohen unazat e brendshme n kod t makins
ose n asembler. Normalisht, ajo q na intereson pa hyr n aspektet e
optimizimit, sht q t dijm sa instruksione nevojiten pr operacionet e
caktuara dhe t kuptojm se prse n praktik nj algoritm mund t jet m i
shpejt se nj tjetr.
Pr problemet e vogla, ka dallim t vogl se ciln metod e prdorim, pasi q
kompjutert modern super t shpejt, e kryejn punn n ast. Por me rritjen e
madhsis s problemit, numrat bhen shum t mdhenj. Pasi q numri i
instruksioneve q duhet t ekzekutohen nga nj algoritm i ngadalshm bhet
vrtet shum i madh, koha e nevojshme pr t ekzekutuar kto instruksione
bhet e pazbatueshme, edhe pr kompjutert e shpejt. N figurn 2.1 shihen
disa faktor t konvertimit prej nj numri mjaft t madh t sekondave, n dit,
126
3
10
32
100
316
1000
10
100
1000
10000
100000
1000000
N lg N
33
664
9966
132877
1660964
19931569
Sekonda
102
104
105
106
107
108
109
1010
1011
N(lg N)2
110
4414
99317
1765633
27588016
397267426
N3/2
N2
32
1000
31623
1000000
31622777
1000000000
100
10000
1000000
100000000
10000000000
1000000000000
Koha
1.7 minuta
2.8 or
1.1 dit
1.6 jav
3.8 muaj
3.1 vjet
3.1 dekada
3.1 shekuj
kurr :)
Avni Rexhepi
Operacione
pr sekond
Madhsia e problemit 1
miliard
N
N lg N
N2
or
or
kurr
106
N
sekonda
N lg N
sekonda
N2
jav
109
n ast
n ast
or
sekonda
sekonda
dekada
1012
n ast
n ast
sekonda
n ast
n ast
jav
128
Emri
Kufiri i poshtm
Kufiri i eprm
Logaritmi binar
Numrat Fibonacci
Vlera tipike
3.14 = 3
3.14 = 4
lg 1024 = 10
F10 = 55
HN
N!
lg(N!)
Numrat Harmonik
Faktorieli
H10 2:9
10! = 3628800
Lg(100!) 520
Prafrimi
x
x
1:44 ln N
N=/ 5
ln N+
(N/e)N
N lg N 1.44 N
e = 2.71818...
Y = 0.57721...
N=(1+ 5
)/2=1.61803...
ln 2 = 0.693147...
lg e = 1/ln 2 =
1.44269
Avni Rexhepi
sht ), ndrsa nse jemi duke operuar me numra jo t plot, mund t prdorim
funksionet floor dhe ceil nga libraria <math.h> (angl. floor-dyshemeja, dhe
ceil, shkurtesa pr ceiling-tavani, pra kufiri i eprm dhe kufiri i poshtm).
Versioni i diskretizuar i funksionit t logaritmit natyral, i quajtur numrat
harmonik, paraqitet shpesh n analizn e algoritmeve. Numri harmonik i N-t
(seria e harmonikve) definohet prmes ekuacionit:
H N 1
1 1
1
...
2 3
N
Seria e numrave:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 ...
130
pr N 2, me F0 = 0 dhe F1 = 1
lg N! N lg N N lg e lg 2N
ose versioni zakonisht i prdorur npr alikacione:
131
Avni Rexhepi
Coptimi i problemeve
Nj prej teknikave m t fuqishme pr zgjidhjen e problemeve sht coptimi i
tyre n probleme m t vogla dhe n pjes m leht t zgjidhshme. Problemet e
vogla jan m t lehta dhe na lejojn t fokusohemi n detajet t cilat humben
kur studiohet problemi i tr. Pr shembull, sa her q t mund ta coptojm
problemin n instanca m t vogla t problemit t tipit t njjt, fillon t bhet i
dukshm algoritmi rekurziv.
Dy paradigma t rndsishme t dizajnit t algoritmeve jan t bazuara n
coptimin e problemeve n probleme m t vogla:
-
Programimi dinamik
Ndonjher ka kuptim q t punohet drejt zgjidhjes s problemit duke krijuar nj
tabel t zgjidhjeve pr versionet m t vogla t problemit. Pr arsye t
prshkruara si historike ky proces sht i njohur me emrin programimi
dinamik ndrsa algoritmet prkatse quhen algoritmet dinamike. Ka aplikim n
problemet e ndryshme t ndrlidhura me krkimin kombinatorik ndoshta
problemi m i thjesht do t ishte shembulli i llogaritjes s koeficientve
binomial prmes ndrtimit t trekndshit t Paskalit, rresht pas rreshti, deri sa
t arrihet t koeficienti i dshiruar.
Pasi ta keni kuptuar mir, programimi dinamik me gjas sht teknika m e
leht e dizajnimit t algoritmeve pr tu aplikuar n praktik. N fakt, algoritmet
e programimit dinamik jan zakonisht m t lehta pr tu rizbuluar sesa t
tentohet t krkohen npr libra. Mirpo, deri sa t kuptohet, programimi
dinamik duket si magji. Para se t prdoret, duhet t kuptohet forja e tyre.
N problemet algoritmike, si sortimi, korrektesia sht e prirur t vrtetohet m
leht sesa efikasiteti. Kjo nuk sht rasti n problemet e optimizimit, ku
krkojm t gjejm zgjidhjen e cila maksimizon ose minimizon funksionin. N
dizajnimin e algoritmeve pr nj problem t optimizimit, duhet t japim prova se
algoritmi gjithmon jep zgjidhjen m t mir t mundshme.
Algoritmet lakmitare, t cilat marrin vendimin m t mir lokal n secilin hap,
rastsisht ndodh q t prodhojn optimume globale pr disa probleme. Kto
jan efikase n mnyr tipike. Mirpo, duhet prov pr t treguar se gjithmon
do t prfundoni me prgjigjen m t mir. Algoritmet e krkimit shterrues, t
cilat provojn t gjitha mundsit dhe zgjedhin m t mirn, sipas definicionit,
gjithmon duhet t prodhojn rezultat optimal, por zakonisht me kosto penguese
n terma t kompleksitetit kohor.
Programimi dinamik kombinon m t mirat nga t dy bott. Teknika
sistematikisht shqyrton t gjitha vendimet e mundshme dhe gjithmon zgjedh
133
Avni Rexhepi
at q vrtetohet se sht m e mira. Duke ruajtur konsekuencat e t gjitha
vendimeve t mundshme deri n momentin aktual dhe duke prdorur kt
informat n mnyr sistematike, minimizohet puna totale. Programimi dinamik
msohet m s miri duke studiuar nj numr t shembujve, gjersa t kuptohet si
duhet. Shembuj prkats jan: Numrat Fibonacci, Problemi i particionimit (The
partition problem), prshtatja e prafrt e stringjeve (approximate string
matching), sekuenca m e gjat rritse (longest increasing sequence),
trekndzimi me pesh minimale (minimum eight triangulation), etj.
Numrat Fibonacci
Pazari ndrmjet hapsirs dhe kohs, i eksploatuar n programimin dinamik,
m s miri ilustrohet n vlersimin e relacioneve t rekurrencs, si jan numrat
Fibonacci. Numrat Fibonacci fillimisht u definuarn nga matematikani italian
Fibonacci, n shekullin e trembdhjet, pr t modeluar rritjen e popullacionit t
lepujve.
Lepujt shumohen shpejt dhe Fibonacci hamendsoi se numri i ifteve t lepujve
t lindur n nj vit sht i barabart me numrin e ifteve t lepujve t lindur n
secilin prej dy viteve paraprake, nse vitin e par fillohet me nj ift lepujsh. Pr
t numruar numrin e lepujve t lindur n vitin e n-t, ai definoi relacionin
vijues t rekurrencs:
Fn = Fn-1 + Fn-2
me rastet fillestare F0=0 dhe F1=1, kshtu q seria vazhdon si:
{3,5,8,13,21,34,55,89,144...}. Si shihet, formula e Fibonacci-t nuk dha rezultat
t mir sa i prket numrimit t lepujve, mirpo ka prdorim pr aplikacione t
tjera dhe tipare interesante.
Pasi q jan definuar prmes formuls rekurzive, sht e leht t shkruhet
programi rekurziv pr llogaritje t numrit t n-t Fibonacci dhe pseudokodi i
algoritmit rekurziv duket si vijon:
Fibonacci[n]
if (n=0) then return(0)
else if (n=1) then return(1)
else return(Fibonacci[n-1]+Fibonacci[n-2])
134
F(5)
F(4)
F(4)
F(3)
F(2)
F(1)
F(1)
F(3)
F(2)
F(1)
F(0)
F(2)
F(1)
F(1)
F(0)
F(3)
F(2)
F(1)
F(1)
F(2)
F(1)
F(0)
F(0)
F(0)
(1 5)
Fn 1
1.61803
Fn
2
kjo do t thot q: Fn>1.6n
Pasi q pema rekurzive e ilusturar m sipr ka vetm gjethe 0 dhe 1, duke
mbledhur deri n nj numr t madh duhet t kemi s paku gjethet ose thirrjet e
procesurave! Ky program i vogl merr koh eksponenciale pr tu ekzekutuar!
N fakt, mund t bhet shum m mir. Mund t llogarisim t njjtn n koh
lineare, duke ruajtur t gjitha vlerat. Kshtu shkmbejm hapsirn pr kohn
dhe algoritmi vijues:
Fibonacci[n]
F[0]=0
F[1]=1
For i=1 to n, Fi=F[i-1]+F[i-2]
Pasi q llogarisim numrat Fibonacci prej m t voglit deri te m i madhi dhe i
ruajm rezultatet, e dijm se e kemi F[i-1] dhe F[i-2] sa her t na duhet t
llogarisim F[i]. Prandaj, secila prej n vlerave llogaritet si shum e thjesht e dy
integer-ave n koh totale O(n), q sht shum m mir sesa koha
eksponenciale e rastit rekurziv.
Kufizimet e programimit dinamik
Programimi dinamik mund t aplikohet n cilindo problem q zbaton principin e
optimalitetit. Thn vrazhd, kjo do t thot q zgjidhjet parciale mund t
zgjerohen optimallisht duke pasur parasysh gjendjen pas zgjidhjes parciale n
vend se vet zgjidhjen parciale. Pr shembull, pr t vendosur se a t zgjerohet
135
Avni Rexhepi
nj krkim i prafrt i stringut me zvendsim, insertim ose fshirje, ne nuk kemi
nevoj ta dijm saktsisht cila sekuenc e operacioneve sht kryer deri m tani.
N fakt, mund t ket disa sekuenca edituese t ndryshme t cilat arrijn kosto C
n p karakteret e para t mostrs P dhe t karakteret e stringut T. Vendimet n t
ardhmen do t bhen bazuar n konsekuencat e vendimeve paraprake, e jo n
vet ventimet aktuale.
Problemet nuk knaqin principin e optimalitetit nse operacionet aktuale kan
rndsi, kundrejt vetm kostos s operacionit. Shqyrtoni nj form t editimit t
distancs ku nuk na lejohet t prdorim kombinime t operacioneve n ndonj
renditje t caktuar. Formuluar si duhet, sidoqoft, shumica e problemeve t
kombinatoriks e respektojn principin e optimalitetit.
Kufizimi m i madh n prdorimin e programimit dinamik sht numri i
zgjidhjeve parciale t cilat duhet t prcillen (ruhen). Pr shembujt q u pan,
zgjidhjet parciale mund t prshkruhen trsisht duke specifikuar pozitat e
ndalimit n hyrje. Kjo pr arsye se objektet kombinatorike n t cilat punohet
(stringjet, sekuencat numerike dhe poligonet) t gjitha kan nj rend implicit t
definuar n baz t elementeve t tyre. Ky rend nuk mund t skremblohet
(angl. scramble prziej) pa ndryshuar problemin n trsi. Kur nj her t jet
fiksuar renditja, ka relativisht pak pozita t ndaljes ose gjendje, ashtu q
prfitojm algoritm efikas. Nse objektet nuk mund t renditen me vendosmri
(qndrueshm), sidoqoft, kemi nj koh eksponenciale t zgjidhjeve t
mundshme parciale dhe jemi t gjykuar q t kemi nevoj pr nj sasi t paarritshme t memories.
Pr t ilustruar kt, shqyrtoni algoritmin vijues t programimit dinamik pr
problemin e shitsit ambulant (agjentit tregtar, angl. Traveling Salesman
Problem TSP). Zgjidhja e problemit TSP nnkupton gjetjen e renditjes me t
ciln vizitohen lokacionet (qytetet, pikat) saktsisht nj her, me distancn totale
minimale ose me koston minimale. Le t jet C(i,j) kostoja e degs s prshkuar
prej piks i n pikn j. Definoni T(i; j1, j2...jk) t jet kostoja e turit optimal prej i
n 1 q kalon npr secilin qytet j1, j2...jk saktsissht nj her, me radh.
Kostoja e turit optimal TSP sht prandaj e definuar t jet T(1,2,...n) dhe mund
t llogaritet rekurzivisht duke identifikuar nyjen e par n kt sekuenc:
T (i, j1 , j1 ,..., j k ) min C (i, j m ) T ( j m , j1 , j1 ,..., j k )
1 m k
Prapaveprimi
Prapaveprimi ose rishikimi (angl. Back-tracking, back-prapa; trackingprcjellje/ndjekje/ gjurmim) sht rasti kur sht i nevojshm rishikimi i
rezultateve t arritura paraprakisht. Nse algoritmi q ju nevojitet prshin
krkimin, mund t jet q rishikimi sht ajo ka nevojitet. Kjo e ndan dizajnin
konceptual t funksionit t krkimit n dy pjes: s pari vetm shkon prpara pr
t hulumtuar at ka mendon se sht shtegu m i arsyeshm pr tu hulumtuar.
Kjo pjes me gjas do t arrij n rrug pa dalje dhe ky sht momenti kur fillon
pjesa e dyt, prapaveprimi (rishikimi). N kt rast ai ka mbajtur informacione
shtes prreth asaj kur pjesa e par ka br zgjidhjet dhe pshtjellon prapa t
gjitha llogaritjet deri n pikn e zgjidhjes paraprake t fundit e pastaj rifillon
krkimin npr nj shteg tjetr. Kjo metod sht shum e dobishme n shum
probleme t ndrlidhura me grafet. sht i njohur problemi i ashtuquajtur
Problemi i tet mbretreshave n tabel shahu, ku tet mbretresha duhet t
pozicionohen n tabeln e shahut, asht q asnjra nuk e sulmon tjetrn.
Prapaveprimi sht mnyr sistematike e kalimit npr t gjitha konfiguracionet
e mundshme t hapsisrs s zgjidhjeve. Kto konfiguracione mund t jen t
gjitha renditjet/rregullimet e mundshme t objekteve (permutacionet) ose t
gjitha mnyrat e ndrtimit t bashksive t tyre (nnbashksit). Ndonj
aplikacion tjetr mund t krkoj t gjitha pemt e shtrirjeve t grafeve ose t
137
Avni Rexhepi
gjitha shtigjet e mundshme mes dy nyjeve ose t gjitha mnyrat e ndarjes s
nyjeve n grupe sipas ngjyrave.
Hill Climbing
Hill Climbing (angl. Hill kodr, Climbe ngjitje, alpinizm), sht poashtu
metod/teknike q prdoret pr problemet e optimimzimit. Hill climbing sht
versioni lakmitar i krkimit lokal. Kjo krkon q s pari t gjeni (n ndonj
mnyr) ndonj form t realizueshme (por me presupozimin q nuk sht
optimale) t zgjidhjes s problemit. Pastaj, shikon pr mnyrat n t cilat do t
mund t bheshin ndryshime t vogla n kt zgjidhje, pr ta prmirsuar at.
Nj varg i ktyre prmirsimeve t vogla do t mund t drgoj n fund n
optimumin e krkuar. Natyrisht, propozimi i mnyrs pr t gjetur prmirsimet
e tilla nuk sht vetvetiu garanc q optimumi global do t arrihet ndonjher: si
zakonisht algoritmi i dizajnuar nuk sht i kompletuar deri sa t keni vrtetuar
(provuar) q ai gjithmon prfundon me arritjen e saktsisht rezultatit q ju
nevojitet/duhet.
139
Avni Rexhepi
Algoritmet Evolutive
Qeniet e gjala jan shum adaptive n ambient dhe i tejkalojn problemet e jets
s prditshme. Aplikimi i principeve t jets dhe t qenieve t gjalla n
zhvillimin e algoritmeve t mira, duke u bazuar n principet natyrore t
evolucionit, krijon kategorin e algoritmeve evolutive (n mesin e tyre m t
njohurat, algoritmet gjenetike). Teoria e evolucionit ka shfaqur mekanizmat t
cilt drgojn n performans m t mir: mutacionet, rekombinimet, mbijetesa
e atyre me aftsi m t mira (t ashtuquajtur fitnes, m t mir). Qasja evolutive
n zgjidhjen e problemeve, nnkupton krijimin e zgjidhjeve t prafrta
fillestare, me rastsi, dhe pastaj aplikimin e ndryshime dhe rekombinimeve
npr gjenerata t ekzekutimit t programit, pr t fituar zgjidhje optimale.
140
Stack - Steku
Radha e pritjes (angl. Queue) sht struktura e t dhnave e cila prbhet nga
lista e elementeve dhe dy pointerve, n elementin e front-it (fillim, para) dhe
elementin rear (fundi, prapa). Elementet mund t insertohen nga prapa dhe t
largohen nga fillimi, d.m.th., operacioni FIFO (First In, First Out - I pari brenda,
I pari jasht). Kjo struktur sht rasti i vargut t pritjes t personave, pr ndonj
shrbim t caktuar (i pari q vjen, i pari shrbehet), lista e punve n printer,
tuneli, nj gyp i hapur n t dy ant, por me qarkullim vetm nj nj kahje, ku
elementet hyjn nga njri skaj, e dalin nga skaji tjetr, etj.
Vargjet dhe listat e lidhura sigurojn mekanizmat themelore t cilat mundsojn
insertimin dhe largimin e elementeve. N t vrtet, vargjet dhe listat e lidhura
jan strukturat themelore t t dhnave, t cilat qndrojn prfundi, si baz e
ndrtimit t disa implementimeve t disa tipeve t prgjitshsuara t radhve
(queue) dhe strukturave t tipeve abstrakte t t dhnave. Dihet se kostoja e
insertimit dhe fshirjes sht e varur nga strutura specifike dhe elementi specifik
q insertohet ose fshihet. Pr nj ADT t dhn, sfida sht se si t zgjedhet
struktura q lejon kryerjen efikase t ktyre veprimeve.
Tipet e t dhave t cilat prbhen nga objektet abstrakte, jan objekt qndror n
studimin e shkencave kompjuterike, sepse ato prkrahin n mnyr direkte
paradigmn themelore t llogaritjeve. Pr shum llogaritje, e gjejm veten n
141
Avni Rexhepi
pozita kur kemi shum objekte me t cilat duhet punuar, por kemi mundsi t
qasjes s vetm nj elementi n koh. Prandaj, duhet t ruhen t tjerat, deri sa t
prpunohet ai i zgjedhuri. Ky prpunim mund t prfshij ekzaminimin e disa
objekteve t ruajtura m par dhe shtimin e t tjerave n koleksion, por
operacionet e ruajtjes s objekteve dhe marrjes s tyre n baz t ndonj kriteri
t caktuar, jan baza e llogaritjes kompjuterike. Shum struktura klasike t t
dhnave dhe shum algoritme, i prkasin ktij modeli.
N C++, klasat t cilat implementojn koleksione t objekteve abstrakte quhen
container classes (klasa bartse, kontejner). Shum struktura t t dhnave
jan t implementuara n librarit e C++-it ose n versionet e bazuara n
shabllonet standarde STL (Standard Template Library).
Stack ADT
Ilustrim i mir nga jeta reale do t ishte nj grumbull i librave (t vendosura
njra mbi tjetren), pra nj stek i librave, ku mund t veproni vetm me majn e
stekut. (angl. top-maja, kulmi, kreu, kreshta, etj. P.sh., shpesh prmendet Top
lista, pra lista e kryesoreve, m t mirave, etj.).
142
Operacionet
Stack create()
krijon stekun e zbrazt
boolean isEmpty(Stack s)
tregon a sht steku s i zbrazt
push(Stack s, Item e)
vendose e n maje(top) t stekut s
Item peek(Stack s)
kthen elementin q ndodhet n maje t stekut s
Parakusht: s nuk sht i zbrazt
pop(Stack s)
largon elementin q ndodhet n maje, nga steku s
Parakusht: s nuk sht i zbrazt
destroy(Stack s)
asgjson stekun s
Aksiomat pr stekun
Avni Rexhepi
Fig. 3.2 Steku dhe vargu jan dy gjra t ndryshme: vargu i ruan vlerat n
memorie; steku prcjell cili element ndodhet n krye t stekut.
Programert prdorin vargjet, pr t ruajtur vlerat t cilat referencohen nga
steku. Si sht thn, vargu prbhet nga seria e elementeve t vargut, q jan t
tipit t njjt, n koncpet t variablave. Steku e prmban indeksin e elementit t
vargut i cili ndodhet n krye t stekut.
144
Push
Pr vendosjen e elementit n stek, prdoret termi push (angl. push-shtyje,
detyroje, godite, etj). Push sht udhzimi se e dhna sht duke u shtuar n
stek. Imagjinojeni kt si shtyerje t elementeve teposht stekut, duke i lvizur
elementet q ve jan brenda stekut teposht, pr t krijuar vend/hapsir pr
elementin e ri.
Ja ka ndodh n realitet. Vlera e re caktohet n pozitn e ardhshme t lir t
vargut (pozita e lir q sht n dispozicion) dhe indeksi i atij elementi t vargut
bhet top (kreu) i stekut, si n figurn 3.3. Programi e inkrementon (e rrit pr
nj) indeksin aktual t stekut. N kt shembull, indeksi rritet pr 1, duke
rezultuar n indeksin 3 si top i stekut, q sht indeksi vlers s re t caktuar t
vargut.
Fig. 3.3 Vlera e re (angl. New Value) i ndahet elementit t ardhshm t vargut
dhe indeksi i tij bhet top i stekut.
Pop
Procesi i kundrt i Push sht Pop (angl. Pop nxirre, Pop Up trhiqe
lart). Me pop largohet nj element nga steku. sht me rndsi t kuptohet se
145
Avni Rexhepi
trheqja e elementit nga steku nuk e kompjon elementin. Kur nj element t
trhiqet nga steku, ai m nuk sht n dispozicion n stek, edhe pse vlera e tij
akoma n varg.
Ja ka ndodh n realitet. Rikujtojm se top-i i stekut ka indeksin e elementit t
vargut, vlera e t cilit sht n maje t stekut. N figurn 3.3, indeksi 3 sht n
majte t stekut, q do t thot se elementi 3 i vargut, Vlera e re, sht n maje
t stekut.
Kur nxirret (pop) Vlera e re prej stekut, dekrementohet (zvoglohet pr 1)
indeksi n top t stekut. Kjo do t thot se indeksi i top tash bhet 2, n vend
t 3. Kjo bn q beni t jet vlera e re n maje t stekut (Figura 3.4). vreni se
Vlera e re dhe elementi i tret i vargut mbesin t paprekur, sepse trheqja
(popping) e vlers prej stekut vetm e ndryshon stekun, e jo vargun q ndodhet
prfundi, nn.
Figura 3.4: Kur trhiqet elementi top nga steku, t gjitha vlerat lvizin te lart,
kah maja e stekut.
Avni Rexhepi
nuk ka elemente. Rikujtoni se vlerat e indeksave jan zhvendosjet e memories
(angl. memory offsets) prej fillimit t vargut. Indeksi 0 do t thot lviz 0 bajta
prej fillimit t vargut. Kshtu, indeksi -1 sht vetm nj lehtsir pr t thn
q vargu sht i zbrazt.
Definicioni i klass Stack do t zgjerohet, mirpo tani pr tani le t krijojm nj
instanc t klass Stack. Instanca e klass sht deklaruar prbrenda funksionit
main( ). Ktu ndodhin tri gjra. S pari, operatori new krijon nj instanc t
stekut n memorie. Operatori new kthen pointerin n at lokacion t memories.
Pastaj, urdhri definon nj pointer pr n stek, q sht emrtuar myStack. Hapi
i fundit sht prcaktimi (ndarja) e pointerit t kthyer nga operatori new,
pointerit myStack. Tutje, myStack prdoret si emr pr t ju referuar instancs
s klass Stack, n program.
public class Stack
{
private:
int size;
int top;
int* values;
public:
Stack(int size)
{
this->size = size;
values = new int[size];
top = -1;
}
};
void main(){
Stack *myStack = new Stack(10);
}
//size=madhesia
//top=kreu
//values=vlerat
//size antar
Funksioni Push
Pas definimit t klass q e krijon stekun, do t shohim funksionin i cili i
mundson klass q t mbush stekun (q t shtyj vlerat n stek).
Vendosja/shtyrja e vlers n stek sht proces dy-hapsh. S pari, duhet t
prcaktohet nse ka vend n stek pr nj vler t r. Nse ka, ather vlera
shtyhet (angl. push) n stek; prndryshe, jo.
Do t krijojm funksione antare pr secilin hap, duke filluar me definimin e
funksioni i cili prcakton nse ka vend n stek. Do ta quajm, isFull( ) (angl. si
full - sht i mbushur) dhe do ta definojm n kodin vijues. Funksioni isFull()
148
//isFull = eshtePlot
Avni Rexhepi
void push(int x)
{
if(!isFull())
{
top++;
values[top] = x;
}
}
Funksioni Pop
Na duhet edhe funksioni pr largimin e elementeve/vlerave nga steku. Pr t
br kt, duhet t definojm edhe dy funksione shtes: isEmpty() (angl. is
empty sht i zbrazt) dhe pop(). Funksioni isEmpty() e verifikon se a ka vlera
n stek. Funksioni pop e largon vlern top nga steku.
S pari definojm funksionin isEmpty(), i cili prmban nj urdhr t
kushtzimit if. Shprehja e kushtit t urdhrit if krahason vlern e atributit top
t stekut, me -1. Rikujtoni se -1 ishte vlera fillestare e top, kur deklarohet steku.
Nse atributi top sht baraz me -1, ather kthehet true sepse steku sht i
zbrazt; prndryshe, kthehet false.
bool isEmpty()
{
if(top == -1)
{
return true;
}
else
{
return false;
}
}
//isEmpty=eshteBosh
Steku n veprim
Tani q e kemi par se si krijohet dhe si prdoret steku, do t shohim nj stek n
veprim. Tri gabime t zakonshme t cilat duhet t prcillen n rastin e stekut,
jan alokimi i memories pr stekun, reagimi n rastin e stekut t mbushur dhe
reagimi n rastin e stekut t zbrazt.
Do t shohim nj shembull t programit q krijon dhe prdor stekun. Programi
prmbahet prbrenda tre fajllave: stack.h, stack.cpp dhe stackDemo.cpp. Fajlli
stack.h sht header file q prmban definicionin e klass Stack, e cila
shrben si shabllon pr krijimin e instancave t stekut. Fajlli stack.cpp sht
kodi burimor i cili prmban implementimin e funksioneve t klass Stack.
Fajlli stacDemo.cpp prmban kodin burimor pr programin n C++ i cili
deklaron instancn e klass Stack dhe thrret funksionet antare t tij.
Le t fillojm duke shikuar header fajllin stack.h, i cili sht paraqitur n
shembullin e kodit vijues. Si dihet nga rregullat e C++-it, fajlli header zakonisht
prmban definicionet dhe instruksionet/direktivat preprocesorike. Preprocesori
sht nj program i cili aplikon direktivat preprocesorike n kodin burimor, para
se te kompajlohet kodi.
Header fajlli stack.h prmban nj direktive preprocesorike, #define, e cila n
kt rast definon nj simbol. Ktu sht definuar simboli DEFAULT_SIZE dhe
atij i sht dhn vlera 10. Preprocesori pastaj i zvendoson t gjitha paraqitjet e
DEFAULT_SIZE me 10, para se t kompajlohet kodi. DEFAULT_SIZE sht
madhsia standarde e stekut nse nuk prcillet ndonj argument tek konstruktori.
151
Avni Rexhepi
Parametrave t funksioneve mund t ju ndahen vlera t nnkuptuara (standarde,
default) n protoptipin e funksionit, nse argumentet ndodhen n funkd t lists
s argumenteve. Nse vlera size nuk prcillet, aje e merr vlern e nnkutpuar
nga DEFAULT_SIZE, e cila n kt shembull sht 10.
Fajlli stack.h gjithashtu prmban definicionin e klass Stack. Definicioni i
klass Stack ka t njjtat vlera pr atributet size, top dhe values, si n
shembullin e mparshm. Mirpo, definicioni i funksioneve antare sht i
ndryshm, sepse funksionet jan implementuar jasht definicionit t klass n
fajllin e kodit burimor stack.cpp. Header fajlli prmban vetm protoptipet e
funksioneve, t cilat krijojn shabllonet e klasave.
Nga klasat n C++, dihet se vetm protoptipi i funksioneve antare sht e
nevojshme q t prfshihet n definicionin e klass. Implementimi i funksioneve
mund t bhet jasht definicionit t klass. Jan dy arsye pr t mbajtur
definicionin (header fajllin) dhe implementimin (burimin) n fajlla t ndar:
Avni Rexhepi
Pr t evituar rrjedhjet e memories (memory leaks), lirimi i memories sht i
rndsishm sa her q bhet alokimi dinamik i memories. Kllapat e mesme, [ ],
prdoren me urdhrin delete, sepse objekti q duhet t fshihet/largohet nga
memoria ka qen i krijuar dinamikisht.
Fajlli stack.cpp kompajlohet si kompajlohet do kod burimor i zakonshm.
Rezultati i fituar sht nj objekt fajll q sht i bashkuar nga linkeri me fajllin e
kompajluar t kodit burimor t stackDemo.cpp, pr t krijuar programin
ekzekutiv.
//stack.cpp
#include "stack.h"
Stack::Stack(int size)
{
this->size = size;
values = new int[size];
top = -1;
}
Stack::~Stack()
{
delete[] values;
}
bool Stack::isFull()
{
if(top < size-1)
{
return false;
}
else
{
return true;
}
}
bool Stack::isEmpty()
{
if(top == -1)
{
return true;
}
else
{
return false;
}
}
void Stack::push(int x)
154
155
Avni Rexhepi
{
cout << stack->pop() << endl;
}
}
Implementimi
Implementimi i stekut t bazuar n vargje sht mjaft i thjesht. Ai prdor
variabln top pr t pointuar n elementin e majes s stekut n varg.
1. Fillimisht top = -1;
2. push operacioni, e rrit top pr nj dhe e shkruan elementin e shtyr
(pushed) n storage[top]; (angl. storage-depo, vend i ruajtjes).
3. pop operacioni, verifikon q top sht i ndryshm nga -1 dhe e zvoglon
vlern e top pr 1;
4. peek operacioni, verifikon q top nuk sht baraz me -1 dhe kthen vleren
storage[top];
5. isEmpty kthen vleren bool-eane (top == -1).
Pjes kodi
#include <string>
using namespace std;
class Stack
{
private:
int top;
int capacity;
156
157
Avni Rexhepi
Prdorimi i stekut
Steku gjen zbatime t shumfisht. N rastin m t thjesht, nse dshirojm t
ndryshojm (rrotullojm) renditjen e elementeve t nj vargu, mund ti
prcjellim n stek dhe ti trheqim nga steku, duke fituar renditjen e kundrt t
vlerave.
Steku mund t prdoret edhe n konvertimin e numrave, p.sh., nga numrat
decimal n numr binar.
Konvertimi i numrave
Steku prdoret n shum aplikacione, gjat kohs s ekzekutimit t tyre:
Avni Rexhepi
Gjithashtu sygjeron se operatoret duhet t ruhen n stek (si + qe mbahet gjersa
prioriteti me i larte * t vlersohet).
Shqyrtoni shprehjen aritmetike vijuese:
x = a * b + c (Notacioni infix operatort ndrmjet operandve).
Kompajleri duhet t gjeneroj instuksionet e makins (angl. machine
instructions) si vijon:
1. LOAD
2. MULT
3. ADD
4. STORE
a
b
c
x
Gjuht programuese dhe kompajlert prdorin notacionin polak (angl. PN Polish Notacion, t definuar nga matematikani polak, Jan Lukasieicz) dhe
notacionin revers polak (angl. RPN-Reverse Polish Notation). Kshtu, shprehja
standarde shndrrohet n forma m t prshtatshme pr llogaritje:
(Infix): 1+2;
PN (Prefix): +12;
RPN (Postfix): 12+
Shumica e kompajlerve konvertojn shprehjet nga notacioni infix n postfix
operatort shkruhen pas operandve.
Kshtu: a * b + c bhet a b * c +
Prparsia e ktij notacioni sht se shprehjet mund t shkruhen pa kllapa.
161
Avni Rexhepi
163
Avni Rexhepi
164
Avni Rexhepi
(queue), verifikohet pr t par nse jemi n fillim (front) t radhs, ashtu q t
mos mbishkruhet elementi n fillim (front) dhe t mos korruptohet queue-ja.
Hapi i dyt sht q ti ndahet vlera 90 elementit 1 t vargut. Kjo sht, vendosja
e vlers 90 n fund (back) t queue-s. rikujtoni se vlerat shtohen n queue nga
ana e pasme, nga fundi (back), njsoj si hyjm n radhn e pritjes pr pagesa n
supermarket. Vreni se vlera 90 i sht ndar vargut n figurn 3.6.
167
Avni Rexhepi
//queue.h
#define DEFAULT_SIZE 8
class Queue{
private:
const int size;
int front;
//front=fillimi
int back;
//back=fundi
int* values;
//values=vlerat
public:
Queue(int size = DEFAULT_SIZE);
virtual ~Queue();
bool isFull();
bool isEmpty();
void enqueue(int);
//vendose ne rresht
int dequeue();
//largo nga rreshti
};
169
Avni Rexhepi
170
171
Avni Rexhepi
}
int Queue::dequeue()
{
if(!isEmpty())
{
front = (front+1) % size;
return queue[front];
}
return 0;
}
Figura 3.10 - Queue dhe vargu pas thirrjes s fundit t funksionit enqueue().
172
//queueProgram.cpp
#include <iostream>
using namespace std;
void main(){
Queue *queue = new Queue(8);
queue->enqueue(10);
queue->enqueue(20);
queue->enqueue(30);
for(int i=0; i<3; i++)
{
cout << queue->dequeue() << endl;
}
}
173
Avni Rexhepi
4. Listat e lidhura
Lista e lidhur sht nj list e elementeve q pointojn t dhnat aktuale,
paraprake dhe t ardhshme. Lista mund t jet e lidhur njfish (me pointert
vetm pr elementin e ardhshm) dhe e lidhur dyfish (me dy pointer, njri pr
elementin paraprak dhe tjetri pr t ardhshmin).
Lista e lidhur sht struktur e t dhnave q e bn t leht rirregullimin
(rirenditjen, korrigjimin) e t dhnave, pa pasur nevoj lvizjen e t dhnave n
memorie. Edhe pse tingllon uditshm, merrni parasysh rastin e nj klase me
student, t cilt jan ulur sipas nj renditjeje t caktuar. Nj numr unik
identifikon seciln ulse, si n figurn 4.1. Jan prfshir edhe t dhnat pr
gjatsin relative t secilit student, t cilat do t prdoren n shembullin vijues.
Avni Rexhepi
elementin e ri n fund t lists s lidhur. Edhe pr t shtuar nyje t reja (antar
t rij, vlera t reja) ndrmjet nyjeve ekzistuese, mjafton vetm t prshtaten
pointert prkats, pr t krijuar vendin pr antarin e ri n list.
Ngjashm, elementi n cilndo poizt n list apo edhe i fundit n list, mund t
largohet nga lista e lidhur, thjesht duke e larguar referencn (poinerin) pr n
elementin e ardhshm nga elementi i parafundit n listn e lidhur.
Kjo sht shum m efikase sesa prdorimi i vargut dhe ndryshimi i madhsis
s vargut gjat kohs s ekzekutimit. Kjo pr arsye se, nse duhet t ndryshohet
madhsia e vargut, sistemi operativ tenton q t rris vargun duke prdorur
lokacionet e memories n vazhdim t vargut. Nse kjo nuk sht e mundur
(lokacionet n vazhdim nuk jan n dispozicion), ather sistemi operativ e
gjen nj lokacion tjetr, n pjesn tjetr t memories, me madhsi t
mjaftueshme pr t mbajtur elementet e vargut dhe elementet e reja t vargut.
176
struct Nyje
{
int vlera;
Nyje* ePerparshme;
Nyje* eArdhshme;
};
Avni Rexhepi
q ruan vlern aktuale t nyjes (data, vlera). Dy urdhrat tjer deklarojn
pointert pr n nyjen e prparshme dhe n at t ardhshme, n listn e lidhur.
Struktura e cila prmban komponente q sht pointer i tipit t njjt me vet
strukturn quhet struktur vet-referente (ang. self-referential structure). Nse
pr definimin e nyjeve shfrytzohen klasat, ather bhet fjal pr klasa vetreferente (ang. self-referencial classes).
Konstruktori inicializon elementet e nyjes kur t krijohet instanca e nyjes. Kjo
funksionon n mnyr t ngjashme me konstruktorin e klass. Si do t shihet m
von, vlera aktuale i sigurohet strukturs kur t krijohet nyja e re. Kjo vler i
ndahet t dhns n listn e argumenteve. Vlera e t dhns pastaj i ndahet
elementit t instancs s strukturs. Gjithashtu, pointeri pr n nyjen e
prparshme dhe at t ardhshme fillimisht inicializohet n NULL, gj q i tregon
programit se nuk ka elemente t tjera n listn e lidhur. NULL zvendsohet me
pointert pr n nyje kur t shtohet nyja e re n listn e lidhur.
178
Ose:
struct Nyje
{
int vlera;
Nyje* eArdhshme;
};
Avni Rexhepi
#include "stdafx.h"
#include <iostream>
using namespace std;
struct nyje
{
int vlera;
nyje *eArdhshme;
};
int main()
{
nyje *n;
//Pointer n strukturn nyje
n=new nyje; //Te pointeri n ruhet adresa e nyjes s re
n->vlera=10;
// Te anetari vlera, vendoset 10
n->eArdhshme=NULL;
// Te pointeri, nyja e ardhshme=NULL
//(d.m.th., s'ka nyje tjetr ne vazhdim)
cout << "\nNyja u perfundua\n";
cout << "Vlera e nyjes : "<<n->vlera<<endl;
cout << "Nyja e ardhshme: "<<n->eArdhshme<<endl;
cout << endl;
system("Pause");
return 0;
}
fundi
fillimi
10
fillimi (ePara)
180
10
20
eArdhshme
eArdhshme
181
Avni Rexhepi
fundi (eFundit)
fillimi (ePara)
10
20
30
40
50
#include "stdafx.h"
#include <iostream>
#include <iomanip>
using namespace std;
struct nyje
{
int vlera;
nyje *eArdhshme;
};
void ShtypeListen(nyje *ePara);
int main()
{
nyje *n,*ePara=NULL,*eFundit=NULL;
int x,k,i;
cout << "\nNumri i nyjeve: ";
cin >> k;
//k-nyje
for (i=1;i<=k;i++)
//pr do i nga 1 deri n k
{
n=new nyje;
//krijojm nyje t re (pointeri n pr nyjen e re)
cout << "Vlera ne nyjen e " << i << ": ";
cin >> x;
//lexojm vlern x
n->vlera=x;
//nyjes aktuale ia ndajm vlern e x-it
n->eArdhshme=NULL;
//nyjes s aktuale, ia caktojm
//pointerin pr n nyjen e ardhshme NULL
if (ePara==NULL)
//nse akoma nuk sht filluar/krijuar lista
ePara=n;
//kjo sht nyja e par e lists
else
eFundit->eArdhshme=n;
// eArdhshme, pointon n nyjen e re n
eFundit=n; //nyje e fundit e lists bhet nyja e re q u shtua
}
cout<<endl;
cout << "Fillimi: " << ePara << "\n"; //Fillimi i lists
cout << "Fundi : " << eFundit<< "\n"; //Fundi i lists
cout << "\n\nPermbajtja e listes:";
cout << "\n\n
Adresa
Vlera
Adresa e Ardhshme\n";
182
183
Avni Rexhepi
Specifikacioni i klass LinkedList sht definuar n header file dhe
implementimi i saj sht i definuar n fajllin e kodit burimor. Implementimi i
funksioneve antare t klass do t paraqitet n vazhdim.
//front=fillimi; back=fundi
class LinkedList
{
private:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
//appendNode=shto nyje
void displayNodes();
//displayNodes=paraqitiNyjet
void displayNodesReverse();
//ParaqitiRevers
void destroyList();
//destroyList=asgjesoListen
};
184
Avni Rexhepi
t deklarohet instanca e klass LinkedList dhe kur funksioni destroyList() i
largon t gjitha nyjet nga lista (e zbraz listn).
Nse lista e lidhur sth e zbrazt, ather nyja e re i ndahet t dyve, edhe
pointerit front edhe pointerit back. Kjo do t thot se pas thirrjes s
funksionit appendNode(), lista e lidhur prmban nj nyje, q sht nyja e re.
Sidoqoft, nse n listn e lidhur ndodhet s paku nj nyje, ather duhet t
bhet nj zhvendosje (shiftim; angl. shift-zhvendos, lviz) e pointerve.
Urdhri else prmban tre urdhra, t cilt e bjn zhvendosjen (shiftimin).
Urdhri i par ia ndan pointerin n nyjen e re, pointerit next t nyjes s fundit
n listn e lidhur. Pastaj pointeri back i ndahet pointerit previous (i
prparshm) t nyjes s re. N fund, nyja e re i ndahet pointerit back, duke e
br nyjen e re si nyje t par n listn e lidhur.
Kjo mund t duket pak konfuze, por shikoni figurn 4.4, n t ciln paraqiten
nyjet e lists s lidhur. Supozoni se lista e lidhur ka dy nyje para vendosjes s
nyjes s re n list. Kjo sht paraqitur n bllokun e eprm.
187
Avni Rexhepi
188
189
Avni Rexhepi
Figura 4.6 - Funksioni destroyList() largon nyjet duke filluar me nyjen e fundit
n listn e lidhur dhe kryen punn e tij deri n fillim t lists s lidhur.
Procesi vazhdon deri sa t gjitha nyjet t largohen nga lista e lidhur. Hapi i
fundit n funksionin destroyList() sht q ti ndahet vlera NULL pointerve n
fillim (front) dhe fund (back) t lists, gj q tregon (indikon) se lista e lidhur
sht e zbrazt.
Avni Rexhepi
cilat krkohen pr t zbrthyer urdhrat n LinkedList.cpp, t cilt i referohen
klass dhe nyjes.
Definicioni i secilit funksion n kt shembull, praktikisht sht i njjt me ato
t diskutuara m hert. Prjashtimi i vetm sht se referenca sht br n
klasn LinkedList n emrin e definicionit t secilit funksion. Kjo e shoqron
secilin definicion me klasn LinkedList pr kompajlerin.
//LinkedList.cpp
#include "LinkedList.h"
LinkedList::LinkedList()
{
front = NULL;
back = NULL;
}
LinkedList::~LinkedList()
{
destroyList();
}
void LinkedList::appendNode(int data)
{
Node* n = new Node();
n->data=data;
if(back == NULL)
{
back = n;
front = n;
}
else
{
back->next = n;
n->previous = back;
back = n;
}
}
void LinkedList::displayNodes()
{
cout << "Nyjet:";
Node* temp = front;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->next;
}
}
192
Avni Rexhepi
pointerin q i referohet lists s lidhur (LinkedList). Pointeri emrtohet list dhe
caktohet pr t ju referuar instancs s klass LinkedList.
Pastaj, thirret tri her funksioni appendNode(), i cili e shton nyjen e re n fund t
lists s lidhur dhe ia ndan vlern e prcjellur funksionit appendNode() antarit
t t dhns s nyjs (vlers s nyjs).
Dy urdhrat e fundit n kt shembull, paraqesin vlern e secils nyje n listn e
lidhur. S pari, funksioni displayNodes() thirret pr t paraqitur nyjet n
renditjen natyrale, duke filluar nga fillimi i lists s lidhur dhe duke prfunduar
n nyjen e fundit (back) t lists s lidhur.
Pastaj, funksioni displayNodesReverese() thirret pr t br paraqitjen e
antarve t lists n renditje t kundrt, duke filluar nga nyja e fundit dhe duke
prfunduar me nyjen e par.
N fund, operatori delete thirret pr t fshir instancat e klass LinkedList, nga
memoria.
Rezultati i kodit t shembullit t paraqitur, sht:
10
20
30
30
20
10
194
Shembull:
Lista njfish e lidhur mund t paraqitet si vijon:
Secila qelul (angl. cell) quhet nyje (angl. node) e lists s lidhur njfish.
Nyja e par quhet kok (angl. head) dhe sht nyje e dedikuar. Duke e ditur
kokn, mund t sigurohet qasja n seciln nyje tjetr n list. Ndonjher,
nyja e fundit, e quajturbisht (angl. tail), gjithashtu ruhet, n mnr q t
shpejtohet operacioni i shtimit/insertimit.
Nyja e par quhet koka (angl. head) dhe nuk ka ndonj nyje tjetr q pointon
(tregon me pointer) n t. Lidhja pr tek koka zakonisht ruhet n klasn e cila
ofron interfejsin pr n strukturn rezultuese t t dhnave. Pr listn e zbrazt,
koka vendoset n NULL.
195
Avni Rexhepi
Poashtu, ka kuptim q t ruhet lidhja pr tek nyja e fundit, e quajtur bishti
(angl. tail). Edhe pse asnj nyje nuk mund t qaset prej bishtit (sepse n listn e
lidhur njfish mund t lvizim vetm prpara), ajo mund t shpejtoj
operacionin e insertimit (shtimit), kur t shtohet nj nyje e re n fund t lists.
Kur lista sht e madhe, ajo e zvoglon kompleksitetin e operacionit t shtimit
n mnyr domethnse, gjersa mbingarkimi i memories sht i parndsishm.
N figurn vijuese, mund t shihet nj reprezentimi i brendshm i plot i lists
s lidhur njfish.
Pjes kodi
Zakonisht, struktura e plot e lists njfish t lidhur vendoset n dy klasa. Klasa
kryesore, SinglyLinkedList sht interfejs publik dhe SinglyLinkedListNode
mjet pr prdorim privat brenda klass kryesore. Pasi q
SinglyLinkedListNode sht klas ndihmse, nuk sht e nevojshme q t
enkapsulohen fushat e saj (t bhen private). Vreni q klasa interfejs
SinglyLinkedList mund t zvendsohet nga nj klas tjetr, si klasa Stack,
gjersa implementimi i brendshm i stekut mbetet list njfish e lidhur.
//SinglyLinkedListNode=NyjeeListesSeLidhurNjefish
//head=koka/fillimi, tail=bishti/fundi
class SinglyLinkedListNode
{
public:
int value;
SinglyLinkedListNode *next;
SinglyLinkedListNode(int value) {
this->value = value;
next = NULL;
}
};
196
Algoritmi i prshkimit
Duke filluar nga fillimi (koka),
1. verifiko, nse nuk sht arritur akoma fundi i lists;
2. kryej ndonj veprim me nyjen aktuale, e cila sht specifike pr
algoritm t veant;
3. nyja aktuale bhet paraprake (e kaluar) dhe nyje a ardhshme bhet
aktuale. Kalo n hapin 1.
Shembull
Si shembull, le t marrim mbledhjen e vlerave t lists s lidhur njfish.
197
Avni Rexhepi
198
199
Avni Rexhepi
200
201
Avni Rexhepi
Rasti i prgjithshm
N rastin e prgjithshm, nyja e re gjithmon insertohet ndrmjet dy nyjeve,
t cilat ve jan n list. Lidhjet e fillimit dhe fundit n kt rast nuk jan t
azhuruara.
202
203
Avni Rexhepi
}
}
//insertAfter=insertoPas
void
SinglyLinkedList::insertAfter(SinglyLinkedListNode
*previous,
SinglyLinkedListNode *newNode)
{
if (newNode == NULL)
return;
else
{
if (previous == NULL)
addFirst(newNode);
else if (previous == tail)
addLast(newNode);
else
{
SinglyLinkedListNode *next = previous->next;
previous->next = newNode;
newNode->next = next;
}
}
}
204
205
Avni Rexhepi
206
Rasti i prgjithshm
N rastin e prgjithshm, nyja q duhet larguar gjithmon ndodhet ndrmjet
dy nyjeve n list. N kt rast, lidhjet e koks dhe bishtit nuk azhurohen (nuk
ndryshohen)
207
Avni Rexhepi
Largimi i till mund t bhet n dy hapa:
1. Azhuro lidhjen e ardhshme t nyjes paraprake, q t pointoj n nyjen
e ardhshme t nyjes q largohet.
Pjes kodi
T gjitha rastet e paraqitura m sipr, mund t implementohen n nj funksion
me nj argument t vetm, i cili sht nyja e prparshme (paraprake) e nyjes
q duhet t largohet. Pr operacionin remove first (largo t parn), argumenti
sht NULL. Pr operacionin remove last (largo t fundit), argumenti sht nyja
e parafundit (e prparshme e bishtit). Megjithat, m mir sht q kto raste
speciale (largo t par dhe largo t fundit) t implementohen n funksione t
veanta. Vreni, se largimi i nyjes s par dhe t fundit kan kompleksitete t
ndryshme, pasi q largo t fundit duhet t prshkoj tr listn.
//removeFirst=largoTeParen, removeLast=largoTeFundit
//removeNode=largoNyjen, removeNext=largoTeArdheshmen
//removePrevious=largoTePerparshmen, previousToTail=paraBishtit
void SinglyLinkedList::removeFirst()
208
209
Avni Rexhepi
void
SinglyLinkedList::removeNext(SinglyLinkedListNode
*previous)
{
if (previous == NULL)
removeFirst();
else if (previous->next == tail)
{
SinglyLinkedListNode *removedNode = previous->next;
tail = previous;
tail->next = NULL;
delete removedNode;
} else if (previous == tail)
return;
else
{
SinglyLinkedListNode *removedNode = previous->next;
previous->next = removedNode->next;
delete removedNode;
}
}
Shembull:
Programin mund ta shkruajm me t gjitha funksionet n nj program t vetm.
N kt shembull kemi programin q mundson manipulimet me antart (nyjet)
e lists s lidhur (insertimi, fshirja, paraqitja). Edhe pr definimin e nyjes sht
prdorur klasa. Dritarja gjat ekzekutimit do t duket si n vijim:
211
Avni Rexhepi
system(Pause);
return 0;
}
212
213
Avni Rexhepi
while(temp->vlera!=x)
{
ePerparshme=temp;
temp=temp->pNeA;
}
ePerparshme->pNeA=temp->pNeA;
cout<<"\n Fshirja e suksesshme \n";
return x;
}
214
Funksioni
back
erase
empty
Shembull-Prshkrimi
cout << list.back() << endl;
Kthen referencn pr n elementin e fundit n list.
list.erase(iter);
Elementi i lists i pointuar prmes iteratorit iter, do t fshihet
list.erase(firstIter, lastIter)
Fshirja e t gjitha elementeve t lists, nga i pari tek i fundit
if (list.empty())
Funksioni empty kthen true nse lista sht e zbrazt,
prndryshe nse lista ka elemente, kthen false.
end
front
insert
merge
pop_back
pop_front
iter = list.end();
end kthen iteratorin dy-drejtimsh pr n fund t lists
cout << list.front() << endl;
front kthen referencn pr n elementin e par n list
list.insert(iter, x);
funksioni insert, inserton nj element n list. N kt form,
inseron elementin me vler x, para elementin t pointuar me
iter.
list1.merge(list2);
bashkon elementet nga list2 n list1. list1 zgjerohet pr t
akomoduar antart e rinj nga list2 (merge pret q t dy listat t
jen t sortuara)
list.pop_back();
pop_back largon elementin e fundit t lists
list.pop_front();
pop_front largon elementin e par t lists
215
Avni Rexhepi
push_back
list.push_back(x);
push_back inserton nj elementi t ri me vler x n fund t
lists
push_front list.push_front(x);
push_front inserton nj element me vler x n fillim t lists
reverse
list.reverse();
kthen mbrapsht (rrotullon) renditjen e elementeve n list
size( )
size () - Kthen numrin e elementeve n list
swap
list1.swap(list2)
Funksioni swap shkmben elementet e ruajtura n dy lista. Pr
shembull, nse list1 dhe list2 jan lista, urdhri i dhn do t
shkmbej vlerat e ruajtura n t dy listat.
unique
list.unique();
unique largon elementet me vler t njjt si ndonj element
para tij (largon duplikatet, e shndrron n list me vlera unike)
Shembull:
// STL list container
#include <iostream>
#include <list>
//List nga STL
using namespace std;
void main(void)
{
list<int> ListaIme;
list<int>::iterator iter;
// Shto vlerat n list
for (int x = 0; x < 100; x += 10)
ListaIme.push_back(x);
// Paraqiti vlerat
for (iter = ListaIme.begin(); iter != ListaIme.end();
iter++)
cout << *iter << " ";
cout << endl;
// Reverse ndrysho renditjen e elemeneteve
ListaIme.reverse();
216
Rezultati:
0 10 20 30 40 50 60 70 80 90
90 80 70 60 50 40 30 20 10 0
Steku
Steku sht strkutura e t dhnave q organizon t dhnat sipas parimit FILO
(First In Last Out), i pari brenda, i fundit jasht. Kjo i prngjan vendosjes s
pjatave nj mbi nj n kuzhin, ku vetm pjata e vendosur e fundit mund t
trhiqet nga grupi. Pr t marr pjatn e tret, s pari duhet larguar dy pjatat e
fundit. Nuk ka mnyr tjetr pr t nxjerr elementet, prveq nga maja (top). Pr
t siguruar qasje t drejtprdrejt ose qasje t rastit n cilindo element, duhet t
prdoret tjetr struktur e t dhnave.
Steku sht i prshtatshm kur duhet t ruhen dhe t trhiqen t dhnat n
renditjen i pari brenda, i fundit jasht. Pr shembull, kompjuteri i proceson
urdhrat prmes prdorimit t stekut, n t cilin instruksioni i ardhshm pr tu
ekzekutuar ndodhet n krye t stekut.
Klasa LinkedList
Edhe pse diskutohet pr vlerat e vendosura (palosura) si stek, fizikisht kjo nuk
ndodh aspak, n memorie t kompjuterit. N vend t ksaj, t dhnat lidhen s
bashku njra pas tjetrs (n mnyr sekuenciale) n nj list t lidhur, ku vlera e
fundit gjithmon paraqitet n fillim (front) t lists. Vlerat largohen vetm nga
fillimi i lists.
217
Avni Rexhepi
Kjo list sekuenciale krijohet prmes prdorimit t lists s lidhur. Si sht
thn, lista e lidhur prmban elementet e quajtura nyje. Nyja ka tri nnelemente: vlern dhe dy pointera. Vlera sht e dhna e ruajtur n stek. Pointert
pointojn n nyjen e prparshme dhe n at t ardhshme (Figura 4.8). Kur
vendoset nj element i ri n listn e lidhur, alokohet nyja e re (new) dhe
pastaj caktohet prointeri n nyjen e prparshme dhe at t ardhshme.
Struktura quhet Node (Nyja). Urdhri i par deklaron nj integer q ruan vlern
aktuale t nyjes. Dy urdhrat tjer n vazhdim, deklarojn pointert pr n nyjen
e prparshme dhe t ardhshme n listn e lidhur.
Kontruktori inicializon elementet e nyjs kur krijohet nyja, gj q sht e
ngjashme me mnyrn se si punon konstruktori n definicionin e klass. Ju
siguroni (ofroni) vlern aktuale pr strukturn kur t krijoni nyje t re. Kjo vler
i ndahet vlers n listn e argumenteve, e cila pastaj i ndahet elementit t
instancs s strkuturs. Nyjet previous (e prparshme) dhe next (e ardhshme)
inicializohen n NULL, gj q tregon se nuk ka elemente t tjera n listn e
lidhur. NULL zvendsohet me pointerin pr n nyje kur t shtohet nyja e re n
listn e lidhur.
Si ju kujtohet nga pjesa e mparshme, klasa LinkedList definohet pr t krijuar
dhe menagjuar listn e lidhur. Jan dy antar t t dhnave dhe gjasht
218
Klasa StackLinkedList
Programeri efikas nse sht e mundur nuk e prsrit kodin. N vend t ksaj,
ai i trashgon atributet dhe sjelljet e klass tjetr, duke definuar klasn
LinkedList pr t krijuar dhe manipuluar listn e lidhur. Programeri efikas
mundet poashtu t krijoj klasn StackLinkedList pr t krijuar dhe manipuluar
stekun-list e lidhur. Klasa StackLinkedList trashgon atributet dhe sjelljet e
klass LinkedList dhe definon sjelljet tjera t nevojshme pr t punuar me
stekun-list e lidhur.
219
Avni Rexhepi
Si shtes ndaj atributeve t definuara n klasn LinkedList, klasa
StackLindekList krkon pes sjellje plotsuese t definuara si funksione antare:
konstruktorin, destruktorin, push(), pop() dhe isEmpty(). Definicioni i klass
StackLinkedList sht si vijon:
class StackLinkedList : public LinkedList
{
public:
StackLinkedList();
virtual ~StackLinkedList();
void push(int);
int pop();
bool isEmpty();
};
221
Avni Rexhepi
Ja si do t jet definicioni i funksionit pop(). Referojoni pamjes s stekut-list e
lidhur, n figurn 4.9, pr t pasur t qart mnyrn e funksionimit t funksionit
pop().
Figura 4.9: Funksioni pop() largon nyjen n krye t stekut, e cila sht nyja n
fillim t lists s lidhur.
int pop()
{
if (isEmpty())
{
return -1;
}
int retVal = back->data;
Node * temp = back;
if (back->previous == NULL)
{
back = NULL;
front = NULL;
}
else
{
back = back->previous;
back->next = NULL;
222
223
Avni Rexhepi
NULL atributit next t nyjes back, q sht Node 1. Kjo e bn Node 1 si krye
t stekut (top).
Urdhri i parafundit e largon nyjen nga memoria duke prdorur operatorin
delete. Pointeri i prkohshm pointon n adresn e memories s nyjes q sht
larguar nga steku. Urdhri i fundit e kthen vlern e nyjes q u largua nga steku.
//LinkedList.cpp
225
Avni Rexhepi
#include "LinkedList.h"
LinkedList::LinkedList()
{
front = NULL;
back = NULL;
}
LinkedList::~LinkedList()
{
destroyList();
}
void LinkedList::appendNode(int data)
{
Node* n = new Node();
n->data=data;
if(back == NULL)
{
back = n;
front = n;
}
else
{
back->next = n;
n->previous = back;
back = n;
}
}
void LinkedList::displayNodes()
{
cout << "Nyjet:";
Node* temp = front;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->next;
}
}
void LinkedList::displayNodesReverse()
{
cout << "Nyjet ne renditje te kundert:";
Node* temp = back;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->previous;
}
226
227
Avni Rexhepi
int StackLinkedList::pop()
{
if(isEmpty())
{
return -1;
}
int retVal = back->data;
Node* temp = back;
if(back->previous == NULL)
{
back = NULL;
front = NULL;
}
else
{
back = back->previous;
back->next = NULL;
}
delete temp;
return retVal;
}
bool StackLinkedList::isEmpty()
{
if(front == NULL)
{
return true;
}
else
{
return false;
}
}
Aplikacioni StackLinkedList
Fajlli StackLinkedListDemo.cpp prmban aplikimin aktual t stekut, si sht
paraqitur n kodin vijues. Aplikacioni fillon me deklarimin e instancs s klass
StackLinkedList. Rikujtoni q ky urdhr thrret (n mnyr indirekte)
konstruktorin e klass LinkedList, q sth trashguar n klasn
StackLinkedList.
Pastaj aplikacioni e thrret funksionin push(), pr t vendosur (shtyr) n stek
vlerat 10, 20 dhe 30. Pr t paraqitur vlerat thirret funksioni displayNodes().
228
Figura 4.10 - Para se t thirret funksioni pop(), n stek jan tri nyje. Pasi t
thirret funksioni pop(), n stek mbesin dy nyje.
//StackLinkedListDemo.cpp
#include <iostream>
using namespace std;
void main()
{
StackLinkedList* stack = new StackLinkedList();
stack->push(10);
stack->push(20);
stack->push(30);
stack->displayNodes();
cout << stack->pop() << endl;
delete stack;
}
229
Avni Rexhepi
Rezultati i programit sht:
Nodes: 10 20 30 10
Queue-List e lidhur
Konceptualisht, queue list e lidhur sht i njjt sikur queue i ndrtuar duke
prdorur vargun. T dy ruajn t dhna (vlera). Mirpo, n queue varg, e dhna
ruhet n elementin e vargut. N queue list e lidhur, e dhna ruhet n nyje t
lists s lidhur. Queue list e lidhur prbhet prej tri komponenteve kryesore:
nyja, definicioni i klass LinkedList dhe definicioni i klass QueueLinkedList.
Bashkarisht, ato grupohen n t dhna t organizuara n radh (queue).
N C++ nyja krijohet si struktur e e tipit t t dhnave t definuar prej
shfrytzueusit dhe prmban tri elemente: vlera dhe pointert n nyjen e
230
231
Avni Rexhepi
struct Node
{
int data;
Node* previous;
Node* next;
};
232
233
Avni Rexhepi
Enqueue
Funksioni enqueue() (vendose n radh), i klass QueueLinkedList thirret sa
her q nj nyje e re t vendoset n queue (radh). Si do t shihet nga definicioni
i funksionit n pjesn vijuese t kodit, funksioni enqueue() sht i rrall sepse
prmban vetm nj urdhr, i cili thrret funksionin appendNode() t klass
LinkedList.
Nuk ka nevoj q t shtohen urdhra plotsues n funksionin enqueue() sepse
vendosja e nyjes n queue sht procesi i njjt si i vendosjes s nyjes n listn e
lidhur. Secila nyje e re vendoset n fund (back) t lists s lidhur. Prandaj, krejt
ka nevojitet, sht funksioni appendNode().
Nyjet shtohen n fund t queue-s, pr shkak se sht duke u riprdorur kodi i
klass LinkedList. Nyja e re vendoset n fund (back) t queue-s, kurse trhiqet
(largohet) nga fillimi i lists (front).
Funksioni enqueue() ka nj argument, i cili sht vlera (e dhna) q do ti ndahet
nyjes s re. N kt shembull, nyja prdoret pr t ruajtur nj integer, mirpo
mund t ruhet fardo tipi i t dhnave. N fakt, e dhna mund t jet pointer pr
n setin e t dhnave, si p.sh. informacionet pr studentit. Pr t ndryshuar kt
shembull nga vlera integer n nj tip tjetr t t dhnave, vetm duhet ndryshuar
elementi i t dhnave n strukturn Node, pr t reflektuar tipin e t dhnave t
cilin dshironi ta ruani n nyje.
Vlera e pranuar nga funksioni enqueue() i prcillet funksionit appendNode().
Figura 4.12 ilustron se si funksioni appendNode() e vendos nyjen e re n fund
(back) t lists s lidhur. N krye t ilustrimit sht lista e lidhur q prmban dy
nyje. Pastaj thirret funksioni appendNode(), pr t shtuar nj nyje t re n fund
t lists s lidhur.
234
235
Avni Rexhepi
Dequeue
Funksioni dequeue() (largoje nga radha) i klass QueueLinkedList largon nyjen
nga fillimi (front) i queue-s (radhs). Pr fat t keq, nuk ka ndonj funksion n
klasn LinkedList i cili largon nyjen nga fundi (back) i lists s lidhur. Prandaj,
funksioni dequeue() duhet t kruej punn.
Funksioni dequeue() fillon me kontrollimin nse ka nyje n queue, duke thirrur
funksionin isEmpty(). Funksioni isEmpty() kthen vlern bool-eane true nse
queue sht i zbrazt, rast n t cilin dequeue() kthen -1. Nse sht s paku
nj nyje n queue, kthehet false.
Vrejtje: N kt rast, termi dequeue prdoret pr funksionin pr nxjerrjen e
elementint nga fillimi i lists. N fakt, DEQueue sht edhe temri q prdoret pr
versionin e dyt t queue-s, i cili mundson qasjen nga t dy ant, si pr insertim ashtu
edhe pr nxjerrje dhe rrjedh nga kombinimi: Double Ended Queue = dequeue.
Figure 4.13: Node 1 largohet nga back i queue, nga funksioni dequeue().
Procesi i largimit fillon me ndarjen e vlers s nyjes n fillim (front) t queue
variabls s quajtur retVal. Vlera e retVal kthehet nga funksioni dequeue(), n
urdhrin e fundit t funksionit.
236
237
Avni Rexhepi
Funksioni isEmpty() kontrollon nse ka nyje n queue dhe ky funksion thirret
nga funksioni dequeue(). Funksioni isEmpty() verifikon vlern e antarit front
t klass LinkedList. Nse vlera e front sht NULL, ather queue (radha e
pritjes) sht e zbrazt, prndryshe, queue ka s paku nj nyje.
Funksioni isEmpty() kthen true nse vlera e front sht NULL, prndryshe
kthen false, si sht treguar n definicionin vijues t funksionit isEmpty():
bool isEmpty()
{
if(front == NULL)
{
return true;
}
else
{
return false;
}
}
238
239
Avni Rexhepi
{
protected:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
};
//LinkedList.cpp
#include "LinkedList.h"
LinkedList::LinkedList()
{
front = NULL;
back = NULL;
}
LinkedList::~LinkedList()
{
destroyList();
}
void LinkedList::appendNode(int data)
{
Node* n = new Node();
n->data=data;
if(front == NULL)
{
back = n;
front = n;
}
else
{
back->next = n;
n->previous = back;
back = n;
}
}
void LinkedList::displayNodes()
{
cout << "Nyjet:";
Node* temp = front;
while(temp != NULL)
{
cout << " " << temp->data;
240
241
Avni Rexhepi
{
appendNode(x);
}
int QueueLinkedList::dequeue()
{
if(isEmpty())
{
return -1;
}
int retVal = front->data;
Node* temp = front;
if(front->next == NULL)
{
back = NULL;
front = NULL;
}
else
{
front = front->next;
front->previous = NULL;
}
delete temp;
return retVal;
}
bool QueueLinkedList::isEmpty()
{
if(front == NULL)
{
return true;
}
else
{
return false;
}
}
Avni Rexhepi
lists e ka indeksin 0; pastaj indeksi inkrementohet pr nj, duke lvizur prpara
kah fundi (back) i lists. Funksioni appendNode() i shton nyjet n fund (back) t
lists s lidhur, kshtu q kjo mund t imagjinohet si varg dinamik q lviz prej
fillimit kah fundi.
Ndonjher mund t mos dihet indeksi i nyjes q dshirojm t lagrohet nga lista
e lidhur. N kt rast, duhet t definohet nj funksion tjetr i cili e largon nyjen
bazuar n vlern (t dhnn) e nyjes, jo n indeksin e nyjes. Ky funksion do t
quhet deleteNode(). Funksioni deleteNode() dallon nga funksioni
removeNodeAt() pr nga mnyra se si funksioni e identifikon nyjn q duhet t
largohet nga lista e lidhur. Funksioni removeNodeAt() e lokalizon nyjen q
duhet larguar duke prdorur vlern e indeksit t nyjes. Funksioni deleteNode() e
lokalizon nyjen q duhet larguar duke prdorur vlern e t dhns s nyjes, e cila
i prcillet funksionit deleteNode().
Deri m tani, nyjet n listn e lidhur jan qasur n mnyr sekuenciale. Mirpo,
n disa aplikacione reale, nyjet qasen edhe n mnyr t rastit. Funksioni i
ardhshm q do t definohet pr klasn LinkedList mundson qasjen n nj nyje
specifike. Ky funksion do t quhet findNode() dhe prdoret kur dihet e dhna
(vlera) e prmbajtur n nyje por nuk dihet pozicioni i nyjs n listn e lidhur
(nuk dihet indeksi). Pr t lokalizuar nyjen, funksionit i jepet e dhna (vlera) e
ruajtur n nyje, kurse funksioni findNode() e kthen indeksin e nyjes.
Lista origjinale LinkedList sht e aft q t shtoj nj nyje t re n listn e
lidhur, duke e shtuar at n fund t lists. Mirpo, do t ket situata kur duhet t
insertohet nj nyje e re diku n mest t lists s lidhur. Pr t br kt, duhet t
definohet funksioni insertNodeAt() (inserto nyjen n). Funksioni insertNodeAt()
do t krkoj dy parametra. Parametri i par sht indeksi i nyjes q do t lvizet
(zhvendoset) n listn e lidhur, pr ti br vend nyjes s re. Ky indeks, bhet
indeksi i nyjes s re. Parametri i dyt sht vlera (e dhna) q do ti ndahet nyjes
s re. Funksioni insertNodeAt() krijon nyjen e re dhe prshtat referencat
(pointert) n listn e lidhur, pr t lidhur nyjen e re me nyjet tjera n listn e
lidhur.
Nj plotsim i rndsishm i klass LinkedList sht nxjerrja (angl. retrieverigjej, rifitoj) e vlers (t dhns) q sht e ruajtur n nyjen specifike. M par
kishim dy funksionet display t cilat prdoreshin pr t shtypur t dhnat n
listn e lidhur. Funksioni i ri, q do t quhet peek() (angl. peek-shikoj
vjedhurazi, prgjoj). Funksioni peek() krkon q ti prcillet indeksi i nyjes q e
prmban vlern t ciln dshironi ta nxirrni (shihni, trhiqni). Ai pastaj e kthen
vlern (t dhnn) q ndodhet n at nyje.
Plotsimi i fundit q do t bhet n klasn LinkedList sht definimi i funksionit
q kthen numrin e nyjeve t lists s lidhur. Ky funksion do t quhet getSize()
dhe do t prdoret sa her q ka nevoj pr t ditur madhsin e lists s lidhur.
244
245
Avni Rexhepi
Figura 4.16 - Lista e lidhur q prmban pes nyje, ku secila nyje identifikohet
prmes indeksit.
Fillojm me definimin e funksionit removeNode(), i cili sht ilustruar n kodin
246
Avni Rexhepi
Vlera e antarit previuous (e prparshme) t nyjes pastaj i ndahet antarit
back t klass LinkedList. Kjo e lviz nyjen e prparshme n fund t lists
dhe n fakt (si rezultat) e largon nyjen q i prcillet funksionit removeNode(),
nga lista e lidhur.
Vlera e antarit next t nyjes s prparshme pastaj caktohet n NULL, pr t
treguar se nuk ka nyje tjetr pas saj sepse ajo sht n fund (back) t lists.
Urdhri q e kruen kt operacion mund t duket konfuz, por duke zvendsuar
referencat (pointert) n nyje dhe previuos me numrin e nyjes, duhet t jet e
qart se ka ndodh. Urdhri sht:
node->previous->next = NULL;
Nse nyja q fshihet nuk sht nyja e vetme n listn e lidhur dhe nuk sht nyja
n fillim ose n fund t lists s lidhur, ather mundsia e vetm sht q nyja
ndodhet diku tjetr prbrenda lists.
Procesi i katrt sht largimi i nyjes n mes t lists s lidhur dhe pastaj lidhja e
nyjes s prparshme dhe t ardhsme t saj. Edhe kjo ilustrohet m mir prmes
shembullit.
Le t themi se do t largohet NodeC. Nyje e prparshme sht NodeB dhe nyje e
ardhshme sht NodeD. S pari, lidhet nyja NodeB me NodeD, duke prdorur
urdhrin vijues:
node->previous->next = node->next;
Tani q NodeB sht lidhur me NodeD, duhet t lidhet edhe NodeD me NodeB:
node->next->previous = node->previous;
T dyja, NodeB dhe NodeD tani jan t lidhura mes tyre dhe NodeC sht
larguar nga lista e lidhur.
Edhe pse nyja e prcjellur n funksionin removeNode() nuk sht m n listn e
lidhur, ajo akoma mbetet n memorie. Prandaj, ajo duhet t largohet nga
memoria duke thirrur operatorin delete.
248
249
Avni Rexhepi
Funksioni removeNodeAt()
Funksioni removeNodeAt() largon nyjen duke prdorur indeksin e nyjes (e jo
pointerin pr n nyje, n memorie). Mbani mend, indeksi sht pozita e nyjes n
listn e lidhur. Le t themi se dshironi t largoni nyjen e tret n listn e lidhur.
Ather, thjesht ia prcillni indeksin 2 funksionit removeNodeAt() dhe
removeNode() e kryen operacionin n mnyr interne. Nuk mund ta thirrni
direkt funksionin removeNode(), pjesrisht pr arsye se sht i mbrojtur, por
edhe pr arsye se jasht ksaj klase ju nuk keni njohuri pr vlerat aktuale t
pointerve. Pasi q indeksat fillojn prej zeros, ju nuk keni nevoj t dini
pointerin aktual n nyje t ciln dshironi ta largoni. Kjo sht ilustruar n kodin
vijues.
Hapi i par n funksionin removeNodeAt() sht t kontrolloj nse indeksi
sht valid. Pr t br kt, funksioni removeNodeAt() kontrollon nse indeksi
sht m i vogl se zero ose m i madh se madhsia e lists minus nj. Ai e
prdor vlern e antarit size t klass LinkedList pr t prcaktuar madhsin
e lists s lidhur. Nse ndonjri prej kushteve sht true, ather indeksi sht
jo valid dhe nuk tentohet t fshihet nyja.
Mirpo, nse t dy kushtet jan false, ather funksioni removeNodeAt() fillon
procesin e largimit t nyjes prej lists. Ky proces i ka dy hapa. S pari, indeksi
lokalizon referencn (pointerin) pr n nyjen korresponduese dhe s dyti, thirret
funksioni removeNode() dhe i prcillet refernca (pointeri).
Funksioni removeNodeAt() fillon krkimin pr referencn (pointerin) n nyje
duke deklaruar pointerin e prkohshm pr n nyje, t quajtur temp_node dhe
duke ia ndar atij referencn (pointerin) pr n nyjen n fillim (front) t lists s
lidhur. Pastaj, unaza for kalon npr seciln nyje n listn e lidhur deri sa t
gjindet nyja e reprezentuar nga indeksi. Gjat secils prsritje (iteracion),
pointerit temp_node i ndahet nyja e pointuar nga antari next i temp_node
aktual.
Kur t arrihet indeksi, vlera e temp_node sht referenc (pointer) pr n nyjen e
cila i prgjigjet (korrespondon) indeksit q i prcjellet funksionit
removeNodeAt(). Thirret funksioni removeNode() dhe i prcillet temp_node.
void removeNodeAt(int index)
{
if(index < 0 || index > size-1)
{
return;
}
250
Funksioni deleteNode()
Funksioni deleteNode() prdor vlern e ruajtur n nyje pr t gjetur dhe larguar
nyjen gjegjse prej lists s lidhur. Funksioni deleteNode() pastaj krkon listn
e lidhur pr t lokalizuar dhe larguar nyjen.
Procesi funksionon si n vijim. S pari, deklarohet nyja e prkohshme e quajtur
temp_node dhe i ndahet referenca (pointeri) pr n nyjen q ndodhet n fillim
(front) t lists s lidhur. Nse temp_node nuk sht NULL, ather lista nuk
sht e zbrazt dhe funksioni prcakton (kontrollon) nse vlera (e dhna n t)
prshtatet me vlern (t dhnn) e nyjes aktuale.
Nse po, ather temp_node i prcillet funksionit removeNode() dhe funksioni
prfundon. Nse t dhnat (vlerat) nuk prshtaten, ather nyja e ardhshme
(next) i ndahet asaj temp_node dhe procesi vazhdon deri sa t gjindet nyja q
prmban vlern ose funksioni deleteNode() arrin fundin e lists s lidhur (dmth
vlera nuk gjindet).
void deleteNode(int data)
{
Node* temp_node = front;
while(temp_node != NULL)
{
if(temp_node->data == data)
{
removeNode(temp_node);
return;
}
else
{
temp_node = temp_node->next;
}
}
}
251
Avni Rexhepi
Funksioni findNode()
Nse duhet t qaset nj nyje e veant n listn e lidhur, por ju nuk e dini
referencn (pointerin) pr n at nyje e as pozitn e nyjs n listn e lidhur,
mirpo e dini vlern (t dhnn) q ruhet n nyje, ather nyja mund t
lokalizohet duke thirrur funksionin findNode() (angl. find-gjej).
Funksioni findNode() krkon q tia prcjellni vlern e ruajtur n nyje. Ai pastaj
e prdor vlern (t dhnn) pr t lokalizuar nyjen dhe kthen indeksin e nyjes,
si paraqitet n shembullin vijues.
Procesi i gjetjes s nyjes fillon kur deklarohet nj variabl e indeksit t cils n
fund do ti ndahet indeksi i nyjes, nse ajo gjindet. Deklarohet gjithasthtu edhe
nyja e prkohshme dhe i ndahet referenca (pointeri) pr n nyjen n fillim
(front) t lists s lidhur.
Gjersa temp_node nuk sht NULL, funksioni findNode() iteron (kalon me
prseritje, n unaz) npr listn e lidhur. Me secilin iteracion (prsritje) vlera e
nyjes aktual krahasohet me vlern e prcjellur si argument n funksionin
findNode().
Nse t dyja jan t barabarta, ather kthehet vlera aktuale e indeksit, q sht
indeksi i nyjes. Nse nuk jan t barabart, ather vlera e antarit t ardhshm
t nyjes aktuale i ndahet asaj temp_node dhe inkrementohet indeksi. Nse vlera
(e dhna) nuk sht gjetur n listn e lidhur, kthehet -1, sepse vlera -1 nuk
mund t jet asnjher vler e kthyer valide (e vlefshme).
int findNode(int data)
{
int index = 0;
Node* temp_node = front;
while(temp_node != NULL)
{
if(temp_node->data == data)
{
return index;
}
else
{
temp_node = temp_node->next;
index++;
}
}
return -1;
}
252
Funksioni insertNodeAt()
Funksioni insertNodeAt() inserton nj nyje t re n lokacionin e caktuar t lists
s lidhur. Dihet se secila pozit n listn e lidhur identifikohet prmes indeksit
dhe lokacioni i par ka vlern e indeksit 0, i dyti 1, e kshtu me radh. Pr t
specifikuar lokacionin ku dshironi t vendosni (insertoni, shtoni) nyjen e re n
listn e lidhur, prdoret indeksi.
Funksioni insertNodeAt() krkon dy argumente: lokacionin ku do t insertohet
nyja n listn e lidhur dhe vlera (e dhna) q do t ruhet n at nyje. Shembulli
n vijim paraqet mnyrn e vendosjes s nyjes n listn e lidhur.
Hapi i apr sht q funksioni insertNodeAt() t kontrolloj nse indeksi i
prcjellur n funksion sht valid. Ai e bn kt duke kontrolluar nse indeksi
sht m i vogl se zero ose m i madh se madhsia e lists (kjo informat
gjindet n antarin size t klass LinkedList). Nse indeksi sht invalid,
ather funksioni insertNodeAt() ndrprehet dhe kthen prgjigjen n urdhrin q
e ka thirrur. Ka nj ndryshim t vogl n rastin e kontrollimit t indeksave, n
krahasim me funksionin removeNodeAt(). Nse indeksi do t ishte i barabart
me size, kjo do t bnte q nyja t shtohet n listn e lidhur. I bie q indeksi
sht pr 1 jasht kufijve t indeksave t vargut, por kjo sht n rregull, sepse
n fakt n kt rast do t shtohet nj nyje e re n fund t lists s lidhur, ndrsa
funksioni removeNodeAt() krkonte indeks valid n rangun nga 0 deri n size1, q sht nyja e fundit n listn e lidhur.
Kur funksioni insertNodeAt() e verifikon se indeksi sht valid, ai vazhdon me
krijimin e nyjes s re dhe e inserton nyjn n listn e lidhur. Ky proces fillon me
krijimin n instance t strukturs s nyjes (node) dhe duke i ndar asaj vlern e
prcjellur n funksion si argument. Kjo instanc pastaj i ndahet pointerit
new_node (nyja e re).
Pastaj, duhet t verifikohet a ka ndonj nyje n listn e lidhur. Kjo bhet duke
kontrolluar vlern e antarit size t klass LinkedList. Nse vlera sht zero,
ather lista e lidhur sht e zbrazt dhe nyja e re do t bhet nyja e par (e
vetme) n list.
Nyja e re vendoset n list duke ia ndar pointerin new_node t dy antarve,
front dhe back, t klass LinkedList. Antart previous dhe next t nyjes
veq jan caktuar n NULL (si vler e nnkuptuar, default) ashtu q nuk duhet
br asgj n nyjen e re.
front = new_node;
back = new_node;
253
Avni Rexhepi
Nse lista e lidhur ka nj ose m shum nyje, ather funksioni insertNodeAt()
kontrollon nse nyja e re duhet t insertohet n pozitn e par n list, duke
vlersuar vlern e indeksit t prcjellur n funksion. Nse vlera e indeksit sht
zero, ather nyja e re do t bhet nyje e par n listn e lidhur.
Kjo bhet si n vijim. Nyja e re (new_node) i ndahet antarit previous (t
prparshm) t nyjes s ndar antarit front; t klass LinkedList. Pastaj,
antarit next t new_node (nyjs s re) i ndahet nyja e ndar antarit front t
klass LinkedList. N fund, antarit front i ndahet new_node.
front->previous = new_node;
new_node->next = front;
front = new_node;
Nse nyja e re nuk do t bhet nyje e par e lists s lidhur, ather funksioni
insertNodeAt() vendos nse nyja do t bhet nyje e fundit e lists s lidhur,
duke krahasuar indeksin me madhsin (antarin size) e klass LinkedList.
Nse kto dy vlera jan t barabarta, ather nyja e re vendoset n fund t lists
s lidhur. Rikujtojm se indeksi 0 sht fillimi i lists (front) dhe indeksi (size-1)
sht fundi i lists (back).
Kjo bhet si n vijim. new_node i ndahet antarit next t nyjes q aktualisht
ndodhet n fund t lists, back. Pastaj, nyja n fund, back, i ndahet antarit
previous (t prparshm) t nyjes s re. N fund, nyja e re (new_node) i ndahet
antarit back t klass LinkedList.
back->next = new_node;
new_node->previous = back;
back = new_node;
Pas iteracionit t par, temp-it i sht ndar NodeB dhe vlera e i-s sht 1, q
sht m pak sesa vlera e indeksit (2), kshtu q ekzekutohet edhe nj iteracion.
Ja se ka ndodh:
temp = temp->next
temp = NodeB->NodeC
temp = NodeC
Tani pointeri temp pointon n NodeC dhe vlera e i-s sht 2, q sht e
barabart me vlern e indeksit, kshtu q nuk ka m iteracione plotsues dhe
pointeri temp pointon n NodeC.
Tani q jemi n lokacionin e dshiruar n listn e lidhur, sht koha q t
shkmbehen pointert prreth, pr t insertuar nyjen e re n list. Ja si bhet kjo:
new_node->next = temp;
new_node->previous = temp->previous;
temp->previous->next = new_node;
temp->previous = new_node;
255
Avni Rexhepi
Hapi i fundit sht q t inkrementohet antari size (madhsia) e klass
LinkedList, pr t reflektuar nyjn e re. Definicioni i plot i funksionit
insertNodeAt(), sht:
void insertNodeAt(int index, int data)
{
if(index < 0 || index > size)
{
return;
}
Node* new_node = new Node();
new_node->data=data;
if(size == 0)
{
front = new_node;
back = new_node;
}
else if(index == 0)
{
front->previous = new_node;
new_node->next = front;
front = new_node;
}
else if(index == size)
{
back->next = new_node;
new_node->previous = back;
back = new_node;
}
else
{
Node* temp = front;
for(int i=0; i<index; i++)
{
temp = temp->next;
}
new_node->next = temp;
new_node->previous = temp->previous;
temp->previous->next = new_node;
temp->previous = new_node;
}
size++;
}
256
Funksioni peek()
Funksioni peek() nxjerr (lexon, trheq) vlern e ruajtur n nyjen e
specifikuar prmes indeksit q i prcillet funksionit peek(). Funksioni peek() e
krkon nj argument, i cili sht indeksi i pozits n listn e lidhur i cili
prmban vlern (t dhnn) q duhet nxjerrur. N shembullin vijues do t ruhet
dhe pastaj do t lexohet nj vler e tipi integer, por mund t ruhet dhe
lexohet fardo tipi i t dhnave, vetm duke e ndryshuar tipin e t dhnave n
definicionin e nyjs.
Le t shohim si punon funksioni peek(). Ai fillon me vlersimin e indeksit duke
prdorur procedurn e njjt t vlersimit sikur ajo e diskutuar n funksionin
removeNodeAt(), me ndryshimin q funksioni peek() i verifikon vlerat brenda
rangut. Nse indeksi sht jovalid, ather funksioni kthen zero.
Nse indeksi sht valid, ather n fillim deklarohet pointeri i emrtuar temp
dhe i ndahet nyja e cila ndodhet n fillim (front) t lists s lidhur. Funksioni
peek() pastaj vazhdon t lviz npr listn e lidhur deri sa t arrin tek nyja n
t ciln jemi t interesuar. Ky proces sht i njjt me at n funksionin
insertNodeAt().
Kur funksioni peek() del prej unazs for, pointeri temp pointon n nyjen e
cila ka vlern (t dhnn) q e kthen funksioni peek(). Pastaj pointoni n vlern
e nyjes n urdhrin return, pr t kthyer vlern (t dhnn) tek urdhri i cili e ka
thirrur funksionin peek().
Definicioni i plot i funksionit peek() sht si vijon:
int peek(int index)
{
if(index < 0 || index > size-1)
{
return 0;
}
Node* temp = front;
for(int i=0; i<index; i++)
{
temp = temp->next;
}
return temp->data;
}
257
Avni Rexhepi
Funksioni getSize()
Funksioni getSize() e merr vlern e antarit size t klass LinkedList. Do t
vreni se ky funksion ka vetm nj urdhr, i cili thjesht e kthen vlern e antarit
size.
Shtrohet pyetja, ather prse duhet funksioni getSize(), pasi q do t kishte
mundsi q antari size t ket qasje publike, duke u vendosur n pjesn
publike t klass dhe do t lexohej direkt nga aplikacioni! Kjo pr arsye se
antart e klass duhet t ken qasje vetm nga funksionet antare prbrenda
klass ose nga klast e derivuara (trashguese). N kt mnyr, ju gjithmon
kontrolloni qasjen n t dhna dhe i mbroni ato edhe nga ndryshimet e
paqllimshme. Lejimi i ndryshimit nga jasht, nga shfrytzuesit e klass, do t
mund t drgonte n gabime.
int getSize()
{
return size;
}
258
Avni Rexhepi
260
261
Avni Rexhepi
void LinkedList::displayNodesReverse()
{
cout << "Elementet: ";
Node* temp = back;
while(temp != NULL)
{
cout << temp->data << " ";
temp = temp->previous;
}
cout << endl;
}
void LinkedList::destroyList()
{
Node* temp = back;
while(temp != NULL)
{
Node* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}
void LinkedList::removeNode(Node* node)
{
if(node->previous == NULL && node->next == NULL)
{
back = NULL;
front = NULL;
}
else if(node->previous == NULL)
{
front = node->next;
node->next->previous = NULL;
}
else if(node->next == NULL)
{
back = node->previous;
node->previous->next = NULL;
}
else
{
node->previous->next = node->next;
node->next->previous = node->previous;
262
263
Avni Rexhepi
while(temp_node != NULL)
{
if(temp_node->data == data)
{
removeNode(temp_node);
return;
}
else
{
temp_node = temp_node->next;
}
}
}
void LinkedList::insertNodeAt(int index, int data)
{
if(index < 0 || index > size)
{
return;
}
Node* new_node = new Node();
new_node->data=data;
if(size == 0)
{
front = new_node;
back = new_node;
}
else if(index == 0)
{
front->previous = new_node;
new_node->next = front;
front = new_node;
}
else if(index == size)
{
back->next = new_node;
new_node->previous = back;
back = new_node;
}
else
{
Node* temp = front;
for(int i=0; i<index; i++)
{
temp = temp->next;
}
264
265
Avni Rexhepi
int data = list->peek(3);
cout<<"Vlera momentale: "<<data<<"\n";
int size = list->getSize();
cout<<"Madhesia e listes: "<<size<<"\n";
delete list;
system(Pause);
return 0;
}
Operacionet
PriorityQueue create()
krijon radhn e zbrazt t prioritetit
removeMin(PriorityQueue pq)
largon elementin minimumal nga radha e prioritetit pq
Parakusht: pq nuk sht zbrazt
destroy(PriorityQueue pq)
asgjson radhn e prioritetit pq
267
Avni Rexhepi
5. Rekursioni
Rekursion sht teknik e ndarjes s problemit n nnprobleme t tipit t njjt.
Shembull i prshtatshm i sqarimit sht llogaritja e faktorielit.
Llogaritja e faktorielit
Faktorieli i n, q shnohet si n!, sht produkti i numrave t plot: n*(n-1)*(n2)...3*2*1, apo e paraqitur n renditjen e kundert, produkti i numrave prej 1 deri
n n. Pr shembull, 5! = 5*4*3*2*1=120, ose n renditjen e kundrt,
5!=1*2*3*4*5=120
Rekursioni sht nj prej teknikave t llogaritjes s faktorielit. N fakt, 5!=5*4!.
Pra, pr t llogaritur faktorielin e n, duhet t llogarisim faktorielin e (n-1). Pr t
llogaritur faktorielin e (n-1), algoritmi duhet t gjej (n-2)!, e kshtu me radh.
Procesi i prshkruar do t zgjaste pafundsisht, sepse nuk sht definuar akoma
rasti themelor (rasti baz). Rasti baz sht kushti kur duhet t ndalet
rekursioni. N rastin e faktorielit, rasti baz sht n=1, pr t cilin rezultati sht
i njohur.
int Faktoriel(int n)
{
if (n <= 1)
return 1;
else
return n * Faktoriel(n - 1);
}
Llogaritja e 3! n detaje
int Faktoriel(int n)
n=3
if (n<=1)
return 1;
else
return n * Faktoriel(n-1);
return 3*2;
3!=6
int Faktoriel(int n)
n=2
if (n<=1)
return 1;
else
return 2*1;
return n * Faktoriel(n-1);
int Faktoriel(int n)
if (n<=1)
n=1
return 1;
return 1;
else
return n * Faktoriel(n-1);
268
Avni Rexhepi
efektive pr nj klas t gjer t problemeve. N fund, do t shqyrtohen pemt,
tiparet e tyre matematike dhe algoritmet e shoqruara me pemt, si prshkimi i
pems, t cilat qndrojn nn programet rekurzive pr procesim t pemve, e
poashtu edhe algoritmet rekurzive pr procesim t grafeve, n veanti depthfirst search (krkimi thellsia-s pari), q shrben si baz pr shum algoritme
t procesimit t grafeve.
Shum algoritme interesante thjesht shprehen me metoda rekurzive dhe shum
dizajner t algoritmeve preferojn shprehjen e metodave n mnyr rekurzive,
edhe pse pr shum raste mund t krijohen edhe alternativat jorekurzive, t cilat
arrijn t njjtin rezultat prmes nj sekuence t llogaritjeve.
Algoritmet rekurzive
Algoritm rekurziv sht ai q e zgjidh problemin duke zgjidhur nj ose m
shum instanca m t vogla t t njjtit problem. Pr t implementuar
algoritmeve rekurzive n C++, prdoren funksionet rekuzive (funksionet t cillat
e therrasin vetveten), t cilat i korrespondojn definicioneve rekurzive t
funksioneve matematike. N fillim do t shohim shembuj t rekurzionet duke
analizuar programet t cilat n mnyr direkte vlersojn funksionet
matematike, e pastaj mekanzimat themelor zgjerohen pr t ofruar nj
paradigm t prgjishme programimi.
Funksioni rekurziv pr llogaritje t funksionit N!,q prdor definicionin
standard rekurziv sht si n vijim. (Kthen vler korrekte kur thirret pr N
jonegativ dhe mjaft t vogl, q mund t shprehet si int).
Programi 5.1. Funksioni i faktorielit implementimi rekurziv
int Faktoriel(int N)
{
if (N == 0) return 1;
return N* Faktoriel (N-1);
}
pr N 1 me 0! = 1.
270
Ai e llogarit 0! (bazn).
Nn supozimin se llogarit k! pr k < N (hipoteza induktive), ai e
llogarit N!.
Avni Rexhepi
N gjuht programuese si C++, ka disa kufizime n llojet e programev q
shkruhen, por prpiqemi t kufizojm veten n prdorimin e funksioneve
rekurzive n ato t cilat mishrojn provn induktive t korrektsis. Jemi t
interesuar q t krijojm programe komplekse pr probleme t vshtira dhe
duhet t kemi siguri q detyra do t zgjidhet si duhet. Mekanizmat si funksionet
rekurzive mund t ofrojn siguri t till duke dhn implementime kompakte.
Thn ndryshe, lidhja me induksionin matematik na tregon se duhet t
sigurohemi q funksionet rekurzive knaqin (plotsojn) dy kushte themelore:
Ku:
//funksioni gcd()
int gcd(int a, int b)
{
int temp;
while (b != 0)
{
temp = b;
b = a % b;
a = temp;
}
return a;
}
272
273
Avni Rexhepi
Avni Rexhepi
ekzekutohet programi rekurziv, bhet ndrthurrja e thirrjes s funksionit
(funksion brenda funksionit), gjersa t arrihet n pikn kur nuk ka m thirrje
rekurzive dhe kthehemi te prapa. N shumicn e ambienteve programuese,
funksionet e tilla implementohen prmes prdorimit t stekut. N kt pjese
analizohen implementimet e ktilla. Thellsia e rekurzionit sht shkalla
maksimale e ndrthurrjes s thirrjeve t funksioneve prgjat llogaritjes. N
prgjithsi, thellsia varet nga hyrja. Pr shembull, thellsia e rekurzionit pr
shembujt e paraqitur n figurn 5.2 dhe 5.3 sht 9 dhe 4, repsektifisht. Kur
prdoren programet rekurzive, duhet marr parasysh se ambienti programues
duhet t mirmbaj edhe stekun me madhsi proporcionale me thellsin e
rekurzionit. Pr probleme t mdha, hapsira e nevojshme pr stek mund t
pengoj prdorimin e zgjidhjes rekurzive.
Strukturat e t dhnave t ndrtuara prej nyjeve me pointer, jan qensisht
rekurzive. Pr shembull, definicioni i listave t lidhura sht rekurziv. Prandaj,
programet rekurzive ofrojn implementime natyrale n shum funksione t
zakonshme, pr manipulimin e strukturave t tilla t t dhnave. Programi 5.5, i
paraqitur n vazhdim, prmban katr shembuj. Implementimet e tilla jan
shum m t lehta pr tu kuptuar, sesa homologt e tyre jorekurziv. Sidoqoft,
gjat prdorimit t programeve t tilla n rastin e procesimit t listave, duhet
pasur kujdes sepse thellsia e rekurzionit pr kto funksione mund t jet
proporcionale me gjatsin e lists, kshtu q hapsira e krkuar pr stekun
rekurzive mund t bhet penges.
Kto funksione rekurzive pr procesim t thjesht t listave jan t thjeshta pr
tu shprehur, por mund t mos jen t dobishme pr lishta shum t mdha
sepse thellsia e rekurzionit mund t jet proporcionale me gjatsin e lists.
Funisioni i par, count, numron numrin e nyjeve t lists. I dytir, traverse,
thrret funksionin visit pr seciln nyje n list, prej fillimit deri n fund. Kto
dy funksione jan t dyja t lehta pr tu implementuar me unaza. Funksioni i
tret, traverseR, nuk ka homologun e tij t thjesht iterativ. Ai e thrret
funksionin visit pr seciln nyje n list, por n renditje t kundrt.
Funksioni i katrt, remove, i largon prej lists t gjitha nyjet q kan elementin
e dhn. elsi i implementimi sht ndryshimi i lidhjes x=x->next n
paraardhsin e secils nyje q fshihet, q sht br e mundur me prdorimin e
parametrit referenc. Ndryshimet strukturore t secils prsritje t unazs
while jan t njjta si ato t paraqitura n figurn 3.3., por x dhe t, t dyja i
referohen nyjs s njjt.
276
277
Avni Rexhepi
279
Avni Rexhepi
Vetia 5.1. Funksioni rekurziv i cili e ndan problemin e madhsis N n dy
pjes t pavarura (jo t zbrazta) t cilat i zgjidh n mnyr rekurzive, e
thrret vetvetn m pak se N her.
Nse pjest jan njra me madhsi k dhe tjetra me madhsin N-k, ather
numri total i thirrjeve rekurzive sht:
TN = Tk + TNk + 1,
pr N 1 me T1 = 0.
280
281
Avni Rexhepi
282
283
Avni Rexhepi
+ 1, pr N 2 me T1 = 1.
Avni Rexhepi
t djatht triku sht n llogaritjen e gjatsive. Pema e rekurzionit n figur,
ndihmon pr t kutpuar llogaritjen: duke lexuar teposht, shohim se gjatsia e
shenjave zvoglohet pr 1 pr seciln thirrje rekurzive t funksionit. Duke
lexuar prgjat, fitojm shenjat n renditjn n t ciln vizatohen, sepse, pr
cilndo nyje t dhn, s pari vizatojm shenjat e shoqruara me thirjjen e
funksionit n t majt, pastaj shenjn e shoqruar me nyjen dhe shenjat e
shoqruara me thirrjen e funksionit n t djatht.
286
287
Avni Rexhepi
289
Avni Rexhepi
290
291
Avni Rexhepi
nnproblemet pak m t mdha, e kshtu me radh, deri sa t zgjidhet i tr
problemi. Kjo qasje mund t quhet edhe kombino-e-sundo.
Fraktalet dy-dimensionale
Nga vizatimi i vizoreve deri te vizatimi i modeleve dy-dimensionale, si n
figurn 5.12, sht nj hap i vogl. Kjo figur ilustron se si nj prshkrim i
thjesht rekurziv, mund t drgoj n nj llogaritje q duket t jet komplekse.
Fraktali n vijim sht versioni dy-dimensional i figurs 5.10. n rastin e fundit,
katrort e kufizuar thkesojn strukturn rekurzive t llogaritjes.
Rekurrenca
Zgjidhja e prafrt
Krahasime
CN = CN/2 + 1
lg N
Thirrje rekurzive
AN = 2AN/2 + 1
CN = 2CN/2 + N
N lg N
Krkimi binar
mergesort
Krahasime
Avni Rexhepi
Algoritmet dinamike
Nj karakteristik themelore e algoritmeve praj-e-sundo sht se ato e ndajn
problemin n nnprobleme t pavarura. Kur nnproblemet nuk jan t pavarura,
situata sht m e komplikuar, pik s pari pasi q implementimet direkte
rekurzive ose edhe algoritmet m t thjeshta t ktij lloji mund t krkojn sasi
t paimagjinueshme t kohs. N kt pjes do t shqyrtohet teknika sistematike
e evitimit t ksaj gracke n disa raste.
Pr shembull, programi 5.10, sht implementim rekurziv direkt i rekurrencs q
definon numrat Fibonacci (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... ku, prveq fillimit, 0
dhe 1, numrat pasardhs, jan shuma e dy numrave paraprak). Normalisht, mos
e prdorni kt program pasi q sht trsisht joefikas. Vrtet, numri i
thirrjeve rekurzive pr t llogaritur FN sht saktsisht FN+1. Mirpo, FN sht N,
ku 1:618 sht raporti i art. E vrteta e pakndshme sht se programi
5.10 sht algoritm i kohs eksponenciale, pr kt llogaritje triviale. Figura
5.14, e cila paraqet thirrjet rekurzive pr nj numr t vogl (p.sh., deri n 8), e
bn t qart sasin e rillogaritjeve q prfshihen.
Pamja e thirrjeve rekurzive t nevojshme pr t llogaritjen e F8 sipas algoritmit
standard rekurziv ilustron se si rekurzioni me nnprobleme pjesrisht t
prputhura (mbuluara) mund t drgoj n kosto eksponenciale. N kt rast,
thirrja e dyt rekurzive injoron llogaritjet e bra gjat thirrjes s par, gj q
rezulton me rillogaritje masive pasi q efekti shumfishohet rekurzivisht.
Thirrjet rekurzive pr t llogaritur F6 = 8 (t cilat jan reflektuar n nnpemn e
djatht t rrnjs dhe nnpemn e majt t nnpems s majt t rrnjs) jan
listuar n vijim.
294
Avni Rexhepi
te lart). Kjo teknik aplikohet n do llogaritje rekurzive, po qe se mund tia
lejojm vetes (nse ka llogari) q t ruhen t gjitha vlerat e llogaritura m par.
Kjo sht nj teknik e dizajnimit t algoritmeve q sht prdorur me sukses
pr nj rang t gjer t problemeve. Duhet pasur kujdes pr teknikn e thjesht e
cila mund t prmirsoj kohn e ekzekutimit t nj algoritmi, prej asaj
eksponenciale n at lineare!
Programimi dinamik top-down (nga lart te posht) sht pamje edhe m e
thjesht e tekniks q lejon t ekzekutojm funksionet rekurzive, me kosto to
njjt (ose edhe m t ult) sikur programimi dinamik bottom-up (prej poshtte lart), n mnyr automatike. E instrumentalizojm programin rekurziv pr t
ruajtur seciln vler t ciln e ruan (si veprim t fundit) dhe pr t verifikuar
vlerat e ruajtura pr t evituar rillogaritjn e cilsdo prej tyre (si veprim t par
t saj). Programi 5.11 sht transformim mekanik i programit 5.10, q e
zvoglon kohn e ekzekutimit q t jet lineare, prmes programimit dinamik
top-down. Figura 5.15, paraqet zvoglimin drastik t numrit t thirrjeve
rekurzive, t arritur prms ktij ndryshimi t thjesht automatik. Programimi
dinamik top-down, ndonjher thirret edhe memorizim.
297
Avni Rexhepi
298
Fig. 5.17
Ka shum aplikacione, pr t cilat zgjidhja e problemit knapsack sht e
rndsishme. Pr shembull, nj kompani transporti dshiron t dij mnyrn m
t mir pr t ngarkuar nj kamion ose nj aeroplan cargo, me elementet t
cilat duhet transportuar. N aplikacione t tilla, mund t dalin variante tjera t
problemit, si pr shembull: mund t ket numr t kufizuar t elementeve n
dispozicion, ose mund t jen dy kamion. Shum variante t tilla mund t
trajtohen me qasjen e njjt sikur n rastin e zgjidhjes s problemit themelor, t
paraqitur paraprakisht (disa dalin t jen shum m t vshtira). Ka nj vij
shum t holl ndrmjet problemeve t zgjidhshme dhe t pazgjidhshme t ktij
lloji.
N zgjidhjen rekurzive t problemit knapsack, seciln her q zgjedhet nj
element, supozojm se mund t gjejm (rekurzivisht) nj mnyr optimale pr
paketuar pjesn tjetr t ants. Pr antn me madhsi cap, prcaktojm pr
secilin element i n mesin e elementeve n dispozicion, sa sht vlera totale q
do t mund t bartet, duke vendosur i-n n ant me nj paketim optimal t
elementeve t tjera prreth. Ky paketim optimal sht thjesht njri q e kemi
zbuluar (ose do ta zbulojm) pr madhsi m t vogl t ants capitems[i].size. Kjo zgjidhje eksploaton principin se vendimet optimale, t
bra nj her, nuk duhet t ndryshohen. Kur nj her t dijm se si t paketohet
anta me kapacitet m t vogl me nj bashksi optimale t elementeve, nuk
kemi nevoj t ri-ekzaminojm kto probleme, pamarr parasysh fare jan
elementet tjera n vazhdim.
Programi 5.12 sht zgjidhja direkte rekurzive e bazuar n kt diskutim.
Prsri, ky program nuk sht i realizuesm pr prdorim n zgjidhjen e
problemeve aktuale, sepse merr koh eksponenciale pr shkak t rillogaritjeve
masive (shiko figurn 5.17), mirpo, mund t aplikojm programimin dinamik
top-down pr t eliminuar kt problem, ashtu si sht paraqitur n programin
5.13. Si edhe m par, kjo teknik eliminon rillogaritjen, si sht paraqitur n
figurn 5.18.
299
Avni Rexhepi
300
301
Avni Rexhepi
for (i = 0, max = 0; i < N; i++)
if ((space = cap-items[i].size) >= 0)
if ((t = knap(space) + items[i].val) > max)
max = t;
return max;
}
303
Avni Rexhepi
6. Pemt
Pema (angl. Tree) si struktur e t dhnave sht e ngjashme me rrugn e
prshkuar pr t arritur n nj pik t caktuar, ku gjat rrugs, n shum vende
kemi degzime dhe duhet t vendosim a t shkojm majtas apo djathta. Pra,
edhe pse n iden e par t pems, menjher na parafytyrohet nj pem (t
ciln e plqejm m s shumti), me trungun, degt, gjethet dhe frutat e saj, n
fakt duhet ta imagjinojm si pem t prmbysur, me struktur t kontrolluar.
Pemt jan abstraksion matematik q luan rol qndror n dizajnin dhe analizn
e algoritmeve, sepse
+
/
a
b
d
c
*
e
305
Avni Rexhepi
Ka shum lloje t ndryshme t pemve dhe sht me rndsi q t kuptohet
dallimi ndrmjet abrstaksionit dhe reprezentimit konkret me t cilin punojm pr
nj aplikacion t dhn. Varsisht prej rastit do t shqyrtojm lloje t ndryshme
t pemve dhe reprezentimet e detajuara t tyre. N fillim do t definohen pemt
si objekte abstrakte dhe do t prezentohet terminologjia themelore e shoqruar
me to. N mnyr joformale do t diskutohen llojet e ndryshme t pemve t
cilat duhet t shqyrtohen sipas radhs s prgjithsis:
Pemt
Pemt e rrnjzuara
Pemt e renditura
Pemt binare dhe M-are
Avni Rexhepi
dhe nyjet interne, t cilat kan saktsisht nga dy fmij. Pasi q dy fmijt e
secils nyje interne jan t renditur, atyre ju referohemi si fmija i majt dhe
fmija i dajtht. Secila nyje interne duhet t ket t dy fmijt, t majtin dhe t
djathtin, edhe pse njra ose t dyja mund t jen nyje eksterne. N pemn M-are,
gjethja sht nyja interne fmijt e s cils jan t gjith eksternal.
Kjo sht terminologjia themelore, ndrsa n vazhdim do t shqyrtohen
definicionet formale, reprezentimet dhe aplikacionet, n renditje rritse t
prgjithsimit.
Definicion 6.1. Pema binare sht ose nyje eksterne ose nyje interne e lidhur me
nj ift t pemve binare, t cilat quhen nnpema e majt dhe npema e djatht e
asaj nyjeje.
Ky definicion e bn t qart se vet pema binare sht nj koncept abstrakt
matematik. Kur punojm me reprezentimin kompjuterik, jemi duke punuar me
vetm nj realizim konkret t ktij abstraksioni. Situata nuk sht e ndryshme
nga reprezentimi i numrave real, me float, numrave t plot me int, etj. Kur
e vizatojm pemn me nj nyje n rrnj, t lidhur me deg pr n nnpemn e
majt n t majt dhe nnpemn e djatht n t djatht, jemi duke zgjedhur nj
reprezentim t prshtatshm. Ka shum mnyra t ndryshme pr t reprezentuar
pemt binare, t cilat fillimisht mund t duken befasuese.
Reprezentimi konkret q prdoret m s shpeshti kur implementohen programet
t cilat prdoren pr manipulim t pemve binare, sht struktura me dy lidhje
(t majtn dhe t djathtn, q jan pointer) pr nyjet interne (shih figurn 5.21).
Kjo struktur sht e ngjashme me at t lists s lidhur. Lidhjet Null i
prgjigjen nyjeve eksterne. N mnyr specifike, n repezentimin standard t
nyjes, kemi:
struct node { Item item; node *l, *r; };
typedef node *link;
308
309
Avni Rexhepi
Definicion 6.2. Nj pem M-are sht ose nyje eksterne ose nyje interne e lidhur
me nj sekuenc t renditur t M pemv, t cilat poashtu jan pem M-are.
Normalisht, nyjet e pems M-are reprezentohen si struktur me M lidhje t
emrtuara (si n pemn binare, pointer) ose si vargje me M lidhje. Prdorimi i
vargjeve pr t mbajtur lidhjet sht i duhur sepse vlera e M sht fikse, edhe
pse, si do t shohim, duhet kushtuar kujdes prdorimit t teprt t hapsirs kur
prdoret reprezentimi i till.
Definicion 6.3. Pema e renditur sht nyja (e quajtur rrnj) e lidhur me nj
sekuenc t pemve t ndara. Sekuenca e till, quhet pyll.
Dallimi ndrmjet pems s renditur dhe pems M-are sht se nyjet e pems s
renditur kan numr t fardoshm t fmijve, ndrsa n pemn M-are, duhet
t ket saktsisht M fmij. Ndonjher prdoret termi pem e prgjithshme, pr
t dalluar pemn e renditur nga pema M-are.
Pasi q nyjet e pems s renditur mund t ken fardo numri t lidhjeve, sht e
natyrshme t shqyrtohet prdorimi i lists s lidhur, prpara vargjeve, pr t
mbajtur lidhjet pr fmijt e nyjes. Figura 6.6 sht nj shembull i reprezentimit
t till. Prej ktij shembulli, sht e qart se secila nyje pastaj prmban dy
lidhje, nj pr listn e lidhur pr ta lidhur at me moshatart (vllezrit/motrat)
e vet dhe tjetra pr listn e lidhur t fmijve t saj.
Avni Rexhepi
Secila pem sht graf, mirpo cilat grafe jan pem? Konsiderojm se grafi pr
t qen pem duhet ti prmbush cilindo prej kushteve vijuese:
Avni Rexhepi
eksterne si fmij. Nse nyja eksterne e zgjedhur sht n nivelin k, gjatsia e
shtegut intern sht rritur pr k, por gjatsia e shtegut ekstern sht rritur pr
k+2 (nj nyje eksterne n nivelin k sht larguar, por jan shtuar dy n nivelin
k+1). Procesi fillon me pemn me gjatsi interne dhe eksterne t shtegut t
barabarta me zero dhe secili prej N hapave, e rrit gjatsin e shtegut ekstern pr
2 m shum sesa gjatsia e shtegut intern.
Vetia 6.5. Lartsia e pems binare me N nyje interne sht m s paku lg N dhe
m s shumti N-1.
Rasti m i keq sht pema e degjeneruar me vetm nj gjete, me N-1 lidhje prej
rrnjs deri tek gjethja (shih figurn 6.7). rasti m i mir sht pema e balansuar
me 2i nyje interne n secilin nivel i prveq nivelit t fundit (si n Fig. 6.7).
Nse lartsia sht h, ather duhet t kemi:
2h
< N + 1 2h ,
m shum se [lg N]. Duke shumzuar kto dhe duke aplikuar vetin 5.7, fitojm
kufirin (N+1)[lgN]2N<Nlg(N/4).
Si do t shihet, pemt binare paraqiten gjersisht n aplikacionet kompjuterike
dhe performansa sht m e mira kur pema binare sht trsisht e balansuar
(ose afrsisht ashtu). Pr shembull, pemt q prdoren pr t prshkruar
algoritmet praj-e-sundo, siq jan krkimi binar dhe meregesort jan trsisht
t balansuara. Do t shqyrtohen edhe strukturat eksplicite t t dhnave t cilat
jan t bazuara n pemt e balansuara.
Kto tipare themelore t pemve sigurojn informatn q nevojitet pr t
zhvilluar algoritme efikase pr nj numr t problemeve praktike. Analiza m e
detajuar e disa algoritmeve specifike krkon analiz t sofistikuar matematike,
edhe pse mund t marrim prafrime t dobishme me argumente t drejtprdrejta
induktive si ato q u prdorn n kt pjes. Tiparet tjera matematike t pemve
do t diskutohen sipas nevojs.
Prshkimi i pems
Para se t shqyrtohen algoritmet t cilat konstruktojn pemt dhe pemt binare,
do t shqyrtojm algoritmet pr funksionin themelor t procesimit t pemve,
at t prshkimit t pems (kalimit me radh npr antart e pems - angl. tree
traversal). Me pointerin e dhn pr pemn, dshirojm t procesojm seciln
nyje n pem n mnyr sistematike. N listn e lidhur, lvizim prej nyjes n
nyje duke prcjellur nj lidhje (pr listn e lidhur njfish). Pr pemt, sidoqoft,
duhet t marrim nj vendim, sepse mund t ket lidhje t shumfishta, pr tu
prcjellur.
Ka shum veprime q mund t kryhen n strukturn e pems. Nj i zakonshm
do t ishte kryerja e nj operacioni t caktuar P n seclin element t pems.
Ather, nnkuptohet q P duhet t kaloj npr seciln nyje t pems, prmes
prshkimit t pems. Nse e konsiderojm operacionin si proces i njshm
sekuencial, ather nyjet individuale vizitohen n nj renditje t caktuar dhe
mund t konsiderohen sikur t ishin shtrir n renditje lineare. N fakt,
prshkrimi i shum algoritmeve lehtsohet shum nse flasimpr procesimin e
elementit t ardhshm n pem bazuar n renditjen e caktuar. Jan tri renditje
parimore t cilat dalin natyrshm nga struktura e pems. Sikur vet struktura e
pems, ato jan t prshtatshme q t prshkruhen n terma rekurziv. Duke ju
referuar figurs n vijim, n t ciln R sht rrnja, kurse A dhe B paraqesin
nndegn (nnpemn) e majt dhe t djatht, tri renditjet e mundshme jan:
315
Avni Rexhepi
1. Preorder: R, A, B (vizito rrnjn, para nndegve)
2. Inorder: A, R, B
3. Postorder: A, B, R (vizito rrnjn, pas nndegve)
Preorder
Postorder
Inorder
316
1. Preorder: * + a / b c - d * e f
2. Inorder: a + b / c * d - e * f
3. Postorder: a b c / + d e f * - *
+
/
a
b
d
c
*
e
Avni Rexhepi
binare, si u pa edhe m hert, kemi dy lidhje dhe prandaj kemi tri renditje
themelore n t cilat mund t vizitojm nyjet:
318
319
Avni Rexhepi
E
F
C
F
C
E
H
B
A
F
C
F
C
B
A
F
C
E
H
B
G
F
A
D
B
F
C
F
C
E
D
F
C
B
G
F
C
H
F
H
F
B
A
D
B
H
F
E
H
F
C
B
A
E
H
F
C
E
D
B
B
A
H
F
321
Avni Rexhepi
if (h->l != 0) s.push(h->l);
}
}
Degt Null nuk i shtyjm n stek. Figura 6.13 apraqet prmbajtjen e stekut
gjersa prdorim seciln prej metodave t prshkimit t pems, pr pemn e
shembullit nga figura 6.12. Prmes induksionit mund t vrtetojm se kjo
metod prodhon t njjtin rezultat sikur ajo rekurzive, pr fardo peme binare.
322
323
Avni Rexhepi
E
D
F
C
E
D
B
A
F
C
E
D
B
A
F
C
E
D
B
A
F
C
E
D
B
A
F
C
E
D
B
A
F
C
E
D
B
A
F
C
E
D
B
A
H
F
325
Avni Rexhepi
Programi 6.4. Llogaritja e parametrave t pems.
//count=numri,numro; link=lidhja,linku; height=lartesia;
int count(link h)
{
if (h == 0) return 0;
return count(h->l) + count(h->r) + 1;
}
int height(link h)
{
if (h == 0) return -1;
int u = height(h->l), v = height(h->r);
if (u > v) return u+1; else return v+1;
}
326
Avni Rexhepi
Figura 5.30 sht shembull i nj strukture eksplicite t pems t ndrtuar prmes
programit 5.19. Ndrtimi i strukturs rekurzive t t dhnave si kjo ndoshta
sht m e preferuar se gjetja e maksimumit duke skenuar t dhnat, si sht
vepruar n programin 5.6, sepse struktura e pems na jep fleksibilitietin pr t
kryer operacione t tjera. Vet operacioni q prdoret pr t krijuar turneun
sht nj shembull i rndsishm: nse jan dhn dy turne, mund ti
kombinojm ato pr t krijuar nj turne t vetm n koh konstante, duke krijuar
nj nyje t re dhe duke br q lidhja e saj e majt (pointeri) t pointoj nj
njrin prej turneve dhe lidhja e saj e djatht (pointeri) t pointroj n tjetrin dhe
duke marr elementin m t madh prej ty dyve (q ndodhet n rrnjt e dy
turneve t dhna) si elementin m t madh n turneun e kombinuar. Gjithashtu
mund t shqyrtojm algoritmet pr shtimin e elementeve, largimin e elementeve
dhe pr kryerjen e operacioneve t tjera.
328
Avni Rexhepi
vlersuar shprehjet e reprezentuara prmes pems me nj prshkim postorder.
Ose, mund t prdorim pemn pr t shtypur shprehjen n infix, prmes
prshkimit inorder ose n postfix me nj prshkim postorder.
Do t shqyrtojm disa shembuj pr t prezentuar konceptin me t cilin mund t
krijohet dhe procesohet strukturat e pems s lidhura n mnyr eksplicite me
an t programeve rekurzive. Pr t br kt n mnyr efektive, duhet t
shqyrtojm performansat e algoritmeve t ndryshme, reprezentimeve alternative,
alternativave jo-rekurzive dhe shum detaje t tjera, t cilat do t bhen tek
algoritmet pr krkim dhe pr hash tabelat.
Programi 6.7. Konstruktimi i pems s analizs
//parse=analizo
char *a; int i; struct node
{ Item item; node *l, *r;
node(Item x)
{ item = x; l = 0; r = 0; }
};
typedef node* link;
link parse()
{ char t = a[i++]; link x = new node(t);
if ((t == '+') || (t == '*'))
{ x->l = parse(); x->r = parse(); }
return x;
}
Prshkimi i grafit
Si shembull t programit rekurziv, shqyrtojm nj prej alroritmeve m t
rndsishme rekurzive: prshkimin rekurziv t grafit ose krkimin thellsia-spari (angl. depth-first search). Ky funksion pr vizitimin sistematik t t gjitha
nyjeve n graf, sht prgjithsim direkt i funksioneve pr prshkimin e pemve
dhe shrben si baz pr shum algoritme t tjera themelore pr procesimin e
grafeve. sht nj algoritm rekurziv i thjesht.
Duke filluar nga cilado nyje v:
330
Vizito v.
(Rekurzivisht) vizito seciln nyje (t pavizituar) t lidhur me v.
Nse grafi sht i lidhur, n fund arrihen t gjitha nyjet. Programi 6.8 sht nj
implementim i ksaj procedure rekurzive.
Pr shembull, supozojm se prdorim reprezentimin e grafit prmes lists s
fqinjsis. Figura 6.18 paraqet thirrjet rekurzive t bra gjat krkimit depthfirst t ktij grafi dhe sekuenca n t majt n figurn 6.19 paraqet mnyrn n
t ciln prcillen degt e grafit. Prcjellim seciln deg n graf, me nj prej dy
rezultateve t mundshme: nse dega na drgon n nj nyje t ciln veq e kemi
vizituar, e injorojm at; nse na drgon tek nj nyje t ciln akoma nuk e kemi
vizituar, e prcjelli at atje prmes thirrjes rekurzive. Bashksia e t gjitha
degve t cilat i prcjellim n kt mnyr formon pemn e shtrirjes pr grafin
(angl. graph spanning tree).
331
Avni Rexhepi
333
Avni Rexhepi
Pr t vizituar t gjitha nyjet e lidhura me nyjen k n graf, i shnojm ato si t
vizituara dhe pastaj (rekurzivisht) i vizitojm t gjitha nyjet e pavizituara n
listn e fqinjsis s nyjes k.
Pasi q poashtu pr t ndrtuar listn e fqinjsis nga nj sekuenc hyrse e
nyjeve merr koh proporcionale me V+E, krkimi thellsia-s-pari na jep nj
zgjidhje t kohs lineare pr problemin e konektivitetit (lidhjes s nj pike me
pikat tjera). Pr grafet shum t mdhaja, sidoqoft, m t preferuara do t ishin
zgjidhjet me union, sepse reprezentimi i tr grafit krkon hapsir
proporcionale me E (numrin e degve), gjersa zgjidhja me union, merr hapsir
proporcionale vetm me V (numrin e nyjeve).
Ashtu si vepruam me prshkimin e pems, mund t definojm funksionin pr
prshkimin e grafit i cili prdor stekun eksplicit, si sht ilustruar n figurn
6.21.
Mund t mendojm pr nj stek abstrakt i cili mban hyrje t dyfishta: nyjen
dhe pointerin n listn e fqinjsis s nyjes. Me stekun e inicializuar n nyjen
fillestare dhe pointerin e inicializuar pr n nyjen e par n listn e fqinjsis s
asaj nyjeje, algoritmi thellsia-s-pari sht ekuivalent me hyrjen n unaz, ku
vizitojm nyjen n krye (top) t stekut (nse nuk ka qen i vizituar); ruajm
nyjen e referencuar nga pointeri i lists aktuale t fqinjsis; azhurojm
referencn e lists s fqinjsis n nyjen e ardhshme (duke trhequr (pop) at
vler nse sht n fund t lists s fqinjsis); dhe duke shtyr (push) n stek
vlern pr nyjen e ruajtur, duke ju referuar nyjes s par n listn e saj t
fqinjsis.
334
Avni Rexhepi
6.21 ilustron se t dy kto metoda jan ekuivalente pr krkimin thellsia-s-pari
pr grafin e shembullit dhe kjo ekuivalenc vlen n prgjithsi.
Algoritmi vizito kreun (top) dhe shtyej n stek t gjith fqinjt sht nj
formulim i thjesht i krkimit thellsia-s-pari, mirpo nga figura 6.21 sht e
qart se vuan nga disavantazhi i mundsis s lnies n stek t kopjeve t
shumfishta t secils nyje. Ky bn kshtu edhe nse testojm nse sht
vizituar secila nyje q sht duke u prgatitur pr t shkuar n stek dhe
prmbahemi nga vendosja e nyjes n stek, nse ajo ka qen e vizituar. Pr t
evituar kt problem, mund t prdorim implementimin e stekut q pamundson
duplikatet, duke prdorur rregulln harro-elementet-e-vjetra, sepse kopja m e
afrt me kreun e stekut sht gjithmon e para q vizitohet, kshtu q t tjerat
thjesht trhiqen (pop).
Dinamikat e stekut pr depth-first search q jan ilustruar n figurn 6.21
varen nga ajo q nyjet n seciln list t fqinjsis prfundojn n stek n
renditjen e njjt n t ciln paraqiten n list. Pr t fituar kt renditje pr
listn e dhn t fqinjsis n rastet kur shtyhet (push) nga nj list n koh, do
t duhej t shtyejm n stek s pari nyjen e fundit, pastaj at t parafundit, e
kshtu me radh. Pr m tepr, pr t limituar madhsin e stekut n numrin e
nyjeve, gjersa n t njjtn koh vizitohen nyjet n renditjen e njjt si n
depth-first search, duhet t prdorim disciplinn e stekut me rregulln harroelementin-e-vjetr. Nse vizitimi i nyjeve n renditje t njjt sikur n rastin e
krkimit thellsia-s-pari (depth-first search) nuk sht me rndsi pr ne, mund
t evitojm t dy kto komplikime dhe t formulojm direkt metodn
jorekurzive t prshkimit t grafit t bazuar n stek. Me stekun e inicializuar n
nyjen fillestare, hyjm n unaz ku vizitojm nyjen n krye t stekut (top), pastaj
vazhdojm npr listn e saj t fqinjsis, duke shtyer (push) n stek seciln
nyje (nse nyja nuk ka qen e vizituar), duke prdorur implementimin e stekut
q pamundson duplikatet me rregulln injoro-elementin-e-ri. Ky algoritm
viziton t gjitha nyjet n graf, n mnyrn e ngjashme me depth-first search,
por nuk sht rekurziv.
336
Avni Rexhepi
me radh. Duke pamundsuar duplikatet me nj rregull injoro-elementin-e-ri
(djathtas), fitojm rezultatin e njjt, pa vlera t teprta n queue.
T dy mnyrat e prshkimit t grafit, gjersia-s-pari dhe thellsia-s-pari i
vizitojn t gjitha nyjet n graf, por mnyra se si e bjn kt sht dramatikisht
e ndryshme. Krkimi gjersia s pari prmblidhet n rastin e nj morie t
krkuesve q shprndahen pr t mbuluar territorin, gjersa krkimi thellsia-spari i korrespondon nj krkuesi t vetm, i cili gjurmon territorin e panjohur sa
me thell q t jet e mundur, duke u kthyer prapa vetm kur arrin n rrug pa
dalje. Kto paradigma themelor t zgjidhjes s problemeve jan me rndsi n
shum sfera t shkencave kompjuterike, prtej krkimit t grafeve.
338
Pema binare
Pema, n t ciln secila deg n vazhdim ndahet n dy deg t tjera apo thn
ndryshe, do nyje (prind) ka dy nyje pasardhse (fmij), e paraqet pemn
binare (angl. Binary tree).
Binare, nnkupton po at q vlen pr sistemin binar t numrave, ku kemi vetm
dy shifra: zero dhe nj. Edhe n pemn binare, secili krcell ka m s shumti
dy deg, por ka raste kur ndonjri ka vetm nj deg ose thjesht mbaron, duke
rezultuar me prfundim t degzimit n at deg, si n figurn 6.24.
Avni Rexhepi
Pikat ku ka ndrprerje ose mbaresa dhe pastaj degzime quhet Nyje (angl.
Node). N pemn binare ka tri lloje t mbaresave (Fig. 6.25): nyja fillestare,
nyja fundore (ose nyja prfundimtare) dhe nyja deg. Nyja fillestare quhet
Rrnj (angl. root node nyje rrnj) dhe ndodhet n nivelin m t lart t
pems. Nse prdoret terminologjia e trungu familjar, quhet Prind. Degt nga
rrnja drgojn n nyjet e degve. Nyja deg sht bigzim (degzim n
dyshe) n rrugn q lidh rrnjn me dy deg t tjera. Secila deg prfundon m
nyje fundore ose t quajtura edhe nyje fmij, nyje gjethe, etj. Degt e dala nga
nyja, quhen dega e majt dhe dega e djatht.
Figura 6.25: Pema binare prbhet prej disa nyjeve, secila e ndrlidhur me
nyjet tejra t pems.
Pra, mund t shihet, se pema binare definon lidhje t fort prind-fmij,
ndrmjet nyjeve. Relacioni prind-fmij sht relativ, varsisht prej nivelit ku
ndodhemi. T gjitha nyjet, prveq nyjes rrnj, kan nyje prind. Mirpo, disa
nyje nuk kan fmij, gjersa disa kan nj ose dy fmij. Relacioni prind-fmij
n programim prcaktohet duke zgjedhur nj nyje, e cila quhet nyje aktuale
(angl. current node). Nyja e cila e ka lindur nyjen aktuale, quhet nyje prind e
nyjes aktuale. Nyja ose nyjet e dala nga nyja aktuale quhen nyje fmij.
340
341
Avni Rexhepi
Le t thmi se pema binare ka pes nivele, q do t thot se thellsia sht 5.
Ather, madhsia e pems llogaritet si:
madhsia 2 5
Pra, madhsia sht vlera e prafrt, sepse pema binare mund t mos jet e
balansuar. Pem e balansuar sht pema binare n t ciln secila nyje ka dy
fmij. Pema binare e pabalansuar sht ajo pem ku nj ose m shum nyje
kan m pak se dy fmij. Formula jep iden e vrazhd se sa sht e balansuar
pema binare. Zakonisht pema binare prdoret pr bashksi shum t mdha t t
dhnave.
342
Figura 6.27 - Nyja e majt sht gjithmon m e vogl sesa nyja prind dhe nyja
e djatht sht gjithmon m e madhe sesa nyja prind.
Modeli i njjt aplikohet n seciln nyje fmij. Prandaj, ID 101 sht fmij i
majt i nyjs q prmban ID 102. Ngashm, ID 105 sht nyje e djatht e ID-s
104 sepse sht m madhe (pr nga vlera).
Le t themi se krkohet t lokalizohet ID 101 n pemn binare. S pari, vlera e
krkuar krahasohet m nyjen rrnj. Nuk ka prputhje (angl. match), prandaj ajo
nuk sht vlera e krkuar. Pasi ID 101 sht m e vogl se ID 103 (e rrnjs),
krahasimi i ardhshm prdor nyjen e majt. Kjo do t eliminoj nevojn e
krahasimit me t gjitha nyjet n ann e djatht t rrnjs (q prmban ID 103).
Pra, mund t injorohen gjysma e ID-ve sepse e dijm se ID 101 nuk ndodhet n
nyjn e djatht ose n fmijt e saj.
Pas krahasimit t ID 101 me ID 102, vrehen dy gjra. S pari, ato nuk
prputhen. S dyti, ID 102 sht m e madhe sesa ID 101. Kjo do t thot q n
vazhdim ID 101 karahasohet me nyjn fmij t majt. Injorohet nyja e djatht
dhe t gjitha nyjet pasuese t saj, sepse ato do t jen m t mdha se ID 101.
Nuk kt rast, nuk ka fmij t djatht t nyjs me ID 102. Krahasimi i
ardhshm do t rezultoj me prshtatje, q do t thot se u gjet vlera e krkuar.
Kshtu, n nj pem t madhe binare, secili krahasim eliminon nga krkimi
gjysmn tjetr t mbetur t nyjeve. Nse do t kishim 1 milion nyje n pem,
ather do t kishim pjestim me dy (ndarje n dy, prgjysme), pr afr 20 her,
pr t zvogluar numrin deri n nj nyje (pasi q 220 sht prafrsisht nj
milion). N kt mnyr, mund t gjeni nyjen e krkuar duke br afr 20
krahasime.
Programert i shohin t gjitha nyjet si nyje rrnj dhe t gjitha nyjet n vazhdim
si nnpem e tyre. Aplikohet kjo mnyr e qasjes, sepse funksionet t cilat
punojn me pem jan rekurzive. Funksioni punon me nyjn fmij dhe kryen
343
Avni Rexhepi
funksionalitetin e njjt sikur ajo nyje fmij t ishte nyje rrnj e nj peme t
tr. Kjo do t thot q, vlera e nyjes fmij krahasohet me vlern e nyjes s saj
t majt dhe t djatht, pr t prcaktuar se npr ciln deg t pems t
vazhdoj tutje.
Vlera els
Secila nyje e pems prmban nj els (angl. key) q i shoqrohet vlers n nj
relacion t ngjashm me at ndrmjet elsit primar n bazn e t dhnave dhe
rreshtit n tabel t bazs s t dhnave. elsi sht vlera q krahasohet n
kriteret e krkimit. Nse indeksi dhe kriteri i krkimit prshtaten (prputhen),
ather aplikacioni nxjerr t dhnat e rreshtit t cilat i prgjigjen atij elsi. E
dhna i referohet vlers s nyjs, si n figurn 6.28.
Figura 6.28 - Secila nyje ka nj indeks (els) dhe vlern: indeksi identifikon
nyjen n mnyr unike dhe nxjerr vlern e nyjes.
Si els mund t prdoret fardo tipi i t dhnave. N shembujt n kt pjes do
t prdoret stringu, edhe pse mund t zgjedhet cilido tip i t dhnave. Pr dallim
nga elsi primar i bazs s t dhnave, elsi i pems nuk sht e nevojshme t
jet n renditje natyrale. D.m.th., elsi nuk duhet t jet n renditje alfabetike
ose numerike. N nj implementim tipik t pems, definohet nj krahasues pr
ti treguar pems se si ti rendit nyjet. N rastin ton, do t prdorim nj
sekuenc natyrale renditse pr stringje, ashtu q t mund t mbajm fokusin n
punn me pem.
344
345
Avni Rexhepi
{
struct Metadata(char* key, char* value)
{
strcpy(this->key, key);
strcpy(this->value, value);
left = NULL;
right = NULL;
}
char key[SIZE_KEY];
char value[SIZE_VALUE];
struct Metadata* left;
struct Metadata* right;
} METADATA;
//shtoNyje()
//merrNyje()
//largoTeGjithaNyjet()
//procesoNyjetMeRadhe()
//merrThellesineePemes()
//permbaneNyjen()
//largoNyje()
//largoNyjenRrenje()
//levizNyjenMeTeMajte()
346
347
Avni Rexhepi
Avni Rexhepi
nyjen e re (ne node). Thirrja e par prcjell argumentin e par si rrnj e
pems. Secila thirrje pasuese prcjell rrnjn e nnpems (nndegs). Rikujtoni
q secila nyje e pems mund t konsiderohet si rrnj pr t gjitha nyjet nn t.
Rregullat e njjta aplikohen n seciln nyje t gjitha nyjet n ann e majt jan
m t vogla dhe t gjitha nyjet n t djatht jan m t mdhaja.
Nse elsi i nyjes s re sht i barabart me nyjen ekzistuese, ather nyja e re
fshihet (largohet) dhe funksioni addNode() kthen vlern Bool-eane false. Kjo
pr arsye se t gjith elsat duhet t jen unik: n pem nuk lejohen elsat
duplikat.
bool addNode(METADATA** current_node, METADATA* new_node)
{
if(*current_node == NULL)
{
*current_node = new_node;
size++;
return true;
}
else
{
if(strcmp(new_node->key, (*current_node)->key) < 0)
{
return addNode(&((*current_node)->left), new_node);
}
else
if(strcmp(new_node->key, (*current_node)->key)> 0)
{
return addNode(&((*current_node)->right), new_node);
}
else
{
delete new_node;
return false;
}
}
}
350
Avni Rexhepi
deri sa t gjindet prshtatja, me rast thirret funksioni removeRootNode(0 dhe i
prcillet referenca pr n nyjen e prshtatur (prputhur).
bool removeNode(METADATA** node, char* key)
{
if(*node != NULL)
{
if (strcmp(key, (*node)->key) == 0)
{
removeRootNode(node);
size--;
return true;
}
else if(strcmp(key, (*node)->key) < 0)
{
return removeNode(&((*node)->left), key);
}
else
{
return removeNode(&((*node)->right), key);
}
}
else
{
return false;
}
}
353
Avni Rexhepi
}
else
{
moveLeftMostNode(&((*root)->right), *root);
}
}
354
Avni Rexhepi
funksionet rekurzive, duhet t definohet pika e ndalimit. N kt rast, nse jeni
n nyje gjethe, pointeri i majt dhe i djatht do t jen NULL dhe thirrjet e
funksionit removeAllNodes() do t kthenin (return), (ato nuk do t vazhdonin
rekurzionin), sepse nyja do t jet NULL.
N ekran do t paraqitet porosia q tregon elsin dhe vlern e nyjes q sht
duke u larguar nga pema. Pastaj prdoret operatori delete pr t larguar nyjen.
void removeAllNodes(METADATA* node)
{
if(node != NULL)
{
removeAllNodes(node->left);
removeAllNodes(node->right);
cout<<"Largohet nyjaelesi (key): "<<node->key<<"\t"
<< node->value << endl;
delete node;
}
}
357
Avni Rexhepi
value[0] = '\0';
return false;
}
else
{
if(strcmp(key, node->key) == 0)
{
strcpy(value, node->value);
return true;
}
else if(strcmp(key, node->key) < 0)
{
return getNode(node->left, key, value);
}
else
{
return getNode(node->right, key, value);
}
}
}
359
Avni Rexhepi
}
else if(strcmp(key, node->key) < 0)
{
return containsNode(node->left, key);
}
else
{
return containsNode(node->right, key);
}
}
}
361
Avni Rexhepi
Funksioni getTreeDepth() sht paraqitur n vazhdim dhe kryen t gjitha
llogaritjet pr t prcaktuar numrin total t niveleve n pem. Funksioni
getTreeDepth() krkon nj argument, i cili sht referenc n nyjen rrnj. Kjo
duhet t jet nyja e par n pem, edhe pse mund t prdoret cilado nyje. Nse
prdorni nyje tjetr, funksioni llgoarit nivelet prej asaj nyjeje deri tek fundi i
pems. Nivelet para ksaj nyjeje nuk mirren n konsiderim n llogaritje.
Procesi fillon me kontrollimin nse nyja sht e zbrazt. Nse sht, ather
nyja rrnj sht NULL dhe kthehet vlera zero. Nse rrnja nuk sht NULL,
ather funksioni getTreeDepth() shkon teposht npr secilin nivel t pems
duke thirrur vetveten n mnyr rekurzive. Kur arrihet n nyje gjethe, fitohet
parametri NULL. Kjo nuk do t thot q pema sht e zbrazt, mirpo vetm se
keni arritur n nyje gjethe. Pastaj, thirrjet rekurzive kthejn (me return), duke
inkrementuar vlern numruese npr secilin rekurzion, pr t mbledhur nivelet.
Seciln her q thirret funksioni getTreeDepth(), atij i prcillen nyja fmij e
majt dhe nyja fmij e djatht dhe funksioni kthen nj numr t plot (integer) i
cili reprezenton nivelin, i cili sht i shoqruar ose me variabln depth_left
(thellesia majtas) ose me variabln depth_right (thellsia djathtas).
Variablat depth_left dhe depth_right krahasohen. Nse vlera e variabls
depth_left sht m e madhe se ajo e variabls depth_right, variabla depth_left
inkrementohet dhe kthehet nga ana e funksionit getTreeDepth(); prndryshe,
inkrementohet dhe kthehet variabla depth_right.
int getTreeDepth(METADATA* node)
{
int depth_left;
int depth_right;
if(node == NULL)
{
return 0;
}
else
{
depth_left = getTreeDepth(node->left);
depth_right = getTreeDepth(node->right);
if(depth_left > depth_right)
{
return depth_left + 1;
}
else
{
return depth_right + 1;
}
}
}
362
363
Avni Rexhepi
{
cout <<"Shtimi i nyjeselesi/key: "<<key<<"vlera: "<<value
<< endl;
tree->add(key, value);
}
else
{
cout << "elesi duplikat i gjeneruar: " << key << endl;
}
}
cout << "\nPershkimi *In order* i pemes:" << endl;
tree->displayInOrder();
cout<<"\nThellesia e pemes para largimit te nyjeve: "
<<tree->getDepth()
<< endl;
cout << "Madhesia e pemes para largimit te nyjeve: "
<< tree->getSize()
<< endl;
cout << "\nLeximi i nje vlere nga pema:" << endl;
if(tree->get("123", value))
{
cout << "Vlera: " << value << endl;
}
cout << "\nLargimi i nje nyjeje nga pema: " << endl;
if(tree->contains("123"))
{
tree->remove("123");
}
cout << "\nPershkimi *In order*i pemes: " << endl;
tree->displayInOrder();
cout << "\nThellesia e pemes pas largimit te nyjeve: "
<< tree->getDepth()
<< endl;
cout << " Madhesia e pemes pas largimit te nyjeve: "
<< tree->getSize()
<< endl;
cout << "\nAsgjesimi i pemes:" << endl;
delete tree;
system(Pause);
return 0;
}
i
i
i
i
365
Avni Rexhepi
elesi: 345 vlera: Beni
elesi: 999 vlera: Suzi
Figura 6.30 - Nyja e majt fmij sht larguar nga pema: pema akoma ka
thellsin 2 nivele.
366
Suzi
Beni
367
Avni Rexhepi
Rrnja, (nyja rrnj, angl. Root node): nyja e par (m e lart) e pems.
sht si nyje kryesore e pems, sepse t gjitha nyjet tjera arrihen prej
rrnjs. Gjithashtu, rrnja nuk ka prind. sht nyja n t ciln zakonisht
fillojn operacionet n pem.
Nyjet interne (nyjet e brendshme, angl. Internal nodes): kto nyje kan
prind (rrnja nuk sht nyje e brenshme) dhe s paku nj fmij.
Nyjet gjethe (angl. Leaf (gjethe), leaves (gjethet)): kto nyje kan prind,
por nuk kan fmij (jan nyjet fundore).
Operacionet
Operacionet bazike (t cilat do t sqarohen n detaje m vone), jan t
ndrlidhura me prshkimin e pems (bredhjen npr pem, kalimin me radh
npr nyje). Jan t definuara tri mnyra standarde t prshkimit t pems:
368
369
Avni Rexhepi
Shtimi i nj vlere t re
Krkimi pr ndoj vler
Largimi (fshirja) e vlers
Marrja me radh e vlerave nga pema binare e krkimit
370
Nyjet gjethe kan lidhje pr tek fmijt, por ato nuk kan fmij. N gjuh
programuese kjo do t thot se lidhjet prkatse jan t prcaktuara n NULL.
Pjes kodi
sht e zakonshme q tr struktura e pems binare pr krkim t vendoset n
dy klasa. Klasa kryesore BinarySearchTree sht interfejsi publik dhe BSTNode
sht mjet prdorim privat prmbrenda klass main. Kjo ndarje sht e
nevojshme, sepse disa operacione, si largimi/fshirja, mund t rezultojn n nj
pem t zbrazt, q do t thot se pema nuk ka as nyje rrnj fare.
class BSTNode {
private:
int value;
BSTNode* left;
BSTNode* right;
public:
BSTNode(int value) {
this->value = value;
left = NULL;
right = NULL;
}
};
class BinarySearchTree {
private:
BSTNode* root;
public:
BinarySearchTree() {
root = NULL;
}
};
371
Avni Rexhepi
Krkimi pr vend
N kt faz algoritmi duhet t prcjell tiparin e krkimit t pems binare. Nse
vlera e re sht m e vogl sesa vlera e nyjes aktuale, shko n nnpemn e
majt, prndryshe shko n nnpemn e djatht. Duke prcjellur kt rregull t
thjesht, algoritmi arrin n nyjen e cila nuk ka as nnpem t majt as t
djatht. N momentin kur t gjindet vendi pr insertim, mund t themi me siguri,
se vlera e re nuk ka duplikat n pem.
Fillimisht, nyja e re nuk ka fmij, kshtu q ajo sht gjethe. N figurn
vijuese, rratht ngjyr hiri tregojn pozitat e mundshme pr nyjen e re.
Shembull
372
373
Avni Rexhepi
Pjes kodi
Dallimi i vetm ndrmjet algoritmit t msiprm dhe rutins reale sht se s
pari duhet t verifikohet a ekziston rrnja. Nse jo, vetm krijoni rrnjn dhe
mos e ekzekutoni algoritmin e zakonshm pr kt rast special. Kjo mund t
bhet n klasn BinarySearchTree. Algoritmi themelor implementohet n klasn
BSTNode.
bool BinarySearchTree::add(int value)
{
if (root == NULL)
{
root = new BSTNode(value);
return true;
}
else
return root->add(value);
}
bool BSTNode::add(int value)
{
if (value == this->value)
return false;
else if (value < this->value)
{
if (left == NULL)
{
left = new BSTNode(value);
return true;
}
else
return left->add(value);
}
374
Algoritmi i krkimit
Le t shohim prshkrimin e detajuar t algoritmit t krkimit. Si n rastin e
operacionit t insertimit dhe pothuajse n do operacion n pemn binare,
algoritmi i krkimit prdor rekurzionin. Duke filluar nga rrnja:
1. verifiko, nse vlera n nyjen aktuale dhe vlera e krkuar jan t
barabarta. Nse po, athere vlera u gjet. Prndryshe,
2. nse vlera e krkuar sht m e vogl sesa vlera e nyjes:
o nse nyja aktuale nuk ka fmij t majt, vlera e krkuar nuk
ekziston n pemn binare t krkimit;
o prndryshe, trajtoje fmijn e majt me t njjtin algoritm.
3. nse vlera e krkuar sht m e madhe sesa vlera e nyjes aktuale:
o nse nyja aktuale nuk ka fmij t djatht, vlera e krkuar nuk
ekziston n pemn binare t krkimit;
o prnrdryshe, trajtoje fmin e djatht me t njjtin algoritm.
Shembull
Krkimi i vlers 3 n pemn e paraqitur m lart:
375
Avni Rexhepi
Pjes kodi
Si n rastin e operacionit t shtimit/insertimit, s pari vrtetoni a ekziston rrnja.
Nse jo, pema sht e zbrazt dhe rrjedhimisht, vlera e krkuar nuk ekziston n
pem. Ky verifikim mund t bhet n klasn BinarySearchTree. Algoritmi
themelor implementohet n klasn BSTNode.
bool BinarySearchTree::search(int value) {
if (root == NULL)
return false;
376
Algoritmi i largimit/fshirjes
Faza e par sht identike me at tek algoritmi pr krkim, prveq faktit se duhet
prcjellur prindin e nyjes aktuale. Pjesa e dyt sht m e ngatrruar. Ekzistojn
tri raste, si n vijim.
1. Nyja q duhet larguar nuk ka fmij.
Ky rast sht krejt i thjesht. Algoritmi e vendos lidhjen prkatse t prindit n
NULL dhe e hedh nyjen.
Shembull. Largimi/fshirja e -4 nga pema binare.
377
Avni Rexhepi
378
prmbajn vlerat e njjta {5, 19, 21, 25}. Pr ta transformuar pemn e par n t
dytn, mund t veproni si n vijim:
379
Avni Rexhepi
o
o
o
Vreni se nyja me vlern minimale nuk ka fmij t majt dhe prandaj largimi i
saj mund t rezultoj vetm n rastin e par ose t dyt.
Shembull. Largoni 12 nga nga pema binare.
380
381
Avni Rexhepi
Pjes kodi
S pari, verifikoni a ekziston rrnja. Nse jo, ather pema sht e zbrazt dhe
prandaj vlera q duhet larguar nuk ekziston n pem. Pastaj, verifikoni nse
vlera e rrnjs sht ajo q duhet larguar. Ky sht rast special dhe prandaj ka
disa qasje pr ta zgjidhur. Ktu do t shohim metodn e nyjs fallse (imitimit t
nyjes) (dummy root method), ku krijohet nj nyje false dhe rrnja e vret
varet/lidhet n t si fmij i majt. Kur t bhet largimi/fshirja, vendosni lidhjen
e rrnjs n lidhjen n fmijn e majt t nyjes fallse.
N gjuht programuese t cilat nuk kan mbledhje automatike t mbeturinave
(automatic garbage collection), si p.sh., C++, nyja e larguar duhet t
hudhet/shkatrrohet (angl. dispose; sht fjala pr largimin nga memoria). Pr
kt arsye, metoda remove e largimit n klasn BSTNode duhet t kthej jo
vler boolean-e, por lidhjen pr n nyjen e shkatrruar dhe t duhet t liroj
memorien n klasn BinarySearchTree.
bool BinarySearchTree::remove(int value)
{
if (root == NULL)
return false;
else
{
if (root->getValue() == value)
{
BSTNode auxRoot(0);
auxRoot.setLeftChild(root);
BSTNode* removedNode = root->remove(value, &auxRoot);
root = auxRoot.getLeft();
if (removedNode != NULL)
{
delete removedNode;
return true;
} else
return false;
} else {
BSTNode* removedNode = root->remove(value, NULL);
if (removedNode != NULL)
{
delete removedNode;
return true;
} else
return false;
}
}
}
BSTNode* BSTNode::remove(int value, BSTNode *parent)
382
383
Avni Rexhepi
1. merrni me radh vlerat nga nndega e majt
2. merrni me radh vlerat nga nndega e djatht
3. rezultati pr nyjen aktuale sht: (result for left subtree) join (current
node's value) join (result for right subtree) (angl. join-lidh, bashko,
bashkangjit).
Ekzekutimi i ktij algoritmi n mnyr rekurzive, duke filluar nga rrnja, do t
jep rezultatin pr tr pemn. Le t shohim nj shembull t ktij algoritmi.
Shembull
384
385
Avni Rexhepi
Pirgu binar
Ekzistojn disa tipe t pirgjeve (angl. Heap pirg, stiv, grumbull). N ket
pjes do t diskutohet pirgu binar. N vazhdim shkurtimisht do ta quajm vetm
pirg. Ky pirg prdoret pr t implementuar radhn me prioritet Priority
Queue (angl. Queue radh, rend i pritjes) dhe algoritmin e sortimit
Heapsort. Pirgu sht pem binare komplete, e cila i prgjigjet tiparit t pirgut
(vlerat e mdha/vogla lart).
Tipari Heap
Kemi dy lloje t mundshme t pirgut binar: pirgu max dhe pirgu min. Dallimi
sht n at se rrnja e pirgut minimal prmban vlern minimale dhe
anasjelltas. Pirgu me prioritet zakonisht ka t bj me pirgun minimal, ndrsa
algoritmi heapsort, gjat sortimit sipas rendit rrits, prdor pirgun max.
387
Avni Rexhepi
388
Right(i) = 2 * i + 2
Parent(i) = (i - 1) / 2
389
Avni Rexhepi
}
int getRightChildIndex(int nodeIndex) {
return 2 * nodeIndex + 2;
}
int getParentIndex(int nodeIndex) {
return (nodeIndex - 1) / 2;
}
public:
BinaryMinHeap(int size) {
data = new int[size];
heapSize = 0;
arraySize = size;
}
int getMinimum() {
if (isEmpty())
throw string("Heap is empty");
else
return data[0];
}
boolean isEmpty() {
return (heapSize == 0);
}
~BinaryMinHeap() {
delete[] data;
}
};
Algoritmi i insertimit
Algoritmi i prgjithshm pr insertimin e elementit t ri n pirg, sht si vijon:
390
Shembull
Inserto -2 n pirgun n vijim:
Avni Rexhepi
392
Vazhdo me shoshitje:
393
Avni Rexhepi
Pirgu fillestar
Analiza e kompleksitetit
Kompleksiteti i insertimit sht i rendit O(h), ku h sht lartsia e pirgut (angl.,
h-height). Duke pasur parasysh pemn komplete, O(h)=O(log n), ku n sht
numri i elementeve n pirg.
Pjes kodi:
void BinaryMinHeap::siftUp(int nodeIndex)
{
int parentIndex, tmp;
if (nodeIndex != 0) {
parentIndex = getParentIndex(nodeIndex);
if (data[parentIndex] > data[nodeIndex])
{
tmp = data[parentIndex];
data[parentIndex] = data[nodeIndex];
data[nodeIndex] = tmp;
siftUp(parentIndex);
}
}
}
void BinaryMinHeap::insert(int value)
{
if (heapSize == arraySize)
throw string("Hapesira e pirgut eshte tejmbushur ");
else
{
heapSize++;
data[heapSize - 1] = value;
394
Algoritmi i largimit
1. Kopjo vlern nga pozita e fundit t vargut n pozitn e rrnjs;
2. Zvoglo madhsin e pirgut pr 1;
3. Shoshit teposht vlern e rrnjs, si vijon:
o nse nyja aktuale nuk ka fmij, shoshitja ka prfunduar;
o ns nyja aktuale ka nj fmij: verifiko, nse sht prishur
rregulla e pirgut, ather shkmbe vlern e nyjes aktuale m
vlern e fmijs; shoshit teposht fmijn;
o nse nyja aktuale ka dy fmij: gjeje m t voglin prej tyre. Nse
rregulla e pirgut sht thyer, ather shkmbe vlern e nyjes
aktuale me at t fmijs s zgjedhur; shoshit teposht fmijn.
Shembull
Largo minimumin nga pirgu vijues:
395
Avni Rexhepi
396
397
Avni Rexhepi
Analiza e kompleksitetit
Kompleksiteti i operacionit t largimit sht i rendit O(h) = O(log n), ku h sht
lartsia e pirgut, n sht numri i elementeve n pirg.
398
399
Avni Rexhepi
Pemt e balansuara
Insertimi i vazhdueshm i vlerave n pemn binare, mund t drgoj n
insertimin e vlerave t njpasnjshme n njrn an t pems, n rastet kur
insertohen vlera n renditje rritse ose zvogluese (q nuk sht fenomen i
pazakont). N kto raste lartsia e pems sht e paprshtatshme dhe krijon
rastin m t keq t performanss s pems pr insertime. Kjo do t krijonte pm
binare t pabalansuar. Shtrohet pyetja a ka mundsi q t dizajnohet pema binare
e cila e ka t garantuar q ka lartsin O(log h), pavarsisht prej rendit t
insertimeve dhe fshirjeve. Ekzistojn teknika t cilat prmirsojn renditjen e
vlerave n pem, duke br rireshtimin dhe ripozicionimin e nyjeve t tyre, me
qllim t krijimit t pems s balansuar. Shembulli i par dhe njherit m i
thjesht i pems s balansuar, q ka rendin logaritmik pr insertim, largim dhe
krkim, sht pema AVL (AVL Tree), (e emrtuar kshtu sipas zbuluesve t saj:
Adelson-Velskii dhe Landis).
Pemt AVL
Pemt AVL jan pem me lartsi t balansuar n t dy ant. Idea sht q n
seciln nyje duhet t prcjellim informacionin e balasimit, i cili tregon
diferencn n lartsi ndrmjet nndegs s majt dhe t djatht. N pemn
binare t balansuar perfekt (pemn komplete), t dy fmijt e cilsdo nyje kan
lartsi t barabarta. Mirpo, mirmbajtja e pems komplete sht e ndrlikuar,
sepse edhe nj insertim i vetm mund t shkaktoj rregullime t mdha n
strukturn e pems. Mirpo, nuk sht e domosdoshme t bhet shum
prmirsim n kto raste. N vend se t krkohet q t dy fmijt t ken lartsi
saktsisht t barabart, krkohet vetm q diferenca mes tyre t jet m s
shumti pr nj. Pema rezultuese quhet pem AVL.
Pra, pema sht e balansuar, nse dhe vetm nse pr seciln nyje, lartsia e dy
nnpemve t saj, dallon m s shumti pr 1.
Pemt AVL mirmbajn invariantn vijuese:
Kushti i balansimit AVL: pr seciln nyje t pems, lartsit e nndegs s
majt dhe t djatht dallojn pr m s shumti 1. (Lartsia e nndegve null
sht e definuar q t jet -1, sipas marrveshjes).
N mnyr q t mirmbahet kushti i balansit, mund t shtohet nj fush e re,
balansi n seciln nyje, e cila ruan diferencn e lartsis s nndegs (nnpems)
s majt dhe asaj t djatht.
N pemn AVL, ky numr do t jet gjithmon -1, 0 ose +1 (prandaj, mund t
ruhet duke prdorur vetm 2 bita pr seciln nyje). N vend t ruajtjes s fushs
s balansit pr seciln nyje, n shembullin n vijim do t prdoret nj metod m
e thjesht, e cila do t ruaj lartsin e nndegve. Para diskutimit t mirmbajtjes
400
1 5
N (h)
401
Avni Rexhepi
Rasti LL Rotacioni djathtas
(Left Left Case Right Rotation)
Rasti LR
(Left Right Case)
Rasti RL
(Right Left Case)
402
Insertimi
Rregulla e insertimit pr pemt AVL fillon saktsisht si regulla e insertimit pr
pemt binare t krkimit, por pas insertimit t nyjes n nnpem, duhet t pyetet
nse pema sht br e pabalansuar. Nse sht br, ather duhet t kryhet
hapi i ribalansimit.
Vet ribalansimi sht operacion i pastr lokal (kjo do t thot, nse nevojitet
koh konstante dhe veprime n nyjet e afrta), por krkon kujdes. Operacioni
bazik q kryhet, quhet rotacion ose rotacion i njfisht (angl. single rotation).
Tipi i rotacionit varet nga natyra e ekuilibrit (jobalansimit). Le t supozojm se
burim i jobalansit sht fakti se nnpema e majt e fmijs s majt sht shum
e thell (Edhe rasti i nnpems s djatht trajtohet simetrikisht). Operacioni i
kryer n kt rast, si n figurn vijuese, sht rotacion i njfisht i djatht.
Vreni se si ndryshojn faktort e balansit pasi q sht kryer ky rotacion.
Lartsit e nnpemve b dhe d tani jan t barabarta mes veti.
403
Avni Rexhepi
405
Avni Rexhepi
407
Avni Rexhepi
408
409
Avni Rexhepi
411
Avni Rexhepi
1. Nse t gjitha n! permutacione e n elsave ndodhin me probabilitet t
barabart, sa sht lartsia e pritur e pems s balansuar t konstruktuar?
2. Sa sht probabiliteti q nj insertim krkon ribalansim?
Analiza matematike e ktij algoritmi sht e komplikuar. Testet empirike
prkrahin supozimin se lartsia e pritur e pems s balansuar t gjeneruar sht
h=log(n)+c, ku c sht konstante e vogl (c0.25). Kjo nnkupton q n
praktik, pema e balansuar AVL sillet aq mir sa pema perfekt e balansuar, edhe
pse sht shum m e thjesht pr tu mirmbajtur. Poashtu, evidenca empirike
sygjeron q mesatarisht, rebalansimi sht i nevojshm n prafrsisht do dy
insertime. Rotacioni i njfisht dhe i dyfisht jan me gjasa t barabarta.
Shembulli i prezentuar sht zgjedhur me qllim q t demonstrohen sa m
shum rotacione t mundshme n numrin minimal t insertimeve.
Kompleksiteti i operacioneve t balansimit sygjeron q pemt e balansuara
duhet t prdoren vetm nse leximet jan shum m t shpeshta se insertimet.
Kjo sht posaqrisht e vrtet pasi q nyjet e pemve t tilla zakonisht
implementohen n struktura sa m dendur q t jet e mundur, pr t
ekonimizuar hapsirn. Shpejtsia e qasjes dhe e azhurimit t faktorve t
balansimit ku secila krkon vetm dy bita rrjedhimisht sht shpesh faktori
vendimtar i efikasitetit t operacioneve t ribalansimit. Vlersimet empirike
tregojn se pemt e balansuara humbin shum atraktivitetin e tyr nse sht i
domosdoshm paketimi i ngjeshur i t dhnave. Vret sht e vshtir q t
mundet algoritmi i thjesht dhe i drejtprdrejt i insertimit.
Fshirja n pemn e balansuar
Prvoja ka treguar se fshirja n rastin e pems s balansuar do t jet m e
komplikaur se insertimi, megjithse operacionet e rebalansimit mbesin n
esenc t njjta, si pr rastin e insertimit. N veanti, ribalansimi konsiston
prsri n rotacion t njfisht ose t dyfisht.
Baz pr fshirje n pemn e balansuar mbetet algoritmi i fshirjes. Rastet e lehta
jan nyjeve fundore (gjethet) dhe nyjet me vetm nj pasardhs (fmij). Nse
nyja q duhet t fshihet ka dy nnpem, prsri ajo do t zvendsohet me nyjen
m t djatht t nnpems s saj t majt. Sikur n rastin e insertimit, shtohet nj
parametr bool-ean me kuptimin lartsia e nnpems sht zvogluar.
Ribalansimi duhet t konsiderohet vetm nse vlera e tij sht true.
Operimi i procedurs sht ilustruar n figurn vijuese (Fig. 6.41). Me pemn e
dhne t balansuar (a), fshirja sukcesive (e njpasnjshme) e nyjeve 4, 8, 6, 5, 2,
1 dhe 7, rezulton n pemt (b)...(h). Fshirja e nyjes 4 sht e thjesht vetvetiu,
sepse ajo paraqet nyje terminale (gjethe). Mirpo, kjo rezulton n nyjen e
pabalansuar 3. Operacioni i saj i ribalansimit prfshin nj rotacion t njfisht
LL. Ribalansimi bhet prsri i nevojshm pas fshirjes s nyjes 6. Kt her,
412
Avni Rexhepi
Edhe fshirja e nj elementi n pemn e balansuar mund t kryhet me O(log n)
operacione, n rastin m t keq. Megjithat, nuk duhet t mos vihet re nj dallim
esencial ndrmjet insertimit dhe fshirjes. Gjersa insertimi i nj elsi mund t
rezultoj n m s shumti nj rotacion (t dy ose tri nyjeve), fshirja mund t
krkoj rotacion n seciln nyje prgjat shtegut t krkimit. Pr shembull,
fshirja e nyjes m t djatht t Pems-Fibonacci krkon numrin maksimal t
rotacioneve, sepse fshirja e secils nyje drgon n zvoglimin e lartsis s
pems. Prandaj, kjo reprezenton zgjidhjen m t keqe t nyjes n rastin m t
keq t pems s balansuar, q sht nj kombinim i pafat i gjasave.
Sa sht probabiliteti i rotacioneve n prgjithsi? Rezultatet befasuese t
testeve empirike tregojn se ndrsa nj rotacion krkohet pr afrsisht do dy
insertime, nj rotacion krkohet pr vetm do t pestn fshirje. Fshirja n
pemn e balansuar pra sht prafrsisht poaq e leht ose poaq e komplikuar sa
insertimit.
Implementimi i zakonshm i AVL pemve nuk sht jashtzakonisht kompleks,
por problemi sht q nuk sht efikas. Pas pemve AVL, jan zbuluar metoda
m t mira t balansimit t pemve, kshtu q implementimi i pemve AVL nuk
ia vlen.
Pema AVL wshtw e balansuar nw mwnyrw strikte, nw mesin e pemwve tw
balansuara, gjw qw dwrgon nw insertim dhe largim mw tw ngadalshwm, por nw
lexim tw shpejtw. Kjo i bwn atraktive pwr pwrdorin nw strukturat e tw dhwnave
tw cilat mund tw ndwrtohen njw herw dhe tw ruhen pa rikonstruktime, si p.sh.,
fjalorwt e gjuhwve (ose fjalorwt e programeve, si opkodet e asemblerit ose
interpreterit).
Pemt kuq e zi
Pema kuq e zi sht struktur e t dhnave e cila sht e tipit pem binare vetbalansuese. Vet-balansimi sigurohet prmes ngjyrosjes s nyjeve me njrn
prej dy ngjyrave (zakonisht kuq dhe zi), n mnyr q rezultati i pems s
vizatuar/shtypur plotson disa tipare t cilat nuk e lejojn pemn q t bhet e
shum e pabalansuar. Kur t modifikohet pema, pema e re riaranzhohet dhe
ringjyroset pr t rivendosur tiparet e ngjyrave. Tiparet jan t dizajnuara ashtu
q riaranzhimi dhe ringjyrosja t mund t kryhen n mnyr efikase.
Balansimi i pems nuk sht perfekt, por sht mjaftueshm i mir pr t
garantuar krkimin n koh O(log n), ku n sht numri i nyjeve n pem.
Insertimi dhe fshirja, prgjat riaranzhimit dhe ringjyrosjes s pems, poashtu
kryhen n koh O(log n).
414
Avni Rexhepi
Sikur pemt binare t krkimit, edhe pemt kuq-e-zi mundsojn prshkimin
efikas in-order t elementeve t tyre (pra: majtas-rrnja-djathtas). Koha e
krkimit rezulton prej prshkimit prej rrnjs n gjethe deh prandaj pema e
balansuar me n nyje, q ka lartsin m t vogl t mundshme, rezulton n
koh t krkimit O(log n).
416
Rotacionet
Rotacioni sht operacion lokal, i cili ruan renditjen e elsave pr prshkimin
in-order.
Avni Rexhepi
Vreni q n t dy rastet, renditja in-order rezulton n: A x B y C.
Kodi i operacionit t rrotullimit majtas (angl. left_rotate) mund t jet si n
vijim:
//parent=prindi, left=majt-as, right=djatht-as
//Tree=pema, node=nyja, rotate=rotullo/rotacion
left_rotate( Tree T, node x )
{
node y;
y = x->right;
/* Shnderro nn-pemn e majt t y,
n nn-pem t djatht t x-it */
x->right = y->left;
if ( y->left != NULL )
y->left->parent = x;
/* prindi i ri i y-it ishte prindi i x-it */
y->parent = x->parent;
/* Cakto prindin q t pointoj n y n vend se n x */
/* Shiko s pari nse ndodhemi n rrnj */
if ( x->parent == NULL ) T->root = y;
else
if ( x == (x->parent)->left )
/* x ishte n t majt t prindit t vet */
x->parent->left = y;
else
/* x duhet t ket qen n t djatht */
x->parent->right = y;
/* N fund, vendose x-in n t majt t y-it */
y->left = x;
x->parent = y;
}
Insertimi
Insertimi sht pak m kompleks dhe prfshin nj numr t rasteve. Fillohet me
insertimin e nyjes s re x n pem, sikur pr do pem t zakonshme binare,
duke prdorur funksionin pr insertim. Nyja e re etiketohet e kuqe dhe me gjas
e prisht tiparin kuq-e-zi. Unaza kryesore lviz prjpet pems, pr t
rivendosur tiparin kuq-e-zi.
//parent=prindi,
//rb_insert=(insertimi_kuq-e-zi)
//red=kuq, black=zi
rb_insert( Tree T, node x )
{
/* Inserto n pem n mnyrn e zakonshme */
tree_insert( T, x );
418
Nga kodi mund t shihet se kemi vetm nj unaz. N at unaz, nyja n rrnj
t nnpems pr t ciln tentohet t restaurohet (rivendoset) tipari kuq-e-zi,
mund t lvizet te lart (prpjet), s paku nj nivel n secilin iteracion t
unazs. Pasi q pema ka lartsi O(log n), ka O(log n) iteracione. Funksioni
tree_insert ka poashtu kompleksitet O(log n), kshtu q n prgjithsi, funksioni
rb_insert ka poashtu kompleksitetin O(log n).
Shembull:
Ja nj shembull i insertimit n pemwn kuq-e-zi: (Cormen, p269).
419
Avni Rexhepi
Pema
fillestare
..
N vazhdim, gjethet (sentinel)
sdo t paraqiten, pr thjeshtsi t
figurave.
420
Algoritmet paralele
421
Avni Rexhepi
Algoritmet paralele pr konstruktimin e pemve kuq-e-zi prej listave t sortuara
t elementeve mund t ekzekutohen n koh konstante O(log log n), varsisht
prej modelit t kompjuterit, nse numri i procesorve sht proporcional me
numrin e elementeve. Jan t njohur edhe algoritmet paralele pr krkim,
insertim dhe fshirje t shpejt paralele.
422
Splay Trees
Pema Splay (angl. Splay Tree, angl. splay-i gjer, i anuar, i pjerrt, i hapur
nga jasht, etj) sht pem binare vet-rregulluese (vet-prshtatse) me nj
tipar plotsues q elementet e qasura s fundi jan t shpejt pr qasje t
srishme. Kjo pem i kryen operacionet themelore t insertimit, krkimit dhe
fshirjes n koh O(long n) t amortizuar. Pr shum sekuenca t operacioneve jo
t rastsishme, splay tree performon m mir se cilado pem tjetr e krkimit,
edhe kur mostra specifike e sekuencs sht e panjohur. Pema splay sht
zbuluar nga Daniel Dominic Sleator dhe Robert Endre Tarjan, n vitin 1985.
T gjitha operacionet normale n pemn e krkimit binar jan t kombinuara me
nj operacion themelor, t quajtur splaying (angl. splaying-zgjerim, hapje nga
jasht). Splay-imi i pems pr nj element t caktuar rirregullon pemn ashtu q
elementi vendoset n rrnj t pems. Nj mnyr pr t br kt sht q s
pari t kryhet krkimi standard i pems binare pr elementin n fjal, e pastaj t
prdoren rotacionet e pems n nj model specifik, pr ta sjellur at element n
rrnj. N form alternative, algoritmi top-don (nga lart-teposht) mund t
kombinoj krkimin dhe rioarganizimin e pems n nj faz t vetme.
Rikujtojm se n diskutimin pr pemt binare, t cilat kan vetin e mir q nse
vlerat insertohen dhe fshihen me rastsi, ather koht e pritshme pr insertim
dhe fshirje jan t rendit O(log n). Pasi q skenaret e rastit m t keq mund t
drgonin n sjellje O(n), ishim t udhhequr kah idea e pems s balansuar pr
kah lartsia, pema AVL, e cila garantonte koh O(log n) pr t gjitha
operacionet, sepe mirmbante balansin e pems gjat tr kohs. Operacionet
themelore t cilat mirmbanin balansin e pems quheshin rotacione (fardo q
ishin, t njfishta ose t dyfishta).
E met e pemve AVL ishte se duhet t mbahej informacioni pr balansin npr
nyje dhe funksionet e azhurimit t pems AVL ishin mjaft t komplikuara (m t
komplikuara sesa q dshirohet zakonisht).
Pema splay poashtu prdor rotacionet pr t prshtatur pemn. Mirpo, pasi
q pema splay nuk ka informacion t balansit, sht e mundur t krijohen
pem t pabalansuara splay. Splay pemt kan nj natyr interesante t vetrregullimit n vetvet. N veanti, kurdo q pema bhet e pabalansuar, qasja n
pjest e pabalansuara t pems do t tentoj natyrshm t balansoj vetveten.
Kjo sht vrtet gj shum e menqur, kur kemi parasysh faktin se pema nuk ka
ide nse sht e balansuar apo jo. Prandaj, si nj pem binare e pabalansuar,
sht e mundur q nj operacion i vetm i qasjes t krkoj koh t rendit O(n)
(e jo si O(log n), si do t dshironim).
423
Avni Rexhepi
Sidoqoft, pemt splay kan nj veti t mir t njohur si kufiri i performanss
s amortizuar t splay pemve: duke filluar nga nj pem e zbrazt, koha totale
e nevojshme pr t performuar sekuencn e m operacioneve t
insertimit/fshirjes/gjetjes n pemn e zgjeruar (splay) sht O(m log n), ku n
sht numri maksimal i nyjeve n pem.
Prandaj, edhe pse cilido operacion mund t jet mjaft i kushtueshm, prgjat
cilsdo sekuence t operacioneve duhet t jet nj numr i madh i operacioneve
efikase, pr t balansuar disa me kosto t lart. Me fjal t tjera, prgjat
sekuencs s m operacioneve, kostoja mesatare e nj operacioni sht O(log n).
Prparsit
Performansa e mir e pems splay varet nga fakti q ajo sht vetoptimizuese, n at q nyjet e qasura m shpesh do t lvizin m afr rrnjs,
prej ku mund t qasen m shpejt. Duke pasur nyjet e prdorura m shpesh afr
rrnjs sht prparsi pr pothuajse t gjitha aplikacionet praktike (pr faktin e
lokalitetit t referencs) dhe sht posaqrisht i rndsishm pr implementimin
e algoritmeve t keshimit (angl. cache, cacheing) dhe t grumbullimit t
mbeturinave (angl. garbage collection). Prparsit jan:
-
T metat
E meta kryesore e pemve splay sht se lartsia e pems splay mund t jet
lineare. Pr shembull, ky do t ishte rasti pas qasjes s t gjitha n-elementeve n
renditje jo-zbritse. Pasi q lartsia e pems i korrespondon kohs s qasjes pr
rastin m t keq, kjo do t thot q kostoja aktuale e nj operacioni mund t jet
e lart. Sidoqoft, kostoja e qasjes s amortizuar e ktij rasti m t keq sht
logaritmike, O(log n). Gjithashtu, kostoja e pritur e qasjes mund t redukohet n
O(log n) duke prdorur variantin me rastsi (e randomizuar).
Pema splay mund t jet m e m e keqe se pema statike pr m s shumti
nj faktor konstant.
424
Operacionet
Zgjerimi, hapja (splaying)
Kur t qaset nj nyje x, operacioni splay performohet n nyjen x, pr ta
lvizur at deri n rrnj. Pr t realizuar operacionin e zgjerimit (splay), duhet
t kryhet nj seri e hapave splay, ku secili e lviz nyjen x m afr rrnjs.
Duke performuar operacionin splay n nyjen me interes, pas secils qasje,
nyjet e qasura s fundi do t mbahen m afr rrnjs dhe pema do t mbetet
afrsisht e balansuar, ashtu q do t arrihen kufitj e amortizuar t dshiruar.
Secili hap i veant varet nga tre faktor:
-
P
C
A
B
425
Avni Rexhepi
Hapi Zig-zig (angl. Zig-zig Step): Ky hap kryhet kur p nuk sht rrnja dhe x e
p jan ose t dy fmij t djatht ose t dy t jan fmij t majt. Figura vijuese
paraqet rastin kur x dhe p jan t dy fmij t majt. Pema rrotullohet n degn
q lidh p-n me prindin e saj g, pastaj rrotullohet n degn q lidh x me p.
Vreni se hapat zig-zig jan e vetmja gj q i dallon pemt splay nga metoda
rrotullo n rrnj, para prezentimit t pemve splay.
x
p
A
x
B
C
A
Hapi Zig-zag (angl. Zig-zag Step): kryhet kur p nuk sht rrnj dhe x sht
fmij i djatht dhe p fmij i majt ose anasjelltas. Pema rrotullohet n degn
ndrmjet p dhe x dhe pastaj rrotullohet n degn rezultuese ndrmjet x dhe g.
g
p
D
x
A
A
B
Insertimi
Pr t insertuar nyjen x n pemn splay:
1. S pari inserto nyjn si n rastin e pems normale binare t krkimit.
2. Pastaj splay-o nyjen e sapoinsertuar x n krye t pems (n rrnj).
Fshirja
Pr t fshir nyjen x, prdoret metoda e njjt si me pemn binare t krkimit:
nse x ka dy fmij, shkmbejm vlern e tij ose me at t fmijs s tij m t
djatht t nnpems s tij t majt (paraardhsin e tij in-order) ose nyjen m t
426
427
Avni Rexhepi
2. Zig-zig
1. Zig-zag
3. Rezultati pwrfundimtar
3. Zig
Fig.6.45 Splay (3,t) sjellja e nyjes 3 n rrnj.
Pr t analizuar performansn, modelohen dy madhsi:
Kostoja reale: koha q merr splay (kjo sht proporcionale me thellsin e
nyjes q splay-ohet), dhe
Rritja n balans: niveli deri n t cilin operacioni splay prmirson balansin
e pems.
Analizat kan treguar se kostoja reale sht e lart, e pastaj pema bhet shum
m e balansuar (dhe rrjedhimisht kostot reale t mpasme bhen m t ulta). N
ann tjetr, nse pema bhet m pak e balansuar, kostoja reale do t jet e ult.
Kshtu, n cilndo prej mnyrave, prfitohet.
428
Operacionet n Splay-Tree
0
8
7
3
1
4
3
1
7
8
7
2
4
2
1
Pas insertimit t:
0, 1, 8
Pas
find(1)
Pas
find(0)
Pas
insert(1)
1
0
9
3
8
4
3
2
Pas largimit
t rrnjs
7
6
6
Pas
find(5)
3
1
8
2
8
2
Pas krkimit
t maksimumit
Pas lidhjes s
Rrnjs s re
Implementimi
N vazhdim do t paraqitet nj implementim i pemve splay, i cili i prdor
pointert pr t reprezentuar seciln nyje n pem. Ky implementim sht i
bazuar n metodn e dyt t fshirjes s pems splay. Poashtu, pr dallim prej
definicionit t msiprm, ky version nuk e bn splay-imin n rastet e krkimti
por vetm pr insertime dhe fshirje.
#include <functional>
#ifndef SPLAY_TREE
#define SPLAY_TREE
template< typename T, typename Comp = std::less< T > >
class splay_tree {
private:
Comp comp;
unsigned long p_size;
struct node {
node *left, *right;
node *parent;
T key;
429
Avni Rexhepi
node( const T& init = T( ) ) : left( 0 ), right( 0 ), parent(0),
key( init ) { }
} *root;
void left_rotate( node *x ) {
node *y = x->right;
x->right = y->left;
if( y->left ) y->left->parent = x;
y->parent = x->parent;
if( !x->parent ) root = y;
else if( x == x->parent->left ) x->parent->left = y;
else x->parent->right = y;
y->left = x;
x->parent = y;
}
void right_rotate( node *x ) {
node *y = x->left;
x->left = y->right;
if( y->right ) y->right->parent = x;
y->parent = x->parent;
if( !x->parent ) root = y;
else if( x == x->parent->left ) x->parent->left = y;
else x->parent->right = y;
y->right = x;
x->parent = y;
}
void splay( node *x ) {
while( x->parent ) {
if( !x->parent->parent ) {
if( x->parent->left == x ) right_rotate( x->parent );
else left_rotate( x->parent );
} else if( x->parent->left == x && x->parent->parent->left == x>parent ) {
right_rotate( x->parent->parent );
right_rotate( x->parent );
} else if( x->parent->right == x && x->parent->parent->right ==
x->parent ) {
left_rotate( x->parent->parent );
left_rotate( x->parent );
} else if( x->parent->left == x && x->parent->parent->right ==
x->parent ) {
right_rotate( x->parent );
left_rotate( x->parent );
} else {
left_rotate( x->parent );
right_rotate( x->parent );
}
430
431
Avni Rexhepi
}
return 0;
}
void erase( const T &key ) {
node *z = find( key );
if( !z ) return;
splay( z );
if( !z->left ) replace( z, z->right );
else if( !z->right ) replace( z, z->left );
else {
node *y = subtree_minimum( z->right );
if( y->parent != z ) {
replace( y, y->right );
y->right = z->right;
y->right->parent = y;
}
replace( z, y );
y->left = z->left;
y->left->parent = y;
}
delete z;
p_size--;
}
const T& minimum( ) { return subtree_minimum( root )->key; }
const T& maximum( ) { return subtree_maximum( root )->key; }
bool empty( ) const { return root == 0; }
unsigned long size( ) const { return p_size; }
};
#endif // SPLAY_TREE
432
Avni Rexhepi
prsri do t dytin element t lists s re dhe e ngrisim nj nivel m lart, ku do
t kemi listn me 1/4 e elementeve t lists fillestare. Kt procedur mund ta
prsrisim [lg n] her, deri sa t ket vetm nj element n listn e nivelit m t
lart.
Avni Rexhepi
Pr t fshir nyjet, thjesht e fshijm nyjen nga secili nivel n t cilin paraqitet
dhe prshtasim pointert.
Vreni se n do koh, lista e lidhur ka strukturn e dshiruar probabilistike. Kjo
pr arsye se n t kundrtn nuk mund t shohim gjeneratorin e numrave t rastit
dhe ai nuk ka mnyr t fshij nyjet n mnyr selektive n nj nivel t caktuar
si dhe secila nyje e hedh monedhn n mnyr t pavarur prej nyjeve t tjera,
kshtu q nivelet n skip list jan t pavarur prej njri tjetrit. Kjo duket t jet
nj prej dallimve m t rndsishme ndrmjet skip listave dhe pemve, pasi q
n pem sht e vshtir t bhet dika e pavarur, pa ndikuar n fmijt e nyjes.
Pra, skip lista sht struktur probabilistike e t dhnave ku elementet mbahen t
sortuara sipas elsave. Skip lista lejon krkim, insertim dhe fshirje t shpejt t
elementeve me algoritme t thjeshta. N esenc sht list e lidhur me pointer
plotsues t till q kaprcejn nyjet e ndrmjetme. Pr ti marr disa vendime,
prdor gjeneratorin e numrave t rastit. Skip lista sht struktur praktike e t
dhnave q jep rezultate t mira ndrsa e ruan implementim t thjesht.
Krkimi n listn e till do t prdor vetm pointert (lidhjet) e nivelit m t
lart, pr t kaprcyer prtej shum elementeve n list dhe pastaj do t zbrizte
npr nivelet e mposhtme sipas nevojs.
Nj shembull i lists (me kaprcim t do t dyts vler) do t ishte si n vijim:
3
11
6
3
15
24
15
11
30
32
30
24
NIL
42
NIL
42
NIL
32
15
6
42
30
11
24
32
Header-i
Sentinel-i
436
Inicializimi
Lista e re inicializohet si vijon:
1. S pari krijohet nyja e quajtur NIL dhe elsi i saj caktohet n nj vler
m t madhe sesa elsi m i madh q do t mund t prdorej n list
(d.m.th., nse lista do t prmbaj vlerat e elsave ndrmjet 1 dhe 999,
ather 1000 do t merrej si els n NIL). Secili nivel prfundon me
NIL.
2. Niveli i lists s re sht 1.
3. T gjith pointert prpara (angl. forard pointers) t Header-it
pointojn n NIL.
Krkimi:
1. Filloni me nivelin m t lart t lists.
2. Lvizni para (djathtas) duke prcjellur pointert n nivelin e njjt,
derisa sa elsi i ardhshm t jet m i madh sesa elsi i krkuar.
3. Nse niveli aktual nuk sht m i ulti, shkoni nj nivel m posht dhe
prsritni krkimin n at nivel prej nyjes aktuale.
4. Ndaloni kur niveli sht 1 dhe elsi i ardhshm sht m i madh sesa
elsi i krkuar.
5. Nse elsi aktual sht elsi i krkuar, ktheni vlern e asaj nyjeje.
Prndryshe, ktheni informacionin pr dshtim (elsi i krkuar nuk
ekziston n list).
Funksioni pr krkim: SEARCH(list, searchKey)
1. x <- list.header
437
Avni Rexhepi
2. for i <- list.level downto 1
3. do while x.forward[i].key < searchKey
4.
do
x <- x.forward[i]
5. x <- x.forward[1]
6. if x.key = searchKey
7.
then return x.value
8.
else return failure
NIL
19
28
4
2
42
33
15
35
NIL
19
28
4
2
42
33
15
35
Krkimi pr 35
NIL
19
28
4
2
15
42
33
35
Krkimi pr 42
Insertimi/Fshirja:
-
438
NIL
19
4
2
16
5
15
28
42
33
35
Insertimi i 16
x <- list.header
for i <- list.level downto 1
do while x.forward[i].key < searchKey
do x <- x.forward[i]
update[i] <- x
x <- x.forward[1]
if x.key = searchKey
then for i <- 1 to list.level
do if update[i].forward[i] <> x
then break
update[i].forward[i] <- x.forward[i]
FREE(x)
439
Avni Rexhepi
13. while list.level > 1 and list.header.forward[list.level] =
NIL
14.
do list.level <- list.level - 1
NIL
19
28
4
2
Fshirja e 9
15
42
33
35
Prpara: level[list]=4
Pas
: level[list]=3
Funksioni: RANDOM-LEVEL()
1.
2.
3.
4.
5.
random-level()
newLevel <- 1
while RANDOM() < p
do newLevel <- newLevel + 1
return MIN(newLevel, MaxLevel)
Analiza
Analiza e skip listave sht nj shembull i analizs probabilistike. Duhet t
vrtetojm se n rastin e pritur (mesatar), koha e krkimit sht O(log n).
440
441
Avni Rexhepi
Implementimi
Nj prej elementeve joshse t skip listave sht lehtsia e tyre e implementimit.
Shumica e procedurave (funksioneve) q operojn n skip list prdorim kodin e
thjesht t njjt me at q prdoret pr operacionet n listat e lidhura. Nj
element shtes sht se duhet t prcillet niveli n t cilin ndodhemi. Mnyra n
t ciln kjo bhet n formn m efikase sht q t kemi nyje me madhsi
variabile (t ndryshueshme), ku madhsia e nyjes sht e prcaktuar me rastsi
me rastin e krijimit. Prfitojm nga rasti se C++ (edhe Java) na mundson
alokimin dinamik t vargjeve me madhsi variabile.
Objekti Skip List prbhet nga nyja e kreut (Header) dhe konstruktori e krijon
nyjen Sentinel, vlera e t cilit prcaktohen n nj vler speciale infinit (e
cila varet nga tipi i i elsit). Supozojm se konstroktorit i jepet numri maksimal
i lejuar i niveleve Ndonj impelemtim m i avansuar (m i menqur) do t
prcaktonte n mnyr adaptive numrin e duhur t niveleve).
Klasat e Skip Lists
Nyja:
//SkipListNode=NyjaeSkipListes, forward=prpara
class SkipListNode
{
Element data;
// data=vlerat e elsave
SkipListNode forward[]; // vargu i pointerve prpara
// Konstruktori (t cilit i jepet vlera dhe niveli)
SkipListNode(Element d, int level)
{
data = d
forward = new SkipListNode[level+1];
}
}
Lista:
class SkipList
{
int maxLevel;
SkipListNode header;
// niveli maksimal
// Nyja header
442
Kodi pr krkimin (gjetjen) e nyjes n skip list sht dhn n vijim. Vreni q
krkimi sht pak a shum i njjt me krkimin standard n listn e lidhur,
prveq unazs q na lviz teposht nga nj nivel n koh.
Element find(Element key)
{
SkipListNode current = header; // starto n header
// start search at max level
for (int i = maxLevel; i >= 0; i--)
{
SkipListNode next = current.forward[i];
while (next.data < key)
// krko para n nivelin i
{
current = next;
next = current.forward[i];
}
}
current = current.forward[0]; // kjo duhet t jet ajo
if (current.data == key)
return current.data;
else
return null;
}
443
Avni Rexhepi
}
444
Pemt M-are
Pema binare ka nj vler n seciln nyje dhe dy nndeg (nnpem). Ky nocion
shum leht mund t prgjithsohet n pemn M-are, e cila ka M-1 vlera pr
nyje dhe M nndeg (nnpem). M quhet shkalla e pems. Pr kt arsye, pema
binare ka shkalln 2.
Nse degt (lidhjet, pointert) prej rrnjs kah nyjet fmij (kah nndegt),
shikohen si rrug (angl. way-rrug, drejtim), ather pemt M-are zakonisht
quhen Multiway Trees (angl. Multiway Trees Pemt shumrrugshe). Pema
shumrrugshe e cila ka rendin rendin M quhet M-way Tree ose M-ary
Tree. N pemn M-are:
N fakt, nuk sht e nevojshme q secila nyje t prmbaj saktsisht (M-1) vlera
dhe t ket saktsisht M nndeg. N nj nnpem M-way, nyja mund t ket
prej 1 deri n M-1 vlera dhe numri i nndegve (jo t zbrazta) mund t jet n
rangun prej 0 (pr gjethet) deri te 1+(numri i vlerave). Prandaj, M sht vetm
kufiri i eprm n numrin e t dhnave q mund t ruhen n nyje.
Vlerat n nyje ruhen n renditje rritse, V1<V2<...<Vk (k<=M-1) dhe nnpemt
(nndegt) jan t vendosura ndrmjet vlerave fqinje, me nga nj nnpem
shtes n secilin skaj. Prandaj, me seciln vler mund t shoqrojm nnpemn
e majt dhe nnpemn e djatht, ku nnpema e djatht e Vi sht e njjt me
nnpmemn e majt t V(i+1). T gjtiha vlerat n nnpemn e majt t V1 jan
m t vogla sesa V1; t gjitha vlerat n nnpemn e Vk jan m t madhaja
sesa Vk, dhe t gjitha vlerat ndrmjet n npnpem ndrmjet V(i) dhe V(i+1)
jan m t mdhaja sesa V(i) dhe m t vogla sesa V(i+1).
Pr shembull, pema 3-are sht si vijon:
10
44
22
55
70
50
66
68
Avni Rexhepi
vetm. Pr t prshpejtuar krkimin, M maksimalizohet: lvizja prej nj nyje n
tjetrn, prfshin leximin e bllokut nga disku (qasjen n disk), q n fakt sht
operacion shum i ngadalshm n krahasim me lvizjen npr strukturn e t
dhnave t ruajtur n memorie.
Kokat lexuese t diskut jan koka lvizse, pra jan pajisje mekanike (elektromekanike). Poashtu edhe pr rrotullimin e diskut, prdoret motorri. Si element
prcaktues pr llojin e diskut shikohet shpejtsia e rrotullimeve pr minut, q sht
numri i cili tregon numrin e rrotullimeve pr minut, rpm (shkurtesa rpm - angl. rottations
per minute), p.sh., HD-xxxGB, 7200rpm).
1024 bajta
447
Avni Rexhepi
B-Pemt (B-Trees)
B-Pema (angl. B-Tree) sht nj pem M-are me dy tipare speciale:
1. sht e balansuar n mnyr perfekte: secila gjethe ndodhet n nivel t
njjt pr nga thellsia.
2. Secila nyje, prveq ndoshta rrnjs, sht s paku prgjysm e mbushur,
d.m.th., prmban M/2 ose m shum vlera (natyrisht, nuk mund t
prmbaj m shum sesa M-1 vlera). Rrnja mund t ket numr t
fardoshm t velrave (prej 1 deri n M-1).
Secila nyje n strukturn standarde prmban pointert pr n nnpem dhe
vlerat, t renditura si:
pointeri|vlera|pointeri|vlera|...|vlera|pointeri
10
66
22
44
55
68
70
10
50
22
44
448
55
66
68
70
Avni Rexhepi
nyja ndahet dhe zvendsohet me dy nyje t reja, me elementin e mesit q sillet
si prind pr dy nyjet e reja fmij.
Krkimi n B-tree
Algoritmi i krkimit (gjetjes s nj vlere/elsi) n B-tree sht i thjesht.
Fillohet me rrnjn dhe prcaktohet se cili pointer duhet t prcillet bazuar n
krahasimin ndrmjet vlers s krkuar dhe fushave t elsave n nyjen rrnj.
Prcillet pointeri i duhur pr n nyjen fmij. Analizoni fushat e elsave n
nyjen fmij dhe vazhdoni prcjelljen e pointerve t duhur deri sa t gjindet
vlera e krkuar ose t arrihet n nyjen gjethe, e cila nuk e prmban vlern e
krkuar.
Insertimi n B-tree
Kushti q t gjitha nyjet duhet t jen n nivel t njjt imponon sjelljen
karakteristike t pemve-B, q pemt-B n fakt nuk lejohet t rriten n gjethe,
por ato detyrohen t rriten n rrnj.
Kur t insertohet nj vler n B-tree, vlera insertohet drejtprdrejt n gjethe. Kjo
drgon n tri situata t zakonshme, t cilat mund t ndodhin:
1. elsi vendoset n gjethen e cila ka akoma vend t lir.
2. Gjethja n t ciln duhet t vendoset elsi sht e mbushur.
3. Rrnja e pems-B sht e mbushur.
Rasti 1: elsi vendoset n gjethen e cila ka akoma vende t lira
Ky sht rasti m i leht pr tu zgjidhur sepse vlera thjesht insertohet n
pozitn korrekte t sortuar n nyjen gjethe. (N vazhdim nuk do t paraqiten
pozitat e pointerve, por nunkuptohet q ato jan n kufijt e vlerave).
450
13
15
13
15
Nyja e re gjethe inkorporohet duke lvizur vlern e mesit n nyjen prind dhe
poashtu n prind shtohet pointeri pr n nyjen gjethe. Ku proces vazhdon te lart
pems deri sa t gjitha vlerat t kne gjetur vendin e tyre.
451
Avni Rexhepi
Insertimi i 6 n B-pemn vijuese:
12
13
15
13
15
13
15
12
12
13
15
452
10
11
12
20
30
14
15
18
19
12
20
21
23
25
28
23
25
31
33
34
35
Rezulton n:
10
11
13
14
15
18
30
19
21
28
31
33
34
35
Nyja 15 duhet t lvizet n rrnj, por ajo sht e mbushur. Kjo do t thot se
rrnja duhet t ndahet:
10
11
12
13
14
18
15
20
30
19
21
23
25
28
31
33
34
35
12
10
11
13
14
18
19
20
30
21
23
25
28
31
33
34
35
Avni Rexhepi
Rasti 1: Fshirja nga nyja gjethe
a). Nse gjethja sht s paku prgjysm e mbushur pas fshirjes s vlers s
dshiruar, vlerat tjera t mbetura q jan m t mdhaja, lvizen pr t
plotsuar zbraztirat.
P.sh., fshirja e 6 nga pema vijuese:
16
13
14
15
18
20
22
25
23
24
27
37
Rezulton n:
16
13
14
15
18
20
22
25
23
24
27
37
b). Nse gjethja sht m e vogl se gjysma pas fshirjes s vlers s dshiruar
(rasti i njohur si underflo (angl. underflo nn-rrjedha), mund t ndodhin
dy gjra:
Fshirja e nyjes 7 nga pema paraprake, rezulton n:
16
13
14
15
18
20
22
25
23
24
27
37
454
13
14
18
15
22
25
23
24
22
25
23
24
20
27
37
27
37
13
14
18
15
20
13
14
13
18
15
14
15
16
18
20
22
22
25
23
24
20
27
37
25
23
24
27
37
Avni Rexhepi
13
14
15
18
22
25
20
23
24
27
37
13
14
15
15
18
20
22
25
23
24
27
37
23
24
27
37
13
14
15
18
20
22
25
13
14
15
18
18
20
22
25
23
24
27
37
13
14
15
18
22
25
20
23
24
27
37
13
14
15
22
18
25
20
23
24
27
37
23
24
27
37
13
14
15
22
18
20
25
457
Avni Rexhepi
7. Grafet
Grafet jan struktur shum e prdorur n shkencat kompjuterike dhe
aplikacionet e ndryshme t kompjuterit. N kt rast nuk themi struktur e t
dhnave sepse grafet jan t destinuara pr t ruajtur dhe analizuar metadata
(meta t dhnat).
Metadata jan t dhna pr t dhnat ("data about data"). Termi prdoret pr
dy koncepte t ndryshme: Structural metadata jan lidhur me dizajnin dhe
specifikimin e strukturave t t dhnave dhe m sakt quhen t dhna lidhur
me kontejnert (bartsit) e t dhnave, kurse Descriptive metadata, jan
lidhur me instancat individuale t t dhnave t aplikacionit, prmbajtja e t
dhnave.
Metadata (metaprmbajtja) definohet si t dhnat q ofrojn informacion lidhur
me nj ose m shum aspekt t t dhnave, si:
nyjet
Seti tjetr i rndsishm sht seti i degve t grafit, i quajtur edge-set (angl.
edge skaj, an, teh, q ne do ta quajm deg e grafit ose lidhje, link). E sht
nnbashksi e V x V. Thn ndryshe apo m thjesht, secila deg i lidh dy
Nse seti q paraqet nj deg ka vetm nj element, kjo e paraqet nj nyje e cila rrotullohet
ose me fjal t tjera niset dhe prfundon n t njjtn nyje.
459
Avni Rexhepi
nyje, duke prfshir edhe rastin kur nyja sht e lidhur me vetveten (n t cilin
rast quhet loop (lak, unaz)).
T gjitha grafet ndahen n dy grupe t mdha: grafet e drejtuara dhe ato t
padrejtuara. Dallimi qndron n faktin se n grafin e drejtuar, degt jan t
drejtuara (orientuara) dhe t shnuara me shigjet e cila tregon kahun e
lvizjes prej nyjes n nyje. T dy llojet kan shum gjra t prbashkta, por
kan edhe elementet t cilat i dallojn. N parim pr do rast t grafit, shihet se a
sht grafi i orientuar/drejtuar apo jo, sepse zakonisht, nse grafi sht i
orientuar, secila deg ka shigjetn treguese.
Grafi i padrejtuar
Grafi i drejtuar
shtegu (i thjesht)
460
cikli (i thjesht)
Grafi i plot sht graf me nj deg ndrmjet secilit ift t nyjeve. Nse ka N
nyje, do t ket (N2-N)/2 rrug/deg n grafin komplet pa rrugt/degt
rrotulluese. Digrafi komplet sht nj digraf me nj rrug/deg q lejon
prshkimin ndrmjet secilit ift t nyjeve. Pasi q rrugt/degt e grafit lejojn
udhtimin n dy drejtime, gjersa rrugt e digrafit lejojn udhtimin vetm nj
nj drejtim, digrafi me N nyje do t ket dyfish m shum rrug/deg, N2-N.
Nngrafi (VS, ES) i grafit ose digrafit (V,E) sht nj graf i cili ka nj
nnbashksi t kulmeve (VS V) dhe degve/rrugve (Es E) t grafit t
plot.
Shtegu/Rruga ndrmjet dy nyjeve t grafit ose digrafit sht nj varg i rrugve t
cilat mund t kalohen n rresht (nj pas nj). Me fjal t tjera, rruga (shtegu)
ndrmjet nyjes A dhe nyjes B do t fillont n nyjen A dhe do t rrugtone
(udhtonte) npr nj set t rrugve deri sa t arrij n nyjen B. Formalisht,
themi se rruga prej nyjes vi deri te vj sht sekuenc e rrugve vi,vi+1, . . ., vj-1,vj
461
Avni Rexhepi
q ndodhen n graf. Ne krkojm q t gjitha nyjet prgjat rrugs t jen unike.
Rruga thuhet t ket gjatsin q paraqet numrin e rrugve t cila e prbjn
rrugn. Rruga AB, BC, CD, DE ka gjatsin 4.
Grafi ose digrafi i peshuar sht ai ku secila rrug ka nj vler, t quajtur
pesh, t shoqruar me t. N vizatimet e grafeve, pesha do t shkruhet afr
rrugs/degs. N definicionet formale, pesha do t jet nj komponent shtes n
setin e nj rruge/dege ose nj ifti t renditur (tash nj treshe). Kur punohet
me grafe me pesh, ne konsiderojm q pesha t jet nj kosto pr prshkimin
(qarkullimin) e rrugs/degs. Shtegu prgjat (npr) grafit me pesh ka nj
kosto q sht shum e peshave t secils rrug/deg prgjat shtegut. N grafin
me pesh, shtegu m i shkurtr ndrmjet dy nyjeve sht shtegu me koston m t
vogl, edhe nse ai nuk ka m s paku deg/rrug. Pr shembull, nse shtegu P1
ka pes rrug/deg me kosto totale 24 dhe shtegu P2 ka tri deg/rrug me kosto
totale 36, shtegu P1 do t konsiderohet shteg m i shkurtr sepse kostoja e tij
sht m e vogl.
Pr shembull, n rastin e grafit t rrjetit rrugor, pesha e secils rrug mund t
jet gjatsia e saj ose koha e nevojshme pr t kaluar npr t.
Grafi i peshuar
Matrica e fqinjsis
Nj matric e fqinjsis, AdjMat (nga angl. Adjacency-fqinjsi) pr grafin G=
(V, E), me |V| = N, do t ruhet si nj varg dy-dimensional me madhsi NxN.
462
Grafi
Matrica e fqinjsis
463
Avni Rexhepi
Dega (2, 5)
Dega (1, 3)
Lista e fqinjsis
Lista e fqinjsis sht nj alternativ e matrics s fqinjsis, pr reprezentimin
e grafit. Ajo krkon m pak memorie dhe n raste t veamta edhe funksionon
m mir sesa matrica e fqinjsis. Pr seciln nyje, lista e ruan nj list t
nyjeve, t cilat jan fqinje ma at aktuale. Le t shohim nj shembull.
465
Avni Rexhepi
Grafi
Lista e fqinjsis
466
467
Avni Rexhepi
}
}
bool isEdge(int i, int j)
{
if (i >= 0 && i < vertexCount && j > 0 && j < vertexCount)
return adjacencyMatrix[i][j];
else
return false;
}
~Graph()
{
for (int i = 0; i < vertexCount; i++)
delete[] adjacencyMatrix[i];
delete[] adjacencyMatrix;
}
};
468
Prshkimi thellsia-s-pari
N prshkimin thellsia-s-pari (Depth-first traversal), ne vizitojm nyjn
startuese dhe pastaj shkojm para pr t prcjellur lidhjet npr graf deri sa t
arrijm n nj rrug-qorre (rrug pa dalje, pik fundore). N nj graf t pa
drejtuar, nyja sht pik fundore nse t gjitha nyjet fqinje me t veq jan
vizituar paraprakisht. N grafin e drejtuar, nse nyja nuk ka rrug dalse,
gjithashtu kemi pik fundore (rrug pa dalje). Kur t arrijm n rrug pa dalje,
ne kthehemi prapa npr shtegun ton deri sa t gjejm nj nyje fqinje t pa
vizituar dhe pastaj vazhdojm n at drejtim t ri. Procesi do t kompletohet kur
t kthehemi prapa n nyjen fillestare/startuese dhe t gjitha nyjet fqinje me t t
jen vizituar. N ilustrimin e ktij algoritmi dhe t gjith t tjert n kt
kapitull, nse na paraqitet opcioni i zgjedhjes mes dy nyjeve, do t zgjedhim
nyjen me vler/etiket numerike ose alfabetike m t vogl. Kur t
implementohet/aplikohet ky algoritm, kjo zgjedhje do t varet nga fakti se si
jan ruajtur degt/rrugt e grafit.
Avni Rexhepi
kthehemi deri te nyja 4 dhe gjejm se nyja 9 nuk ka qen e vizituar, por prsri
kemi prnjeher rrug pa dalje. Pastaj vazhdojm kthimin prapa, deri sa t
arrijm n nyjn startuese/fillestare dhe pasi q t gjitha nyjet fqinje me t jan
vizituar, kemi prfunduar.
Algoritmi rekurziv pr rrugtimin/prshkimin thellsia-s-pari sht:
DepthFirstTraversal(G, v)
G is the graph
v is the current node
PershThellSePari(G, v)
G eshte grafi
v eshte nyja aktuale
Visit( v )
Mark( v )
for every edge vw in G do
if w is not marked then
DepthFirstTraversal(G, w)
end if
end for
Vizito( v )
Sheno( v )
for cdo dege vw ne G bj
if w nuk eshte shenuar
PershThellSePari(G, w)
end if
end for
Algoritmi
N DFS, secila nyje ka tri ngjyra t mundshme pr reprezentimin e gjendjes:
e bardh: nyja e pavizituar;
hiri: nyja sht n progress;
e zez: DFS ka prrunduar procesimin e nyjes.
470
471
Avni Rexhepi
Shno
nyjn
ngjyr hiri.
4 me
Shno
nyjn
ngjyr hiri.
472
2 me
Shno
nyjn
ngjyr hiri.
5 me
Shno
nyjn
ngjyr hiri.
3 me
473
Avni Rexhepi
474
Si mund t shihet nga shembulli, DFS nuk shkon (nuk kalon) npr t gjitha
degt. Nyjet dhe degt t cilat i ka vizituar DFS jan pema (angl. tree). Kjo
pem prmban t gjitha nyjet e grafit (nse ai sht i lidhur) dhe quhet pema e
shtrirjes s grafit (graph spanning tree). Kjo pem saktsisht i prgjigjet
thirrjeve rekurzive t DFS-it.
Nse grafi nuk sht i lidhur, DFS nuk do ti vizitoj t gjitha nyjet e tij.
Analiza e kompleksitetit
Supozojm se grafi sht i lidhur. Algoritmi DFS viziton seciln nyje t grafit
dhe verifikon seciln nyje t tij. Prandaj, kompleksiteti i DFS sht O(V + E). Si
sht prmendur m hert, nse pr reprezentimin e grafit prdoret matrica e
475
Avni Rexhepi
fqinjsist, ather t gjitha degt, fqinje me nyjen, nuk mund t gjenden n
mnyr efikase, gj q rezulton n kompleksitet t rendit O(V2).
Pjes kodi
// VertexState=GjendjaeNyjes;
// White=eBardhe; Gray=eHirit; Black=eZexe
// state=gjendja
enum VertexState { White, Gray, Black };
void Graph::DFS()
{
VertexState *state = new VertexState[vertexCount];
for (int i = 0; i < vertexCount; i++)
state[i] = White;
runDFS(0, state);
delete [] state;
}
void Graph::runDFS(int u, VertexState state[])
{
state[u] = Gray;
for (int v = 0; v < vertexCount; v++)
if (isEdge(u, v) && state[v] == White)
runDFS(v, state);
state[u] = Black;
}
Prshkimi gjersia-s-pari
N prshkimin/rrugtimin gjersia-s-pari (Breadth-first traversal) s pari
vizitojm nyjen startuese/fillstare dhe pastaj n kalimin e par vizitojm t gjitha
nyjet e lidhura drejtprdrejt me t. N kalimin e dyt, vizitojm nyjet q
ndodhen dy rrug larg nga nyja startuese. Me secilin kalim t ri, ne vizitojm
nyjet q ndodhen nj rrug (nj distanc) m tutje. Pasi q mund t ket cikle n
graf, sht e mundur q nyja t jet n dy shtigje t gjatsive t ndryshme nga
nyja startuese. Pr shkak se do ta vizitojm at nyje pr her t par prgjat
shtegut m t shkurtr nga nyja startuese, nuk do t kemi nevoj ta shqyrtojm
prsri. Prandaj, do t kemi nevoj ose t mbajm nj list t nyjeve q i kemi
vizituar ose do t duhet t prdorim nj variabl n nyje pr ta shnuar at si t
vizituar, pr t parandaluar (penguar) vizitat e shumfishta.
476
PershGjeresiaSePari(G, v)
G - Grafi
v nyja aktuale
Vizito( v )
Shno( v )
Enqueue( v ) // n Queue
Dequeue( x )
for every edge xw in G do
if w is not marked then
Visit( w )
Mark( w )
Enqueue( w )
end if
end for
end while
Vizito( w )
Shno( w )
Enqueue( w )
end if
end for
end while
Avni Rexhepi
dhe do t marrim t parn nga kto nyje. Do t duhej t vrenit se pr shkak se
nyjet shtohen n fund t radhs, asnj nyje q ndodhet dy rrug/deg larg prej
rrnjs nuk do t shqyrtohet prsri gjersa t gjitha nyjet n distanc nj
rrug/deg t jen larguar nga radha dhe t jen prpunuar (procesuar).
Si sht puna me efikasitetin e ktyre algoritmeve? Supozimi i jon sht se
puna e br gjersa vizitojm seciln nyje sht pjesa m komplekse e ktij
procesi. Kshtu, puna e br pr t vrtetuar dhe pr t par nse nj nyje fqinje
ka qen e vizituar dhe puna pr t prshkuar rrugt nuk sht e rndsishme n
kt rast. Kshtu, rendi i algoritmit sht numri i herave q nyja vizitohet. Pasi
q kemi thn se kto algoritme e vizitojn seciln nyje saktsisht nj her, pr
grafin me N nyje, procesi i vizitimit do t kryhet N her. Prandaj,
prshkimet/rrugtimet jan t rendit O(N).
478
Algoritmi Dijkstra-Prim
Algoritmi vijues pr gjetjen e PSHM u zhvillua nga Edsger Dijkstra dhe R.C.
Prim, n fund t viteve t 50-ta. Ata punuan dhe i publikuan rezultetet e tyre
pavarsisht nga njri tjetri.
Pr t gjetur PSHM, ata do t prdorin at q njihet si greedy algoritmm
lakmitar (angl. Greedy llups, i pangopur, tahmaqar, lakmitar, etj). Algoritmet
lakmitare punojn duke shikuar n nnbashksin e problemit m t madh dhe
479
Avni Rexhepi
duke br vendimin m t mir bazuar n at informacion. N kt rast, n
secilin hap t procesit, do t shikojm n nj grumbull (koleksion) t
degve/rrugve potenciale pr tu shtuar n pemn e shtrirjes dhe do t zgjedhim
at me peshn m t vogl (minimale). Duke br kt n mnyr t
vazhdueshme (n mnyr t prsritur) ne do t rrisim pemn e shtrirjes e cila ka
totalin e prgjithshm minimal.
Pr t prmbushur (realizuar) kt proces, ne do t konsiderojm se nyjet e grafit
jan n njrn prej tri kategorive: n pem, n periferi t pems dhe akoma t
pa-shqyrtuara (t pa marrura n konsiderim). Ne fillojm duke zgjedhur nj nyje
t grafit dhe duke e vendosur at n pemn e shtrirjes. Pr shkak se rezultati
sht nj pem e parrnj, zgjedhja e nyjes fillestare nuk ka ndikim n rezultatin
final (prveq nse ka PSHM t shumfishta). Pastaj ne vendosim t gjitha nyjet
t cilat jan t lidhura me kt nyje fillestare n katgorin e periferis
(fqinjsis). Kur t gjitha nyjet t jen shtuar n pem, kemi prfunduar.
Algoritmi i prgjithshm pr kt proces sht si vijon:
1. Zgjedhe nyjen startuese
2. Nderto vijat fillestare prej nyjeve te lidhura me nyjen
startuese, periferine e saj
3. While (Gjersa) ka akoma nyje t mbetura
zgjedhe degn me peshen me te vogl
shtoje nyjen e shoqruar/bashkuar/lidhur n pem
azhuro vijat (periferin) duke:
-shtuar nyjet n periferi (t lidhura) me nyjn e re
-azhuro degt me periferi ashtu q t jen m t voglat
end while
480
Figura 7.3A
Grafi origjinal
Figura 7.3B
Nyja e par u shtua (vijat e ndrprera
paraqesin rrugt/degt deri tek nyjet e
periferis (fqinjsis)
Figura 7.3C
U shtya nyja e dyt. Degt/rrugt tek
nyjet D, E dhe G u azhuruarn (Vijat e
plota paraqesin rrugt n PSHM).
Figura 7.3D
U shtua nyja e tret. Dega/rruga tek
nyja G u azhurua.
Pasi nyja B t shtohet n pem (Fig. 7.3(c)), duhet t prcaktojm nse ka nyje
q duhet t shtohet n bashksin e periferis/fqinjsis dhe gjejm se nyjet E
dhe G duhet t shtohen. Pr shkak se nyje e vetme e pems pr t ciln ato jan
t lidhura sht nyja B, ne i shtojm keto deg/rrug tek ato t cilat do ti marrim
n konsiderat n vazhdim. N kt koh, gjithashtu duhet t verifikojm pr t
par nse degt/rrugt prej nyjes A kah nyjet C, D dhe F jan akoma m t
shkurtrat ose nse ka deg/rrug m t mira nga nyja B tek kto tri nyje. N
481
Avni Rexhepi
grafin origjinal, nuk ka lidhje direkte nga nyja B tek nyjet C dhe F, kshtu q
kto nuk do t ndryshojn. Por dega/rruga nga nyja B tek nyja D ka pesh m t
vogl sesa ajo nga nyja A dhe kshtu dega/rruga BD tani e zvendson at AD.
Prej pes degve/rrugve tek nyjet n fqinjsi/periferi, shohim se BE ka peshn
m t vogl dhe kshtu ajo dhe nyja E i shtohen pems (Fig. 7.3(d)). Dega/rruga
EG ka pesh m t vogl sesa dega/rruga BG, kshtu q tani kjo prdoret. Prej
katr degve/rrugve pr n fqinjsi/periferi, shohim se AC ka pesh m t
vogl kshtu q ajo shtohet n vazhdim.
Figura 7.3E
Nyja C e shtuar n pem.
Figura 7.3F
Nyja F e shtuar n pem dhe
degt/rrugt D dhe G jan azhuruar.
482
Figura 7.3G
Vetm nj nyje ka mbetur n
fqinjsi/periferi.
Figura 7.3H
PSHM e kompletuar me rrnj n
nyjen A.
Algoritmi i Kruskal-it
Gjersa algoritmi Dijkstra-Prim filloi n nj nyje t caktuar dhe e ndrtoi PSHM
tutje (te jasht), algorimti i Kruskal-it koncentrohet m shum n degt/rrugt e
grafit.
N kt algoritm, ne fillojm me nj pem t zbrazt t shtrirjes dhe i shtojm
degt/rrugt n renditje sipas madhsis s peshs gjersa t gjitha nyjet t jen
lidhur n graf. Nse mbesim pa deg/rrug para se t gjitha nyjet t jen shtuar,
grafi origjinal nuk ka qen i lidhur dhe rezultati q kemi gjeneruar sht PSHM
e secils prej komponenteve t lidhura t grafit origjinal.
Fillojm n Fig. 7.4(a) me grafin e njjt q e prdorm pr algoritmin DijkstraPrim. N kt rast, s pari e shtojm degn/rrugn me peshn m t vogl, e cila
sht ajo ndrmjet nyjeve D dhe F, duke dhn rezultatin parcial (e pjesshm) n
Fig. 7.4(b).
Dega/rruga me pesh 2 shtohet n vazhdim (Fig. 7.4(c)) ndrmjet nyjeve A dhe
B dhe pastaj shtohet dega/rruga me pesh 3, duke dhn Fig. 7.4(d).
Degt/rrugt me peshat 4 dhe 5 shtohen n vazhdim t rezultatit ton, ashtu si
mund t shihni n Fig. 7.4(e) dhe Fig. 7.4(f). Vetm nyja G sht akoma e
palidhur. Degt/rrugt e ardhshme pr tu shqyrtuar jan ato me pesh 6.
Prej katr degve/rrugve me pesh 6, dy prjashtohen (hudhen posht) pr
shkak se ato do t krijon cikle t cilat prmbajn nyjen A dhe dega/rruga
ndrmjet nyjes B dhe nyjes D do t formonte cikl q i prfshin nyjet A dhe F.
Dy nyjet tjera jan t dyja alternativa t mira dhe varsisht prej asaj t
zgjedhurs, fitojm PSHM n cilndo prej Fig. 7.4(g) ose Fig. 7.4(h).
483
Avni Rexhepi
Figura 7.4A
Grafi origjinal.
Figura 7.4B
Shtohet dega/rruga e par.
Figura 7.4C
Shtohet dega/rruga e dyt.
Figura 7.4D
Shtohet dega/ruga e par.
Figura 7.4E
Shtohet dega/rruga e katrt.
Figura 7.4F
Shtohet dega/ruga e pest.
484
Figura 7.4G
Pema e shtrirjes minimale.
Figura 7.4H
Nj pem tjetr/alternative e shtrirjes
minimale.
Avni Rexhepi
rrnj t njjt. Detajet e rutinave (nnprogrameve) GjejeRrenjen dhe Union
do t jepen n seksionin 6.7.
Kompleksiteti i ktij algoritmi do t jet kompleksiteti i algoritmit t sortimit t
prdorur pr shkak se unaza While sht e lidhur linearisht me numrin e
degve/rrugve. Kjo e bn kompleksitetin e algoritmit t PSHM-s s Kruskal-it
O(E lg E).
486
Figura 7.5A
Figura 7.5B
Grafi i unazs.
Algoritmi i Dijkstras
Algoritmi i pemss s shtrirjes minimale nuk do t funksionoj pr gjetjen e
shtegut m t shkurtr sepse greedy algoritmi i tij merr parasysh peshn e vetm
nj dege/rruge n secilin kalim. Nse e ndryshojm algoritmin ashtu q ai
zgjedh degn/rrugn deri n fqinjsi e cila sht pjes e shtegut m t shkurtr
t trsishm q prej nyjes fillestare/startuese, ather do t fitojm rezultatin e
dshiruar. M saktsisht, algoritmi i jon tani bhet si vijon:
select a starting node
build the initial fringe from nodes connected to the starting
node
while we are not at the destination node do
choose the fringe node with the shortest path to the
starting node
add that node and its edge to the tree
update the fringe by:
adding nodes to the fringe connected to the new node
for each node in the fringe do
update its edge to the one connected to the tree
on the shortest
path to the starting node
end for
end while
Avni Rexhepi
Figura 7.6A
Grafi origjinal.
Figura 7.6B
Shtegu m i shkurtr sth deri tek
nyja B.
Figura 7.6C
Shtegu me gjatsi 4 pr tek nyja C
sht m i shkurtri prej
opcioneve.
Figura 7.6D
Shtegu me gjatsi 5 oft deri tek
nyja E ose nyja F, sht m i
shkurtri.
Figura 7.6E
Shtegu tjetr me gjatsi 5 pr
tek nyja F sht i ardhshmi
Figura 7.6F
Shtegu me gjatsi 6 pr tek nyja D sht
m i shkurtr sesa shtegu pr tek nyja
G.
N Fig. 7.6(f), duhet t jet e qart se shtegu pr tek nyja D sht m i shkurtr
sesa shtegu tek nyja G. Zgjedhja e nyjes D rezulton me Fig. 7.6(g) dhe pastaj
489
Avni Rexhepi
nyja G sht e fundit q duhet t shtohet, duke dhn pemn e shtegut minimal
final n Fig. 7.6(h). Shtegu m i shkurtr prej nyjes A deri tek nyja G ka gjatsi
10. Nse shikojm prapa n Fig. 7.3(h), do t shihni nj shembull tjetr t pems
s shtrirjes minimale e cila nuk ka shtegun m t shkurtr, sepse ajo figur ka
shtegun prej nyjes A deri tek nyja G me gjatsi 11.
Figura 7.6G
Shtegu pr tek nyja G sht
i vetmi i mbetur.
Figura 7.6H
Pema e shtegut m t shkurtr
komplet q fillon n nyjen A.
490
Topologjia
Topologjia sht nj prej degve m t reja t matematiks. Nj mnyr e
thjesht pr t prshkruar topologjin sht si nj geometri e siprfaqs s
lakueshme topologjistt i studiojn tiparet e formave t cilat mbesin t njjta
kur format trhiqen/zgjaten ose shtypen/kompresohen.
Fillet e topologjis i vendosi Leonhard Euler-i n vitin 1735 gjat puns s tij t
inspiruar nga problemi i shtat urave t Konigsberg-ut (tash i quajtur
Kaliningrad pas pushtimit ne fund t lufts s dyt botrore dhe tash pjes e
Rusis) .
N Konigsberg, lumi rrjedh n qytet n at mnyr q n qendr t tij ndodhet
nj ishull dhe pas kalimit t ishullit rrjedha e lumit ndahet n dy pjes. Pr t
kaluar prej njrs pjes n tjetrn ishin ndrtuar shtat ura. Nj hart e thjesht e
qendrs s Konigsberg-ut do t dukej si n vijim:
491
Avni Rexhepi
Problemi
2
Supozoni se do t ishte ndrtuar nj ur m pak n Konigsberg, ashtu q harta t
dukej si n vijim:
Avni Rexhepi
vizatuar me laps. Kshtu, mund t keni vetm deri n dy nyje teke. Prandaj,
sht e pamundur q t vizatohet figura e msiprme, me nj t shkruar t
lapsit, pa e ngritur nga fleta ose pa ri-kaluar n ndonjrn deg.
Prgjithsimi n teorin e grafeve
Euler-i vazhdoi me prgjithsimin e ksaj mnyre t t menduarit, duke
vendosur kshtu bazat e teoris s grafeve. Me fjalorin modern, bhen
definicionet vijuese dhe vrtetohet teorema:
Definicion: Nj rrjet sht figur e prpbr prej pikave (nyjeve) t lidhura me
deg (lakore) q nuk priten.
Definicion: Nyja quhet teke nse ka nj numr tek t degve t cilat shkojn n
t, prndyshe quhet nyje ifte.
Definicion: Shteg i Euler-it sht shtegu i vazhdueshm i cili kalon npr
seciln nyje nj dhe vetm nj her.
Teorem: Nse grafi (rrjeti) ka m shum se dy nyje teke, ai nuk ka shteg t
Euler-it.
Euler-i poashtu vrtetoi kt:
Teorem: Nse grafi (rrjeti) ka dy ose zero nyje teke, ai ka s paku nj
shteg t Euler-it. N veanti, nse rrjeti ka saktsisht dy nyje teke, ather
shtegu i tij i Euler-it mund t filloj n njrn prej tyre dhe t prfundoj
n tjetrn.
Problemet
Pr secilin prej grafeve vijuese, prcaktoni nse ka nj shteg t Euler-it. Nse ka,
gjejeni nj prej tyre.
Figura 1
494
Figura 2
Figura 3
Figura 5
Figura 4
Figura 6
Zgjidhjet:
Figura 1
Figura 2
Figura 3
Figura 4
495
Avni Rexhepi
Figura 5
Figura 6
Cikli Euler-ian
Prshkrimi i problemit: Gjeni rrugtimin m t shkurtr n grafin G, duke
kaluar seciln deg s paku nj her.
Diskutimi: Supozoni se ju sht dhn harta e nj qyteti dhe ju sht dhn
detyra e dizajnimit t itinerarit (rrugs s qarkullimit) pr kamiont e mbledhjes
s mbeturinave, pastrimit t bors ose postierit. N t gjitha kto raste, secila
rrug e qytetit duhet t kalohet s paku nj her, q t sigurohet se jan kryer
grumbullimet/dorzimet.
Pr efikasitet, krkohet t minimizohet koha totale e udhtimit ose n mnyr
ekuivalente, distanca totale ose numri i degve t prshkuara. Aplikaiconet e
ktilla jan variante t problemit t ciklit t Eulerit, i karakterizuar m s miri
prmes enigms (angl. puzzle) s fmijve, kur atyre ju krkohet t vizatojn
ndonj figur t caktuar, pa e ngritur lapsin dhe pa prsritur ndonj deg (vij,
rrug) gjat vizatimit. Pra, krkojm ciklin npr graf q viziton seciln deg
saktsisht nj her.
Jan t prcaktuara mir kushtet pr prcaktimin nse grafi prmban cikl t
Euler-it ose shteg t Euler-it:
1. Grafi i pa-drejtuar (pa-orientuar) prmban nj cikl t Euler-it nse (1)
sht i lidhur dhe (2) secila nyje e tij sht e shkalls ifte.
2. Grafi i pa-drejtuar (pa-orientuar) prmban nj shteg t Euler-it ns (1)
sht i lidhur dhe (2) t gjitha prveq dy nyjeve t tij jan t shkalls
ifte. Kto dy nyje do t jen fillimi dhe fundi i shtegut.
3. Grafi i drejtuar (orientuar) prmban cikl t Euler-it nse (1) sht i
lidhur dhe (2) secila nyje ka shkall t njejt t hyrjes sikur at t daljes.
4. S fundi, grafi i drejtuar (orientuar) prmban cikl t Euler-it nse (1)
sht i lidhur dhe (2) t gjitha, prveq dy nyjeve kan shkall t njjt t
496
497
Avni Rexhepi
8. Algoritmet e krkimit
Sistemet kompjuterike shpeshher prdoren pr t ruajtur sasi t mdha t t
dhnave, prej t cilave duhet t nxirren rekordet individuale n baz t ndonj
kriteri t krkimit, prandaj nj shtje me shum rndsi sht ruajtja efikase e
t dhnave pr mundsuar krkim t shpejt. Njri nga operacionet m t
performuara n t dhnat e ruajtura n kompjuter sht krkimi. Krkimi bhet
duke krahasuar vlern e krkuar (elsin) me antart e bashksis s
elementeve. N momentin q kushti i krahasimit tregon prputhje (angl. matchprputhje, barazim, etj), do t thot q sht gjetur vlera e krkuar. Me rndsi
sht numri i krahasimeve t bra deri n gjetjen e elementit t krkuar.
N prgjithsi, pr t gjetur nj vler n nj varg t pasortuar duhet shikuar
npr elementet e vargut, nj nga nj, deri sa t gjindet vlera e krkuar. Nse
vlera e krkuar nuk ndodhet n varg, do t kalohet npr t gjitha vlerat e vargut.
Mesatarisht, kompleksiteti i nj algoritmi t till sht proporcional me gjatsin
e vargut.
Situata ndryshon dukshm kur vargu sht i sortuar. Nse e dijm se vargu sht
i sortuar, mundsia e qasjes s rastit mund t shfrytzohet n mnyr shum
efikase pr t gjetur shum shpejt vlern e krkuar. Kostoja e algoritmit t
krkimit zvoglohet n logaritmin binar (logaritmin me baz 2) t gjatsis s
vargut. Pr referenc, log2(1,000,000) 20. Kjo do t thot se, n rastin m t
keq, algoritmi i bn 20 hapa, pr t gjetur vlern e krkuar n vargun e sortuar
prej 1 milion elementeve ose pr t treguar q ajo nuk gjendet n varg.
Pra, krkimi i nj liste t parenditur t t dhnave bhet n mnyr lineare, duke
krkuar prej fillimit t lists kah fundi i lists, deri sa t takohet elementi i
krkuar ose deri sa t arrihet n fund t lists dhe t arrihet n prfundimin se
elementi i krkuar nuk gjindet n list. Kjo mnyr e krkimit njihet si krkimi
sekuencial. Nse lista sht e sortuar dhe e ruajtur n nj struktur t
prshtatshme t t dhnaev, krkimi mund t bhet n mnyr shum m
efikase. Krkimi i lists s sortuar mundson prgjysmimin e hapsirs s
krahasimit (krkimit), n do hap. Ky krkim njihet si krkimi binar.
Krkimi sekuencial
Krkimi sekuencial, bn krahasimin e njpasnjshm t t gjith antarve t
lists, me antarin q krkohet (cakun). Pseudokodi i ktij krkimi sht si
vijon:
KerkimiSekuencial( lista, caku, N )
//lista
elementet n t cilat krkohet caku
//caku
vlera e cila krkohet
498
for i = 1 to N do
if (caku = lista[i])
return i //lokacioni-pozita ku u gjet caku
end if
end for
return 0;
499
Avni Rexhepi
Krkimi binar
Nse elementet i vendosim n nj varg dhe i sortojm n renditje rritse ose
zbritse s pari sipas elsit, ather mund t prfitojm performans shum m
t mir me algoritmin e krkimit binar.
N krkimin binar, caktohen vlerat e kurfirit t poshtm (vlera e par), kufirit
t eprm (vlera e fundit) dhe vlera e mesit. S pari krahasohet elsi me
elementin n pozitn e mesit t vargut. Nse ka prputhje, d.m.th., elementi i
krkuar sht gjetur, mund t kthejm rezultatin dhe t prfundojm krkimin
menjher. Nse elsi i krkuar sht m i vogl se elsi i vlers s mesit,
ather elementi i krkuar duhet t jet n gjysmn e poshtme t vargut.
Prndryshe, nse sht m i madh, ather elementi i krkuar duhet t jet n
gjysmn e eprme t vargut. N mnyr t njjt e prsrisim n mnyr
rekurzive procedurn n gjysmn e poshtme (e eprme) t vargut. do krahasim
largon nga krkimi i mtejm gjysmn e mbetur t vlerava, n secilin hap.
Vazhdojm duke prshtatur vlerat e kufijve t pjess s mbetur pr krkim.
KerkimiBinar(lista, caku, N )
//lista
elementet n t cilat krkohet caku
//caku
vlera e cila krkohet
//N
numri i elementeve n list
kp = 1
//kp - kufiri i poshtem, fillimi
ke = N
//ke kufiri i eprm, fundi
while kp ke do
mesi = (kp + ke) / 2
selekto (Krahaso(lista[mesi], caku)) nga
case -1: kp = mesi + 1
case 0: return mesi
case 1: ke = mesi - 1
end selekto
//perfundo selektimin
end while
return 0
kp
vk<A[mesi]
ke
mesi
~n/2 elemente
vk>A[mesi]
~n/4 elemente
kp
mesi
log2n hapa
ke
*
*
*
vk=A[mesi]
kp ke
mesi
501
Avni Rexhepi
lim
Shembuj
Shembulli 1. Gjejeni vlern 6 n {-1, 5, 6, 18, 19, 25, 46, 78, 102, 114}.
Hapi 1 (elementi i mesit sht 19 > 6):
-1 5 6 18 19 25 46 78 102 114
-1 5 6 18 19 25 46 78 102 114
-1 5 6 18 19 25 46 78 102 114
Shembulli 2. Gjejeni vlern 103 n {-1, 5, 6, 18, 19, 25, 46, 78, 102, 114}.
Hapi 1 (elementi i mesit sht 19 < 103): -1 5 6 18 19 25 46 78 102 114
Hapi 2 (elementi i mesit sht 78 < 103): -1 5 6 18 19 25 46 78 102 114
Hapi 3 (elementi i mesit sht 102 < 103): -1 5 6 18 19 25 46 78 102 114
Hapi 4 (elementi i mesit sht 114 > 103): -1 5 6 18 19 25 46 78 102 114
Hapi 5 (vlera e krkuar
1 5 6 18 19 25 46 78 102 114
mungon
(nuk
sht
prezente)):
503
Avni Rexhepi
Analiza e kompleksitetit
Prparsi shum e madhe e ktij algoritmi sht se kompleksiteti i tij varet
logaritmikisht nga madhsia e vargut, n rastin m t keq. N praktik, kjo do
t thot q algoritmi do t bj m s shumti log2(n) iteracione, q sht numr
shum i vogl edhe pr vargjet e mdha. Kjo mund t vrtetohet shum thjesht.
N t vrtet, n secilin hap, madhsia e pjess s krkuar zvoglohet
prgjyshm. Algoritmi ndalet kur nuk ka m elemente pr t krkuar. Prandaj,
zgjidhja e inekuacionit vijues pr numra t plot:
n / 2iteracione > 0
rezulton n
iteracione <= log2(n).
Kjo do t thot q, kompleksiteti kohor i algoritmit t krkimit binar sht
O(log2(n)).
Pjes kodi
Zgjidhja me rekursion dhe ajo iterative.
Me rekursion:
/* krkon vlern n vargun e sortuar
*
* array vargu ku krkohet vlera
* value vlera q krkohet
* left - indeksi i kufirit t majt
* right indeksi i kufirit t djatht
* return kthen pozitn e vlers s krkuar, nse ajo ndodhet
* n varg, ose -1, nse ajo nuk ndodhet fare n varg
*/
int kerkimiBinar(int[] array, int value, int left, int right)
{
if (left > right)
return -1;
int middle = (left + right) / 2;
if (array[middle] == value)
return middle;
else if (array[middle] > value)
return kerkimiBinar(array, value, left, middle - 1);
else
return kerkimiBinar(array,value,middle+1, right);
}
504
505
Avni Rexhepi
Prmirsimet
Algoritmi mund t prmirsohet n shum mnyra. Pr shembull, sht e
arsyeshme t verifikohet, nse A[m - 1] < B[0] ose B[n - 1] < A[0]. N cilindo
prej ktyre rasteve, nuk ka nevoj t bhen t tjera krahasime. Algoritmi mundet
vetm t kopjoj vargjet fillestare n vargun rezultues n renditjen e duhur.
Plotsim m i komplikuar do t ishte krkimi pr pjest q mund t
shtresohen(grshetohen) dhe ekzekutimi i algoritmit t bashkimit vetm pr
to. Do t mund t kursehet shum koh, kur madhesit e vargjeve t bashkuara
dallojn n llogari t kohs.
Analiza e kompleksitetit
Kompleksiteti kohor i algoritmit t bashkimit sht O(n + m). Pr m tepr, ai
krkon hapsir shtes O(n + m) pr t ruajtur vargun rezultues.
506
507
Avni Rexhepi
Algoritmet teorike-numerike
Vrejtje. Numri 1 nuk sht numr primar, sepse nuk e plotson definicionin!
508
Nj prmirsim tjetr
Duhet theksuar edhe nj prmirsim. Supozojm se n sht numr ift (2 nuk
sht plotpjestues). Nse n nuk sht i plotpjestueshm me 2, ather ai nuk
sht i plotpjestueshm fare (n trsi) me asnj numr tjetr ift. Algoritmi,
pas ktyre dy prmirsimeve, duket si vijon:
Analiza e kompleksitetit
Duke supozuar q plotpjestueshmria e dy numrave sht operacion njsi (gj q
nuk sht korrekte pr numrat e mdhnj), kompleksiteti i algoritmit sht
O(n). (Prmirsimi i fundit e ndryshon vetm konstanten). Kur t verifikohen
vetm plotpjestuesit primar, kompleksiteti i algoritmit bhet O(n / ln(n)).
Aplikimi kryesor i numarve primar sht kriptografia, e cila pret nga ne
gjenerimin e numrave primar me qindra shifra. Me sa duket, algoritmi nuk do ti
verifikoj numrat rreth 10100 n koh t arsyeshme. Megjithat, algoritmi
aplikohet n praktik pr t br para-verifikimin (angl. pre-check). Numri
shum i madh q testohet se a sht primar, kontrollohet se a sht i
plotpjestueshm me nj milion primart e par. Nse nuk sht gjetur asnj
plotpjestues, athere prdoren algoritmi probabilistik.
3
37
79
131
181
239
293
359
421
479
5
41
83
137
191
241
307
367
431
487
7
43
89
139
193
251
311
373
433
491
11
47
97
149
197
257
313
379
439
499
13
53
101
151
199
263
317
383
443
503
17
59
103
157
211
269
331
389
449
509
19
61
107
163
223
271
337
397
457
521
23
67
109
167
227
277
347
401
461
523
29
71
113
173
229
281
349
409
463
541
509
Avni Rexhepi
Pjes kodi
bool isPrime(int number)
{
if (number == 1)
return false;
if (number == 2)
return true;
if (number % 2 == 0)
return false;
for (int d = 3; d <= (int)sqrt((double)number); d++)
if (number % d == 0)
return false;
return true;
}
510
Modifikimi
Vreni q algoritmi mund t filloj shnimin e shumfisheve duke filluar nga
katrori i numrit t gjetur si i pashnuar. Vrtet,
Pr m = 2, algoritmi shnon:
2 * 2
3 * 2
4 * 2
5 * 2
6 * 2
7 * 2
...
Pr m = 3,
2 * 3
3 * 5
4 * 5 (= 10 * 2)
Shembull
Apliko sitn e Eratostenit pr gjetjen e numrave primar nga 2 deri n 100.
511
Avni Rexhepi
Rrjeta integrale
512
513
Avni Rexhepi
7 shtr primar, shno t gjitha shumfishet e 7, duke filluar nga 49
514
Analiza e kompleksitetit
Kompleksiteti llogarits i algoritmit sht O(nlog(log(n))). Vrtetsia e ktij
fakti sht e bazuar n disa prafrime dhe n teorin e numrave primar.
Kompleksiteti hapsinor sht O(n). Aktualisht, n hardverin modern, algoritmi
do t tejkaloj shum m shpejt kufizimet pr nga memoria sesa q do t
tejkaloj kohn.
Pjes kodi
void runEratosthenesSieve(int upperBound)
{
int upperBoundSquareRoot=(int)sqrt((double)upperBound);
bool *isComposite = new bool[upperBound + 1];
memset(isComposite, 0, sizeof(bool) * (upperBound + 1));
for (int m = 2; m <= upperBoundSquareRoot; m++)
{
if (!isComposite[m])
{
cout << m << " ";
for (int k = m * m; k <= upperBound; k += m)
isComposite[k] = true;
515
Avni Rexhepi
}
}
for (int m=upperBoundSquareRoot; m<=upperBound; m++)
if (!isComposite[m])
cout << m << " ";
delete [] isComposite;
}
516
9. Algoritmet e sortimit
Dy prej operacioneve m t performuara n t dhnat e ruajtura n kompjuter,
q nga krijimi i kompjuterit e deri m sot, jan sortimi dhe krkimi. Prandaj,
kto dy operacione jan edhe m t studiuarat nga shkencat kompjuterike.
Algoritmi i sortimit sht nj algoritm i cili rendit elementet e nj liste.
Renditjet m t shpeshta jan: renditja numerike dhe renditja alfabetike.
Elementet mund t renditn n mnyr rritse apo zvogluese.
Shumica e t dhnave me t cilat punojm jan t sortuara. Pr shembull,
definicionet e ndryshme npr fjalor mund ti gjejm t sortuara sipas alfabetit,
numrat e telefonave i gjejm t sortuara sipas radhitjes alfabetike t emrave.
Poashtu, edhe letrat postare sortohen n disa mnyra: sipas zip kodit, sipas
rrugs dhe n fund sipas emrit.
Sot ekzistojn shum algoritme t sortimit. Ato kan veti t ndryshme. N baz
t vetive t tyre, ato mund t konsiderohen si m shum apo m pak efektive se
ndonj algoritm tjetr pr sortim. Secili algoritm vepron ndryshe n raste t
ndryshme. Disa punojn m shpejt n nj list t caktuar, por m ngadal n nj
list tjetr. Prandaj, pr secilin algoritm duhet t analizohen tri raste: rasti m i
mir, rasti m i keq dhe rasti mesatar.
Rasti m i mir pr nj algoritm t sortimit sht kur algoritmi kryen m s
paku pun dhe njkohsisht sortohet lista. Me m s paku pun nnkuptohet
numri minimal i krahasimeve dhe shkmbimeve t vendeve t elementeve q i
nevojitet algoritmit pr t sortuar listn. N kt rast, algoritmi sht m i shpejt
se n cilindo rast tjetr, pasi q kryen m pak pun.
E kundrta e rastit m t mir sht rasti m i keq. N kt rast analizohet se
maksimalisht sa krahasime dhe shkmbime vendesh i nevojiten algoritmit pr
ta sortuar listn. N kt rast, algoritmi krkon m s shumti koh pr ta
sortuar listn.
Rast mesatar pr nj algoritm t sortimit konsiderohet t jet ather kur pas
disa krahasimeve dhe shkmbime vendesh, lista t prfundoj sortimin. Rasti m
i mir dhe m i keq ka m pak gjasa t ndodhin n nj list t rastsishme.
Prandaj, rasti mesatar ka rndsi t madhe q t analizohet.
Prve shpejtsis dhe kohs q krkon algoritmi, duhet t analizohet edhe
hapsira memorike q z algoritmi. Meq hapsira memorike sht e kufizuar,
pr tu konsideruar nj algoritm m efektiv duhet t z sa m pak hapsir.
Disa algoritme zn m pak hapsir memorike e disa shum m shum.
N kt mnyr, algoritmet e sortimit mund t klasifikohen sipas: kompleksitetit
t krahasimeve t elementeve, kompleksitetit t shkmbimeve t vendeve,
517
Avni Rexhepi
hapsirs memorike, rekurzivitetit (disa algoritme jan rekurzive e disa jo),
krahasimit (disa i krahasojn elementet e disa jo), metods s prgjithshme
(insertimi, shkmbimi, selektimi, shkrirja), etj.
Bubble Sort
Sorti Bubble (angl. bubble-flusk (theksohet: babll)) sht algoritmi m i
thjesht dhe i mirnjohur i sortimit. Prdoret rrall n praktik, por prdorimi
kryesor i tij sht m shum si mjet shkollor q t bj prezentimin e
algoritmeve t sortimit. Sorti Bubble i takon grupit t algoritmeve t sortimit t
rendit O(n2), q e bn shum joefikas pr srotimin e vllimit t madh t vlerave
t t dhnave. Sorti Bubble sht stabil dhe adaptiv (angl. stable-stabil, kshtu
njihen sortet t cilat pr vlerat e njjta, i ruajn renditjet fillestare t pozitave
(majtas, djathtas) n raport me njra tjetrn; angl. adaptive adaptive, q
prshtaten, kshtu njihen sortet t cilat gjat shkmbimeve, n rrug e sipr, e
prmirsojn situatn duke e prafruar renditjen prfundimtare, pra pas nj
shkmbimi, vlera i afrohet m shum pozits prfundimtare).
Algoritmi
1. Krahaso secilin ift t elementeve fqinje duke filluar nga fillimi i vargut
dhe nse jan n renditje t kundrt, shkembeji vendet/pozitat e tyre
(angl. Swap-shkmbim, ujdi).
2. Nse ka ndodhur s paku nj shkmbim i vendeve, prsrite hapin 1.
N secilin hap, fluskat e mdha pluskojn n siprfaqe dhe mbesin atje. N
hapin kur nuk lviz m asnj flusk, sortimi ndalon. Prmes shembullit n
vijim, do t sqarohet m mir idea e sortit bubble.
Shembull. Sortoni vargun {5, 1, 12, -5, 16} duke prdorur sortin bubble.
518
Analiza e kompleksitetit
Kompleksiteti i rastit mesatar dhe m t keq t sortit buble sht i rendit O(n2).
Gjithashtu, i bn O(n2) shkmbime n rastin m t keq. Sorti bubble sht
adaptiv. Kjo do t thot, q pr vargun pothuajse t sortuar, jep nj vlersim
O(n). Evitoni zbatimet t cilat nuk e verifikojn nse vargu sht i sortuar n
secilin hap (n secilin shkmbim t br). Ky verifikim sht i nevojshm, n
mnyr q t ruhet tipari i adaptivitetit.
519
Avni Rexhepi
520
521
Avni Rexhepi
Pjes kodi
Ka disa mnyra t implementimit t sortit bubble. Vreni se verifikimi pr
"saps" (shkmbime) sht absolutisht i nevojshm, pr t ruajtuar vetin
adaptive.
//swapped=shkmbyer (nderrim vendesh)
void bubbleSort(int arr[], int n)
{
bool swapped = true;
int j = 0;
int tmp;
while (swapped)
{
swapped = false;
j++;
for (int i = 0; i < n - j; i++)
{
if (arr[i] > arr[i + 1])
{
tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
swapped = true;
}
}
}
}
Selection Sort
Sorti i selektimit sht nj prej algoritmeve O(n2) t sortimit, gj q e bn
trsisht t paprshtatshm pr sortim t numrit t madh t t dhnave. Sorti i
selektimit sht i shquar pr thjeshtsin e tij t programimit dhe mund t
performoj m mir sesa sortet e tjera n disa situatat t caktuar.
522
Algoritmi
Idea e algoritmit t selection sort-it sht mjaft e thjesht. Vargu ndahet n
mnyr imagjinare n dy pjese: pjesa e sortuar dhe ajo e pasortuar. N filllim,
pjesa e sortuar sht empty (e zbrazt), gjersa pjesa e pasortuar, e parmban
tr vargun. N secilin hap, algoritmi e gjen vlern minimale n pjesn e
pasortuar dhe e shton at n fund t pjess s sortuar. Kur pjesa e pasortuar t
bhet zbrazet (bhet empty), algoritmi ndalet.
Kur algoritmi sorton vargun, ai e shkmben elementin e par t pjess s
pasortuar me elementin minimal dhe ather ai prfshihet (vendoset, futet) n
pjesn e sortuar. Sorti i selektimit nuk sht stabil. N rast se sortohet lista e
lidhur dhe n vend t shkmbimit, elementi minimal lidhet n pjesn e
pasortuar, sorti i selektimit sht stabil.
Shembulli vijues i sortimit t vargut e ilustron iden e sortit t selektimit.
Shembull. Sortoni vargun {5, 1, 12, -5, 16, 2, 12, 14} duke prdorur selection
sort-in.
523
Avni Rexhepi
Analiza e kompleksitetit
Sorti i selektimit ndalet kur pjesa e pasortuar bhet e zbrazt. Si dihet, n secilin
hap, numri i elementeve t pasortuara zvoglohet pr nj. Prandaj, sorti i
selektimit i bn n hapa (n numri i elementeve n varg) n unazn e jashtme,
para se t ndalet. N seciln hap t unazs s jashtme, krkohet gjetja e
minimumit
t
pjess
s
pasortuar.
Duke
mbledhur
n + (n - 1) + (n - 2) + ... + 1, fitojm numrin e krahasimeve O(n2). Numri i
shkmbimeve mund t ndryshoj prej zero (n rastin e vargut t sortuar) deri n
n-1 (nse vargu sht i sortuar n kahjen e kundrt), gj q rezulton n numrin
O(n) t shkmbimeve. Kompleksiteti i prgjithshm i algoritmit sht O(n2).
Fakti q sorti i selektimit krkon numrin prej m s shumti n-1 shkmbimeve, e
bn at shum efikas n situatat kur operacioni write (shkruaj) sht dukshm
m i kushtueshm sesa operacioni read (lexo).
524
Insertion Sort
Sorti i insertimit, i takon grupit t algoritmeve t sortimit O(n2). Pr dallim prej
shum algoritmeve t sortimit me kompleksitet kuadratik, ky aplikohet n
praktik pr sortimin e vargjeve t vogla t t dhnave. Pr shembull, prdoret
pr t prmirsuar rutinn e quicksort-it. Algoritmi i sortimit prdoret gjat
renditjes s letrave, n lojn e letrave.
Algoritmi
Algoritmi i sortit t insertimit i prngjan algoritmit t sortit t selektimit. Vargu
ndahet n dy pjes imagjinare pjesa e sortuar dhe ajo e pasortuar. N fillim,
pjesa e sortuar e prmban elementin e par t vargut, kurse pjesa e pasortuar
elementet tjera. N secilin hap, algoritmi i merr elementin e par t pjess s
pasortuar dhe e inserton n vendin e duhur t pjess s sortuar. Kur pjesa e
pasortuar t bhet e zbrazt (empty), algoritmi ndalet.
Ilustrimi i nj hapi t algoritmit t sortimit me insertim, duket si vijon:
525
Avni Rexhepi
bhet
526
Idet e insertimit
Operacioni kryesor i algoritmit sht insertimi. Detyra sht t insetohet vlera
n pjesn e sortuar t vargut. Kjo mund t bhet n disa mnyra.
"Sifting down" - shoshitja teporsht duke prdorur shkmbimet
Mnyra m e thjesht pr t insertertuar elementin e ardhshm n pjesn e
sortuar sht q t shoshitet teposhte (kah vlerat e vogla), gjersa t zr vendin e
duhur. Fillimisht elementi qndron djathtas, pas pjess s sortuar. N secilin
hap, algoritmi krahason elementin me at para tij dhe nse jan n renditje t
kundrt, i shkmben (ua ndrron vendet, pozitat), si n vijim:
527
Avni Rexhepi
Analiza e kompleksitetit
Kompleksiteti i prgjithshm i sortit t insertimit sht mesatarisht O(n2),
pavarsisht prej metods s insertimit. N vargun pothuajse t sortuar, sorti i
insertimit shfaq performans m t mir, mesatarisht deri n O(n), por numri i
krahasimeve mund t ndryshoj varsisht prej algoritmit t insertimit. Ai sht
O(n2) kur kur prdoret metoda e shiftimit ose e shkmbimit dhe O(n log n) pr
sortin binar t insertimit.
Nga kndveshtrimi i aplikacionit praktik, nj kompleksitet mesatar i sortit t
insertimit nuk sht aq i rndsishm. Si u theksua edhe m par, sorti insertion
aplikohet pr bashksi t vogla t t dhnave (8 deri 12 elemente), prandaj s
pari duhet t mirret n konsiderim nj performans praktike. N praktik, sorti
i insertimit performom m mir se shumica e algoritmeve kuadratike t sortimit,
si sorti i selektimit dhe sorti bubble.
529
Avni Rexhepi
j--;
}
//me shkmbime(swaps)
void insertionSort(int arr[], int length) {
int i, j, tmp;
for (i = 1; i < length; i++) {
j = i;
while (j > 0 && arr[j - 1] > arr[j]) {
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
j--;
}
}
}
Quicksort
Quicksort (angl. quick i shpejt), sht algoritm i shpejt i sortimit, i cili
prdoret jo vetm pr qllime shkollore, por sht edhe shum i prdorur n
praktik. N rastin mesatar, ka kompleksitetin O(n log n), q e bn quicksort-in
t prshtatshm pr sortimin e vllimeve t mdha t t dhnave (numrin e madh
t vlerave, elementeve). Idea e algoritmit sht mjaft e thjesht dhe kur t
kuptohet qart, mund t shkruhet poaq shpejt sa edhe sorti bubble.
Algoritmi
Pr sortin e shpejt prdoret strategjia praj e sundo (angl. divide-andconquer). Ktu prshkruhet hapi i rekurzionit:
1. Zgjedhni vlern pivot. (angl. pivot-qendr, bosht). Marrim vlern e
elementit n mes si vler pivot (mirpo mund t mirret edhe cilado
vler q ndodhet n brezin/rangun e vlerave q sortohen, edhe nes
nuk sht prezente n varg).
2. Ndarja (Particioni). Rivendosni (rirreshtoni) elementet ashtu q, vlerat
m t vogla se pivoti shkojn n pjes (ann) e majt dhe t gjitha
elementet m t mdha se pivoti, shkojn n pjesn e djatht t vargut.
Vlerat e barabarta me pivotin mund t rrijn n cilndo an. Vreni se
vargu mund t ndahet n pjes jo t barabarta.
3. Sortoni t dy pjest. Apliko quicksort-in n mnyr rekurzive n pjesn
e majt dhe n pjesn e djatht.
530
Algoritmi i ndarjes
Jan dy indeksa, i dhe j, n fillim t algoritmit t ndarjes (angl. part-pjes, cop;
partition-ndarje, coptim, prandaj ndonjhere edhe n shqip thuhet particioni).
Indeksi i pointon n elementin e par t vargut dhe j pointon n elementin e
fundit. Pastaj, algoritmi e lviz indeksin i prpara (te lart, djathtas), gjersa t
gjindet nj element me vler m t madhe ose baraz me pivotin. Indeksi j lviz
prapa (te posht, majtas) gjersa t gjindet nj element me vler m t vogl ose
baraz me pivotin. Nse i j ather ato shkmbehen dhe i bn nj hap n
pozitn e ardhshme (i + 1), j bn nj hap n pozitn paraprake (j 1).
Algoritmi ndalet kur i bhet m e madhe se j.
Pas particionimit, t gjitha vlerat para elementit t i-t jan m t vogla ose
baraz me pivotin dhe t gjitha vlerat pas elementetit t j-t jan m t mdha ose
baraz me pivotin.
Shembull. Sortoni vargun {1, 12, 5, 26, 7, 14, 3, 7, 2} duke prdorur quicksortin.
531
Avni Rexhepi
Vreni se ktu sht paraqitur vetm hapi i par i rekurzionit, ashtu q shembulli
t mos dal shum i gjat. Mirpo, n fakt pjest {1, 2, 5, 7, 3} dhe {14, 7, 26,
12} pastaj vazhdon sortimin n mnyr rekurzive.
Analiza e kompleksitetit
N rastin mesatar, quicksort-i ka kompleksitetin O(n log n) (prova e fort e ktij
fakti nuk sht triviale dhe nuk prezentohet ktu). N rastin m t keq,
quicksorti punon n koh, por n rastet e t dhnave praktike, punon shum mir
dhe tejkalon performansat e O(n log n) algoritmeve t tjerat t sortimit.
Pjes kodi
Algoritmi i particionimit sht vetvetiu i rndsishm, prandaj mund t
realizohet edhe si si funksion i veant. N vijim prezentohen t dy rastet, me
funksion t prbashkt dhe me funksion t ndare pr particionin dhe sortimin.
void quickSort(int arr[], int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
/* partition */
while (i <= j) {
while (arr[i] < pivot)
532
Me funksione t ndara:
int partition(int arr[], int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
while (i <= j)
{
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
533
Avni Rexhepi
return i;
}
void quickSort(int arr[], int left, int right) {
int index = partition(arr, left, right);
if (left < index - 1)
quickSort(arr, left, index - 1);
if (index < right)
quickSort(arr, index, right);
}
534
SHELLSORT
Shell sorti u zhvillua nga Donald L. Shell. Ky sort sth i pazakont n faktin q
fillon me shqyrtimin e lists s plot t vlerave si nj set i nnlistave t
ndrthurrura (shtresuara). N kalimin e par, ai mund t punoj me nnlista q
jan vetm nj ift i elementeve. N kalimin e dyt, mund t punoj me grupe
prej nga katr elementeve. Ky proces prsritet, duke rritur numrin e elementeve
pr nnlist dhe rrjedhimisht duke zvogluar numrin e nnlistave. Figura 3.1
tregon nnlistat t cilat mund t prdoren n procesin e sortimit t lists prej 16
elementeve.
N Fig. 3.1(a), shohim se ekzistojn tet nnlista me nga dy vlera secila, t cilat
iftojn elementin e par dhe t nnt, t dytin dhe t tetin e kshtu me radh.
N Fig. 3.1(b), shohim se tani kemi katr nnlista me nga katr vlera secila.
Avni Rexhepi
dhjet dhe t katrmbdhjet. N Fig. 3.1(c), shohim se ka dy nnlista, t cilat
kan elementet e lokacioneve teke dhe atyre ifte n to. N kalimin e fundit, t
treguar n Fig. 3.1(d), kthehemi n nj list.
Sortimi i nnlists bhet vetm prmes nj variante t sortit t insertimit t
bazuar n seksionin 3.1. Kjo e bn algoritmin vijues:
Shellsort( list, N )
list elementet qe duhet sortuar
N numri i elementeve ne liste
passes = lg N
//passes=kalime
while (passes 1) do
increment = 2passes - 1
for start = 1 to increment do
InsertionSort( list, N, start, increment )
end for
passes = passes - 1
end while
536
RADIX SORT-i
Sorti Radix (angl. Radix Rrnj, baz ) i prdor vlerat kye (elsat) pr t
br sortimin pa i krahasuar n t vrtet ato mes veti. N kt sort, do t
krijojm nj set t kovave dhe do ti shprndajm t dhnat hyrse n kov
bazuar n vlerat e tyre kye. Pasi t grumbullohen vlerat dhe t prsrtitet
procedura pr pjest pasuese t vlers kye, mund t krijojm listn e sortuar.
Shprndarja dhe grumbullimi duhet t bhen me shum kujdes q kjo t
funksionoj (punoj).
Nj proces i ngjashm me kt sht prdorur pr ti sortuar letrat (letrat e lojs
me letra) me dor.
Versioni i kompjuterizuar i ktij procesi pr sortim t nj seti t vlerave
numerike do t prdorte 10 kova dhe do t kishte algoritmin vijues:
RadixSort( list, N )
list elementet qe duhet sortuar
N numri i elementeve ne liste
shift = 1
for loop = 1 to keySize do
for entry = 1 to N do
bucketNumber = (list[entry].key / shift) mod 10
Append( bucket[bucketNumber], list[entry] )
end for entry
list = CombineBuckets()
shift = shift
end for loop
10
Avni Rexhepi
do t jen prfundimisht t sortuara. Figura 3.2 paraqet tri kalimet q do t
bheshin pr vlerat kye (elsat) me tri shifra. Pr ta br kt shembull m t
thjesht, t gjitha vlerat kye prdorin vetm shifrat prej 0 deri n 3, kshtu q
do t nevojiten katr kova.
Duke shikuar Fig. 3.2(c), do t duhej t shihni se nse kovat kombinohen prsri
n radh, listo tani do t jet e sortuar.
List origjinale
310 213 023 130 013 301 222 032 201 111 323 002 330 102 231 120
Numri i kovs
Prmbajtja
Lista e kalimit 1
310 130 330 120 301 201 111 231 222 032 002 102 213 023 013 323
Numri i kovs
Prmbajtja
538
Prmbajtja
539
Avni Rexhepi
HEAPSORT
Heapsort (sorti i pirgut angl. Heap pirg, stive, tog, grumbull, etj) sht i
bazuar n nj tip special t pems binare t quajtur Heap ku pr seciln
nnpem vlera n rrnj sht m e madhe se t gjitha vlerat n t dy fmijt.
Nuk ka relacion renditjeje ndrmjet dy fmijve, kshtu q ndonjher fmija i
majt mund t jet m i madh dhe herave t tjera fmija i djatht do t jet m i
madh. Pirgu konstruktohet q t jet pem e plot ku s pari secili nivel i pems
mbushet para se t fillohet niveli i ri dhe t gjitha pozicionet e nyjeve n nivel t
jen mbushur ne radh prej t majts kah e djathta.
Idea e prgjithshme e Heapsort-it sht q s pari t konstruktohet pirgu.
Ather elementi m i madh do t jet n rrnj t pems, sepse t gjitha
elementet m t vogla duhet t jen n fmij ashtu q ky t jet pirg. Rrnja
pastaj kopjohet n lokacionin e fundit t lists dhe pirgu rikonstruktohet pa kt
elementin m t madh. Elementi i dyt m i madh pastaj do t jet n rrnj,
kshtu q mund ta largojm at dhe t rikonstruktojm pirgun. Ky proces
prsritet deri sa t gjitha elementet t jen vendosur prapa n list.
Algoritmi i prgjithshm pr kt sht:
Konstrukto pirgun
for i = 1 to N do
kopjo rrenjen ne liste
fikso pirgun
end for
>
541
Avni Rexhepi
largerChild = largerChild + 1
end if
// a eshte key prmbi kt fmij?
if key > list[ largerChild ] then
// po, ndalo unazen
break
else
// jo, levize femijen me te madh lart
list[ vacant ] = list[ largerChild ]
vacant = largerChild
end if
end while
list[ vacant ] = key
Konstruktimi i pirgut
Mnyra q kemi zgjedhur pr t zbatuar funksionin FixHeap do t thot se ne
mund t prdorim kt pr konstruktimin inicial (fillestar) t pirgut. Cilat do dy
vlera mund t trajtohen si gjethe t nyjes s lir (boshe, zbrazt). Nuk duhet t
bjm ndonj pun n gjysmn e dyt t lists sepse ato jan t gjitha gjethe.
Duhet vetm t konstruktohen pirgjet e vogla prej gjetheve dhe pastaj t
kombinohen kto gjersa prfundimisht t gjitha vlerat t jen n pirg. Kjo arrihet
prmes unazs vijuese:
//down to = teposht deri n
for i = N/2 down to 1 do
FixHeap( list, i, list[ i ], N )
end for
Algoritmi Final
Bashkimi i ktyre pjesve dhe shtimi i detajeve finale t nevojshme pr lvizjen
e elementeve prej pirgut n list, jep algoritmin:
for i = N/2 down to 1 do
542
Sorti Merge
Merge sort-i (angl. Merge bashkim, shkrirje) sht i pari prej algoritmeve tona
rekurzive t sortimit. Ai sht i bazuar n iden se bashkimi i dy listave t
sortuara mund t bhet shpejt. Pasi q lista me vetm nj element sht e
sortuar, sorti merge do t ndaj listn n pjes nj-elementshe dhe pastaj do t
bj sortimin gjersa i bashkon kto pjes prsri s bashku. Prandaj, e tr puna
pr kt algoritm ndodh n bashkimin e dy listave.
Sorti merge (sorti i bashkimit) mund t shkruhet si algoritm rekurziv i cili e bn
punn e tij gjat rrugs lart n procesin rekurziv. Duke shikuar n algoritmin q
pason, do t vreni se ai e ndan listn prgjysm gjersa i pari sht m i
vogl sesa i fundit. Kur t arrijm n pikn ku i pari dhe i fundit jan t
barabart, kemi nj list prej nj elementi e cila sht vetvetiu (qensisht) e
sortuar. Kur t kthehemi, prej dy thirrjeve t MergeSort q kan madhsi t
lists 1, pastaj e thrrasim MergeSort pr t bashkuar ato pr t krijuar listn e
sortuar me madhsi 2. N nivelin e ardhshm te lart, do t kemi dy lista me
madhsi 2 t cilat bashkohen n nj list t sortuar me madhsi 4. Ky proces
543
Avni Rexhepi
vazhdon deri sa arrijm t thirrja m e lart, e cila i bashkon dy gjysmat e
sortuara t lists, n nj list t sortuar. Shohim se MergeSort e ndan listn n
gjysma rrugs te posht n procesin rekurziv dhe pastaj i bashkon gjysmat e
sortuara s bashku n rrugn te lart. Algoritmi i cili e realizon kt sht:
MergeSort( list, first, last )
list-elementet qe duhet te sortohen
firstindeksi i elem. te pare ne pjesen e listes per sortim
last- indeksi i elem. te fundit ne pjesen e listes per sortim
if first < last then
middle = ( first + last ) / 2
MergeSort( list, first, middle )
MergeSort( list, middle + 1, last )
MergeLists( list, first, middle, middle + 1, last )
end if
Avni Rexhepi
Analiza e MergeLists
Pr shkak se t gjitha krahasimet e elementeve ndodhin n MergeLists, fillojm
analizn prej aty. Le t shohim n rastin kur t gjitha elementet e lists A jan
m t vegjl sesa elementi i par i lists B. ka do t ndodh n MergeLists?
Do t fillojm me krahasimin e A[1] dhe B[1] dhe pasi q A[1] sht m i vogl
do ta vendosim n C. Pastaj do ta krahasojm A[2] me B[1] dhe do ta lvizim
A[2]-shin, sepse ai sht m i vogl. Ky proces do t vazhdoj t krahasoj
secilin element t A me B[1], sepse ata t gjith jan m t vegjl. Kjo do t
thot se algoritmi i bn NA krahasime, ku NA sht numri i elementeve n listn
A. Vreni se nse t gjitha elementet e lists B do t ishin m t vogla se
elementi i par i A, numri rezultues i krahasimeve do t ishte NB, kur NB sht
numri i elementeve n listn B.
ka nse elementi i par i A sht m i madh se elementi i par i B por t gjitha
elementet e A jan m t vogla sesa elementi i dyt i B? Do t krahasonim A[1]
dhe B[1] dhe do t lviznim B[1] n C. Tani e gjejm veten n pozitn e njjt
ku ishim n rastin e fundit, ku do t krahasojm secilin element t A me B[2]
gjersa ata t lvizen (vendosen) n rezultat (n at q del si rezultat, rezulton).
Kt her, sidoqoft, nuk e kemi br vetm NA krahasime t elementeve t A
me B[2], por gjithashtu e kemi br edhe krahasimin e A[1] dhe B[1], kshtu q
numri total i krahasimeve n kt rast sht NA + 1. Nse i marrim n shqyrtim
renditjet tjera, do t fillojm t shohim se rasti i prezentuar n paragrafin e par
t ktij nnseksioni mund t jet rasti m i mir, dhe sht. E pam se nse t
gjitha elementet e lists A ishin ndrmjet B[1] dhe B[2], bm m shum
krahasime sesa nse t gjitha elementet e A t ishin t ishin m t vogla sesa t
gjitha elementet e B. Le t shohim se nse shkojm n ekstremin tjetr a do t
fitojm rastin m t keq? Shqyrtoni se ka ndodh nse elementet e A dhe B
546
547
Avni Rexhepi
Koncepte t programimit
for
i 0 to n - m
{ testo shifto i-n e mostrs P}
j 0
while j < m T[i + j] = P[j]
j j + 1
if j = m
549
Avni Rexhepi
return
i {prputhje n pozitn i}
else
return
Nuk ka nevoj
t prsritet
krahasimi i
ksaj pjese
Vazhdo
krahasimin
ktu
Nga figura mund t shihet, q nse prefiksi (n kt rast ab) sht i njjt me
sufiksin e pjess q sht prputhur deri n kt moment (n kt rast ab),
ather, zhvendosja bhet deri n pozitn ku pjesa e prefiksit (q sht njjt si
sufiksi) prputhet me sufiksin dhe nuk ka nevoj t krahasohet rishtazi, por
vazhdohet me krahasimin e pozitave m tutje.
550
551
Avni Rexhepi
else
i i + 1
j j + 1
else
if
return
j > 0
j F[j - 1]
else
i i + 1
-1 { ska prputhje/no match}
552
553
Avni Rexhepi
Menjher, n krahasimin e ardhshm 13, prsri ka mosprputhje (T=c me
P=a) dhe zhvendosemi pr nj pozit.
N vazhdim, t gjitha krahasimet rezultojm me prputhje dhe sinjalizohet gjetja
e pozits n tekst, ku kemi prputhje me mostrn (Pattern Match).
Analiza:
Algoritmi KMP ekzekutohet n koh optimale O(m+n), kshtu q:
-
Algoritmi Boyer-Moore
Algoritmi Boyer-Moore sht i bazuar n dy llogaritje heuristike:
-
Shembull:
N tekstin T=a pattern matching algorithm (algoritmi i prputhjes s mostrs),
krkohet mostra (pattern-i) P=rithm (ritmi).
554
Pra, n fillim rendited mostra me tekstin nga pozita e par, mirpo krahasimi
fillon nga karakteri i fundit i mostrs (nga ana e djatht n t majt). N
krahasimin e par kemi mosprputhje (T[4]=t, P[4]=m). Tani, shikohet a
ekziston shkronja e analizuar e tekstit (n kt rast t) n kuadr t mostrs?
Meqen se shkronja t ekziston edhe n kuadr t mostrs, ather bhet
zhvendosja deri n pozitn kur vendosen n vij t drejt karakteret t t tekstit
dhe mostrs. Krahasimi i dyt, prsri fillon nga skaji i djatht dhe krahasohet
e e tekstit me m t mostrs. Meqen se nuk ka prputhje dhe nga ana tjetr
shkrona e e tekstit nuk ekziston fare n kuadr t mostrs, ather nnkuptohet
se ska gjasa t ket prputhje n tr gjatsin, prandaj mostra shiftohet
(zhvendoset) deri n pozitn pas karakterit e (duke evituar kshtu nj numr t
madh t krahasimeve t panevojshme, t cilat do ti bnte algoritmi naiv i
krahasimit). Krahasimi i tret, vazhdon me parimin e njjt, duke krahasuar a
nga teksti me m nga mostra dhe prsri zhvendosje e plot e mostrs, sikurse
edhe n rastin e krahasimit t 4 (pr karakterin n) dhe atij t 5 (pr karakterin
555
Avni Rexhepi
g). N krahasimin 6, karakteri h i tekstit me karakterin m nga mostra,
prsri nuk ka prputhje, mirpo karakteri h ekziston n kuadr t mostrs,
keshtu q bhet shiftimi pr ta vendosur mostrn n pozitn kur vijn n vij t
drejt karakteret h t tekstit dhe t mostrs. Krahasimet n vazhdim, nga 7 deri
n 11 rezultojn me prputhje t plot dhe del q sht gjetur mostra n kuadr
t tekstit.
Funksioni i rastitsjes s fundit
Algoritmi Boyer-Moore paraproceson mostrn P dhe alfabetin , pr t ndrtuar
funksionin e rastisjes s fundit L( ) (angl. Last Occurrence Function), i cili
pasqyron n numra t plot, ku L(c) definohet si:
-
556
557
Avni Rexhepi
Shembull:
T = aaa...a
P = baaa
Rasti m i keq mund t paraqitet n sekuenca t ADN-ve ose n imazhe, por pak
ka gjasa q t paraqitet n tekste t zakonshme.
559
Avni Rexhepi
11.
Hash tabelat
Avni Rexhepi
dyti, nse elementet nuk jan numra t plot por stringje ose objekte t
prgjithshme, ather ato nuk mund t prdoren pr t indeksuar vargun.
Problemi i dyt, n realitet edhe nuk sht problem n vete. Mirpo, njsoj
sikur q numri 1234 sht bashksi e shifrave, 1, 2, 3 dhe 4, edhe stringu
dita sht bashksi e karaktereve d, i, t dhe a. Mirpo, numri 1234
sht thjesht: 1*103+2*102+3*101+4*100. Nse marrim parasysh se
karakteret n mnyr tipike mund t reprezentohen n 7 bita, si numra prej 0
deri n 127 dhe pasi q karakteri n mnyr themelore sht numr i vogl i
plot, athere mund t paraqesim stringun si nj integjer. Nj mundsi do t
ishte: d*1283+i*1282+t*1281+a*1280. Kjo do t mundsonte
implementim si varg i thjesht, mirpo problemi me kt strategji sht se ky
reprezentim do t prfaqsonte numra jashtzakonisht t mdhenj, e stringjet
m t gjata do t gjeneronin numra shum m t mdhenj. Kjo na kthen n
fillim, tek problemi i par: Si t evitojm prdorimin e vargjeve absurd t
gjata?
Kjo arrihet duke prdorur nj funksion i cili i pasqyron (mapon) nurmat e
mdhenj (ose stringjet e interpretuara si numra), n numra m t vegjl dhe
m t menaxhueshm. Funksioni i cili pasqyron nj element n indeks t
vogl njihet si hash funksion. Nse x sht nj numr i plot arbitrar
(jonegativ), ather x % madhesiaTabeles (ku % - moduli, mbetja nga
plotpjestimi) gjeneron numra ndrmjet 0 dhe madhesiaTebeles-1, t
prshtatshm pr indeksim n nj varg me madhsi madhesiaTabeles.
Ns s sht string, mund ta konvertojm s-in n nj integer t madh x duke
prdorur metodn e sugjeruar m par dhe pastaj t aplikojm operatorin e
modulit (%) pr t prfituar indeks t prshtatshm. Prandaj, nse madhsia e
tabels madhesiaTabeles sht 10000, fjala dita do t indeksohen
prbrenda rangut.
Pra, hash funksioni i konverton elementet n numra t plot t prshtatshm pr
indeksim t vargut ku do t ruhet elementi. Nse hash funksioni do t ishte nj n
nj, do t mund ti qaseshim elementit prmes indeksit t vargut.
562
Hash funksioni
Hash funksioni sht pjes shum e rndsishme e dizajnit t hash tabels.
Hash funksioni konsiderohet i mir, ns ofron shprndarje uniforme t hash
vlerave. Tiparet tjera t hash funksioneve, t krkuara pr hashing kualitativ
do t analizohen m von. Arsyeja se prse hash funksioni sht subjekt i
shqetsimeve sht se hash funksionet e kqija shkaktojn kolizione (angl.
collision-konflikt, ndeshje, prplasje) dhe efekte t tjera t padshiruara, t
cilat ndikoj keq n performansn e prgjithshme t hash tabels.
Kolizionet
ka ndodh, nse hash funksioni kthen hash vler t njjt pr elsa t
ndryshm? Kjo rezulton n efektin e quajtur kolizion. Kolizionet jan
praktikisht t paevitueshme dhe duhet t mirren n konsiderim kur
implementohet hash tabela. Pr shkak t kolizioneve, elsat gjithashtu ruhen
n tabel, ashtu q t mund t dallohen iftet els-vler q kan t njjtin
hash. Ka mnyra t ndryshme t zgjidhjes s kolizioneve. N esenc, jan dy
strategji t ndryshme:
563
Avni Rexhepi
Hash tabela
Vargu themelor ka madhsi konstante pr ruajtjen e 128 elementeve dhe secili
sllot prmban iftin els-vler. elsi ruhet pr t br dallimin ndrmjet
ifteve els-vler, t cilat kan hash t njjt.
Hash funksioni
Tabela lejon vetm vlera t plota (integer). Hash funksioni q do t prdoret
sht mbetja e pjestimit me 128 (moduli me 128). N aspektin e implementimit,
ky hash funksion mund t kodohet prmes prdorimit t operatorit t mbetjes
ose duke prdorur AND me 127 n nivel bitash.
(Vrejtje: N praktik shpesh prdoren tabelat me madhsi t fuqis s dyshit.
Kur prdoren kto, ather ka edhe nj hash funksion special, i cili aplikohet si
shtes e atij kryesor. Kjo mas parandalon kolizionet e hash kodeve t cilat nuk
dallojn n bitat e ult).
Pjes kodi
Implementimi i ktill ka nj problem (bug). Kur nuk ka m vend n tabel,
unaza e krkimit pr slot t lir do t punoj pandrprer. Kjo nuk do t ndodh
n hash tabel reale t bazuar n adresim t hapur, sepse ajo zakonisht sht me
madhsi dinamike (t ndryshueshme). Gjithashtu, sht ln anash
implementimi i largimit (fshirjes), pr t ruajtur thjeshtsin. Implementimi i
plot do t paraqitet tek pjesa e adresimit t hapur.
class HashEntry {
private:
564
565
Avni Rexhepi
if (table[hash] != NULL)
delete table[hash];
table[hash] = new HashEntry(key, value);
}
~HashMap() {
for (int i = 0; i < TABLE_SIZE; i++)
if (table[i] != NULL)
delete table[i];
delete[] table;
}
};
Analiza e kompleksitetit
566
567
Avni Rexhepi
}
LinkedHashEntry *getNext()
{
return next;
}
void setNext(LinkedHashEntry *next)
{
this->next = next;
}
};
const int TABLE_SIZE = 128;
class HashMap
{
private:
LinkedHashEntry **table;
public:
HashMap()
{
table = new LinkedHashEntry*[TABLE_SIZE];
for (int i = 0; i < TABLE_SIZE; i++)
table[i] = NULL;
}
int get(int key)
{
int hash = (key % TABLE_SIZE);
if (table[hash] == NULL)
return -1;
else
{
LinkedHashEntry *entry = table[hash];
while (entry != NULL && entry->getKey() != key)
entry = entry->getNext();
if (entry == NULL)
return -1;
else
return entry->getValue();
}
}
void put(int key, int value)
{
int hash = (key % TABLE_SIZE);
568
569
Avni Rexhepi
LinkedHashEntry *entry = table[i];
while (entry != NULL)
{
prevEntry = entry;
entry = entry->getNext();
delete prevEntry;
}
}
delete[] table;
}
};
Zgjidhja e kolizioneve
Le t marrim n konsiderim operacionin e insertimit. Nse sloti, elsi n t cilin
hashohet, del t jet i zn, algoritmi fillon t krkoj pr vend t zbrazt (angl.
prdoret termi free bucket kov e lir). Ai fillon me slotin n t cilin u
hashua dhe vazhdon me kontrollimin n varg (n sekuenc), gjersa t gjen
vend t lir. Ka disa sekuenca kontrolluese:
Kontrollimi linear
Po t kemi nj hash funksionin si vijon:
unsigned int hash (const string &key, int madhesiaTabeles)
{
...
return hashVlera % madhesiaTabeles)
}
hash(89,10)
hash(89,10)
hash(89,10)
hash(89,10)
hash(89,10)
=
=
=
=
=
Pas
insertimit
t 89
9
8
9
8
9
Pas
insertimit
t 18
Pas
insertimit
t 49
Pas
insertimit
t 9
49
49
58
58
18
18
18
18
89
89
89
89
89
49
Pas
insertimit
t 58
Avni Rexhepi
gjithmon do t gjindet nj vend i lir. Mirpo, koha e nevojshme pr t gjetur
vend t lir mund t rritet shum. Pr shembull, nse ka vetm nj vend t lir t
mbetur, mund t ket nevoj q t krkohet prgjat tr tabels pr ta gjetur at.
N krkimin linear, kolizionet zgjidhen duke br skenimin sekuencial t vargut (me
rrotullim) deri sa t gjendet nj vend i lir.
572
573
Avni Rexhepi
hash(89,10)
hash(89,10)
hash(89,10)
hash(89,10)
hash(89,10)
=
=
=
=
=
Pas
insertimit
t 89
9
8
9
8
9
Pas
insertimit
t 18
Pas
insertimit
t 49
49
Pas
insertimit
t 58
49
Pas
insertimit
t 9
49
1
2
58
18
18
18
18
89
89
89
89
89
58
Operacioni i largimit
Ka disa nuanca, kur bhet largimi i elsit nga hash tabela bhet me adresim t
hapur. Merrni n konsiderim rastin vijues:
575
Avni Rexhepi
Analiza e kompleksitetit
Hash tabelat e bazuara n adresim t hapur jan shum m t ndjeshme n
zgjedhjen e duhur t hash funksionit. Nn supozimin se hash funksioni sht i
mir dhe hash tabela sht e dimensionuar mir, komoleksiteti i amortizuar i
operacioneve t insertimit, fshirjes dhe krkimit sht konstant.
Performansa e hash tabelave t bazuara n skemn e adresimit t hapur sht
shum e ndjeshme n faktorin e ngarkess s tabels. Nse faktori i ngarkess
kalon pragun 0.7, shpejtsia e tabels degradon drastikisht. N t vrtet,
gjatsia e sekuencs s kontrollimit sht proporcional me vlern:
(faktoriNgarkeses) / (1 faktoriNgarkeses). N rastet ekstreme, kur
faktoriNgarkeses i afrohet 1-shit, gjatsia e sekuencs i afrohet vlers pakufi. N
praktik, kjo do t thot se nuk ka slote t lira n tabel dhe algoritmi kurr nuk
do t gjej vend pr t insertuar elementin e ri. Prandaj, ky lloj i hash tabelave
duhet t prkrah ndryshimin dinamik t madhsis n mnyr q t jet efikas.
Adresimi i hapur
Open addressing
Mbingarkim sa mdhsia e
pointerit pr vler (duke
1
Nuk ka mbingarkim (overhead)
ruajtur krert e lists n
tabel)
Varsia e
performanss nga
Direkt proporcional
faktori i ngarkess s
tabels
Proporcional me
(faktoriNgark.) / (1-faktoriNgark.)
Lejon t ruaj m
shum vlera sesa
madhisa e hash
tabels
Po
Krkesat pr Hash
funksionin
Shprndarje Uniforme
Trajtimi i fshirjeve
Fshirjet jan OK
Implementimi
I thjesht
577
Avni Rexhepi
Hash tabelat me vargzim mund t punojn n mnyr efikase me faktor t
ngarkess m shum se 1. N t njjtn koh, tabelat e bazuara n skemn e
adresimit t hapur krkojn q faktori i ngarkess t mos kaloj vlern 0.7, pr
t qen efikase. Prandaj, 30% e sloteve mbesin t zbrazta, gj q on n
shprdorim t dukshm t memories.
Pjes kodi
Kodi i mposhtm implementon kontrollimin linear. Implementimi aktual sht
i mbrojtur nga hyrja n unaz t pafund.
class HashEntry
{
private:
int key;
int value;
public:
HashEntry(int key, int value)
{
this->key = key;
this->value = value;
}
int getKey()
{
return key;
}
int getValue()
{
return value;
}
void setValue(int value)
{
this->value = value;
}
};
class DeletedEntry: public HashEntry
{
private:
static DeletedEntry *entry;
DeletedEntry() :
HashEntry(-1, -1)
578
579
Avni Rexhepi
}
void put(int key, int value)
{
int hash = (key % TABLE_SIZE);
int initialHash = -1;
int indexOfDeletedEntry = -1;
while (hash != initialHash && (table[hash]
== DeletedEntry::getUniqueDeletedEntry()
|| table[hash] != NULL
&& table[hash]->getKey() != key))
{
if (initialHash == -1)
initialHash = hash;
if (table[hash] ==
DeletedEntry::getUniqueDeletedEntry())
indexOfDeletedEntry = hash;
hash = (hash + 1) % TABLE_SIZE;
}
if ((table[hash] == NULL || hash == initialHash) &&
indexOfDeletedEntry
!= -1)
table[indexOfDeletedEntry] = new
HashEntry(key, value);
else if (initialHash != hash)
if (table[hash] !=
DeletedEntry::getUniqueDeletedEntry()
&& table[hash] != NULL &&
table[hash]->getKey() == key)
table[hash]->setValue(value);
else
table[hash] = new HashEntry(key, value);
}
void remove(int key) {
int hash = (key % TABLE_SIZE);
int initialHash = -1;
while (hash != initialHash && (table[hash]
== DeletedEntry::getUniqueDeletedEntry()
|| table[hash] != NULL
&& table[hash]->getKey() != key))
{
if (initialHash == -1)
initialHash = hash;
hash = (hash + 1) % TABLE_SIZE;
580
Analiza e kompleksitetit
Ndryshimi dinamik i madhsis nuk ndikon n kompleksitetin e amortizuar t
operacioneve t hash tabels. Por, ndryshimi bhet prnjher dhe operacioni i
cili e inicion ndryshimin e madhsis merr kohn O(n) pr tu kompletuar, ku n
581
Avni Rexhepi
sht numri i vlerave n tabel. Ky fakt mund ta bj hash tabeln me madhsi
dinamike, t paprshtatshme pr alikacione real-time (angl. real-time - koh
reale).
Nj tjetr implementim i plot i Hash Tabels sht dhn n shtojcn n fund t
librit.
583
Avni Rexhepi
12. STL
STL-i (Standard Template Library Libraria standarde e templlejtave) ofron
nj numr t madh t shablloneve t kontejnerve dhe t algoritmeve t
prgjithshme t cilat operojn n nj numr t madh t kontejnerve.
STL Kontejnert
Kontejnert standard
Kontejneri sht objekt mbajts q ruan nj koleksion t objekteve t tjera
(elementet e tij). Kontejnert implementohen si templlejta t klasave, t cilat
ofrojn fleksibilitet t lart pr tipet e prkrahura si elementet t tyre.
Kontejneri menaxhon hapsirn memorike pr elementet e tij dhe ofron
funksionet pr qasje n to, ose drejtprdrejt ose prmes iteratorve
(referencave).
Kontejnert replikojn strukturat e prdorura zakonisht n programim: vargjet
dinamike (vector), reshtat (queue), stekun (stack), pirgun (priority_queue), listat
e lidhura (list), pemt (set) dhe vargjet asociative (map).
Shum kontejner kan disa funksione antare (member functions) t
prbashkta dhe bashkndajn funksionalitetin e tyre. Vendimi se cili tip i
kontejnerit t prdoret pr cilin problem specifik, nuk varet n prgjithsi vetm
nga funksionaliteti i ofruar nga kontejneri, por poashtu edhe nga efikasiteti i disa
prej antarve t tij (kompleksiteti). Kjo sht posaqrisht e vrtet pr
kontejnert e sekuencave, t cilt ofrojn pazare n kompleksitet ndrmjet
insertimit/largimit t elementeve dhe qasjes n to.
<stack>, <queue> dhe <priority_queue> jan implementuar si adaptor t
kontejnerve. Adaptort e kontejnerve nuk jan klasa t plota t kontejnerve,
por klasa t cilat ofrojn interfejs specifik q mbshtetet n ndonj objekt t
klasave t kontejnerve (si p.sh. deque ose list) pr t trajtuar elementet.
Kontejnert nn siprfaqe jan t enkapsuluar n at mnyr q elementet e tyre
qasen nga ana e antarve t adaptorve t kontejnerve pavarsisht prej
kontejner klass nn siprfaqe, q prdoret.
Templejtat e klasave t kontejnerve
584
Unordered associative
parenditur):
unordered_set
unordered_multiset
unordered_map
unordered_multimap
containers
(kontejnerwt
asociativ
tw
Tiparet e kontejnerve
Sekuenca
Elementet n kontejner t sekuencave jan t renditur n sekuenc lineare strikte.
Elementet individuale jan t qasshme n baz t pozits s tyre n kt sekuenc.
Listat e lidhura dyfish
Secili element mban informacionet pr t lokalizuar elementin paraprak dhe
pasardhs, duke mundsuar operacione t insertimit dhe fshirjes n koh konstante
para ose pas elementit specifik (bile edhe pr rangje t tra), por nuk ka qasje direkte
t rastit.
T vetdijshme pr alokator (Allocator-aware)
Kontejneri prdor nj objekt alokator (shprndars) pr t trajtuar (manipuluar)
krkesat e veta pr hapsir memorike.
585
Avni Rexhepi
Kontejnert:
<array> (prej versionit C++ 11 e tutje)
<array> - Array header-i
Header-i q definon kontejner klasn e vargut me madhsi fikse.
Klasat: array (class template )
Funksionet:
begin Iterator-i pr fillimin e vargut (function template )
end Iteratori pr fundin e vargit (function template )
template < class T, size_t N > class array;
Array class
Parametrat e templejtit
template < class T, size_t N > class array;
Kapaciteti
size
Avni Rexhepi
max_size Kthe vlern maksimale ((funksion publik)
empty
Testo/verifiko a sht vargu i zbrazt (funksion publik)
Qasja n elemente
operator[ ]
at
front
back
data
Modifikatort
fill
swap
Shembulli 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// array::begin shembull
#include <iostream>
#include <array>
//using namespace std;
int main ()
{
std::array<int,5> varguIm = { 2, 16, 77, 34, 50 };
std::cout << "elementet e vargut tim:";
for( auto it=varguIm.begin(); it!=varguIm.end(); ++it )
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
Rezultati/Dalja:
// array::front
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,3> varguIm = {2, 16, 77};
cout << "fillimi: "<< varguIm.front() <<endl;
cout << "fundi: " << varguIm.back() <<endl;
// 2
// 77
varguIm.front() = 100;
cout << "varguIm tani permbane:";
for ( int& x : varguIm ) cout << ' ' << x;
cout << '\n';
return 0;
}
Rezultati/dalja:
fillimi: 2
fundi: 77
varguIm tani permbane: 100 16 77
Shembulli 3:
1
2
3
4
5
6
// array::front
#include <iostream>
#include <array>
using namespace std;
int main ()
589
Avni Rexhepi
7 {
8
array<int,3> varguIm = {2, 16, 77};
9
cout<<"fillimi: "<<varguIm.front()<<endl;
10
cout<<"fundi : "<<varguIm.back()<<endl;
11
12
varguIm.front() = 100;
13
14
cout << "varguIm tani permbane:";
15
for ( int& x : varguIm ) cout << ' ' << x;
16
17
cout << '\n';
18
19
return 0;
20 }
// 2
// 77
Rezultati/dalja:
fillimi: 2
fundi : 77
varguIm tani permbane: 100 16 77
// array::at
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,10> varguIm;
// caktohen disa vlera:
for (int i=0; i<10; i++) varguIm.at(i) = i+1;
// shtyp prmbajtjen:
cout << "varguIm permbane:";
for (int i=0; i<10; i++)
cout << ' ' << varguIm.at(i);
cout << '\n';
return 0;
}
Rezultati/dalja:
590
// array::operator[]
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,10> varguIm;
unsigned int i;
// cakto vlerat:
for (i=0; i<10; i++) varguIm[i]=i;
// print content
cout << "varguIm permbane vlerat:";
for (i=0; i<10; i++)
cout << ' ' << varguIm[i];
cout << '\n';
return 0;
}
Rezultati/dalja:
varguIm permbane vlerat: 0 1 2 3 4 5 6 7 8 9
591
Avni Rexhepi
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <array>
using namespace std;
int main () {
array<int,6> varguIm;
varguIm.fill(5);
cout << "varguIm permbane:";
for ( int& x : varguIm) {cout << ' ' << x; }
cout << '\n';
return 0;
}
Rezultati/dalja:
varguIm permbane: 5 5 5 5 5 5
// array::rbegin/rend
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,5> varguIm = {1, 2, 3, 4, 5} ;
592
prshkimin
automatik
antarve
593
Avni Rexhepi
<stack> - Steku
Stack header-i
size (madhsia)
push_back (shtyje)
pop_back (trhiqe)
Klasat standarde q i plotsojn kto krkesa jan: <vector>, <deque> dhe <list>.
Shembull 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <iomanip>
#include <stack>
using namespace std;
int main ()
{
stack<int> stekuIm;
int shuma (0);
for(int i=1;i<=10;i++)
stekuIm.push(i);
594
// cout
// setw
// stack
"<<shuma<<\n;
Rezultati/dalja:
Madhesia aktuale e stekut: 10; Ne krye: 10
Madhesia aktuale e stekut: 9; Ne krye: 9
Madhesia aktuale e stekut: 8; Ne krye: 8
Madhesia aktuale e stekut: 7; Ne krye: 7
Madhesia aktuale e stekut: 6; Ne krye: 6
Madhesia aktuale e stekut: 5; Ne krye: 5
Madhesia aktuale e stekut: 4; Ne krye: 4
Madhesia aktuale e stekut: 3; Ne krye: 3
Madhesia aktuale e stekut: 10; Ne krye: 2
Madhesia aktuale e stekut: 10; Ne krye: 1
Shuma e anetareve te stekut, gjithsej: 55
595
Avni Rexhepi
<deque>
Deque header-i
Header-i q definon kontejner klasn deque.
Klasat:
deque (Double ended queue rreshti me dy skaje (class template)
Funksionet:
begin Iteratori n fillim (function template)
end Iteratori n fund (function template)
N STL sht implementuar vetm kontejneri queue me dy skaje i njohur si
dequeue.
deque (zakonisht i lexuar si "deck") sht nj shkurtes (acronim) pr doubleended queue queue me dy skaje/prfundime. Dequeue-t jan kontejner
sekuencash me madhsi dinamike, q mund t zmadhohen ose zvoglohen n t
dy skajet (qoft n fillim, qoft n fund).
Librarit specifike mund t implementojn dequeue-n n mnyra t ndryshme,
n prgjithsi si ndonj form e vargut dinamik. Por n cilindo rast, mundsojn
qasjen direkte n elementet individuale prmes iteratorve me qasje t rastit, me
hapsir t ruajtjes t menaxhuar automatikisht prmes rritjes ose zvoglimit t
kontejnerit, sipas nevojs.
S kndejmi, ata ofrojn funksionalitet t ngjashm me vektort (<vector>), por
me insertim dhe fshirje efikase t elementeve gjithashtu edhe ne fillim t
sekuencs dhe jo vetm n fund t saj. Por, pr dallim prej vektorve, dequeue-t
nuk garantojn ruajtjen e t gjitha elementeve n lokacione t njpasnjshme t
memories, kshtu q qasja n elementet e dequeue-s prmes zhvendosjes s
pointerit (aritmetiks s pointerve) do t shkaktonte sjellje t
paparashikueshme.
Dequeue dhe vektori ofrojn interfejs t ngjashm dhe mund t prdoren pr
qllime t ngjashme, por prbrenda (angl. internally) punojn n mnyr
trsisht t ndryshme: gjersa vektort prdorin nj varg t vetm q ka nevoj t
realokohet pr rritje (zgjerim), elementet e deuque-s mund t shprndahen npr
copza t memories, me kontejnerin q ruan s brenshmi informacionet e
nevojshme pr t ofruar qasjen direkte n cilindo element, n koh konstante
dhe me interfejs uniform sekuencial (prmes itereatorve). Prandaj, dequeue-t
jan pak m kompleks sesa vektort, por kjo mundson t rriten n mnyr me
efikase nn rrethana t caktuara, posaqrisht pr sekuenca shum t gjata, ku
realokimet bhen m t kushtueshme.
596
deque::back (fundi)
deque::begin (fillimi)
deque::emplace (vendose)
deque::empty (zbraze)
deque::end (fundi)
deque::erase (fshije)
deque::insert (inserto)
deque::operator= (operatori =)
Avni Rexhepi
deque::size (madhsia)
deque::swap (shkmbe)
Kapaciteti (Capacity):
size
max_size
resize
empty
shrink_to_fit
back
Modifikatort (Modifiers):
assign
push_back
push_front
pop_back
pop_front
insert
erase
swap
clear
emplace
emplace_front
emplace_back
Alokator (Allocator):
get_allocator Alokatori get (funksion publik )
Funksionet jo-antare, mbingarkime:
relational operators
sap
599
Avni Rexhepi
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 }
600
// 1 20 20 10 2 3
it = deque1.begin()+2;
vector<int> vektoriIm (2,30); //vektori me dy int, me vlere 30
//nga pozita e iteratorit, ne deque1 inserto anetaret e vektorit
deque1.insert (it,vektoriIm.begin(),vektoriIm.end());
// 1 20 30 30 20 10 2 3
//shtype deque1
cout << "deque1 tani permbane:";
for (it=deque1.begin(); it!=deque1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
system("Pause");
return 0;
deque1 permbane: 5 4 3 2 1 1 2 3 4 5
deque1 tani permbane: 1 20 30 30 20 10 2 3
601
Avni Rexhepi
<queue>
Queue header
Header-i qw definon klasat adaptore tw kontejnerwve queue dhe
priority_queue.
Klasat:
queue - FIFO queue (class template )
priority_queue - (class template )
template <class T, class Container = deque<T> > class queue;
FIFO queue (First In, First Out queue) - sht tipi i kontejnerit i dizajnuar n
mnyr specifike pr t operuar n kontekstin FIFO, ku elementet insertohen
nga njri skaj dhe dalin (nxirren, trhiqen) nga skaji tjetr.
Queue implementohet si adaptor kontejneri, q jan klasa q enkapsulojn
objektet e klass specifike t kontejnerit, si n-kontejner, duke ofruar nj set
specifik t funksioneve pr qasje n elementet e tij. Elementet shtyhen (push)
nga ana e prapme (back) e kontejnerit dhe dalin (pop) nga pjesa e prparme
(front).
Nn-kontejneri mund t jet ndonjri prej templejtave t klasave standarde t
kontejnerve ose ndonj i dizajnuar n mnyr specifike. Nn-kontejneri duhet
ti prkrah s paku operacionet vijuese:
empty (i zbrazet?)
size (madhsia)
front (fillimi)
back (fundi)
push_back (shtyje_prapa)
pop_front (nxirre_para)
//queue::front
#include <iostream>
#include <queue> // std::queue
using namespace std;
int main ()
{
queue<int> queue1; //deklarimi i queue1
int x;
cout << "Ju lutemi jepni disa numra te plote (jepni 0 per fund):\n";
do {
cin >> x;
queue1.push (x);
//shtyji vlerat ne queue1
} while (x);
cout << "queue1 permban: ";
while (!queue1.empty()) //deri sa te zbrazet queue1
{
603
Avni Rexhepi
20
21
22
23
24
25
26
27
28
29
30
31
32 }
Rezultati/dalja:
// queue::size
#include <iostream>
#include <queue>
using namespace std;
// std::queue
int main ()
{
queue<int> myints;
cout << "0. size: " << myints.size() << '\n';
for (int i=0; i<5; i++) myints.push(i);
cout << "1. size: " << myints.size() << '\n';
myints.pop();
cout << "2. size: " << myints.size() << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
0. size: 0
1. size: 5
2. size: 4
// queue::emplace
#include <iostream>
#include <queue>
#include <string>
using namespace std;
// std::cin, std::cout
// std::queue
// std::string, std::getline(string)
int main ()
{
std::queue<std::string> queue1;
queue1.emplace ("Fjalia e pare");
queue1.emplace ("Fjalia e dyte");
std::cout << "queue1 permban:\n";
while (!queue1.empty())
{
std::cout << queue1.front() << '\n';
queue1.pop();
}
system("Pause");
return 0;
}
Rezultati/dalja:
queue1 permban:
Fjalia e pare
Fjalia e dyte
<Priority queue>
Versioni i rreshtit me prioritet <priority_queue> bn q qasja t jet gjithmon
n elementin e par (top).
Kthen referenc konstante n elementin top n "priority queue. Elementi top
sht elementi i cili me krahasim renditet m s larti n rreshtin me prioritet dhe
605
Avni Rexhepi
ai do t jet elementi i ardhshm q trhiqet nga rreshti me rastin e thirrjes s
funksionit pop.
Ky antar efektivisht e thrret funksionin front t objektit t nn-kontejnerit.
Parametrat
Asnj (nuk ka)
Vlera kthyese
Referenca n elementin top (n krye) t rreshtit t prioritetit.
Shembull 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// priority_queue::top
#include <iostream>
#include <queue>
using namespace std;
// cout
// priority_queue
int main ()
{
std::priority_queue<int> rrp1;
rrp1.push(10);
rrp1.push(20);
rrp1.push(15);
cout << "rrp1.top() eshte " << rrp1.top() << '\n';
rrp1.push(50);
cout << "rrp1.top() eshte " << rrp1.top() << '\n';
rrp1.pop();
cout << "rrp1.top() eshte " << rrp1.top() << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
rrp1.top() eshte 20
rrp1.top() tani eshte 50
rrp1.top() tani eshte 20
Shembull :
1 // priority_queue::push/pop
2 #include <iostream>
3 #include <queue>
//priority_queue
606
607
Avni Rexhepi
<list>
List header-i
Header-i qw definon kontejner klaswn list.
Klasat:
list - (class template )
Funksionet:
begin iteratori nw fillim (function template)
end iteratori nw fund (function template)
Listat (<forward_list> dhe <list>) jan kontejner sekuencash q lejojn
insertim dhe fshirje n koh konstante kudo n sekuenc dhe iteracion n nj ose
n t dy drejtimet (kahjet).
Listat implementohen si lista t lidhura dyfisht (doubly-linked lists); Listat e
lidhrua dyfish mund t ruajn secilin element q e prmbajn n lokacione t
ndryshme t pa ndrlidhura t memories. Renditja ruhet n mnyr interne duke
i shoqruar secilit antar elementin e lidhjes pr n elementin e prparshm dhe
at t ardhshm.
List <list> sht shum e ngjashme me <forward_list> (lista prpara). Dallimi
kryesor ndrmjet tyre sth se objektet e krijuara nga forward list jan lista t
lidhura njfish (singly linked list) dhe prandaj mund t iterohen vetm prpara.
Zakonisht jan pak m t vogla dhe m efikase.
Krahasuar me kontejnert tjer standard t sekuencave (array, vector dhe
deque), list performon n prgjithsi m mir n insertim, ekstraktim (nxjerrje)
dhe lvizje (zhvendosje) t elementeve n cilndo pozit prbrenda kontejneir
pr t cilin veq sht marr nj iterator dhe rrjedhimisht edhe n algoritmet q e
prdorin intensivisht at, si p.sh., algoritmet e sortimit.
E met kryesore e list-ave dhe forward-list-ave krahasuar me kta kontejner
t tjer t sekuencave sht se atyre u mungon qasja direkte n elemente n baz
t pozits s tyre; pr shembull pr t ju qasur elementit t gjasht n list, duhet
t iterohet prej nj pozite t njohur (si filllimi ose fundi) deri n at pozit, gj
q merr koh lineare me distancn mes tyre. Ato poashtu konsumojn memorie
shtes pr t mbajtur informacionet lidhur me secilin element (q mund t jet
nj faktor i rndsishm pr listat e mdha me elemente t vogla).
608
// lista1
#include <iostream>
#include <list>
using namespace std;
int main ()
{
int vlerateMia[] = {10,20,30,40,50};
list<int> lista1(vlerateMia,vlerateMia+5);
cout << "lista1 permbane:";
for(list<int>::iterator it=lista1.begin();it!=lista1.end();++it)
cout << ' ' << *it;
cout << '\n';
//Krijimi i listes 'lista2' me mbushje nga fundi
list<int> lista2;
for (int i=1; i<=5; ++i)
lista2.push_back(i); //1 2 3 4 5
//Shtypja e listes nga skajet reverse (te rrotulluara)
cout << "lista2 mbrapsht:";
for (list<int>::reverse_iterator rit=lista2.rbegin();
rit!=lista2.rend(); ++rit)
cout << ' ' << *rit;
cout << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
deque1 permbane: 5 4 3 2 1 1 2 3 4 5
deque1 tani permbane: 1 20 30 30 20 10 2 3
609
Avni Rexhepi
N rreshtin 23, premes iteratorit revers n unazn for kalojm npr antart e
lists nga skajet reverse (mbrapsht). N rreshtin 25 shtypim antart e lists
(mbrapsht).
Shembulli 2: Sortimi i vlerave list::sort
Funksioni i sortimit (renditjes sw vlerave sipas madhwsisw/alfabetit) i sorton
elementet nw kontejner.
void sort();
template <class Compare>
(2)
void sort (Compare comp);
(1)
// list::sort
#include <iostream>
#include <cmath>
#include <list>
using namespace std;
int main ()
{
int vlerat1[]={ 9, 2, 10, 3, 4, 1, 8, 7, 6, 5 };
list<double> lista1 (vlerat1,vlerat1+10);
610
: 9 2 10 3 4 1 8 7 6 5
: 1 2 3 4 5 6 7 8 9 10
Shembulli 2b:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//list::sort
#include <iostream>
#include <list>
#include <string>
#include <cctype>
using namespace std;
// Krahasimi, nuk merr parasysh madhesine e shkronjave.
// Funksioni tolower (angl. ne shkronje te vogel)
bool compare_nocase (const string& vlera1, const string& vlera2)
{
unsigned int i=0;
while ( (i<vlera1.length()) && (i<vlera2.length()) )
{
if (tolower(vlera1[i])<tolower(vlera2[i])) return true;
else if (tolower(vlera1[i])>tolower(vlera2[i])) return false;
++i;
}
return ( vlera1.length() < vlera2.length() );
}
int main ()
{
611
Avni Rexhepi
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 }
53
list<string> lista1;
list<string>::iterator it;
lista1.push_back ("Zero");
lista1.push_back ("nje");
lista1.push_back ("dy");
lista1.push_back ("Tre");
cout << "lista1 para sortimit
:";
for (it=lista1.begin(); it!=lista1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
lista1.sort();
cout << "lista1 pas sortimit
:";
for (it=lista1.begin(); it!=lista1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
lista1.sort(compare_nocase);
cout << "lista1 pas sortimit me atribut
:";
for (it=lista1.begin(); it!=lista1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
cout<<"\n\n\n";
system("Pause");
return 0;
Rezultati/dalja:
Versioni pa parametra (1), largon t gjtiha prveq elementit t par nga secili
grup i vlerave t njjta t njpasnjshme n kontejner. Vini re q elementi
largohet nga lista nse krahasohet me elementin e barabart n pozitn para tij.
612
// list::unique
#include <iostream>
#include <cmath>
#include <list>
using namespace std;
// parakusht binar i implementuar si funksion:
bool pjesaIntBaraz (double first, double second)
{
return ( int(first)==int(second) );
}
// parakusht binar i implementuar si klas:
class eAfert
{
public:
bool operator() (double first, double second)
{
return (fabs(first-second)<5.0); }
};
int main ()
{
double vleratDouble[]={ 12.15, 2.72, 73.0, 12.77, 3.14, 12.77,
73.35, 72.25, 15.3, 72.25 };
list<double> listaIme (vleratDouble,vleratDouble+10);
listaIme.sort(); //sorton elementet e listes
613
Avni Rexhepi
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 }
// 2.72, 3.14, 12.15, 12.77, 12.77, 15.3, 72.25, 72.25, 73.0, 73.35
cout << "listaIme e sortuar :";
for (list<double>::iterator it=listaIme.begin(); it!=listaIme.end();
++it)
cout << ' ' << *it;
cout << '\n';
listaIme.unique(); //largon duplikatet
// 2.72, 3.14, 12.15, 12.77, 15.3, 72.25, 73.0,
73.35
Rezultati/dalja:
listaIme e sortuar: 2.72 3.14 12.15 12.77 12.77 15.3 72.25 72.25
73.0 73.35
listaIme : 2.72, 12.15, 72.25
614
lista1.remove(30);
std::cout << "lista1 permbane:";
for (std::list<int>::iterator it=lista1.begin();
it!=lista1.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
system("Pause");
(2)
nj element
(3) rangu i
elementeve
//nderthurrja e listave
#include <iostream>
#include <list>
int main ()
{
std::list<int> lista1, lista2;
std::list<int>::iterator it;
//cakto disa vlera fillestare:
for (int i=1; i<=4; ++i)
lista1.push_back(i);
// lista1: 1 2 3 4
for (int i=1; i<=3; ++i)
lista2.push_back(i*10);
it = lista1.begin();
++it;
// lista2: 10 20 30
// pointon ne 2
615
Avni Rexhepi
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 }
//
//
//
//
lista1: 1 10 20 30 2 3 4
lista2 (e zbrazt)
"it" akoma pointon ne 2
(elementi i 5-t)
Rezultati/dalja:
lista1 permban: 30 3 4 1 10 20
lista2 permban: 2
(2)
#include <iostream>
#include <list>
using namespace std;
//Krahaso vetem pjesen e plote:
bool krahasimiiPlote (double vlera1, double vlera2)
{ return ( int(vlera1)<int(vlera2) ); }
int main ()
{
list<double> lista1, lista2;
lista1.push_back (3.1);
lista1.push_back (2.2);
lista1.push_back (2.9);
lista2.push_back (3.7);
lista2.push_back (7.1);
lista2.push_back (1.4);
lista1.sort();
lista2.sort();
lista1.merge(lista2);
cout << "lista1 permban:";
for (list<double>::iterator it=lista1.begin();
617
Avni Rexhepi
27 it!=lista1.end(); ++it)
28
cout << ' ' << *it;
29
cout << '\n';
30
31
//(lista2 tani eshte e zbrazet)
32
33
lista2.push_back (2.1);
34
35
lista1.merge(lista2,krahasimiiPlote);
36
//pasi qe tash krahasohet vetem pjesa e plote,
37
//2.1 vjen pas krejt vlerave ekzistuese me pjese te plote 2
38
cout << "\n\nTani lista1 permban:";
39
for (list<double>::iterator it=lista1.begin();
40 it!=lista1.end(); ++it)
41
cout << ' ' << *it;
42
cout << '\n';
43
system("Pause");
44
return 0;
45 }
Rezultati/dalja:
618
<map>
Map header-i
Headeri qw definon klasat e kontejnerwve map dhe multimap.
Klasat:
map (class template )
multimap map me elwsa tw shumwfishtw (class template).
Funksionet:
begin iteratori nw fillim (function template)
end iteratori nw fund (function template)
Map (angl.hart, plan, skem), jan kontejner asociativ t cilt i ruajn elementet
e formuara prej kombinimit t key value (vlers els) dhe mapped value
(vlers s pasqyruar), sipas nj rregulli t specifikuar.
Jan templejt klase <map>:
template < class
class
class
class
Key,
// map::key_type
T,
// map::mapped_type
Compare = less<Key>, // map::key_compare
Alloc = allocator<pair<const Key,T> >
// map::allocator_type
> class map;
Avni Rexhepi
Tiparet e kontejnerit
Asociativ (Associative)
Elementet n kontejnert asociativ referohen sipas elsit (key) t tyre dhe jo
sipas pozits absolute n kontejner.
I renditur (Ordered)
Elementet n kontejner prcjellin nj renditje strikte gjat gjith kohs. T gjitha
elementeve t insertuara u jepet nj pozit n kt renditje.
Map
Secili element e shoqron elsin (key) me vlern e mapuar. elsat prdoren
pr t identifikuar elementet prmbajtja e t cilve sht vlera e mapuar.
elsat unik (Unique keys)
N kontejner nuk ka dy elemente me elsa ekuivalent.
T vetdijshm pr alokatorin (Allocator-aare)
Kontejneri prdor nj objekt alokator pr t manipuluar nevojat pr memorie n
mnyr dinamike.
Parametrat e templejtit
Key (elsi) secili element n map identifikohet n mnyr unike prmes
vlers s elsit. Alias tipi i antarit: map::key_type.
T tipi i vlers s mapuar. Secili element n map ruan disa t dhna, si vler e
tij e mapuar. Alias si tipi i antarit: map::mapped_type.
Compare (krahaso) atributi binar i cili merr dy elsa t elementeve si
argument dhe kthen vler bool-eane. Shprehja comp(a,b), ku comp sht nj
objekt i ktij tipi dhe a dhe b jan vlera t elsave, duhet t kthej true nse
a konsiderohet t shkoj prpara b n renditjen e definuar prej funksionit.
Map objekti e prdor kt shprehje pr t prcaktuar edhe renditjen e
elementeve edhe nse t dy elementet jan ekuivalente (duke i krahasuar n
mnyr refleksive: ata jan ekuivalent nse !comp(a,b) && !comp(b,a)). Nuk
guxon t ket dy elemente me elsa ekuivalent n map.
Alloc tipi i objektit alokator, q pdoret pr t definuar modelin e alokimit t
memories. Si default, prdoret allocator class template q definon modelin
m t thjesht t alokimit t memories dhe sht i pavarur nga vlerat. Alias si
tipi i antrit map::allocator_type.
Member functions:
620
Capacity:
empty
size
max_size
Qasja n elemente:
I qaset elementit (funksion publik)
I qaset elementit (funksion publik)
operatori [ ]
At
Modifikatort:
insert
erase
swap
clear
emplace
emplace_hint
Observert:
key_comp
Avni Rexhepi
value_comp Kthen objektin e krahasimit t vlerave (f.p.)
Operacionet:
find
count
lower_bound
upper_bound
equal_range
Shembull 1: map::begin/end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <map>
using namespace std;
int main ()
{
map<char,int> map1;
map<char,int>::iterator it;
map1['b'] = 100;
map1['a'] = 200;
map1['c'] = 300;
//paraqite permbajtjen:
for (map<char,int>::iterator it=map1.begin(); it!=map1.end(); ++it)
cout << it->first << " => " << it->second << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
a => 200
b => 100
c => 300
// map::at
#include <iostream>
622
#include <string>
#include <map>
using namespace std;
int main ()
{
map<string,int> map1;
map1["alfa"]= 0;
map1["beta"]= 0;
map1["gama"]= 0;
map1.at("alfa") = 10;
map1.at("beta") = 20;
map1.at("gama") = 30;
for (map<string,int>::iterator it=map1.begin(); it!=map1.end(); ++it)
cout << it->first << ": " << it->second << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
alfa: 10
beta: 20
gama: 30
N rreshtat 8-11 sht deklaruar dhe inicializuar map1. N rreshtat 13-15, jan
ndar vlerat e reja tek (angl. at) elementet prkatse.
Shembull 3: map::find
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// map::find
#include <iostream>
#include <map>
using namespace std;
int main ()
{
std::map<char,int> map1;
std::map<char,int>::iterator it;
map1['a']=50;
map1['b']=100;
map1['c']=150;
map1['d']=200;
it=map1.find('b');
map1.erase (it);
map1.erase (map1.find('d'));
623
Avni Rexhepi
19
20
21
22
23
24
25
26
27 }
//shtypja e permbajtjes:
cout << "elementet ne map1:" << '\n';
cout << "a => " << map1.find('a')->second << '\n';
cout << "c => " << map1.find('c')->second << '\n';
system("Pause");
return 0;
Rezultati/dalja:
elementet ne map1:
a => 50
c => 150
Shembull 4: map::insert
Funksioni i insertimit mund t realizohet n disa forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// map::insert
#include <iostream>
#include <map>
using namespace std;
int main ()
{
map<char,int> map1;
// versioni i pare i funksionit insert (nje parameter):
map1.insert ( pair<char,int>('a',100) );
map1.insert ( pair<char,int>('z',200) );
pair<map<char,int>::iterator,bool> ret;
ret = map1.insert ( pair<char,int>('z',500) );
if (ret.second==false) {
cout << "elementi 'z' veq ekziston ne map1";
cout << " me vleren " << ret.first->second << '\n';
}
// versioni i dyte i funksionit insert (me poziten e dhn):
map<char,int>::iterator it = map1.begin();
map1.insert (it, pair<char,int>('b',300)); // insertim me efikasitet
max
map1.insert (it, pair<char,int>('c',400)); // pa insertim me
efikasitet max
624
permban:
100
300
ka 2 elemente
625
Avni Rexhepi
<set>
Set header-i
Header-i q definon klasat e kontejnerve set dhe multiset.
Klasat:
set (class template )
multiset Seti me elsa t shumfisht (class template )
Funksionet:
begin iteratori n fillim (function template)
end iteratori n fund (function template)
template < class T,
// set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T>
// set::allocator_type
> class set;
Tiparet e kontejnerit
Asociative
Elementet n kontejnert asociativ referohen sipas elsit t tyre dhe jo sipas
pozits absolute n kontejner.
T renditur (ordered)
Elementet n kontejner prcjellin nj renditjs strikte gjat tr kohs. T gjitha
elementeve t insertuara ju caktohet pozita n baz t ksaj renditjeje.
626
Parametrat e templejtit
T Tipi i elementeve. Secili element n kontejner t set-it poashtu identifikohet
n mnyr unike prmes vlers s tij (secila vler sht vet edhe els i
elementit). Alias si tipet e antarve set::key_type dhe set::value_type.
Compare atributi binar q merr dy argumente t tipit t njjt si elementet dhe
ktehn vlern bool-eane. Shprehja compa(a,b), ku comp sht objekti i ktij tipi,
ndrsa a dhe b jan vlerat e elsave, do t kthej true nse a konsiderohet se
duhet t renditet para b-s, n renditjen strikte t but (eak ordering) q e
definon funksioni. Set objekti e prdor kt shprehje edhe pr t prcaktuar
renditjen e elementeve edh epr t kontrolluar nse dy elsa t elementeve jan
ekuivalent (duke i krahasuar n mnyr refleksive: ata jan ekuivalent
nse !comp(a,b) && !comp(b,a)). Nuk mund t ket dy elemente ekuivalente n
kontejner. Ky mund t jet pointer funksioni ose objekt funksioni. Kjo merert
si less<T>, q kthen rezultat t njjt si aplikimi i operatorit m i
vogl (a<b).
Alias si member type: set::key_compare dhe set::value_compare.
Alloc tipi i objektit alokator q prdoret pr t definuar modelin e alokimit t
memories. Si i parazgjedhur (default), prdoret templlejti i klass allocator, i cili
definon modelin m t thjesht t alokimit t memories dhe sht i pavarur nga
vlerat.
Alias si member type set::allocator_type.
Member functions:
(constructor) Kontejneri pr konstruktim t set-it (funksion publik)
(destructor) Destruktori i set-it (funksion publik)
Kopjon prmbajten e kontejnerit (funksion publik)
operatori =
Iteratort:
627
Avni Rexhepi
begin
end
rbegin
rend
cbegin
cend
crbegin
crend
Capacity:
Empty
Size
max_size
Modifikatort:
insert
erase
swap
clear
emplace
emplace_hint
Observert:
Kthen objektin e krahasimit t elsave (funksion publik)
key_comp
value_comp Kthen objektin e krahasimit t vlerave (f.p.)
Operacionet:
find
count
lower_bound
upper_bound
equal_range
Allocator:
get_allocator Merr alokatorin (funksion publik)
628
// set::begin/end
#include <iostream>
#include <set>
using namespace std;
int main ()
{
int vlerat[] = {74,26,63,42,15};
set<int> set1 (vlerat,vlerat+5);
cout << "set1 permban:";
for (set<int>::iterator it=set1.begin(); it!=set1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
Set1 permban:
15 26 42 63 74
Shembulli 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// set::find
#include <iostream>
#include <set>
using namespace std;
int main ()
{
std::set<int> set1;
std::set<int>::iterator it;
// set some initial values:
for (int i=1; i<=5; i++) set1.insert(i*10);
it=set1.find(20);
set1.erase (it);
set1.erase (set1.find(40));
// set: 10 20 30 40 50
629
Avni Rexhepi
Rezultati/dalja:
Set1 permban:
10 30 50
Shembulli 3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// set::get_allocator
#include <iostream>
#include <set>
using namespace std;
int main ()
{
set<int> set1;
int * p;
unsigned int i;
// aloko vargun me 5 elemente duke perdorur alokatorin:
p=set1.get_allocator().allocate(5);
// cakto vlerat per vargun
for (i=0; i<5; i++) p[i]=(i+1)*10;
cout << "Vargu i alokuar permban:";
for (i=0; i<5; i++)cout << ' ' << p[i];
cout << '\n';
set1.get_allocator().deallocate(p,5); //dealoko memorien
system("Pause");
return 0;
}
Rezultati/dalja:
10 20 30 40 50
Shembulli 4: set::emplace
set::emplace
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);
// set::emplace
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main ()
{
set<std::string> set1;
string hyrja;
for (int i=1;i<=3;i++)
{
cout<<"Jepe qytetin: ";
cin>>hyrja;
auto ret = set1.emplace(hyrja);
if (!ret.second) cout << hyrja<< " veq ekziston ne set1\n";
}
system("Pause");
return 0;
}
Rezultati/dalja:
631
Avni Rexhepi
Jepe qytetin: Prizreni
Jepe qytetin: Prishtina
Prishtina veq ekziston n set1
Shembulli 5: set::key_comp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// set::key_comp
#include <iostream>
#include <set>
using namespace std;
int main ()
{
set<int> set1;
int max;
set<int>::key_compare krahaso = set1.key_comp();
for (int i=0; i<=5; i++)
set1.insert(i);
cout << "set1 permban:";
max=*set1.rbegin();
set<int>::iterator it=set1.begin();
do
{
cout << ' ' << *it;
} while (krahaso(*(++it),max));
cout << '\n';
system("Pause");
return 0;
}
Rezultati/dalja:
Set1 permban: 0 1 2 3 4
632
// set::emplace
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main ()
{
set<std::string> set1;
string hyrja;
for (int i=1;i<=3;i++)
{
cout<<"Jepe qytetin: ";
cin>>hyrja;
auto ret = set1.emplace(hyrja);
if (!ret.second) cout << hyrja<< " veq ekziston ne set1\n";
}
system("Pause");
return 0;
}
Rezultati/dalja:
0
1
2
3
4
5
6
7
8
9
// set::rbegin/rend
#include <iostream>
#include <set>
using namespace std;
int main ()
{
int myints[] = {4,2,5,1,3};
set<int> set1 (myints,myints+5);
set<int>::iterator it;
633
Avni Rexhepi
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 }
set<int>::reverse_iterator rit;
cout << "set1 permban:";
for (it=set1.begin(); it != set1.end(); ++it)
cout << ' ' << *it;
cout << "\n\nNe renditje te kundert/reverse\n";
cout << "set1 permban:";
for (rit=set1.rbegin(); rit != set1.rend(); ++rit)
cout << ' ' << *rit;
cout << '\n';
system("Pause");
return 0;
Rezultati/dalja:
set1 permban: 1 2 3 4 5
Ne renditje te kundert/reverse
set1 permban: 5 4 3 2 1
634
<vector>
template < class T, class Alloc = allocator<T> > class vector;
// generic template
Vector
Vektort jan kontejner t sekuencave q prfaqsojn vargjet t cilat mund t
ndryshojn madhsin.
Njsoj sikur vargjet, vektort prdorin lokacionet e njpasnjshme t memories
pr elementet e tyre, gj q do t thot se elementet e tyre mund t qasen prmes
zhvendosjes s pointerve t rregullt n antart e tyre dhe me efikasitet t njjt
sikur n rastin e vargjeve. Por, pr dallim prej vargjeve, madhsia e vektorve
mund t ndryshoj n mnyr dinamike, me manipulimin e hapsirs s tyre n
mnyr
automatike
nga
ana
e
kontejnerit.
S brendshmi, vektort prdorin vargun e alokuar n mnyr dinamike, pr
ruajtjen e elementeve t tyre. Ky varg mund t ket nevoj q t realokohet
ashtu q q t rritet madhsia e tij, kur t insertohen elemente t reja, gj q
implikon alokimin e nj vargu t ri dhe zhvendosjen e elementeve n t. Kjo
sht detyr relativisht me kosto t lart n terma t kohs s procesimit dhe
prandaj vektort nuk realokohen seciln her q t shtohet nj element i ri n
kontejner.
N vend t kesaj, kontejenert e vektorve mund t alokojn nj hapsir shtes
pr t akomoduar rritjen e mundshme dhe prandaj kontejnert mund t ken
kapacitet aktual m t madh sesa hapsira q ju nevojitet pr t prmbajtur
elementet e tyre (d.m.th, madhsin e tyre). Librarit mund t implementojn
strategji t ndryshme pr rritjen pr t balansuar ndrmjet prdorimit t
memories dhe realokimit, por n cilindo rast, realokimet duhet t ndodhin vetm
n intervale llogaritmike t rritjes s madhsis ashtu q insertimi i elementeve
individuale n fund t vektorit t mund t ofrohet me nj kompleksitet t
amortizuar konstant kohor.
Prandaj, krahasuar me vargjet, vektort konsumojn m shum memorie n
shkmbim pr aftsin e menaxhimit t memories dhe rritjes dinamike n
mnyr efektive.
Krahasurar me kontejert tjer t sekuencave dinamike (deque, list dhe
forward_list), vektort jan shum efikas n qasjen n elementet e tyre (sikurse
vargjet) dhe relativisht efikas n shtimin dhe largimin e elementeve n fundin
(skajin end) t tyre. Pr operacionet t cilat prfshijn insertimin dhe largimin
e elementeve n pozita t tjera, ata performojn m dobt sesa t tjert dhe kan
m pak iterator konsistent dhe referenca sesa list-at dhe forward_list-at.
Tiparet e kontejnerit
635
Avni Rexhepi
Sekuenca (Sequence)
Elementet n kontejnert e sekuencave jan t renditur n sekuenc strikte
lineare. Elementet individuale qasen n baz t pozits s tyre n kt sekuenc.
Vargu dinamik (Dynamic array)
Lejon qasje direkte n cilindo element n sekuenc, edhe prmes aritmetiks s
pointerve dhe siguron shtim/largim relativisht efikas t elementeve n fund t
sekuencs.
I vetdijshm pr alokator (Allocator-aare)
Kontejneri e prdor nj objekt alokator pr t manpuluar n mnyr dinamike
nevojat e tij pr memorie.
Funksionet (Member functions):
(constructor) Konstruktori i vektorit (funksion publik)
(destructor) Destruktori i vektorit (funksion publik)
Ndaja vlern (funksion publik)
operatori =
Iteratort:
begin
end
rbegin
rend
cbegin
cend
crbegin
crend
Kapaciteti
size
max_size
resize
capacity
empty
reserve
shrink_to_fit
Qasja n elemente
operator[ ]
636
Modifikatort
assign
push_back
pop_back
insert
erase
swap
clear
emplace
emplace_back
Alokatort:
get_allocator Merr alokatorin (funksion publik)
Mbingarkimet e funksioneve jo-antare
relational_operators Operatort relacional pr vektor (funksion publik)
swap
Shkmbe prmbajtjet e vektorve (funksion publik)
Specializimet e Template-ave
vector<bool> Vektor i vlerave bool (specializim i template-it t klass)
637
Avni Rexhepi
Krijimi i vektorit ndarja e vlerave (vector::assign)
template <class InputIterator>
void assign (InputIterator first,
InputIterator last);
fill (2) void assign (size_type n, const value_type& val);
range (1)
initializer
list (3)
// vector::assign
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> vektori1;
vector<int> vektori2;
vector<int> vektori3;
vektori1.assign (8,100);
// 7 int me vler 10
vector<int>::iterator it;
it=vektori1.begin()+1;
//5 vlerat qendrore t 'vektori1'
vektori2.assign (it,vektori1.end()-1);
int integjerat1[] = {17,7,14,25};
vektori3.assign (integjerat1,integjerat1+4);
//vektori1
cout<<"vektori1: ";
for (vector<int>::iterator it=vektori1.begin();it!=vektori1.end();
++it)
cout << ' ' << *it;
cout << '\n';
//vektori2
cout<<"vektori2: ";
for (vector<int>::iterator it=vektori2.begin();it!= vektori2.end();
++it)
cout << ' ' << *it;
cout << '\n';
//vektori3
cout<<"vektori3: ";
for (vector<int>::iterator it=vektori3.begin(); it!=vektori3.end();
++it)
cout << ' ' << *it;
cout << '\n';
cout << "Madhesia e vektori1: " << int (vektori1.size()) << '\n';
cout << "Madhesia e vektori2: " << int (vektori2.size()) << '\n';
639
Avni Rexhepi
45
cout << "Madhesia e vektori3: " << int (vektori3.size()) << '\n';
46
system("Pause");
47
return 0;
48 }
Rezultati/dalja:
vektori1: 10 10 10 10 10 10 10 10
vektori2: 10 10 10 10 10 10
vektori3: 17 7 4 25
Madhesia e vektori1: 8
Madhesia e vektori2: 6
Madhesia e vektori3: 4
Shembulli2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> vektori1;
for (int i=1; i<=5; i++) vektori1.push_back(i);
cout << "vektori1 permban:";
for (vector<int>::iterator it = vektori1.begin() ; it !=
vektori1.end(); ++it)
cout << " " << *it;
640
cout<<"\n\n";
cout << "vektori1.front()=" << vektori1.front() << "\n";
cout << "vektori1.back() =" << vektori1.back() << "\n\n";
cout<<"\n";
vector<int> vektori2;
vektori2.push_back(78);
vektori2.push_back(16);
//vektori2.front()=78
//vektori2.back()=16
cout << "vektori2.front()=" << vektori2.front() << "\n";
cout << "vektori2.back() =" << vektori2.back() << "\n";
vektori2.front() -= vektori2.back();
cout << "vektori2.front()=vektori2.front()-vektori2.back() \n";
cout << "vektori2.front() tani eshte " << vektori2.front() <<"\n";
cout << "vektori2.back() tani eshte " << vektori2.back() << "\n";
Rezultati/dalja:
vektori1 permban: 1 2 3 4 5
vektori1.front()=1
vektori1.back() =5
vektori2.front()=78
vektori2.back() =16
vektori2.front()=vektori2.front()-vektori2.back()
vektori2.front() tani eshte 62
vektori2.back() tani eshte 16
vektori3 permban: 10 9 8 7 6 5 4 3 2 1
vektori3.front()=10
vektori3.back() =0
Shembulli 3:
1
2
3
4
5
6
7
8
// vector::pop_back
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> vektori1;
int shuma (0);
641
Avni Rexhepi
9
10
11
12
13
14
15
16
17
18
19
20
21
22 }
Rezultati/dalja:
Shembulli 4:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// vector::resize
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> vektori1;
// permbajtja fillestare:
for (int i=1;i<=10;i++)
vektori1.push_back(i);
cout << "vektori1 permban:";
for (int i=0;i<vektori1.size();i++)
cout << ' ' << vektori1[i];
642
vektori1.resize(5);
vektori1.resize(8,100);
vektori1.resize(12);
cout << "\nvektori1 permban:";
for (int i=0;i<vektori1.size();i++)
cout << ' ' << vektori1[i];
cout<<"\n\n";
vector<int> vektori2 (100);
cout << "1. Kapaciteti i vektori2: " << vektori2.capacity() << '\n';
vektori2.resize(10);
cout << "2. Kapaciteti i vektori2: " << vektori2.capacity() << '\n';
Rezultati/dalja:
vektori1 permban:1 2 3 4 5 6 7 8 9 10
vektori1 permban:1 2 3 4 5 100 100 100 0 0 0 0
1. Kapaciteti i vektori2: 100
2. Kapaciteti i vektori2: 100
3. Kapaciteti i vektori2: 10
Shembulli 5:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// swap - shkmbe
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> vektori1 (3,100);
// three ints with a value of 100
vector<int> vektori2 (5,200);
// five ints with a value of 200
cout << "vektori1 permban:";
for (unsigned i=0; i<vektori1.size(); i++)
cout << " " << vektori1[i];
cout << "\n";
cout << "vektori2 permban:";
for (unsigned i=0; i<vektori2.size(); i++)
cout << " " << vektori2[i];
cout << "\n\n";
vektori1.swap(vektori2);
cout << "Pas shkembimit,\n";
cout << "vektori1 permban:";
for (unsigned i=0; i<vektori1.size(); i++)
cout << " " << vektori1[i];
cout << "\n";
cout << "vektori2 permban:";
for (unsigned i=0; i<vektori2.size(); i++)
cout << " " << vektori2[i];
643
Avni Rexhepi
31
cout << "\n";
32
33
system("Pause");
34
return 0;
35 }
Rezultati/dalja:
644
Dictionary create()
Krijon fjalor t zbrat
boolean isEmpty(Dictionary d)
tregon a sht i zbrazt (angl. empty) fjalori d
remove(Dictionary d, Key k)
largon (fshin) elsin k adhe vlern e asociuar me t
destroy(Dictionary d)
asgjson fjalorin d (angl. destroy-asgjso, shkatrro)
645
Avni Rexhepi
Avni Rexhepi
kshtu do t krkoj vetm nj krkim t shkurtr pr ta gjetur. Pasi
shumica e aplikacionieve shfaqin edhe frekuenc jo t barabart t qasjes
dhe lokalitet t referencs, koha mesatare e krkimit pr listat e vetorganizuara sht zakonisht shumm e mir sesa n listat e sortuara ose
pasortuara. Natyrisht, strukturat e vet-organizuara t t dhnave mund t
ndrtohen edhe prej vargjeve edhe prej listave t lidhura.
F(S)=
char (S ) a
i 1
648
i 1
mod m
649
Avni Rexhepi
Shtojcat
Shtojca A - Templates - Shabllonet
Pr funksionet dhe klasat n C++ mund t krijojm shablonet (angl. TemplateShabllon, model) prmes s cilave prgjithsojm modelin e tyre dhe
mundsojm prdorimin universal pr tipe t ndryshme. Nse ndonjher kemi
dashur t krijojm funksionin pr mbledhjen e numrave t plot (int), e pastaj
at pr mbledhjen e numrave jo t plot (double, float), i cili do t ishte n
gjendje t kthej tipin e duhur t rezultatit, varsisht prej tipit t parametrave q i
mbledh, sht dashur t bazohemi n konceptin e mbingarkimit t funksioneve,
ashtu q t kemi dy funksione me emr t njejt, por me tipe t ndryshme t
parametrave.
Prmes konceptit t templejtave, kjo gj realizohet n mnyr m t
prshtatshme.
Templejtat e funksioneve
Templejtat e funksioneve jan funksione speciale t cilat mund t operojn me
tipe t prgjithsuara, tipe gjenerike (angl. generic types). Kjo na mundson
krijimin e funksioneve funksionaliteti i t cilave mund t prshtatet pr m
shum tipe ose klasa, pa pasur nevoj prsritjen e kodit pr secilin tip.
N C++ kjo mund t arrihet prmes prdorimit t parametrave t templejtit.
Parametri i templejtit sht nj lloj special i parametrave q mund t prdoret
pr t prcjellur tipin si argument: njsoj si parametrat e zakonshm t
funksioneve mund t prdoren pr t prcjellur vlerat n funksion, parametrat e
templejtave mundsojn prcjelljen e tipeve tek funksionet. Kto templejte t
funksioneve mund t prdorin kta parametra sikur t ishin ndonj tip tjetr i
zakonshm.
Formati i deklarimit t templejtave t funksioneve me kt lloj t parametrave
sht:
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
650
651
Avni Rexhepi
double o=1.5, p=2.5, q;
k = VleraMax <int>(i,j);
n = VleraMax <long>(l,m);
q = VleraMax <long>(o,p);
cout << k << endl;
cout << n << endl;
cout << q << endl;
return 0;
}
Rezultati do t jet:
6
10
2.5
652
Rezultati:
6
10
2.5
Avni Rexhepi
Normalisht, ne mund t definojm templejte funksionesh q pranojn tipe t
ndryshme t parametrave, thjesht duke specifikuar parametrat prkats t
templejtave dhe tipet e tyre. Pr shembull:
template <class T, class U>
T VleraMin (T a, U b)
{
return (a<b?a:b);
}
ose thjesht:
i = VleraMin(j,l);
edhe pse, j dhe l kan tipe t ndryshme, pasi q kompajleri sidoqoft mund t
prcaktoj tipin e nevojshm.
Templejtat e klasave
Edhe pr klasat mund t krijohen templejtet, ashtu q klasa mund t ket antar
t cilt prdorin templejtet e parametrave si tipe. Pr shembull:
template <class T>
class dyshja
{
T vlerat[2];
public:
dyshja (T ePara, T eDyta)
{
vlerat[0] = ePara; vlerat[1] = eDyta;
}
};
654
Rezultati do t ishte:
100
655
Avni Rexhepi
Pra, jan tri T n kt deklarim: e para sht parametri i templejtit, e dyta i
refereohet tipit t kthyer prej funksionit dhe e treta (mes kllapave t kndore< >)
sht poashtu e nevojshme: ajo specifikon se parametri i templejtit t funksionit
sht poashtu parametr i templejtit t klass.
Specializimi i templejtave
Nse dshirojm t definojm implementim tjetr pr templejtin kur tipi i
specifikuar sht i prcjellur si parametr templejt, mund t deklarojm
specializimin e atij templejti.
Pr shembull, le t supozojm se kemi nj klas shum t thjesht t emrtuar:
kontejneriIm e cila mund t ruaj nj element t fardo tipi dhe ajo ka vetm nj
funksion t quajtur rrite, i cili e rrit vlern e tij. Por, e shohim se kur ai ruan
elemente t tipit char, do t ishte m e prshtatshme t kemi implementim
krejtsisht tjetr pr at funksion, p.sh., uppercase (angl. upper case
shkronj e madhe), ashtu q vendosim t deklarojm specializimin e templejtit
t klass pr at tip:
// Specializimi i templejtave
#include <iostream>
using namespace std;
// class template:
template <class T>
class kontejneriIm
{
T elementi;
public:
kontejneriIm(T arg) {elementi=arg;}
T rrite () {return ++elementi;}
};
// class template specialization:
template <>
class kontejneriIm<char>
{
char elementi;
public:
kontejneriIm(char arg) {elementi=arg;}
char uppercase()
{
if ((elementi>='a')&&(elementi<='z'))
elementi+='A'-'a';
return elementi;
}
};
656
int main ()
{
kontejneriIm<int> itegeriIm (7);
kontejneriIm<char> karakteriIm ('j');
cout << itegeriIm.rrite() << endl;
cout << karakteriIm.uppercase() << endl;
return 0;
}
Rezultati:
8
J
657
Avni Rexhepi
template <class T, int N>
class sekuencaIme
{
T bllokuMem[N];
public:
void caktoAnetarin (int x, T vlera);
T merrAnetarin (int x);
};
template <class T, int N>
void sekuencaIme<T,N>::caktoAnetarin (int x, T vlera)
{
bllokuMem[x]=vlera;
}
template <class T, int N>
T sekuencaIme<T,N>::merrAnetarin (int x)
{
return bllokuMem[x];
}
int main()
{
sekuencaIme <int,5> vlerat_int;
sekuencaIme <double,5> vlerat_float;
vlerat_int.caktoAnetarin (0,100);
vlerat_float.caktoAnetarin (3,3.1416);
cout << vlerat_int.merrAnetarin(0) << '\n';
cout << vlerat_float.merrAnetarin(3) << '\n';
system("Pause");
return 0;
}
Rezultatati:
100
3.1416
658
Avni Rexhepi
fajllit t njejt t templejtit m t dyjat, edhe me deklarimet edhe me
definicionet, pa gjeneruar gabime (angl. errors) t linkimit (ndrlidhjes s
fajllave).
660
Shtojca B - Rekurrenca
Rekurrencat themelore
Shum algoritme jan t bazuara n principin e shprbrjes (dekompozimit)
rekurzive t problemit t madh n nj ose m shum probleme t vogla, duke
prdorur zgjidhjet e nnproblemeve pr t zgjidhur problemin origjinal
(fillestar). N kt pjes do t shohim metodat themelore pr analizn e
analizimin e algoritmeve t tilla dhe derivojm zgjidhje disa formula standarde
t cilat paraqiten n analizn e shum algoritmeve. T kuptuarit e tipareve
matematikore t formulave n kt pjes do t jep pasqyr n tiparet e
performanss s algoritmeve.
Dekompozimi rekurziv n nj algoritm reflektohet drejtprdrejt n analizn e
tij. Pr shembull, koha e ekzukutimit t algoritmeve t tilla prcaktohet nga
madhsia e numrit t nnproblemeve dhe koha e krkuar pr dekompozim.
Matematikisht, varsia e kohs s ekzekutimit t nj algoritmi pr nj madhsi t
hyrjes N, nga koha e ekzekutimit t tij pr vlera m t vogla hyrse, shprehet
leht prmes formulave t quajtura relacionet e rekurrencs (angl. requrrencerishfaqje, rikthim, prsritje). Formulat e tilla prshkruajn n mnyr precize
performansn e algoritmeve gjegjse: pr t nxjerr kohn e ekzekutimit, i
zgjidhim rekurrencat. Argumente m rigoroze lidhur me algoritmet specifike
paraqiten n algoritmet prkatse, kurse ktu do t koncentrohemi n vet
formulat e rekurrencs.
Formula 2.1 Rekurrenca q paraqitet pr programet rekurzivet t cilat kalojn
npr tr hyrjen (npr t gjitha vlerat hyrse), pr t eliminuar nj element,
sht:
CN = CN-1 + N,
pr N2, me C1 = 1,
Zgjidhja:
CN sht afrsisht N2/2. Pr t zgjidhur nj rekurrenc t till, e teleskopojm
(e zgjasim brenda njra-tjetrs) duke e aplikuar at n vetvetn e saj, si n vijim:
CN = CN-1 + N
CN = CN-2 + (N-1) + N
CN = CN-3 + (N-2) + (N-1) + N
...
Duke vazhduar n kt mnyr, n fund gjindet se
CN = C1 + 2 + ... + (N-2) + (N-1) + N
661
Avni Rexhepi
CN = 1 + 2 + ... + (N-2) + (N-1) + N
CN =
N ( N 1)
2
662
pr N2, me C1 = 0.
Zgjidhja:
CN sht afrsisht 2N. Rekurrenca teleskopohet n shumn N + N/2 + N/4 +
N/8 + ... . (Ngjashm sikur formula 2.2, rekurrenca sht e definuar n mnyr
precize vetm kur N sht fuqi e 2-shit). Nse sekuenca sht e pakufi, kjo
shum e thjesht gjeometrike rezulton saktsisht n 2N. Pasi q prdorim
pjestimin e plot dhe ndalemi n 1, kjo vler sht nj prafrim i prgjigjes s
sakt. Zgjidhja precize prfshin tiparet e reprezentimit binar t N.
Formula 2.4 rekurrenca q paraqitet pr programin rekurziv i cili duhet t
bj nj kalim linear npr hyrjen, para, gjat ose pas ndarjes s asaj hyrjeje n
dy gjysma, sht:
CN = 2CN/2 + N,
pr N2, me C1 = 0.
Zgjidhja:
663
Avni Rexhepi
CN sht afrsisht N lg N. kjo zgjidhje sht m e cituara prej t gjithave t
prmendura ktu, sepse kjo rekurrenc aplikohet n familjen e algoritmeve
standarde praj-e-sundo.
pr N2, me C1 = 1.
Zgjidhja:
CN sht prafrsisht 2N.
Kjo zgjidhje mund t derivohet n mnyr t njjt si zgjidhja n formuln 2.4.
Mund t zgjidhim variantet minore t ktyre formulave, duke prfshir edhe
kushtet fillestare t ndryshme ose ndryshime t vogla n termin aditiv, duke
prdorur teknikat e njjta t zgjidhjes, edhe pse duhet t jemi t vetdijshm se
disa rekurrenca duken t ngjashme me kto, n fakt mund t jen t vshtira pr
tu zgjidhur. Ka llojllojshmri t teknikave t prgjithshme t avansuara pr tu
marr me ekuacione t tilla me rreptsi (saktsi) matematikore.
664
665
Avni Rexhepi
BinarySearchTree::BinarySearchTree()
{
root = NULL;
size = 0;
}
BinarySearchTree::~BinarySearchTree()
{
removeAll();
}
bool BinarySearchTree::add(char* key, char* value)
{
if(key == NULL || value == NULL || strlen(key) > SIZE_KEY-1
|| strlen(value) > SIZE_VALUE-1)
{
return false;
}
METADATA* new_node = new METADATA(key, value);
return addNode(&root, new_node);
}
bool BinarySearchTree::addNode(METADATA** current_node, METADATA*
new_node)
{
if(*current_node == NULL)
{
*current_node = new_node;
size++;
return true;
}
else
{
if(strcmp(new_node->key, (*current_node)->key) < 0)
{
return addNode(&((*current_node)->left), new_node);
}
else if(strcmp(new_node->key, (*current_node)->key) > 0)
{
return addNode(&((*current_node)->right), new_node);
}
else
{
delete new_node;
return false;
}
}
}
bool BinarySearchTree::remove(char* key)
{
return removeNode(&root, key);
}
//function
bool BinarySearchTree::removeNode(METADATA** node, char* key)
{
666
667
Avni Rexhepi
strcpy(root->key, (*node)->key);
strcpy(root->value, (*node)->value);
*node = (*node)->right;
delete(temp);
}
else
{
moveLeftMostNode(&((*node)->left), root);
}
}
void BinarySearchTree::removeAll()
{
removeAllNodes(root);
root = NULL;
size = 0;
}
void BinarySearchTree::removeAllNodes(METADATA* node)
{
if(node != NULL)
{
removeAllNodes(node->left);
removeAllNodes(node->right);
cout<<"Largohet nyja elesi(key):"<<node->key<<"\t"<<node->value
<< endl;
delete node;
}
}
bool BinarySearchTree::get(char* key, char* value)
{
return getNode(root, key, value);
}
bool BinarySearchTree::getNode(METADATA* node, char* key, char* value)
{
if(node == NULL)
{
value[0] = '\0';
return false;
}
else
{
if(strcmp(key, node->key) == 0)
{
strcpy(value, node->value);
return true;
}
else if(strcmp(key, node->key) < 0)
{
return getNode(node->left, key, value);
}
else
{
return getNode(node->right, key, value);
668
669
Avni Rexhepi
int depth_left;
int depth_right;
if(node == NULL)
{
return 0;
}
else
{
depth_left = getTreeDepth(node->left);
depth_right = getTreeDepth(node->right);
if(depth_left > depth_right)
{
return depth_left + 1;
}
else
{
return depth_right + 1;
}
}
}
Shembull 2:
// PemaBinare2.cpp
#include "stdafx.h"
//Program: "Binary Search Tree"
#include <iostream>
#include <cstdlib>
using namespace std;
class BinarySearchTree
{
private:
struct tree_node
{
tree_node* left;
tree_node* right;
int data;
};
tree_node* root;
public:
BinarySearchTree()
{
root = NULL;
}
670
};
// Elementet e vogla majtas, te medhajat djathtas
void BinarySearchTree::insert(int d)
{
tree_node* t = new tree_node;
tree_node* parent;
t->data = d;
t->left = NULL;
t->right = NULL;
parent = NULL;
// a eshte pema e zbrazet?
if(isEmpty()) root = t;
else
{
//Verejtje: Te gjitha insertimet jane si nyje gjethe
tree_node* curr;
curr = root;
// Gjeje prindin e nyjes
while(curr)
{
parent = curr;
if(t->data > curr->data) curr = curr->right;
else curr = curr->left;
}
if(t->data < parent->data)
parent->left = t;
else
parent->right = t;
}
}
void BinarySearchTree::remove(int d)
{
//Lokalizo elementin
bool found = false;
if(isEmpty())
{
cout<<"\n Pema eshte e zbrazet! "<<endl;
return;
671
Avni Rexhepi
}
tree_node* curr;
tree_node* parent;
parent = NULL;
curr = root;
while(curr != NULL)
{
if(curr->data == d)
{
found = true;
break;
}
else
{
parent = curr;
if(d>curr->data) curr = curr->right;
else curr = curr->left;
}
}
if(!found)
{
cout<<" \nVlera e dhene nuk ekziston! "<<endl;
return;
}
// 3 raste :
// 1. Largohet nyja gjethe
// 2. Largohet nyja me vetem nje femije
// 3. Largohet nyja me dy femije
// Nyja me vetem nje femije
if((curr->left == NULL && curr->right != NULL)|| (curr->left != NULL
&& curr->right == NULL))
{
if(curr->left == NULL && curr->right != NULL)
{
if(parent->left == curr)
{
parent->left = curr->right;
delete curr;
}
else
{
parent->right = curr->right;
delete curr;
}
}
else // Ekziston femija i majte, nuk ka femije te djathte
{
if(parent->left == curr)
672
//Nyja me 2 femije
// zevendesoje nyjen me vleren me te vogel ne nen-pemen e djathte
if (curr->left != NULL && curr->right != NULL)
{
tree_node* chkr;
chkr = curr->right;
if((chkr->left == NULL) && (chkr->right == NULL))
{
curr = chkr;
delete chkr;
curr->right = NULL;
}
else // femija i djathte ka femije
{
//nese femija i djathte i nyjes ka femije te majte
//shko deri ne fund te anes se majte, per te gjetur elementin
me te vogel
if((curr->right)->left != NULL)
{
tree_node* lcurr;
tree_node* lcurrp;
lcurrp = curr->right;
lcurr = (curr->right)->left;
while(lcurr->left != NULL)
{
lcurrp = lcurr;
lcurr = lcurr->left;
}
673
Avni Rexhepi
curr->data = lcurr->data;
delete lcurr;
lcurrp->left = NULL;
}
else
{
tree_node* tmp;
tmp = curr->right;
curr->data = tmp->data;
curr->right = tmp->right;
delete tmp;
}
}
return;
}
}
void BinarySearchTree::print_inorder()
{
if(isEmpty())
{
cout<<"\n Pema eshte e zbrazet! "<<endl;
return;
}
inorder(root);
}
void BinarySearchTree::inorder(tree_node* p)
{
if(p != NULL)
{
if(p->left) inorder(p->left);
cout<<" "<<p->data<<" ";
if(p->right) inorder(p->right);
}
else return;
}
void BinarySearchTree::print_preorder()
{
if(isEmpty())
{
cout<<"\n Pema eshte e zbrazet! "<<endl;
return;
}
preorder(root);
}
void BinarySearchTree::preorder(tree_node* p)
{
674
675
Avni Rexhepi
case 2 :
case 3 :
case 4 :
case 5 :
b.insert(tmp);
break;
cout<<endl;
cout<<" Pershkimi: In-Order "<<endl;
cout<<" -------------------"<<endl;
b.print_inorder();
break;
cout<<endl;
cout<<" Pershkimi: Pre-Order "<<endl;
cout<<" -------------------"<<endl;
b.print_preorder();
break;
cout<<endl;
cout<<" Pershkimi: Post-Order "<<endl;
cout<<" --------------------"<<endl;
b.print_postorder();
break;
cout<<" Jepni vleren qe duhet te fshihet: ";
cin>>tmp1;
b.remove(tmp1);
break;
case 6 :
return 0;
}
}
}
Shembull 3:
// PemaBinare3.cpp
#include "stdafx.h"
#include <iostream>
using namespace std;
//Node class
class Node {
int key;
Node* left;
Node* right;
public:
Node() { key=-1; left=NULL; right=NULL; };
void setKey(int aKey) { key = aKey; };
void setLeft(Node* aLeft) { left = aLeft; };
void setRight(Node* aRight) { right = aRight; };
int Key() { return key; };
Node* Left() { return left; };
Node* Right() { return right; };
};
676
677
Avni Rexhepi
// Shty nyje (private)
void Tree::addNode(int key, Node* leaf) {
if ( key <= leaf->Key() ) {
if ( leaf->Left() != NULL )
addNode(key, leaf->Left());
else {
Node* n = new Node();
n->setKey(key);
leaf->setLeft(n);
}
}
else {
if ( leaf->Right() != NULL )
addNode(key, leaf->Right());
else {
Node* n = new Node();
n->setKey(key);
leaf->setRight(n);
}
}
}
// Shtypja "in-order" e pemes
// Pershko nen-pemen e majte, rrenjen, nen-pemen e djathte
void Tree::inOrder(Node* n) {
if ( n ) {
inOrder(n->Left());
cout << n->Key() << " ";
inOrder(n->Right());
}
}
// Shtypja "pre-order" e pemes
// Pershko rrenjen, nen-pemen e majte, nen-pemen e djathte
void Tree::preOrder(Node* n) {
if ( n ) {
cout << n->Key() << " ";
preOrder(n->Left());
preOrder(n->Right());
}
}
// Shtypja "post-order" e pemes
// Pershko nen-pemen e majte, nen-pemen e djathte, rrenjen
void Tree::postOrder(Node* n) {
if ( n ) {
postOrder(n->Left());
postOrder(n->Right());
cout << n->Key() << " ";
}
}
678
Shembull 4:
//Pema binare4
#include <iostream>
using namespace std;
// Klasa e pergjithshme e nyjes se pemes
class Node {
int key;
Node* left;
Node* right;
Node* parent;
public:
Node() { key=-1; left=NULL; right=NULL; parent = NULL;};
void setKey(int aKey) { key = aKey; };
void setLeft(Node* aLeft) { left = aLeft; };
void setRight(Node* aRight) { right = aRight; };
void setParent(Node* aParent) { parent = aParent; };
int Key() { return key; };
Node* Left() { return left; };
Node* Right() { return right; };
Node* Parent() { return parent; };
};
// Klasa e pemes binare te kerkimit (BST)
class Tree {
679
Avni Rexhepi
Node* root;
public:
Tree();
~Tree();
Node* Root() { return root; };
void addNode(int key);
Node* findNode(int key, Node* parent);
void walk(Node* node);
void deleteNode(int key);
Node* min(Node* node);
Node* max(Node* node);
Node* successor(int key, Node* parent);
Node* predecessor(int key, Node* parent);
private:
void addNode(int key, Node* leaf);
void freeNode(Node* leaf);
};
// Constructor
Tree::Tree() {
root = NULL;
}
// Destructor
Tree::~Tree() {
freeNode(root);
}
// Liroje nyjen
void Tree::freeNode(Node* leaf)
{
if ( leaf != NULL )
{
freeNode(leaf->Left());
freeNode(leaf->Right());
delete leaf;
}
}
// Shto nyje [O(lartesi te pemes) mesatarisht]
void Tree::addNode(int key)
{
// S'ka elemente. Shto nyjen rrenje
if ( root == NULL ) {
cout << "Shto nyjen rrenje... " << key << endl;
Node* n = new Node();
n->setKey(key);
root = n;
}
else {
cout << "Shto nyje tjeter ... " << key << endl;
addNode(key, root);
}
}
680
681
Avni Rexhepi
// Gjeje nyjen me eles (vlere) minimale
// Pershko nen-pemen e majte ne menyre rekurzive
// deri sa nen-pema e majte te jete e zbrazet, per te marre vleren min
Node* Tree::min(Node* node)
{
if ( node == NULL )
return NULL;
if ( node->Left() )
min(node->Left());
else
return node;
}
// // Gjeje nyjen me eles (vlere) maksimale
// Pershko nen-pemen e djathte ne menyre rekurzive
// deri sa nen-pema e djathte te jete e zbrazet, per te marre vleren max
Node* Tree::max(Node* node)
{
if ( node == NULL )
return NULL;
if ( node->Right() )
max(node->Right());
else
return node;
}
// Gjeje nyjen pasuese te nyjes
// Gjeje nyjen, merre nyjen me vlere max
// per nen-pemen e djathte, per ta marre nyjen pasuese
Node* Tree::successor(int key, Node *node)
{
Node* thisKey = findNode(key, node);
if ( thisKey )
return max(thisKey->Right());
}
// Gjeje nyjen paraardhese te nyjes
// Gjeje nyjen, merre nyjen me vlere max
// per nen-pemen e majte, per ta marre nyjen paraardhese
Node* Tree::predecessor(int key, Node *node)
{
Node* thisKey = findNode(key, node);
if ( thisKey )
return max(thisKey->Left());
}
// Fshije nyjen
// (1) Nese eshte gjete, vetem fshije
// (2) Nese ka vetem nje femije, fshije nyjen dhe zevendesoje me femijen
682
683
Avni Rexhepi
}
// Programi kryesor
int main() {
Tree* tree = new Tree();
//Shto nyjet
tree->addNode(300);
tree->addNode(100);
tree->addNode(200);
tree->addNode(400);
tree->addNode(500);
// Pershko pemen
cout<<"Nyjet e pemes: ";
tree->walk(tree->Root());
cout << endl;
// Gjeji nyjet
if ( tree->findNode(500, tree->Root()) )
cout << "Nyja 500 u gjet" << endl;
else
cout << "Nyja 500 nuk u gjet" << endl;
if ( tree->findNode(600, tree->Root()) )
cout << "Nyja 600 u gjet" << endl;
else
cout << "Nyja 600 nuk u gjet" << endl;
// Min & Max
cout << "Min=" << tree->min(tree->Root())->Key() << endl;
cout << "Max=" << tree->max(tree->Root())->Key() << endl;
// Pasardhese dhe paraardhese
cout << "Pasardhes i 300=" <<
tree->successor(300, tree->Root())->Key() << endl;
cout << "Paraardhes i 300=" <<
tree->predecessor(300, tree->Root())->Key() << endl;
// Fshirja e nyjes
cout << "U fshi nyja 300\n";
tree->deleteNode(300);
// Pershkimi i pemes
cout<<"Nyjet e pemes: ";
tree->walk(tree->Root());
cout << endl;
delete tree;
system("Pause");
return 0;
}
684
685
Avni Rexhepi
9 // void remove( x ) --> Remove x
10 // Object find( x ) --> Return item that matches x
11 // void makeEmpty ( ) --> Remove all items
12 // ***************ERRORS****************************
13 // Throws exceptions as warranted.
14
15 template <class Object>
16 class HashTable
17 {
18 public:
19 HashTable ( ) ;
20
21 void makeEmpty ( i ;
22
23 Cref<Object> find( const Object & x ) const;
24 void insert( const Object & x 1;
25 void remove( const Object & x ) ;
26
27 enum EntryType ( ACTIVE, EMPTY, DELETED 1;
28
29 private:
30 struct HashEntry
31 {
32 Object element;
33 EntryType info;
34
35 HashEntry ( const Object & e = Object ( ) ,
36 EntryType i = EMPTY i
37 : element( e ), info( i ) { }
38 };
39
40 vector<HashEntry> array;
41 int occupied;
42
43 boo1 isActive( int currentpos ) const;
44 int findPos ( const Object & x ) const;
45 void rehash( ) ;
46 };
47
48 unsigned int hash( const string & key );
687
Avni Rexhepi
2
3
4
5
6
688
689
Avni Rexhepi
11
12
13
14
15
16
17
{
currentpos += 2 * ++i - 1; // Compute ith probe
if( currentpos >= array.size( ) )
currentpos -= array.size( ) ;
}
return currentpos;
}
690
691
Avni Rexhepi
Literatura:
1. Mark Allen Weiss Data Structures and Problem Solving using C++
2. Martin Richards - Data Structures and Algorithms
3. Adam Drozdek - Data Structures and Algorithms in C++
4. John Morris Data Structures and Algorithms
5. Michael T. Goodrich, Roberto Tamassia , Michael H. Goldwasser, - Data
Structures and Algorithms in Python
6. Dave Mount Data Structures
7. Robert Sedgewick Algorithms in C++
8. Jim Keogh, Ken Davidson Data Structures Demystified
9. Stiven S. Skiena The Algorithm Design Manual
10.
11.
Approach
12.
692
Prmbajtja:
Parathnie ............................................................................................................ 3
Hyrje ..................................................................................................................... 5
Algoritmet ........................................................................................................ 5
Strukturat e t dhnave ..................................................................................... 9
Strukturat themelore t t dhnave ............................................................. 11
Konceptet themelore t programimit t orientuar n objekte ......................... 15
Strukturat e t dhnave dhe reprezentimi i tyre.......................................... 18
Lista e lidhur............................................................................................... 19
Lista e lidhur njfish ................................................................................... 20
Lista e lidhur dyfish.................................................................................... 21
Steku ........................................................................................................... 22
Rreshti, radha e pritjes ................................................................................ 23
1.
Avni Rexhepi
Vargjet dhe stringjet ........................................................................................ 46
1.2.1 Objektet First-Class dhe Second-Class ....................................... 47
Prdorimi i vector-it ............................................................................ 48
1.2.3 Ndryshimi i madhsis s vektorit ..................................................... 50
1.2.5 Mekanizmat e prcjelljes s parametrave t funksionit ..................... 54
1.2.6 Vargjet primitive t konstanteve ........................................................ 56
1.2.8 Tipi string nga libraria standarde..................................................... 57
Sintaksa e pointerve n C++ ..................................................................... 58
1.4 Menaxhimi dinamik i memories (Dynamic Memory Management) ........ 63
1.4.1. Operatori new ................................................................................ 63
1.4.2 Pastrimi i mbetjeve dhe fshirja........................................................... 64
1.4.3 Stale Pointert, fshirja e dyfisht dhe problemet tjera .................... 64
1.5 Referencat ................................................................................................. 66
1.6 Strukturat dhe pointert ............................................................................. 69
1.6.1 T dhnat ekzogjene kundrejt atyre indigjene dhe kopjimi i cekt
kundrejt atij t thell ................................................................................... 71
1.6.1 Listat jo t vazhduara listat e lidhura .............................................. 73
1.7.1 Kontejnert ......................................................................................... 75
1.7.2 Iteratort ............................................................................................. 76
1.7.3 STL Algoritmet .................................................................................. 76
Aritmetika e pointerve ............................................................................... 77
Pointert n pointer ................................................................................... 80
Vargjet primitive ......................................................................................... 82
Vargu dhe stringu ............................................................................................ 89
Prparsit ................................................................................................... 89
T metat....................................................................................................... 90
Vargjet statike dhe dinamike....................................................................... 90
Madhsia fikse dhe vargjet dinamike.......................................................... 90
Lidhja me stringjet ...................................................................................... 91
694
3.
Avni Rexhepi
Stack (Steku) ................................................................................................. 141
Steku prmes vargjeve .............................................................................. 142
Stack ADT................................................................................................. 142
Operacionet ............................................................................................... 143
Aksiomat pr stekun.................................................................................. 143
Krijimi i stekut n C++ ............................................................................. 146
Steku n veprim ........................................................................................ 151
Implementimi i stekut t bazuar n vargje ................................................ 156
Implementimi ............................................................................................ 156
Prdorimi i stekut ...................................................................................... 158
Rreshti - Queue (Kju) .................................................................................... 162
Queue prmes vargut n C++ ................................................................... 167
4.
696
6.
Avni Rexhepi
Pema e krkimit binar - Binary search tree ................................................... 369
Pema binare e krkimit reprezentimi i brenshm................................... 370
Shembull ................................................................................................... 372
Pema binare e krkimit operacioni i krkimit ........................................ 375
Shembull ................................................................................................... 375
Shembull ................................................................................................... 384
Pirgu binar ..................................................................................................... 386
Pema binare komplete ............................................................................... 386
Shembull korrekt i pems binare komplete............................................... 386
Shembull jo korrekt, niveli i mesm sht i pakompletuar ....................... 386
Shembull jo korrekt, niveli i fundit ka "vrim" ........................................ 386
Tipari Heap ............................................................................................... 387
Pirgu binar - Reprezentimi i brendshm i bazuar n vargje .......................... 388
Pasqyrimi i pirgut n varg ......................................................................... 388
Insertimi i elementit n pirg ...................................................................... 390
Shembull ................................................................................................... 391
Largimi i minimumit nga pirgu ................................................................. 395
Shembull ................................................................................................... 395
Pemt e balansuara ........................................................................................ 400
Pemt AVL ............................................................................................... 400
Pemt kuq e zi ............................................................................................... 414
Tiparet e pemve kuq e zi ......................................................................... 416
Splay Trees .................................................................................................... 423
Operacionet ............................................................................................... 425
Lista me kaprcime - Skip List ..................................................................... 433
Pemt M-are .................................................................................................. 445
B-Pemt (B-Trees) ........................................................................................ 448
7.
698
9.
Avni Rexhepi
Quicksort ....................................................................................................... 530
Algoritmi i ndarjes .................................................................................... 531
SHELLSORT ................................................................................................ 535
RADIX SORT-i ............................................................................................ 537
HEAPSORT .................................................................................................. 540
Sorti Merge ................................................................................................ 543
Koncepte t programimit .............................................................................. 548
10.
11.
701