Vous êtes sur la page 1sur 701

Algoritmet dhe strukturat e t dhnave

Avni Rexhepi

Prishtin 2014
1

Avni Rexhepi

Algoritmet dhe strukturat e t dhnave

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

Algoritmet dhe strukturat e t dhnave

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).

Abu Ja'far Abdallah Muhammad ibn


Musa Al-Khwarizmi
[rreth 780-850 n Baghdad]

Kur n ndonj gjuh programuese shkruajm programe pr kompjuter, ne n


prgjithsi implementojm metodn q sht zbuluar (shpikur, krijuar) m par,
6

Algoritmet dhe strukturat e t dhnave


pr zgjidhjen e ndonj problemi, gjegjsisht algoritmin pr zgjidhjen e
problemit. do program sht ilustrim i ndonj algoritmi.
Kjo metod sht zakonisht e pavarur prej gjuhs programuese dhe prej
kompjuterit t veant q do t prdoret dhe zakonisht sht njsoj e
prshtatshme pr shum kompjuter dhe pr shum gjuh programuese. N fakt,
m shum sht metoda sesa vet programi kompjuterik q duhet t studiohet
pr t msuar se si sht duke u atakuar problemi. Termi algoritm prdoret
n shkencat kompjuterike pr t prshkruar metodn e prshtatshme pr
zgjidhjen e problemit dhe pr ta implementuar si program kompjuteri.
Algoritmet jan material (lnd e par, lnd pune) pr shkencat
kompjuterike. Ato jan objekti qndror i studimit n t gjitha pjest e ksaj
fushe.
Shumica e algoritmeve t rndsishme prfshijn metodat pr organizimin e t
dhnave t prfshira n llogaritje. Objektet e krijuara n kt mnyr quhen
struktura t t dhnave dhe kto jan gjithashtu objekte qndrore t studimit
n shkencat kompjuterike. Prandaj, algoritmet dhe strukturat e t dhnave,
shkojn dor pr dore (s bashku). Pra, pr t kuptuar algoritmet duhet
studiuar edhe strukturat e t dhnave. Ka raste kur algoritmet e thjeshta nxjerrin
n pah struktura t komplikuara dhe anasjelltas, algoritmet e komplikuara
mund t prdorin struktura t thjeshta t t dhnave. Parimisht, do t studiohen
dhe prezentohen tiparet (vetit, karakteristikat) e shum strukturave t t
dhnave.
Kur prdorim kompjuterin pr t zgjidhur nj problem, zakonisht ballafaqohemi
me nj numr t qasjeve t ndryshme t mundshme pr zgjidhjen e problemit.
Pr problemet e vogla, rrall her sht me rndsi se cila qasje prdoret,
prderisa e kemi at q e zgjidh problemin si duhet. Mirpo, pr problemet e
mdha (ose pr aplikacionet ku duhet njnumr i madh i problemeve t vogla),
shpejt motivohemi q t krijojm metoda t cilat prdorin kohn dhe hapsirn
(memorike) n mnyr sa m efikase t mundshme.
Arsyeja kryesore pr studimin e dizajnit t algoritmeve sht se kjo disciplin na
jep potencialin pr t br kursime t shumta edhe deri n pikn e mundsimit t
kryerjes s detyrave t cilat ndryshe do t ishte e pamundur t kryhen. N nj
aplikacion ku procesohen miliona objekte, nuk sht e pazakont q t bhet nj
program miliona her m i shpejt, duke prdorur nj algoritm t dizajnuar
mir. Kurse, investimi n blerjen e kompjuterit t ri me performansa m t mira,
pr t njjtin problem, ka potencial t prshpejtimit me faktor prej vetm 10 ose
100 her. Dizajni i kujdesshm i algoritmit sht pjes jashtzakonisht efektive e
procesit t zgjidhjes s problemeve t mdha, n do sfer t aplikimit.
Kur duhet zhvilluar nj program jashtzakonisht i madh ose i komplikuar, duhet
investuar shum prpjekje n t kuptuarit dhe definimin e problemit q duhet
7

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

Algoritmet dhe strukturat e t dhnave


Dega e shkencave kompjuterike e cila prfshin studimin e pyetjeve t tilla,
quhet analiza e algoritmeve. Shum prej algoritmeve prmes analizs s till
jan treguar q kan performans t shklqyeshme, ndrsa t tjerat thjesht dihet
se punojn mir, nga prvoja. Qllimi kryesor sht q t msohen algoritmet e
arsyeshme pr detyrat e rndsishme, mirpo duke u kujdesur pr krahasimin e
performansave t metodave. Nuk duhet t prdoret nj algoritm pa pasur ide se
sa resurse mund t konsumoj dhe duhet prpjekur q t jemi t vetdijshm pr
at se si mund t pritet t performoj algoritmi.
Algoritmet manipulojn me t dhnat, t cilat mund t jen vlera t veanta t
tipeve t thjeshta t t dhnave ose t quajtura ndryshe primitive (primitive
data), si bitat, karakteret, numrat natyral, numrat real, etj dhe mund t jen t
dhna t strukturuara n forma m t avansuara, pr t ju prshtatur nevojave
nga realiteti, t ashtuquajtura struktura t t dhnave (angl. Data Structures).

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

Algoritmet dhe strukturat e t dhnave


konkret i zhvilluar n ndrtimin e tipeve abstrakte t t dhnave plotson nevojat
e shum apliacioneve.

Strukturat themelore t t dhnave


T dhnat ruhen n memorie. Kur kemi pr t ruajtur vlera t veanta,
interpretimi logjik prputhet shum leht me realitetin fizik, sepse p.sh., kur
deklarojm int x=10; themi q kemi deklaruar nj numr t plot, me emrin
x dhe i kemi dhn vlern 10. sht leht t imagjinohet, se diku n memorie,
do t ruhet vlera 10.
Kur kemi nj bashksi t t dhnave, q dshirojm ta ruajm si nj trsi, p.sh.,
notat e studentit, pagat e puntorve, etj., ather e krijojm nj varg. Antart e
vargut jan t njjt pr nga tipi dhe lokalizohen n memorie n lokacione t
njpasnjshme. Deklarimi i vargut, bn q t rezervohet hapsira e duhur n
memorie dhe pastaj aty vendosen vlerat e antarve t vargut. M von, prmes
qasjes direkte ose pointerve, mund t bhet qasja n antart e vargut. Edhe n
kt rast, interpertimi logjik sht i thjesht, sepse e imagjinojm vargun e
lokacioneve t njpasnjshme n memorie, ku i kemi t vendosura disa vlera.
Mirpo, pr arsye t ndryshme, ndonjher nuk ka mundsi ose nuk sht e
prshtatshme q t gjitha vlerat e bashksis t ruhen n lokacione t
njpasnjshme n memorie. Ather kemi mosprputhje ndrmjet realitetit
fizik dhe interpretimit logjik nga ana e jon.
Pra, t dhnat e nj bashksie, pr nga pozicionimi fizik n memorie, mund t
jen:
-

N lokacione t njpasnjshme (sekuenciale) n memorie


N lokacione t shprndara (jo-sekuenciale).

Vargu sht struktur me antar t vendosur n lokacione sekuenciale. Listat


jan me antar n lokacione josekuenciale. Nse t dhnat nuk jan t
vendosura fizikisht n lokacione t njpasnjshme n adresat e memories, por
ato logjikisht duhet t prcillen si antar t njpasnjshm t bashksis,
ather krijojm strukturn e t dhnave, e cila m nuk prmban vetm vlerat
(t dhnat) e tipit t caktuar, por secili antar sht i prcjellur edhe me
informacione plotsuese, t cilat mundsojn ndrlidhjen logjike me antrt e
tjer t bashksis. Kto trsi t reja, tani prveq vlers, kan edhe
elementin pr ndrlidhje, pra elementin e ri plotsues (pointerin), ashtu q t
na mundsojn q t lvizim prej nj antari n tjetrin, ngjashm sikur lvizim
npr antart e vargut t zakonshm, prej nj lokacioni t memories n tjetrin
(n fakt duke kaluar prej nj antari n tjetrin). Kjo trsi e re, e krijuar prej vet
vlers dhe prej pointerve t cilt e lidhin me antarin e prparshm dhe/ose at
11

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.

Figura 1 Krahasimi i vargut dhe lists s lidhur


Pr nga aspekti i renditjes logjike t antarve, strukturat mund t jen:
-

Lineare (vargu, listat e lidhura, steku, rreshti i pritjes, etj),


Jo-lineare (pemt, grafet).

Strukturat lineare jan lineare n at q ndrmjet objekteve n struktur ruhet


renditja lineare. Relacioni linear sht logjik, n at q pr dallim prej vargjeve,
nuk mund t bhet ndonj presupozim pr lidhjen ndrmjet renditjes lineare t
objekteve dhe lokacioneve t tyre aktuale n memorie. Strukturat lineare
dallojn prej njra tjetrs pr nga kufizimet n mnyrat te qasjes n antart e
tyre.
12

Algoritmet dhe strukturat e t dhnave


N varsi t zgjedhjeve t opcioneve pr numrin e lidhjeve (pointerve pr
lidhje) dhe pr lidhjen e pointerit t fundit n struktur, jan katr lloje t
reprezentimit t listave.
Pr nga mnyra e lidhjes s pointerit t fundit, jan dy mundsi: ose pointeri i
fundit bhet Null (angl. Null-asgj, nuk ekziston) ose kthehet n antarin e
par n struktur. Nse antari i fundit tregon n Null, thuhet se struktura
sht e tokzuar dhe paraqitet zakonisht me simbolin elektronik t tokzimit.
Nse pointeri i fundit ktheht n antarin e par n struktur, ather thuhet se
struktura sht qarkore (cirkulare).
Pr nga numri i lidhjeve, mund t ket vetm nj pointer pr n elementin e
ardhshm n struktur ose dy pointer, q pointojn njri n elementin e
prparshm dhe tjetri n elementin e ardhshm. Struktura lineare e lidhur me
vetm nj element pr lidhje (pointer) quhet list e lidhur n nj kahje ose list e
lidhur nj-fish. Struktura me dy lidhje formon listn e lidhur n dy kahje ose
listn e lidhur dy-fish.
Nga kjo del se listat e lidhura mund t jen: nj-fishe t tokzuara, nj-fishe
cirkulare, dy-fishe t tokzuara dhe dy-fishe cirkulare.

...

...

...

...

...

...

Figura 2 Llojet e listave t lidhura


Pr nga aspekti i krijimit/rezervimit
vargjet/listat/strukturat ndahen n:
-

hapsirs

memorie,

Statike, dhe
Dinamike.

Kur bhet deklarimi i zakonshmm i vargut, si p.sh., inta A[10];, n fakt


bhet prcaktimi i tipit dhe numrit t antarve dhe rezervohet hapsira e
nevojshme n memorie (n lokacione t njpasnjshme). Gjat ekzekutimit t
programit, madhsia e vargut dhe lokacioni n memorie nuk ndryshojn, ksht
q themi se kemi t bjm me varg/struktur statike.
13

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.

Figura 3a - Vendosja e katr elementeve n memorie

Figura 3b - Nj mnyr e ruajtjes s pointerve pr prcjellje t lokacioneve


Kur krijojm ADT dhe deklarojm strukturn prkatse, m nuk kemi t bjm
vetm m vlern (t dhnn) q ruhet n memorie, por edhe me t gjitha
14

Algoritmet dhe strukturat e t dhnave


elementet prcjellse, t cilat mundsojn trajtimin logjik t t dhnave, si jan
pointert t cilt mundsojn lvizjen npr dhe prcjelljen e antarve si dhe
funksioneve prkatse, t cilat shrbejn pr ti dhn jet t
dhnave/elementeve t strukturs.
Funksionet krijohen pr operacionet e zakonshme t cilat ndodhin me t dhnat:
insertimi, leximi, shtypja, editimi, fshirja (largimi), etj. Kshtu strukturat e
kompletuara, t realzuara n C++, si struktur ose klas ose edhe ato t gatshmet
nga STL-i, i kan t gjitha kto funksione.
T gjitha realizohen duke u bazuar n konceptet e programimit t orientuar n
objekte.

Konceptet themelore t programimit t orientuar n


objekte
Programimi i orientuar n objekte - POO (angl. Object orientet programming
OOP), karakterizohet me konceptet e klasave, objekteve, trashgimis,
abstraksionit, riprdorimit, polimorfizmit, etj.
Klasat
Klasa sht nj struktur (struct) e zgjeruar, q ofron tiparet e orientuara n
objekte t C++-it. Klasa definohet nga shfrytzuesi duke prshkruar nj bashksi
t t dhnave (vlerave) q mund ti prfaqsoj dhe nj bashksi t funksioneve
t cilat mund t veprojn (operojn) n ato t dhna. Kto t dhna dhe
funksione t klass quhen antar t klass (angl. class members). Klasat jan
tipe t t dhnave nga t cilat krijohen objektet. Klasat enkapsulojn (angl.
encapsulate-fut n kapsul) t dhnat prmes prdorimit t antarve t dhna
dhe antarve funksione.
Objektet
Bashkimi i t dhnave dhe funksioneve sht koncepti n prapavi t gjuhve
programuese t orientuara n objekte. Njsia e till (e bashkuar) quhet objekt.
Objekti sht nj instanc e klass (nj rast konkret, nj konkretizim i klass).
Klasa ka relacion t njjt me objektet sikur tipet themelore t t dhnave me
variablat e tipit t tyre. Nj objekt mund t definohet n mnyr unike prmes
nj emri specifik (identifikatori). Objekteve u ndahet memoria dhe nj objekt
mund t prmbaj disa atribute.
Trashgimia
Trashgimia (angl. inheritance) sht nj prej vetive m t fuqishme t
programimit t orientuar n objekte. Trashgimia sht procesi prmes t cilit
15

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

Algoritmet dhe strukturat e t dhnave


dhnave (angl. Abstract data type-ADT) me setin e vet t operacioneve.
Abstraksioni i t dhnave i referohet veprimit t reprezentimit t vetive
themelore, pa i prfshir detajet ose shpjegimet. Klasat ndihmojn n krijimin e
tipeve abstrakte t t dhnave. Klasat prdoren pr abstraksionin e t dhnave
duke fshehur implementimin e tipit n pjesn private t definicionit t klass dhe
duke ofruar interfejsin prmes pjess publike t funksioneve.
Polimorfizmi
Polimorfizmi sht vetija q lejon q nj emr t prdoret pr dy ose m shum
qllime t ndrlidhura, por teknikisht t ndryshme. (Fjala polimorfizm rrjedh
nga greqishtja e vjetr: poli-shum, morphe-form). Polimorfizmi lejon q nj
emr t specifikohet pr veprimet e prgjithshme t klass. Polimorfizmi do t
thot q nj pjes e kodit (zakonisht funksion) ose operacionie apo objekte, kan
sjellje t ndryshme n kontekste t ndryshme. N klasn e prgjithshme, ndonj
veprim specifik q duhet t aplikohet, prcaktohet (varet) nga tipi i t dhnave.
Prparsia e polimorfizmit sht se ndihmon n zvoglimin e kompleksitetit t
programit duke lejuar q nj interfejs t specifikoj nj klas t prgjithshme t
veprimeve. N C++, polimorfizmi kryesisht i referohet prdorimit t
funksioneve virtuele. Mund t ket dy objekte t krijuara nga nje klas, por t
cilat funksionin virtuel (me emr t njjt pr t dyjat), e prdorin pr llogaritje
t ndryshme.
Mbingarkimi sht veti shum e dobishme n C++. Wsht e mundur q t
prdoret emri i njjt i funksionit pr qllime t ndryshme. Funksioni i duhur do
t thirret bazuar n numrin, radhn ose tipin e parametrave (argumenteve) t
funksionit. Ky proces referohet si mbingarkim i funksioneve ose polimorfizm i
funksioneve. Polimorfizmi mund t aplikohet poashtu edhe n operatort e
ndryshm dhe ky proces njihet si mbingarkimi i operatorve ose polimorfizmi i
operatorve.

Prparsit e programimit t orientuar n objekte


Programimi i orientuar n objekte ofron shum prparsi pr programert dhe
shfrytzuesit. POO zgjidh shum probleme t ndrlidhura me zhvillimin e
softverit, ofron kualitetit t prmirsuar dhe softver me mime m t ulta.
Prparsit e POO jan:
1. Programet e orientuara n objekte mund t promovohen (angl. upgrade)
n do koh dhe me lehtsi.
2. Duke prdorur trashgimin, mund t eliminohen kodet redundante (t
teprta) dhe mund t vazhdohet prdorimi klasave t definuara m par.

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.

Strukturat e t dhnave dhe reprezentimi i tyre


Strukturat e t dhnave klasifikohen si lineare dhe jo-lineare. Struktura e t
dhnave thuhet se sht lineare nse elementet e saj t t dhnave formojn
sekuenc. Kjo do t thot q nse e dijm adresn e elementit t par, ne mund
t marrim (prfitojm) t dytin, t tretin, e kshtu me radh deri n elementin e
fundit t t dhnave. Shembuj t strukturave lineare jan vargjet, steku, queue,
listat e lidhura, etj. Ndrsa n strukturat jo-lineare, elementet e t dhnave ruhen
n hierarki ose n nivele. Shembuj t tyre jan pemt dhe grafet.
Mnyrat e ruajtjes
Synimi kryesor i strukturave t t dhnave sth ruajtja e disa t dhnave
(vlerave) ose organizimi i t dhnave n ndonj form t veant. Pr nga
mnyra se si ruhen ose mirmbahen n memorie t kompjuterit, jan dy mnyra
t reprezentimit (prfaqsimit) t strukturave t t dhnave (lineare ose jolineare) n memorie. Njra sht metoda sekuenciale e ruajtjes ndrsa tjetra
sht metoda e alokimit t lidhur. Ato poashtu quhen edhe si alokimi statik dhe
alokimi dinamik, repsektivisht.
Metoda e ruajtjes sekuenciale: nse elementet jan t ruajtura n lokacione t
njpasnjshme n memorie, ato njihen si metoda e ruajtjes sekuenciale. Kjo do
t thot q s brendshmi sht e ruajtur n nj varg nj-dimensional. Poashtu
quhet edhe si alokimi statik, pasi q memoria n esenc sht nj varg i adresave
ose lokacioneve t memories.
Reprezentimi i lidhur: nse elementet do t ruhen n lokacione t ndryshme (t
shprndara) n memorie dhe pointert do t japin (krijojn) renditjen lineare, kjo
quhet reprezentimi i lidhur.

18

Algoritmet dhe strukturat e t dhnave

Operacionet n strukturat e t dhnave


Pr fardo strukture t t dhnave, mund t kryhen operacionet themelore
vijuese:
1.
2.
3.
4.
5.
6.

Krijimi ose insertimi


Fshirja/largimi
Paraqitja/prshkimi
Sortimi
Krkimi
Bashkimi

N rastin e vargjeve, kto operacione kryhen si vijon.


Krijimi ose insertimi. Kur struktura sht fillimisht e zbrazt dhe n t insertohet
elementi i par, ather bhet krijimi. N vazhdim, kur ajo m nuk sht e
zbrazt, pr do element t ri kemi operacionin e insertimit.
Le t supozojm se dshirojm t insertojm nj element t ri n varg, n
pozitn e krkuar ndrmjet 0 dhe (n-1). Logjika sht q t zhvendoset (lvizet)
elementi i (n-1)-t n pozitn e n-t, ai i (n-2)-t n at t (n-1)-t e kshtu me
radh, deri sa t arrihet n pozitn e insertimit. Pastaj vendoset/insertohet
elementi i ri n at pozit. Kshtu, numri total i elementeve rritet pr nj.
Fshirja. Fshirja bn largimin e nj elementi nga pozita e krkuar. Pas fshirjes s
elementit, duhet t zhvendosim/lvizim t gjitha elementet pasuese pr nga nj
pozit majtas dhe numri total i elementeve zvoglohet pr nj.
Prshkimi i vargut. Prshkimi (angl. Traversal) nnkupton shtypjen ose
procesimin e secilit element t vargut. Nse vargu sht i zbrazt athere
shtypet porosia se vargu sht i zbrazt, prndryshe shtyen elementet prej t
parit deri tek i fundit.
Sortimi. Prmes nj procedure (funksioni) t sortimit renditen elementet e vargut
n renditje prej t voglit kah i madhi ose anasjelltas.

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

N figurn a sht paraqitur nyja e lists s lidhur njfish, e cila ka dy fusha:


Info dhe Lidhja. Fusha Info prdoret pr t ruajtur informacionin (vlern, t
dhnn), ndrsa fusha Lidhja prdoret pr t ruajtur adresn e nyjes s
ardhshme. Nse nyja sht e fundit, ather n fushn e lidhjes adresa sht
NULL, q do t thot se nuk tregon n ndonj nyje.
N figurn nn b sht paraqitur nyja e lists s lidhur dyfish dhe ajo
prmban dy fusha t adresave, t emrtuara Lidhja_m (lidhja majtas) dhe
Lidhja_d (lidhja djathtas). Zakonisht emrtohen edhe ePrparshme dhe
eArdhshme, pr t treguar n nyjen e prparshme dhe nyjen e ardhshme n
list. Fusha Info prdoret pr t ruajtur informacionin aktual.
Supozojm se p sht adresa e nyjes s lists s lidhur njfish. Ather, qasja
n t dhnn (vlern) e nyjes bhet prmes p->Info, ndrsa qasja n fushn e
lidhjes, prmes p->Lidhja.
N mnyr t njjt, pr nyjen q t lists s lidhur dyfish, do t kemi: q->Infor,
q->Lidhja_d dhe q->Lidhja_m.

Lista e lidhur njfish


Lista e lidhur nj-fish (ose lista nj-kahshe) prbhet prej nj bashksie t
renditur t elementeve, t cilat mund t ndryshojn n numr. Nj mnyr e
thjesht pr t reprezentuar listn lineare sht q t paraqiten nyjet t cilat
prmbajn t dhnat dhe lidhjet gjegjsisht pointert pr tek nyja e ardhshme.
Pr t prcjellur adresn e fillimit t lists, zakonisht prdoret edhe nj variabl
ndihmse (nj pointer) me emrin Fillimi, i cili jep lokacionin e nyjes s par n
list. Nyja e fundit n list nuk ka ndonj nyje pasardhse, kshtu q n fushn e
saj nuk duhet ndonj adres. N raste t tilla n fushn e adress zakonisht
ruhet NULL.
10

12

18

...

Fillimi

Figura 5 Lista e lidhur njfish


20

22

Algoritmet dhe strukturat e t dhnave


Le t supozojm se t dhnat n list mirmbahen n renditje rritse dhe sipas
fushs me informacion dhe pr thjeshtsi le t marrim vlerat t plota (integer).
Secila nyje mund t ruaj nj numr t plot. N kt struktur t t dhnave
mund t kryhen operacione t ndryshme, si insertimi, fshirja paraqitja ose
prshkimi i lists dhe krkimi.
N nnprogramin (funksionin) e insertimit ose krijimit, s pari krijojm nyjen e
re dhe e vendosim n pozitn e duhur n list. N rast se nyja insertohet n list
t zbrazt, fusha e saj e lidhjes (pointeri pr n nyjen e ardhshme) do t ket
vlern NULL ndrsa adresa e ksaj nyje ruhet (vendoset) n variabln
(pointerin) Fillimi, q tregon nyjen e par t lists. Rasti i dyt sht insertimi i
nyjes n fillim t lists. N kt rast, adresa e variabls Fillimi ruhet (vendoset)
n fushn e lidhjes (Pointerit) pr n nyjen e ardhshme, ndrsa adresa e adresa e
nyjes s par (t re) vendoset n variabln Fillimi. Rasti tjetr sht insertimi
ndrmjet dy nyjeve n list ose n fund t lists. N kt rast, prshkojm listn
duke krahasuar elementin e insertuar me seciln nyje me radh n list deri sa t
vije n pozitn e duhur (vlera m e madhe ose baraz me nyjen aktuale). Kur t
jet gjetur vendi i duhur, insertohet nyja e re ndrmjet nyjes paraardhse t nyjes
aktuale dhe nyjes aktuale. Sipozojm se adresa e nyjes paraardhse sht e
ruajtur dhe adresa e nyjes aktuale sht ptr. Ather kjo adres ruhet n fushn
e adress s nyjes s re dhe adresa e nyjes s re n fushn e lidhjes s nyjes
paraprake.
Fshirja: pr t fshir nyjen e krkuar (q ka vlern e krkuar), s pari e
krkojm n list. Edhe ktu rast paraqiten raste t ndryshme. Nj sht fshrija
prej lists s zbrazt dhe ajo bhen nn-rrjedh. Nse elementi i fshir sht
n fillim t lists, ather pjesa e saj e lidhjes (adresa) ruhet n variabln
Fillimi. Prndryshe, krkohet nyja q duhet t fshihet dhe poashtu nyja
paraardhse e saj. Pastaj, adresa e nyjes s ardhshme t nyjes q fshihet,
vendoset n adresn e nyjes s ardhshme t nyjes para-ardhse, e kshtu me
radh.

Lista e lidhur dyfish


Lista e lidhur dyfish (ose lista dy-kahshe) sht e ngjashme me listn njkahshe, prveq se ajo e lidh nyjen n t dy kahjet, edhe me nyjen paraardhse
edhe me at pasardhse. Lista sht koleksion linear i nyjeve me lidhje t
dyfishta dhe prandaj quhet lista e lidhur dyfish, lista dy-kahshe ose zingjiri dykahsh. N figurn xxx sht paraqitur lista e lidhur dyfish.

21

Avni Rexhepi
10

20

30

40
Fundi

Fillimi

Figura 6 - Lista e lidhur dy-fish


Lista e lidhur dy-fish prmban dy variabla pointer, t quajtura Fillimi dhe
Fundi (ose edhe Koka dhe Bishti, apo Skaji i majt dhe Skaji i djatht).
Pointeri i majt Lidhja_m i nyjes s par ka vlern NULL, ashtu si edhe
pointeri i djatht i nyjes s fundit Lidhja_d. Prparsi e ksaj lise sht se
mund t qarkullohet n t dy kahjet. Edhe n kt struktur t t dhnave kryhen
operacionet si: insertimi, fshirja, krkimi, prshkimi dhe bashkimi. Pr t krijuar
ose insertuar, s pari do t krijojm nyjen e par t re, me operatorin Ne dhe
pastaj e ruajm vlern e elementit n fushn e vlers (informacionit) dhe e
vendosim nyjen n pozitn e duhur n list. Kjo nyje vendoset n listn e zbrazt
ose si nyje e skajit t majt (fillimit) apo skajit t djatht (fundit) ose diku n
list, ndrmjet nyjeve ekzistuese dhe i azhurohen vlerat e lidhjeve (pointerve).
Pr t fshir nyjen, s pari verifikohet mos sht lista e zbrazt dhe pastaj
krkohet vlera q duhet t fshihet. Nse gjindet, ajo nyje fshihet prmes urdhrit
Delete dhe pastaj azhurohen pointert e nyjeve fqinje. Ngjashm kryhen edhe
operacionet tjera.
Pr nga mnyra mnyra e organizimit dhe pr nga mnyra se si e lejojn ose si e
mundsojn qasjen n t dhna (n elemente, n antar), dallojm struktura t
ndryshme.

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

Algoritmet dhe strukturat e t dhnave


Operacioni i insertimit quhet Push
(angl. Push-shtyje, godite, etj), kurse
ai i nxjerrjes Pop (angl. Pop
nxjerrje, trheqje). Kto jan dy
operacionet e stekut, t cilat
programohen prmes funksioneve
prkatse.

Figura 7 - Steku

Rreshti, radha e pritjes


Nse imagjinojm gypin e hapur n t dy ant, por ku lvizet vetm n nj
drejtim (kahje), ather antart futen n list n njrn an, kurse
trhiqen/nxirren nga skaji tjetr, thuhet se kemi t bjm me strukturn e quajtur
Queue (lexohet si Kju) (angl. Queue radh e pritjes, rresht, etj). Ky sht
organizimi i njjt sikur vargu i pritjes pr bileta t teatrit, pr pagesa n ark,
etj., ku i pari q vije i pari shrbehet. Kjo njihet si struktur FIFO (First In, First
Out I pari brenda, i pari jasht) (ose n t kundrtn, LILO (Last In, Last Out
i fundit brenda, i fundit jasht). Queue lejon qasjen n dy skaje t gypit, n
njrin pr insertim dhe n tjetrin pr nxjerrje. Skaji i insertimit, quhet Back
(Fundi, skaji ose pjesa e pasme), kurse skaji i nxjerrjes njihet si Front (Fronti,
fillimi).
Operacionet e insertimit dhe nxjerrjes edhe tek queue, quhen Push dhe Pop.
Nnkuptohet q insertimi bhet vetm n fund t radhs, kurse nxjerrja vetm n
fillim t radhs.
Nse gypi do t jet lejohet qasja nga t dy ant, edhe pr insertim edhe pr
nxjerrje, ather kemi t bjm me deque (Double Ended Queue rreshti me
dy skaje). Operacionet prkatse, pr insertim dhe nxjerrje tani do t quhen:
push back, pop back, push front, dhe pop front.

23

Avni Rexhepi
Stack - Steku
...
push

pop

FIFO queue - rreshti


...
pop

push
Deque Rreshti dy-kahor
...
popFront pushFront

pushBack popBack

Figura 8 - Operacionet n stek dhe n queue.


Nse nuk sht prcaktuar ndryshe, radha e insertimit, prcakton edhe radhn e
nxjerrjes nga queue. P.sh., nse printeri sht n rrjet dhe shum kompjuter
kan qasje n t, ather me queue ruhet lista e punve pr shtypje n printer. I
pari q jep urdhrin pr shtypje, i pari e merre rezultatin.
Mirpo, n raste t ndryshme, ka antar m t rndsishm, t cilve ju jipet
prioritet dhe pavarsisht renditjes n hyrje n listn e pritjes, ata shrbehen
jasht radhs, me prioritet. P.sh., ngashm me at q kemi n realitet tek lvizja
n trafik e makinave me prparsi kalimi (ndihma e shpejt, policia, zjarrfiksit
dhe ata t madhrishmit q pa nevoj e shfrytzojn kt prparsi
(presidenca, qeveria), etj).
N kt rast, kemi t bjm me Priority Queue (radha me prioritet). N kt
rast, antareve ju bashkangjitet edhe informacioni pr prioritetin, i cili
prcakton radhn e ekzekutimit.
Fizikishit, steku dhe queue mund t realizohen edhe si struktura statike, prmes
vargut edhe si struktura dinamike, prmes listave t lidhura (njfishe ose
dyfishe).
Pr nga aspekti i renditjes logjike t antarve, u tha se strukturat mund t jen
Lineare ose Jo-lineare.
Tek strukturat lineare, dallohet antari i fillimit ose fundit dhe kalimi prej antari
n antar, mund t bhet vetm n mnyr t renditur (lineare) dhe zakonisht
vetm npr nj rrug t mundshme. Kryesisht, lidhja prej nyjes n nyje sht
pointeri prkats, q mundson kalimin tek nyja e prparshme ose ajo e
ardhshme.
Steku dhe Queue jan struktura lineare.
24

Algoritmet dhe strukturat e t dhnave


Prveq strukturave lineare, shfrytzohen edhe strukturat jolineare, si: Pemt
(angl. Trees) dhe Grafet (angl. Graphs). N esenc, pema sht nj rast special i
grafit.
Tek strukturat jolineare, ekzistojn rrug t ndryshme t lidhjeve t antarve
mes veti dhe lvizja prej njrs nyje tek tjetra mund t realizohet npr rrug t
ndryshme. Pastaj, nj nyje mund t jet e lidhur me disa nyje t tjera, e jo vetm
me at paraprake dhe at t ardhshme (si n rastin e listave t lidhura).
A

B
D
G

F
H

F
G

Figura 9 - Pema dhe Grafi


Pema sht struktur e organizuar e nyjeve dhe degve (rrugve) q lidhin nyjet
mes veti. Pema zakonisht sht struktur tek e cila dallohet nyja fillestare, e
quajtur rrnja e pems dhe pastaj prej saj shprndahen nyjet tjera, t organizuar
n form t pems. N aspektin hierarkik, nse i shikojm si trung familjar,
dallojm nyjet prind dhe nyjet fmij. Pema mund t jet me organizime
speciale, si pem binare (do nyje, sht prind pr dy fmij), pem n-are (do
nyje sht prind pr n-fmij), pem e balansuar (do an e pems, sht me
numr t balansuar t nyjeve), etj.
Grafi, sht struktur e organizuar, e nyjeve dhe degve q lidhin nyjet mes veti.
Nuk ka nyje fillestare t prcaktuar sikur te pema. Nyjet mund t jen t lidhura
mes veti me rrug t ndryshme (sikur n realitet, rrugt e qytetit, pikat e
caktuara n qytet), etj. Lidhjet mes degve mund t jen t orientuara (me
lvizje vetm n kahun e caktuar, q n graf paraqitet me shenj t shigjets) ose
t paorientuara (lidhjet jan dykahshe). Degt e grafit, mund t plotsohen me
informacione plotsuese, q quhen pesha ose kostoja e rrugs, si p.sh., koha e
kalimit prej nyjes n nyje, kualiteti i rrugs, shpejtsia e lvizjes, etj.
T gjitha kto struktura do t prdoren npr algoritme t ndryshme pr
llogaritje t ndryshme varsisht prej nevojs. Me rndsi sht q pr rastin
konkret t problemit q duhet zgjidhur, t zgjidhet edhe struktura adekuate, e
cila n mnyrn m t mir do ta pasqyroj dhe do ta interpretoj logjikisht
strukturn reale t problemit.
Kto jan strukturat themelore, t cilat pastaj prdoren n mnyra t ndryshme,
pr realizimin e strukturave t tjera. N praktik, gjithmon synohet q natyra e
25

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)

T definuara prej shfrytzuesit


(User-Defined)

Pointer

Vargjet

Listat

Listat lineare

Steku

Queue

Figura 10 Strukturat e t dhnave

26

Fajllat

Listat jo-lineare

Pemt

Grafet

Algoritmet dhe strukturat e t dhnave

1. Memoria, Tipet Abstrakte t t dhnave dhe


Adresat
Shtrohet pyetja: Sa sht numri maksimal i tentimeve pr t gjetur nj emr n
listn prej nj milion emrash? Idea e par do t ishte nj milion! Mirpo, kjo nuk
sht aspak afr, sepse prgjigja e sakt sht 20, nse lista ka nj struktur q e
bn krkimin t leht dhe nse krkimi bhet me nj struktur efikase. Krkimi i
lists sht nj prej mnyrave se si strukturat e t dhnave na ndihmojn pr t
manipuluar t dhnat e ruajtura n memorie t kompjuterit. Mirpo, pr t pasur
t qart funksionimin, duhet pasur t qart se si funksionon memoria e
kompjuterit apo si dhe prse vetm zerot dhe njshet ruhen n memorie.
N fillim, do ta bjm nj vshtrim mbi gjrat themelere t cilat duhet t dihen,
pr t vijuar punn. Pr t punuar me algoritmet dhe strukturat e t dhnave,
duhet t jen trsisht t qarta konceptet e memories, lokacioneve t memories,
adresave t tyre, ndarja e lokacionieve n memorie (alokimi i memories),
mnyrs s ruajtjes s t dhnave n memorie, qasja n to (pr insertim, lexim,
editim, fshirje), pastaj alokimi statik dhe dinamik, etj. Poashtu do t shohim
edhe nj her gjrat e nevojshme pr pointert, prdorimin dhe funksionimin e
tyre.

Vshtrim mbi memorien


Memoria e kompjuterit sht e ndar n tri seksione:
-

memoria kryesore (angl. main memory),


kesh memoria (angl. cache memory) n procesor (angl. CPU Central
Processing Unit), dhe
memoria e prhershme (disku).

Memoria kryesore, e njohur edhe si RAM (Random Access Memory memoria


me qasje t rastit) sht vendi ku ruhen instruksionet (programet) dhe t dhnat
(angl. data). Memoria kryesore sht volatile (e paqndryeshme) sepse
instruksionet dhe t dhnat n t fshihen (humben) porsa t ndalet furnizimi
(fiket kompjuteri).
Kesh memoria n CPU prdoret pr t ruajtur instruksionet e shpeshta dhe t
dhnat q jan, do t jen, ose kan qen duke u prdorur nga CPU-ja. Nj
segment i kesh memories n CPU quhet regjistr (angl. register). Regjistri
sht nj pjes e vogl e memories prbrenda CPU-s dhe prdoret pr ruajtjen e
prkohshme t instruksioneve dhe t dhnave.

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)

Figura 1.1: Bus-i e lidh procesorin,


memorien kryesore dhe diskun, si dhe
pajisjet tjera.
Disku (angl. Persistent storage(memoria e qndrueshme, persistente), ose
Hard Disk (disku i fort))) sht pajisje e jashtme (eksterne) pr ruajtje t
instruksioneve dhe t dhnave. Memoria e qndrueshme sht jovolatile, q do
t thot se instruksionet dhe t dhnat mbesin t ruajtura edhe kur kompjuteri
sht i fikur.
Disku shpeshher prdoret nga sistemi operativ edhe si memorie virtuele.
Memoria virtuele (angl. Virtual Memory) sht teknik e sistemit operativ pr t
rritur kapacitetin e memories kryesore prtej kufijve t RAM-it prbrenda
kompjuterit. Kur kapaciteti i memories tejkalohet, sistemi operativ prkohsisht
kopjon prmbajtjen e bllokut t memories n disk. Nse programi ka nevoj pr
t ju qasur instruksionev ose t dhnave n at bllok, sisktemi operativ e
shkmben bllokun e vendosur n disk me ndonj nga memoria, e q nuk sht
duke u prdorur pr momentin. Pra, nj pjes e diskut, funksionon thuajse sht
pjes e RAM-it (memories).
Keshi sht tipi i memories me qasjen m t shpejt. Me shpejtsi t prafrt, e
dyta, sht memoria kryesore. Disku sht i treti, por shum larg pr nga
shpejtsia, pasi q prfshin procese mekanike, gj q kufizon/pengon transferin
e shpejt t t dhnave.
28

Algoritmet dhe strukturat e t dhnave


Memoria kryesore (RAM-i) sht lloji i memories q prdoret nga strukturat e t
dhnave, edhe pse strukturat e t dhnaev dhe teknikat e manipulimit t tyre
mund t aplikohen edhe n file systems (sistemet e fajllave) n disk.

T dhnat dhe memoria


T dhnat q prdoren nga programi jan t ruajtura n memorie dhe
manipulohen nga teknika t ndryshme t stukturave t t dhnave, varsisht prej
natyrs s programit. T shohim se si ruhen t dhnat n memorie para se t
hulumtojm manipulimin e tyre.
Memoria sht nj grumbulli ndrprersave elektronik, t quajtur transistor, t
cilt mund t vendosen n njrn prej dy gjendjeve t mundshme: kyur ose
kyur (ndezur/fikur). Gjendja e ndrprersit fiton kuptim, kur secils gjendje i
ndahet nj vler, gj q bhet prmes prdorimit t sistemit numerik binar.
Sistemi binar prbhet prej dy shifrave, zero dhe nj, t quajtura shifra binare
(angl. binary digit) ose shkurt bit. Pra, ndrprersi n gjendjen e fikur
reprezenton zeron dhe kur sht i kyur/ndezur, paraqet nj-shin. Pra, kjo do
t thot se transistori reprezenton njrn prej shifrave.
Sidoqoft, dy shifra nuk ofrojn t dhna t mjaftueshme pr t br gj tjetr
prveq ruajtjes s zerove dhe njsheve nmemorie. Mirpo grupimi logjik i
ndrprersave t memories, mudnson ruajtjen e t dhnave me kuptim logjik.
Pr shembull, dy ndrprersa mudnsojn ruajtjen e dy shifrave binare, q q
mudnson katr kombinime t ndryshme, si n tabeln vijuese dhe kto
kombinime mund t ruajn tri vlera numerike: 0 deri n 3. Shifrat jan me baz
zero, gj q do t thot se shifra e par n sistemin numerik binar sht zeroja.
Memoria organizohet n grupe prej tet bitave, t quajtuara bajta (angl. bytebajt). Kjo mundson 256 kombinime t zerove dhe njsheve t cilat mund t
ruajn numrat prej 0 deri n 255, pasi q kombinimet me gjatsi tet prej 2
elementve jan 28=256.
Tabela 1-1: Kombinimet e dy bitave dhe ekuivalenti i tyre decimal
Switch 1
0

Switch 2
0

Decimal Value
0

29

Avni Rexhepi

Sistemi numerik binar


Sistemi numerik sht nj mnyr e numrimit t gjrave dhe kryerjes s
veprimeve aritmetike. Pr shembull, njerzit e prdorin si m t prshtatshm
sistemin decimal, ndrsa kompjutert at binar. T dy sistemet numerike bjn
t njjtn gj: mundsojn numrmin e gjrave dhe kryerjen e aritmetiks. Mund
t mbledhet, zbritet, shumzohet, pjestohet dhe do t arrihet tek prgjigjet e
njjta sikur t jet prdorur sistemi numerik decimal.
Mirpo, ekziston nj diference e dukshme ndrmjet sistemit numerik decimal
dhe atij binar: sistemi decimal prbhet prej 10 shifrave (0 deri n 9) ndrsa
sistemi binar prbehet prej vetm dy shifrave (0 dhe 1).
Pr t rikujtuar, sigurisht e mbani mend t msuarit e mbledhjes dhe bartjes n
nivelin m t lart (mbajtjes n mend, p.sh. 9+1=0 e 1 n mend, gjegjsisht 1 n
nivelin m t lart)! Pra, kur arrihet velra maksimale ose kufiri, kalojm n nivel
m t lart. P.sh., nse n kolonn e djatht kishim 9 dhe shohej edhe 1, ather
ndryshohet 9 n 0 dhe vendoset 1 n t majt t zeros, pr t fituar 10:

Teknika e njjt e bartjes n nivel m t lart prdoret edhe gjat mbledhjes n


sistemin binar, me dallimin q tash n vend t kufirit maksimal 9, kemi 1. Nse
kemi 1 n kolonn e djatht dhe shtojm 1, ather e ndrrom 1 n 0 dhe
vendosim 1 n t majt t 0, pr t fituar 10 binar.

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

Algoritmet dhe strukturat e t dhnave


kompjutrit se sa hapsir t rezervoj pr t dhnat, duke prdorur nj tip
abstrakt t t dhnave (angl. Abstract Data Type-ADT).
ADT sht nj fjal e rezervuar e gjuhve programuese e cila specifikon sasin
nevojshme t memories pr t ruajtur t dhnat dhe tipin e t dhnave q do t
ruhet n at lokacion t memories. Sidoqoft, nj ADT nuk i tregon kompjuterit
se sa bajta t rezevoj pr t dhnat. Numri i bajtave t rezervuar pr nj ADT
ndryshon, varsisht prej gjuhs programuese t prdorur pr shkruarjen e
programit prkats dhe nga tipi i kompjuterit q prdoret pr t kompajluar
programin (angl. compile-hartoj, prpiloj).
N C dhe C++, madhsia e nj ADT-je sht e bazuar n madhsin e
regjistrave t kompjuterit t prdorur pr kompajlim t programit. N Java,
ADT-t kan madhsi fikse, pr tu ekzekutuar n t gjitha ambientet
ekzekutuese t Java-s (Java runtime environment).
Imagjinojeni nj ADT si termin pako mollash. Nse i thoni menagjerit t
shitjes se duhet rezevuar hapsir n rafta pr pes pako mollash, ai e di se sa
rafta duhet ti rezervoj sepse e di madhsin e pakove t mollave.
E njjta vlen edhe pr tipet abstrakte t t dhnave. Ju i tregoni kompjuterit q
t rezervoj hapsirn pr nj numr t plot, duke prdorur ADT-n (tipin)
int (shkurtesa pr integer-numr i plot). Kompjuteri ve e di se sa memorie
duhet t rezervoj pr t ruajtur nj numr t plot (integer).
ADT-ja poashtu i tregon kompjuterit tipin e t dhnave q do t ruhet n at
lokacion t memories. Kjo sht e rndsishme sepse kompjutert i manipulojn
t dhnat e nj tipi t caktuar ndryshe nga t dhnat e tipit tjetr abstrakt. Kjo
sht ngjashm me at se si menagjeri i shitjes i trajton pakot e letrave dhe
lngjeve ndryshe nga ato t mollve.
Tabela 1-2 prmban listn e tipeve abstrakte t t dhnave. Kolona e par
prmban fjalt e rezervuara (angl. keyword) pr seclilin tip. Kolona e dyt
tregon numrin gjegjs t bitave t cilt rezervohen n memorie. Kolona e tret
tregon rangun e vlerave q mund t ruhen n at tip abstrakt dhe kolona e fundit
tregon grupin t cilit i takon tipi prkats.
Tabela 1-2: Tipet e thjeshta t t dhnave
Tipi
byte

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

16 (Unicode) 65,536 (Unicode)

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)

Programeri e zgjedh tipin abstrakt t t dhnave i cili m s miri i prshtatet t


dhnave q dshiron ti ruaj n memorie dhe e prdor tipin prkats n urdhrat
e deklarimit, pr t deklaruar variablat dhe tipet e tyre. Variabla sht nj
referenc pr n lokacionin e memories i cili rezervohet duke prdorur urdhrin
e deklarimit.
Gjithmon duhet rezervuar sasin e duhur t memories s nevojshme pr t
ruajtur t dhnat, sepse mund t humben t dhnat nse rezervohet hapsir
shum e vogl. Kjo sht njsoj si t drgohen 10 pako mollsh n vendin ku
sht rezervaur hapsira pr 5 t tilla. Sigurisht q 5 pako do t hedhen diku
anash.

Grupet e tipeve abstrakte t t dhnave


Ju prcaktoni sasin e memories q duhet rezervuar duke prcaktuar grupin e
duhur pr tipin e t dhnave abstrakte dhe duke vendosur se cili tip prbrenda
grupit sht i duhuri pr t dhnat.
Jan katr grupe t tipeve t t dhnave:

32

Integer ruan numrat e plot dhe numrat me shenj. Shum i mir pr


ruajtjen vlerave t plota t eurove, kur nuk nevojiten vlerat decimale.
Floating-point ruan numrat real (vlerat e thyesave, pjest e t plotave).
Perfekt pr ruajtjen e depozitave bankare, kur centt (pjest e euros)
mund t mblidhen s bashku.

Algoritmet dhe strukturat e t dhnave

Character ruan karakteret (shkronjat, numrat, shenjat e piksimit). Ideal


pr ruajrne e emrave.
Boolean ruan velrn true ose false (sakt ose pasakt, e vrtet ose
jo e vrtet). value. sht zgjidhja e duhur pr t ruajtur prgjigjet po ose
jo, apo e sakt ose jo e sakt, pr pyetjen e br.

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.

Figura 1.2: byte rezervon 8 bita (1 bajt) n memorien kryesore.

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.

Figura 1.3: short rezervon 16 bita (2 bajta) n memorien kryesore.


int
Tipi int (Fig. 1.4) sht tipi i prdorur m s shpeshti pr grupin integer, pr
nj numr arsyesh:

Pr variabla kontrolluese t unazave


N vargje t indeksave
Kur performohet matematik me numra t plot

Figura 1.4: int rezervon 32 bita (4 bajta) n memorien kryesore.


long
Tipi long (angl. long-i gjat) (Figura 1-5) prdoret sa her q prdoren numrat
e plot t cilt jan prtej rangut t vlerave t tipit int. Zgjedheni si tip, pr t
ruajtur vlern e pags s Bill Gate-it p.sh.

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

Algoritmet dhe strukturat e t dhnave

Figura 1.5: tipi long rezervon 64 (8 bajta) bita n memorien kryesore.


Termi floating-point i referohet mnyrs se si referohen decimalet n memorie.
Jan dy pjes t numrit floating-point: pjesa reale, e cila ruhet si numr i plot
dhe pozita e presjes/piks decimale brenda numrit t plot. Kjo sht arsyeja pse
thuhet se pika decimale floats (angl. float-noton, lviz, pluskon) prbrenda
numrit.
Pr shembull, vlera 43.23 ruhet si 4323 (pa pik decimale). Krahas me t shkon
referenca n numr e cila tregon se pika decimale sht e vendosur pas shifrs s
dyt.
float
ADT-ja float (Figura 1.6) prdoret pr numrat real t cilt krkojn precizitet t
njfisht, si sht rasti me vlerat monetare. Preciziteti i njfisht (angl. single
precision) do t thot se vlera sht precize deri n 7 shifra djathtas prej
decimales. Pr shembull, supozojm se vlera 53.50 ndahet n pjes t barabarta
pr 17 persona. Secili person do t marr nga 3.147058823529. shifrat prtej
(djathtas) vlers 3.1470588 nuk garantohet t jen precize pr shkak t
mnyrs se si ruhet n memorie vlera float. Tipi float zgjedhet sa her q t
nevojitet t ruhet vlera decimale ku vetm 7 shifra djathtas piks decimale duhet
t jen t sakta.

Figura 1.6: float rezervon 32 bita (4 bajta) t memories kryesore.

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.

Figura 1.7: double rezervon 64 bita (8 bajta) t memories kryesore.

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.

Figura 1.8: char rezervon 16 bita t memories kryesore.


Pr shembull, shkronja A ruhet n memorie si vlera 65, e cila i prgjigjet
shkronjs A n setin e karaktereve. Kompjuteri di ta trajtoj vlern 65 si
shkronja A e jo si numri 65 pr shkak se memoria sht rezervuar duke prdorur
tipin char. Fjala e rezervuar char i tregon kompjuterit se numri integer i ruajtur
n at lokacion trajtohet si karakter e jo si numr.
Jan dy sete t karaktereve q prdoren n programim: American Standard Code
for Information Interchange (ASCII) dhe Unicode. ASCII sht gjyshi i seteve
t karaktereve dhe prdor nj bajt pr t reprezentuar maksimalisht 256
karaktere. Mirpo, pas disa viteve t prdorimit, ishte evident problemi i
paraqitjes s karaktereve t gjuhve t ndryshme, si arabe, japoneze, kineze, etj,
t cilat kan m shum se 256 karaktere n gjuhn e tyre. Pr t zgjidhur kt
problem, u zhvillua nj set i ri i karaktereve, i quajtur Unicode. Unicode prdor

36

Algoritmet dhe strukturat e t dhnave


2 bajta pr t reprezentuar secilin karakter. Zgjedhni char sa her q duhet
ruajtur nj karakter t vetm n memorie.

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.

Figura 1.9: ADT bool-ean rezervon 1 bit t memories kryesore.

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.

Figura 1.10: Adresa e memories e bajtit t par prdoret si referenc pr t


gjith bajtat e rezervuar pr nj tip abstrakt t t dhnave.
Adresa e memories prdoret direkt ose indirekt prbrenda programit pr t ju
qasur t gjith tet katrorve. Pr shembull, nse programi i tregon/urdhron
kompjuterit q dshiron t kopjoj t dhnat e ruajtura n lokacionin 423 t
memories, d.t.th, katrorit me adres 423. Kompjuteri shkon tek ai lokacion i
memories dhe i kopjon t dhnat (zerot dhe njshet) nga katrori 423 dhe shtat
katrort vijues. Kta shtat katrort vijues nuk kan adres t memories.
Ndryshe thuhet se kta shtat katrort tjer e ndajn bashkarisht adresn e
katrorit 423.
37

Avni Rexhepi

Adresat reale t memories


Edhe pse adresat e memories u prezentuan si vlera decimale, si m par katrori
423, n realitet adresat e memories jan numra 32-bitsh ose 64-bitsh,
varsisth nga sistemi operativ i kompjuterit dhe at reprezentohen si vlera
heksadecimale.
Sistemi Hexadecimal sht sistem numerik i ngajshm me sistemin decimal dhe
at binar. Kjo do t thot se vlerat heksadecimale prdoren pr t numruar dhe
kryer veprimet aritmetike. Sistemi numerik heksadecimal ka 16 shifra, prej 0
deri n 9 dhe prej A deri n F, t cilat prfaqsojn numrat prej 10 deri n 15.
Pr shembull, adresa e memories 258,425,506 reprezentohet n foramtin
heksadecimal si 0x0F6742A2.

ADT dhe adresat e memories


M par u tha se rezervimi i memories pr t dhnat bhet duke prdorur tipin
abstrakt t t dhnave. Disa ADT rezervojn memorie n madhsi m t madhe
se 1 bajt.
Pasi q secili bajt i memories e ka adresn e vet memorike, mund t supozohet
se tipi short i ka dy adresa t memories sepse i prdor 2 bajta t memories.
Mirpo kjo nuk ndodh. Kompjuteri e prdor adresn e memories s bajtit t
par pr t ju referuar cilit do tipi abstrakt t t dhnaveq rezervon bajta t
shumfisht n memorie.
Le t themi se n memorie sht rezervuar hapsira pr tipin short (shih Fig.
1-10). Me kt rast rezervohen dy lokacione t memories, me adresat 400 dhe
401. Mirpo, vetm adresa e memories 400 prdoret pr t ju referuar vlers
short. Kompjuteri automatikisht e di se vlera e ruajtur n adresn 401 sht
pjes e vlers s ruajtur n adresn 400, sepse hapsira sht rezervuar duke
prdorur tipin abstrakt short. Prandaj, kompjuteri i kopjon t gjith bitat nga
adresa e memories 400 dhe t gjith bitat nga adresa 401, sa her q nga
programi i bhet krkes q t kopjoj numrin/vlern e ruajtur n adresn e
memories 400.

Variablat dhe Pointert


Edhe pse me t prmendur pointert, disa programer kan iden se kjo sht
shum komplekse, n esenc pointert jan thjesht tregues q pointojn
(tregojn, shenjojn) n adresat e memories, si nj fmij i vogl q tregon me
gisht (pointon) n gjrat q i dshiron. Pra, pointeri sht variabl q prdoret
pr t pointuar n adresat e memories, prmbajtjen e t cilave dshirojm ta
prdorim n program.
38

Algoritmet dhe strukturat e t dhnave

Deklarimi i variablave dhe objekteve


Memoria rezervohet prmes prdorimit t urdhrit pr deklarim t variablave,
duke prdorur tipin e t dhnave. Forma e deklarimit varet nga gjuha
programuese q prdoret. N C++ dhe n Java, urdhri i deklarimit t nj
variable sht p.sh., si n vijim:
int VariablIme;

Jan tri pjes (elemente) n kt urdhr t deklarimit:

Tipi i t dhnave (Data type) q tregon se sa memorie rezervohet pr


kt lloj t t dhnave q do t ruhet n at lokaicon t memories,
Emri i variabls (Variable name) emri q prdoret n program, pr t
ju referuar prmbajtjes (vlers) n at lokacion t memories,
Pikpresja (Semicolon) i tregon kompjuterit se ky sht nj urdhr
(instruksion) dhe sht shenja e fundit t urdhrit prkats.

Tipet primitive t t dhnave dhe tipet e definuara prej


shftyrzuesit
sht sqaruar koncepti i tipeve abstrakte t t dhnave, t cilat prdoren pr t
rezervuar memorien kompjuterike. Tipet abstrakte t t dhanave ndahen n dy
kategori: tipet primitive t t dhnave dhe tipet e t dhnave t definuara prej
shfrytzuesit. Tipet primitive jan t definuara prej gjuhs programuese dhe jan
ato q i prmendm m par: char, int, short, float, double, etj., t cilat njihen
edhe si built-in data types (angl. built-in t ndrtuara s brendshmi, t
brendshme, etj).
Tipet e definuara prej shfrytzuesit, jan grup i t dhnave primitive t definuara
nga programeri. Pr shembull, nse duhet t ruhen t dhnat e studentit n
memorie, ather do t duhen disa elemente t t dhnave, si : ID e studentit,
Emri, Mbiemri, Nota, etj. Pr seciln ve e ve mund t prdoren tipet primitive
t t dhnave, mirpo tipet primitive nuk jan t grupuara s bashku. Secila prej
tyre ekziston n elemente t ndara t t dhnave.
Qasje m e mir sht q t dhnat primitive t grupohen n t dhna t
definuara prej shfrytezuesit pr t formuar nj record (angl. record
regjistrim, shnim, dosje, dokument, koleksion t dhnash, etj., por do t
prdorim termin rekord, si n origjinal). Termi rekord sht i zakonshm n
bazat e t dhnave. Baza e t dhnave prbhet nga nj ose m shum tabela.
Tabela, prbhet nga rreshtat dhe kolonat. Nj rresht i tabels njihet si rekord,
kurse kolona si fush. Ngjashm si n tabel, kolonat/fushat e t cils do t
ishin: ID, Emri, Mbiemri, Nota, etj., nj rresht (t gjitha fushat e rreshtit) do t
prmbante t dhnat e nj studenti. Pra, tipi i t dhnave i definuar prej
39

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.

Definimi i tipeve t definuara nga shfrytzuesi


Definicioni i strukturs prbhet nga katr elemente:

struct (fjala e rezervuar struct) I tregon kompjuterit se jeni duke


definuar nj struktur
Emri i strukturs (Structure name) Emri q prdoret pr t
identifikuar n mnyr unike strukturn dhe q prdoret pr t deklaruar
instancat e strukturs,
Trupi i strukturs (Structure body) kllapa e madhe e hapur dhe e
mbyllur, brenda t cilave ndodhen tipet primitive t t dhnave t cilat
deklarohen kur deklarohet nj instanc e klass,
Pikpresja (Semicolon) I tregon kompjuterit se ky sht nj urdhr
(instruksion).

Trupi i strukturs mund t mbaj fardo kombinimi t tipeve t t dhnave


primitive dhe tipeve t definuara m par nga shfrytzuesi, varsisht prej natyrs
s t dhnave q krkohen n programin konkret. P.sh, definojm strukturn e
40

Algoritmet dhe strukturat e t dhnave


cila definon rekordin e studentit, i cili prbhet nga numri i studentit dhe nota.
Emri i ksaj strukture t definuar prej shfrytzuesit sht StudentRecord:
struct StudentRecord
{
int numriStudentit;
char nota;
};

Deklarimi i tipit t definuar prej shftyzuesit


Deklarimi i nj instance t tipit t definuar prej shfrytzuesit bhet n mnyr t
njjt si deklarimi i varibalave. Mirpo, n kt rast, me rastin e deklarimit, pra
n urdhrin e deklarimit, n vend t tipit primitiv t t dhnave, prdoret emri i
strukturs.
Le t marrim se dshirojm t krijojm nj instance t strukturs StudentRecord,
t definuar m par. Ja, si duhet t bhet deklarimi dhe prdorimi n program:
#include <iostream>
using namespace std;
struct StudentRecord
{
int numriStudentit;
char nota;
} ;
int main()
{
StudentRecord studenti1;
studenti1.numriStudentit = 10;
studenti1.nota = 'A';
cout << "notat: " << studenti1.numriStudentit << " "
<< studenti1.nota << endl;
}

Urdhri i deklarimit i tregon kompjuterit q t rezervoj memorie me madhsin


e krkuar pr t ruajtur tipin e definuar prej shfrytzuesit, StudentRecord dhe ta
shoqroj studenti1 me at lokacion t memories. Madhsia e tipit t definuar
prej shfrytzuesit, sht e barabart me shumn e madhsive t tipeve primitive
t definuara n trupin e sturkturs.
Nse krahasojm deklarimet:
int
StudentRecord studenti1;

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.

Tipet e definuara prej shfrytzuesit dhe memoria


Elementet e t dhnave t definuara prbrenda trupit t strukturs vendosen
njra pran tjetrs n memorie, n mnyr sekuenciale (t njpasnjshme). N
figurn 1.11 ilustrohet memoria e rezervuar memoria e rezervuar me rastin e
deklarimit t instancs s tipit StudentRecord.

Figura 1.11: Elementet e strukturs vendosen n lokacione sekuanciale t


memories, kur deklarohet nj instanc e strukturs.
Emri i instancs studenti1 sht nj alias (angl. alias-pseudonim, nofk) pr
adresn e memories s rezervuar pr tipin e par primitiv t definuar prbernda
strukturs StudentRecord, q sht lokacioni me adresn e memories 1, n
figur. Pr thjeshtsi, le t themi se secili bllok n figurn 2-1 reprezenton 1 bajt
t memories dhe se madhsia e tipit int pr kt rast sht 2 bajta.
Secili tip primitiv i t dhnave t strukturs, ka adresn e vet t memories. Tipi
i par primitiv n kt shembull sht numriStudentit dhe emri i tij i referohet
lokacionit 1 t memories. Tipi i dyt primitiv sht nota dhe emri i saj i
referohet lokacionit 3 t memories.
42

Algoritmet dhe strukturat e t dhnave


ka ndodhi me lokacionin 2? Mbani mend, se secili bajt i memories i ndahet nj
adres unike memorike. Disa tipe primitive t t dhnave jan m t mdha sesa
nj bajt dhe prandaj duhet t zne m shum se nj adres t memories, si n
kt shembull q sht rasti me tipin int. Pra, n kt rast, tipi i par primitiv i
ka zn 2 bajta t memories, prandaj, tipi i dyt primitiv i definuar n struktur,
vendoset n bajtin e ardhshm n dispozicion (t lir) n memorie, q sht
lokacioni 3 i memories.

Qasja n elementet e tipit t definuar prej shfrytzuesit


Elementet e strukturs s t dhnave qasen prmes emrit t instancs s
strukturs dhe emrit t elementit, t ndar me operatorin . (angl. dot operator operatori pik). Le t themi se dshironi t jepni vlersimin A pr notn, n
elementin nota t instancs studenti1 t strukturs StudentRecord. Ja, si duhet
shkruar urdhri prkats:
studenti1.nota = 'A';

Elementet e strukturs prdoren n program n mnyr t njjt si edhe variablat


tjera, prveq se duhet referuar edhe emri i instancs edhe emri i elementit, n
mnyr q ti qaseni elementit. Kombinimi i emrit t instancs dhe emrit t
elementit sht nj alias pr lokacionin e memories s elementit.

Tipet e t dhnave t definuara prej shftytzuesit dhe klasat


Strukturat prdoren m shum n gjuht procedurale si C. Gjuht e orientuara n
objekte, si C++ dhe Java i prdorin t dyja, strukturat dhe klasat, pr ti grupuar
s bashku t tipet e ndryshme t t dhnave primitive n nj njsi kohezive (t
lidhur, t bashkuar).
Definicioni i klass sht shabllon i ngjashm me konceptin e definicionit t
strukturs, pr faktin se t dyja prdorin definicionin pr t krijuar instancat.
Definicioni i strukturs krijon instanc t strukturs, gjersa definicioni klass
krijon instanc t klass.
Definicionin i klass i prkthen atributet dhe sjelljet (angl. behaviours sjelljet,
mnyrat e veprimit) t objekteve nga jeta reale n simulim t atij objekti
prbrenda programit. Atributet jan elemente t t dhnave ngjashm me
elementet e strukturs. Sjelljet jan instruksione t cilat kryejn detyra specifike,
t njohura ose si methods (metoda) ose si functions (funksione), varsisht
prej gjuhs programuese. C++ ju referohet si funksione, gjersa Java si metoda.

Definimi i klass (Class)


Definicioni i klass i prngjan definicionit t sturkturs. Edhe definicioni i
klass, prbehet nga katr elemente:

43

Avni Rexhepi

class fjala e rezervuar class, i tregon kompjuterit se jeni duke definuar


nj klas,
Emri i klass (Class name) emri q prdoret pr t identifikuar klasn
n mnyr unike dhe pr t deklaruar instanca t klass,
Trupi i klass (Class body) kllapa e madhe e hapur dhe e mbyllur,
prbrenda t cilave jan tipet primitive t t dhnave t cilat deklarohen
kur deklarohet nj instanc e klass dhe definicionet e metodave dhe
funksioneve t cilat jan antar t klass
Pikpresja (Semicolon) i tregon kompjuterit se ky sht nj urdhri
(instruksion)

Definicioni vijues i klass, i shkruar n C++ definon rekordin e njjt t


studentit, q ishte definuar n strukturn e mparshme. Mirpo, definicioni i
klas do t definoj gjithashtu edhe funksionin i cili i paraqet (i shtyp) t
dhnat e studentit, emrin dhe notn, n ekran.
class StudentRecord
{
int numriStudentit;
char nota;
void shtypeNoten() {
cout<<"Student: " << numriStudentit << " Nota: "
<< nota << endl;
}
};

Deklarimi i nj instance t klass dhe memoria


Deklarimi i nj instance t klass bhet njsoj si deklarimi n rastin e strukturs.
Pra, n urdhrin e deklarimit, prdoret emri i klass dhe emri i instancs s
klass. Ja si deklarohet nj instanc e klass StudentRecord:
StudentRecord studenti1;

Kur deklarohet nj instanc e klass, memoria pr atributet e definicionit t


klass rezervohet n mnyr sekuenciale, njsoj si rezervohej memoria pr
elementet e strukturs. Fig. 1.12 paraqet alokimin e memories pr intancn
studenti1 t klass StudentRecord. Vreni se n esenc sht e njjta mnyr e
alokimit t memories, si n rastin e strukturs.

44

Algoritmet dhe strukturat e t dhnave

Figura 1.12: Memoria pr atributet e klass vendoset n lokacione sekuenciale


t memories kur t deklarohet instanca e klass.
Metodat dhe funksionet ruhen n memorie vemas prej atributeve, kur t
deklarohet nj instanc, sepse metodat dhe funksionet ndahen bashkarisht nga t
gjitha instancat e s njjts klas.

Qasje n antart e klass


Atributet, metodat dhe funksionet ndryshe referohen edhe si antart e klass
(class members). Ju mund ti qaseni antarve t nj instance t klass, duke
prdorur emrin e instancs, dot operatorin (operatorin pik) dhe emrin e antarit,
njsoj si i qaseni elementit t strukturs.
Ja si i qaseni atributit nota t instancs studenti1, t klass StudentRecord dhe si
thirret metoda shtypeNoten():
studenti1.nota = 'A';
studenti1.shtypeNoten();

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:
-

Kur profesori thot: Zgjidheni problemin 1.1 n libr, detyra aktuale


sht duke u dhn n mnyr indirekte;
Krkimi i nj titulli/teme n indeksin e librit, sht nj shembull i qasjes
indirekte. Indeksi tregon se ku mund t gjeni msimin prkats.

45

Avni Rexhepi
-

Adresa e banimit sht pointer. Ajo tregon se ku banon dikush. Adresa


prcjellse sht pointer n pointer.
Adresa n internet, URL-ja, si www.uni-pr.edu sht pointer. URL-ja
tergon se ku ndodhet eb faqja cak.. Nse eb faqja cak sht
zhvendosur, URL-ja bhet bajat (angl. Stale - bajat, e ndenjur) dhe
pointon n nj faqe q nuk ekziston m.

N t gjitha kto raste, informacioni jepet n mnyr indirekte duke ofruar


pointerin pr informacionin. N C++, pointeri sht nj objekt i cili ruan adresn
(d.m.th,. nj lokacion n memorie), ku sht ruajtur nj e dhn tjetr. Pra,
thjesht, pointeri ruan adres. sht e pritshme q adresa t jet nj numr i
plot (integer), ashtu q pointeri zakonisht mund t reprezentohet n mnyr
interne si nj unsigned int (numr pa shenj). Ajo q e bn pointerin m
shum sesa vetm nj numr i thjesht, sht se prmes tij ne mund ti qasemi t
dhns (vlers) n t ciln pointon ai. Ky veprim, ndryshenjihet edhe si
dereferencim i pointerit. Pointeri ruan adresn n t ciln banon nj e dhn
tjetr.
Nj prmbledhje (angl. aggregate-grumbullim, shum totale, etj) sht nj
koleksion i objekteve t ruajtura n nj njsi. Vargu sht mekanizmi bazik i
ruajtjes s nj koleksioni/bashksie t objekteve t tipit t njjt. Nj
prmbedhs tjetr sht struktura, e cila ruan nj koleksion t objekteve q nuk
jan t tipit t njjt. Si shembull abstrakt, shqyrtoni planin e banesave n nj
ndrtese. Secili kat mund t ket banesa nj-dhomshe, dy-dhomshe, tridhomshe etj. N kt rast, secili kat ruhet si struktur, ndrsa e tr ndrtesa
sht nj varg i kateve.

Vargjet dhe stringjet


N C++ mund t deklarojm dhe prdorim vargjet n dy mnyra themelore.
Metoda primitive sht q t prdoret nj varg i brendshm (built-in).
Alternativa tjetr sht prdorimi i libraris vector. Sintaksa pr t dy metodat
sht pak a shum e njjt, mirpo vector sht shum m e leht dhe pak m
e sigurt sesa vargu primitiv dhe preferohet pr shumicn e aplikacioneve.
Dallimi m i madh filozofik ndrmjet dy metodave sht se vector sillet si tip
i klass s par (angl. first-class type) edhe pse sht i implementuar n librari,
gjersa vargu primitiv sht tipi i klass s dyt (angl. second-class type).
Ngjashm, C++ ofron t dy opcionet edhe pr variablat tekstuale (string).
Stringjet primitive (jan thjesht vargje primitive char) dhe at shum m t
preferuarn string.
46

Algoritmet dhe strukturat e t dhnave

1.2.1 Objektet First-Class dhe Second-Class


Studiuesit e gjuhve programuese shpesh i prcaktojn konstrukteve t nj gjuhe
programuese, emrtimin firs-class objects ose second-class objects.
Definicioni sht paksa jopreciz, por n prgjithsi, idea sht q objektet e
klasit t par mund t manipulohen n t gjitha mnyrat e zakonshme, pa raste
speciale dhe prjashtime, ndrsa objektet e klass s dyt mund t manipulohen
vetm n ndonj mnyr t prcaktaur dhe t kufizuar.
Cilat jan mnyrat e zakonshme? N rastin specifik t C++, ato mund t
prfshijn gjrat si kopjimi. Rikujtoni se vargu ruan nj koleksion t objekteve.
sht e pritshme q kopjimi i nj vargu t bj kopjimin e tr koleksionit (t
gjith antarve t tij), mirpo kjo nuk ndodh n rastin e vargjeve primitive.
Gjithashtu, do t mund t prisnim q nj varg ta dij se sa antar i ka n
koleksionin e tij. Me fjal t tjera, do t ishte e pritshme q madhsia e vargut
sht pjes e vet qenies s tij. Prsri, kjo nuk sht e vrtet pr vargjet
primitive. Arsye pr kt sht se vargjet n C++ jan pak m shum se variabla
pointer (nj lloj specifik pointeri), sesa vet tipi i tyre i klasit t par. Gjithashtu
do t mund t prisnim q kur vargjet e alokuara m nuk jan t nevojshme (pr
shembull kur funksioni n t cilin ato jan deklaruar, ka kthyer rezultatin dhe ka
mbaruar pun), ather memoria q ata e zn, t lirohet. Kjo nuk sht e vrtet
pr vargjet (ndonjher edhe sht), kshtu q i bn shum t ngatrruar pr
kodim.
Stringu primitiv mund t konsiderohet i nivelit edhe t ult, sesa objekti i klasit
t dyt, sepse i mungojn t gjitha vetit e vargut t klass s dyt. Pr m tepr,
operatort e tij t krahasimit (p.sh., == dhe <) nuk bjn at q normalisht sht
e pritshme prej tyre dhe prandaj duhet t trajtohen si raste speciale.
Pr t siguruar trajtim t klasit t par pr vargjet dhe stringjet, duhet prdorur
vector dhe string. Klasa vector prmban operacionet themelore t
vargut primitiv plus tiparet/veorit shtes. Prandaj, ai m shum sillet si
struktur e t dhnave sesa si varg i thjesht. Sidoqoft, prdorimi i tij sht
shum m i sigurt sesa vargjet primitive t C++. vector sht pjes e STL-it.
Klasat vector dhe string pasi jan pjes e STL, jan edhe e pjes e C++, mirpo
disa kompajler akoma nuk i prkrahin. Si pjes e STL, klasat vector dhe
string i trajtojn vargjet si objekte t klasit t par. Pra, vector, e di se sa
sht i madh. Gjithashtu, objektet string mund t krahasohen me operatort
relacional, ==, <, etj. Edhe vector edhe string mund t kopjohen prmes
=. Prandaj, prveq rasteve speciale, duhet evituar vargjet dhe stringjet e
brendshme (built-in) t C++-it. Pasi mnyra e implementimit t librarive nuk
krkon njohjen e implementimit t tyre t brendshm, mjafton t dihet si
47

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

Algoritmet dhe strukturat e t dhnave


vector<int> a; // 0 int objekte
a.resize( 3 ) ; // 3 int objekte: a[O], a[l], dhe a[2]

N programin 1.1, ilustrohet prdorimi i vector-it. Programi n mnyr t


prsritur e zgjedh nj numr prbrenda kufijve 1 dhe 100 (inkluziv, duke
prshir edhe vlerat kufitare 1 dhe 100). Si rezultat fitohet numri i herave t
paraqitjes s secilit numr.
1 #include <stdlib.h>
2 #include <iostream>
3 #include <vector>
4 using namespace std;
5
6 // Gjenero numrat (prej 1-100).
7 // Shtyp numrin e paraqitjeve t secilit numer.
8 int main( )
9 {
10
const int DIFFERENT-NUMBERS = 100;
11
12
// Kerko dhe lexo numrin e lojeve.
13
int totalNumbers;
14
cout << "Sa numra te gjenerohen?: ";
15
cin >> totalNumbers;
16
17
vector<int> numbers( DIFFERENT-NUMBERS + 1 );
18
19
// Inicializo vektorin me zero.
20
for( int i = 0; i < numbers.size( ); i++ )
21
numbers[ i ] = 0;
22
23
// Gjenero numrat.
24
for( int j = 0; j < totalNumbers; j++ )
25
numbers[ rand( ) % DIFFERENT-NUMBERS + 1]++;
26
27
// Shtype permbledhjen.
28
for( int k = 1; k <= DIFFERENT-NUMBERS; k++ )
29
cout << k << " paraqitet " << numbers[ k ]
30
<< here \n";
31
32
return 0;
33 }

Programi 1.1 Demonstrim i thjesht i vargjeve prmes vector-it..


Rreshti 17 deklaron nj varg t integjerave t cilt numrojn numrin e
paraqitjeve t secilit numr. Pasi q vargjet indeksohen duke filluar prej 0,
ather +1 sht kritike ns dshirojm ti qasemi elementit n pozitn
DIFFERENT_NUMBERS. Pa t, do t kishim nj varg, rangu i indeksueshm i
t cilit do t ishte prej 0 deri n 99, dhe kshtu qasja tek indeksi 100 do t mund
49

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.

1.2.3 Ndryshimi i madhsis s vektorit


Nj kufizim i vargjeve primitive sht se kur t deklarohen nj her, madhsia e
tyre nuk mund t ndryshohet. Shpeshher ky sht kufizim i rndsishm.
Sidoqoft, ne e dijm se mund t prdorim resize pr t ndryshuar madhsin
e vector-it. Teknika e prdorur ilustron disa prej shtjeve t rndsishme t
efikasitetit. Ajo q ndodh sht se pointert (t cilt do t diskutohen n detaje
pak m von) prdoren pr t krijuar iluzionin e nj vargu q mund t ndryshoj
madhsin (rritet). Nuk nevojitet njohuri e C++ pr t kuptuar algoritmin: t
gjitha detajet jan t fshehura brenda implementimit t vector-it.
Idea themelore sht e treguar n figurn 1.13, ku, arr reprezenton vector-in
me 10 elemenete. Diku, brenda implementimit, alokohet memoria pr 10
elemenete. Supozojm se dshirojm t zgjerojm kt memorie n 12 elemente.
Problemi sht se elementet e vargut duhet t memorohen (ruhen) n lokacione
t afrta (t njpasnjshme angl. contiguous locations lokacione t afrta, t
puthitura) dhe se memoria q ndodhet menjher pas arr mund t jet zn
ndrkoh. Ather, veprojm si vijon:

50

Algoritmet dhe strukturat e t dhnave


arr

(a)
arr
original
(b)

arr
original
(c)

arr
(d)
original

Figura 1.13. Zgjerimi i vargut, fillimisht: (a) N pikn fillestare, arr


reprezenton 10 integer-a. (b) pas hapit 1, original reprezenton t njjtit 10
integer-a; (c) pas hapave 2 dhe 3, arr reprezenton 12-integer-a, ku 10 t part
jan ata q u kopjuan prej origi nial-it; dhe (d) pas hapit 4, 10-integer-at e fillimit,
lirohen.
1. E mbajm mend (e ruajm) ku ka qen memoria pr vargun me 10
elemente (synimi, qllimi i original-it).
2. Krijojm nj varg t ri me 12 elemente dhe bjm q vargu arr ta prdor
at.
3. Kopjojm 10 elementet nga original n arr; dy elementet shtes n arr t
ri kan ndonj vler t nnkuptuar (default),
4. E informojm sistemin se vargu 10 elementsh (memoria e tij) mund t
riprdoret sipas nevojs (si e sheh ai t arsyeshme).
Pas nj momenti mund t bindemi se ky sht nj operacion i shtrenjt, sepse
kopjohen t gjitha elementet prej vargut t alokuar fillimisht n vargun e ri.
Nse, pr shembull, ky zgjerim i vargut sht reagim n leximin e nj vlere
hyrse, ather zgjerimi i vargut seciln her q lexohet ndonj element do t
ishte shum joefikas. Prandaj, kur implementohet zgjerimi i vargut, gjithmon
duhet t bhet pr ndonj konstant multiplikative her m i madh. P.sh., dy
51

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

Algoritmet dhe strukturat e t dhnave


30
31 return 0;
32 }
Programi 1.2 Kodi pr leximin e nj numri t pakufizuar t int-ave dhe shtypja e
tyre, duke prdorur dyfishimin e vargut.

N fillim t getInts, itemsRead vendoset n 0. N mnyr t prsritur


lexojm elementet e reja n rreshtin 14. Nse vargu sht i mbushur, si
sinjalizohet prmes testit t suksesshm n rreshtin 16, ather vargu zgjerohet,
n rreshtin 17. E zgjerojm pr dy her madhsin e vjetr. E shtojm 1 ashtu q
dyfishi fillestar konverton vargun e madhsis 0 n varg t madhsis 1. N
rreshtin 18, elementi aktual hyrs i ndahet vargut dhe numri i elementeve t
lexuara inkrementohet (rritet pr 1). N rreshtin 20, e ricaktojm (ndryshojm)
madhsin e vargut pr t ju prshtatur numrit t elementeve q u lexuan. Nj
alternativ sht q itemsRead t bhet parametr referenc q pfundimisht
vendoset n madhsin e re t vargut. Kur hyrja dshton (pr fardo arsyeje),
thjesht kthehemi (return). Rutina (funksioni) main e thrret getInts, duke ia
prcjellur vector-in. Madhsia fillestare e vector-it ndodh t jet 0.
Teknika e prdorur n programin 1.2 sht aq e zakont, saq vector ka
funksionalitetin e brendshm pr t imituar kt. Idea themelore sht q
vector t mirmbaj jo vetm madhsin (angl. size), por gjithashtu edhe
kapacitetin (angl capacity). Kapaciteti sht sasia e memories q ai e ka
rezervuar. Kapaciteti i vector-it sht vrtet nj detal i brendshm, jo dika pr
ka q ju duhet t brengoseni.
Funkscioni push_back( ), (angl. push back shtyje prapa), e rrit
madhsin pr nj dhe e shton elementin e ri n pozitn e duhur. Ky sht nj
veprim i thjesht nse nuk sht arritur kapaciteti, por nse sht arritur, ather
kapaciteti zgjerohet/rritet automatikisht, duke prdorur strategjin e prmendur
m hert. (Disa kompajler nuk e dyfishojn madhsin, por e rrisin pr nj
vler t vogl konstante, gj q rezultaon n performans t dobt), N mnyr
tipit, e fillojm vector-in me madhsi 0.
Kodi n Programin 1.3, tregon se si mund t prdoret push_back n getInts,
q sht shum m i thjesht sesa funksioni getInts n programin 1.2.
1
#include <stdlib.h>
2
#include <iostream>
3
#include <vector>
4
using namespace std;
5
6
// Lexo nje numer te pakufizuar te int-ev pa perpjekje per
rregullim gabimesh

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 }

Programi 1.3 Kodi pr t lexuar nj numr t pakufizuar t int-eve dhe pr ti


shkruar prmes prdorimit t push_back.
Rreshti 13 e ricakton madhsin e vector-it n 0 elemente. Kjo mundet ose
mundet t mos e redukoj kapacitetin e tij, varsisht prej implementimit t
brendshm t vector-it. Vreni q nse nuk e bni resize, ather elementet
e reja do t vendosen n fund t vector-it, prandaj elementet q ishin n
vector kur u thirr getInts, akoma do t jen aty.

1.2.5 Mekanizmat e prcjelljes s parametrave t funksionit


Supozojm se dshirojm t prcjellim nj vektor n funksionin i cili e gjen
vlern maksimale n varg. Deklarimi natyral i funksionit do t ishte:
int findMax(vector<int> a);

Ky deklarim i funksionit ka nj problem fundamental: mekanizmi standard


(default, i nnkuptuar) i prcjelljes s parametrave tek funksioni, kur thirret
funksioni, sht call by value (angl. call by value thirrja sipas/prmes
vlers). N kto raste krijohet nj kopje e argumentit aktual (prbrenda
funksionit) dhe prdoret si parametr formal pr seciln thirrje t funksionit
findMax. Pasi q a mund t jet i madh, ky operacion sht i shtrenjt (i
kushtueshm), kshtu q thirrja sipas vlers sht e paprshtatshme. Nj
alternativ, sht q t prdoret thirrja sipas referencs.
int findMax(vector<int> &a);

Tani, mund t evitohet mbingarkesa e kopjimit. Sidoqoft, as ky funksion


akoma nuk sht perfekt, sepse deklarimi i tregon lexuesit dhe poashtu
kompajlerit, se argumenti aktual mund t ndryshohet si rezultat i thirrjes s
funksionit findMax. Kur parametri prcillet sipas vlers, e kishim t garantuar
54

Algoritmet dhe strukturat e t dhnave


q parametri aktual nuk do t ndryshohet, pasi q krijohej kopja e tij pr
funksionin. Pr t fituar sjellje t till, mund t prdorim formn e tret t
prcjelljes s parametrave, thirrja sipas referencs konstante (angl. call by
constant reference):
int findMax(const vector<int> &a);

Referenca konstante garanton q:


- Mbingarkesa e kopjimit sht evituar, dhe
- Parametri aktual nuk ndryshohet prej thirrjes s funksionit.
Kur thirret funksioni, prmes mekanizmit prcjells t parametrave atij i
prcillen parametrat aktual pr t cilt funksioni e kryen punn. Ato jan vlerat,
pr t cilat thirret funksioni. Varsisht prej mnyrs s thirrjes, efekti n
parametrat aktual ndryshon. Thirrjet mund t bhen, sipas vlers (e
zakonshmja), sipas referencs dhe sipas referencs konstante.
Thirrja prmes vlers (Call by value) sht mekanizmi i nnkuptuar i
prcjelljes s parametrave. Me rastin e thirrjes s funksionit, argumenti aktual,
kopjohet n parametrin formal.
Thirrja prmes referencs (Call by reference) sht mekanizmi i prcjelljes
s parametrave i cili eviton kopjimin. Mirpo, lejon ndryshimin e parametrave
aktual.
Thirrja prmes adress (Call by address) sht mekanizmi i prcjelljes s
parametrave sipas adress. Me rastin e thirrjes s funksionit, argumenti aktual
sht pointer, i cili e prcjell adresn e parametrit aktual. Edhe n kt rast,
funksionit i lejohet/mundsohet ndryshimi i parametrave aktual, pasi q sht
prcjellur adresa n mmorie e parametrit aktual, kshtu q ndryshimet
ndodhin n origjinalin e prcjellur si parametr tek funksioni.
Thirrja prmes referencs konstante (Call by constant reference) sht
mekanizmi i prcjelljs s parametrave, i cili eviton kompjimin dhe garanton q
parametri aktual nuk do t ndryshohet.

Zgjedhja e mekanizmit t prcjelljes s parametrave (thirrjes s funksionit) sht


nj detyr e nnvlersuar shpesh nga programert. Tek e fundit, cila do mnyr
q zgjedhet, programi sht korrekt, mirpo n C++ zgjedhja me kujdes e
mekanizmit t prcjelljes s parametrave sht e rndsishme pr efikasitet,
lexueshmri dhe mirmbajtje t programit.
Thirrja sipas referencs krkohet pr objektet t cilat mund t ndryshohen nga
funksioni.
55

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.

1.2.6 Vargjet primitive t konstanteve


Ndonjher, i kthehemi vargjeve primitive kur kemi konstante globale. Arsye
pr kt sht forma e prshtatshme e shkruarjes prmes shkurtesave, si
ilustrohet n deklarimin vijues t ditve t muajit:
const int DITET-NE-MUAJ[ ]={31,28,31,30,31,30,31,31,30,31,30,31};

Ktu, madhsia e vargut primitiv inicializohet automatikisht dhe madhsia e tij


nxjerret n prfundim nga numri i inicializuesve q jan prezent. Nse ky varg
sht global, numri i elementeve mund t prcaktohet duke ndar sasin e
memories s prdorur nga vargu primitiv sizeof (DITET-NE-MUAJ) me
sasin e memories s prdorur nga njri element n vargun primitiv: sizeof
(DITET-NE-MUAJ [0]), si n vijim:
const int NUM-MUAJVE=sizeof(DITET-NE-MUAJ)/sizeof(DITET-NE-MUAJ[O]);

1.2.6.1 Vargjet shumdimensionale (multidimensionale)


Ndonjher qasja n vargje duhet t jet e bazuar n m shum se nj indeks.
Varg multidimensional sht vargu q qaset me m shum se nj indeks dhe
versioni primitiv i tij sht i klass s dyt. Klasa matrix mund t prdoret pr
t implementuar vargjet dydimensionale. Nuk ka version t klasit t par n
STL. Madhsit e indeksave t saj jan t specifikuara dhe secili element qaset
duke vendosur indekset prkatse n kllapa t mesme t veanta. Pr shembull,
deklarimi:
matrix<int> x( 2, 3 ) ; // x ka dy rreshta dhe tri kolona

definon vargun dy-dimensional x, me indeksin e par n rangun 0 deri n 1 dhe


me indeksion e dyt n rangun 0 deri n 2 (me gjithsej 6 objekte). matrix
56

Algoritmet dhe strukturat e t dhnave


rezervon gjasht lokacione t memories pr objektet: x[0][0], x[0][1],
x[0][2], x[1][0], x[1][1], dhe x[1][2].

1.2.8 Tipi string nga libraria standarde


Pr t prdorur tipin string t libraris standarde (STL), duhet t prfshihet
direktiva:
#include <string>

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.

Sintaksa e pointerve n C++


Q pointeri t pointoj n nj objekt, ne duhet t dijm adresn e memories s
objektit cak (d.m.th., vendin ku sht ruajtur). Pr, (pothuajse) secilin objekt
obj, adresa e tij memorike jepet prmes aplikimit t operatorit unar &
address-of (angl. address-of adresa e) ose themi operatori i adress. Pra,
operatori i adress e kthen adresn e objektit. Prandaj, &obj sht lokacioni n
memorie kur ruhet obj. Objektet e ruajtura prmes prdorimit t klass s
ruajtjes register nuk mund t jen cak (angl. target-cak) i operatorit t adress.
N mund t deklarojm se objekti ptr pointon n nj objekt int, duke shkruar:
int *ptr;

Vlera e reprezentuar nga ptr sht nj adres. Si n rastin e deklarimit t tipeve


t zakonshme, ky deklarim nuk e inicializon pointerin ptr n ndonj vler t
veant, kshtu q prdorimi i ptr para se ti ndahet ndonj vler, prodhon
rezultate t kqija (programi bllokohet). Supozojm se kemi br deklarimet:
int x = 5;
int y = 7;

N mund ta bjm pointerin ptr q t pointoj n x duke ia ndar ptr-s


lokacionin n memorie ku ruhet x-i. Prandaj:
ptr = &x; // LEGAL

cakton ptr-n t pointoj n x. Figura 1.14 ilustron kt n dy mnyra. N


pjesn (a) modeli i memories tregon ku ruhet secili objekt. N pjesn (b)
prdoret nj shigjet pr t treguar pointimin.
Vlera e t dhnave q pointohen (tregohen me pointer) fitohet prmes operatorit
unar t dereferencimit *. Operatori unar i dereferencimit i qaset vlert prmes
pointerit. N Figurn 1.6 *ptr do t ket vlern 5, e cila sht vlera e variabls x
n t ciln pointohet. Dereferencimi i objektit q nuk sht pointer sht ilegal.
58

Algoritmet dhe strukturat e t dhnave


Operatori * sht e kundrta e operatorit & (d.m.th., *&x=5 sht njsoj si x=5,
pr aq sa &x sht legal). Dereferencimi funksionon jo vetm pr leximi e
vlerave nga objekti, por edhe pr shkruarjen e vlerave t reja n objekt. Prandaj,
nse kemi:
*ptr = 10 //e lejueshme (tekstualisht: aty ku tregon pointeri, vendose vlern 10
i bie q kemi ndryshuar vlern e x-i n 10. Figura 1.157 paraqet ndryshimet q
rezultojn dhe problemin me pointert: ndryshimet e pakufizuara jan t
mundshme dhe pointeri q e huq adresn mund t mbishkruaj vlerat n
memorie t fardo variable, pa qllim.
Kemi mundur ta inicializojm pointerin ptr n kohn e deklarimit, duke br q
ai t pointoj n x:
int x = 5;
int y = 7;
int *ptr = &x; // OK

(&x) 1000

x=5

(&y) 1004

y=7

5
ptr

(&ptr) 1200

1000
(b)

(a)

Fig. 1.14 Ilustrim i pointerit.

(&x) 1000

x = 10

(&y) 1004

y=7

10
ptr

(&ptr) 1200

7
y

ptr = &x = 1000


(a)

(b)

Fig. 1.15 Rezultati i *ptr = 10.

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

Supozojm se kemi harruar operatorin e adress. Ather urdhri:


60

Algoritmet dhe strukturat e t dhnave


ptr = x; // ILLEGAL: x nuk sht adres

do t gjeneroj gabim kompajleri. Jan dy raste kur kompajleri nuk reagon.


Njri sht prdorimi i operatorit t adres n ann e djatht, si n sintaksn
korrekte. Tjetri sht i gabuar:
*ptr = x; // Semantikisht jokorrekt

Kompajleri do t hesht, sepse urdhri thot q int n t cilin pointon ptr


duhet t merr vlern e x. Pr shembull, nse ptr sht &y, ather y i ndahet
vlera e x. ky urdhr sht trsisht legal, por nuk e bn at q synohej, q ptr t
pointoj n x. pr m tepr, nse ptr sht i painicializouar, dereferencimi do t
shkaktoj gabim gjat kohs s ekzekutimit, si u diskutua m hert. Ky gabim
sht i dukshm nga Figura 1.8. si rregull e preferuar, sht q n shenjn e par
t problemeve me pointer, t vizatohet figura, gj q e sqaron situatn.
Prdorimi i *ptr=x n vend t ptr=&x sht gabim i shpesht pr dy arsye. S
pari, sepse heshtja e kompajlerit, bn q programeri t ndjehet komod prkundr
semantiks jokorrekte. S dyti, duket ngjashm sikur sintaksa e prdorur pr
inicializimin n kohn e deklarimit. Dallimi sht se * n kohn e deklarimit
nuk sht * i dereferencimti por vetm nj tregues se objekti sht i tipit pointer.
Ndonjhere duhet prcaktuar n mnyr eksplicite se pointeri nuk sht duke
pointuar askund, si rast i kundrt me at t lokacionit t padefinuar. N kto
raste prdoret NULL pointeri, i cili pointon n lokacion q sht e garantuar
se sht i paaft q t mbaj ndonj vler. Rrjedhimisht, NULL pointeri nuk
mund t dereferencohet. NULL pointeri ka vlern 0 dhe nuk duhet t
dereferencohet asnjher. Prdoret pr t treguar se pointeri nuk pointon askund.
Konstanta simbolike NULL sht e definuar n disa header fajlla dhe mund t
prdoret kjo ose edhe vlera zero. M s miri sht q pointert t inicializohen
n NULL pointer, sepse n shum raste nuk kan vlera fillestare t nnkuptuara
(rregulla e njjt vlewn edhe pr tipet tjera t predefinuara).
Pointeri i dereferencuar njsoj si objekti n t cilin pointon. Prandaj, pas tre
urdhrave vijues, vlera e ruajtur n x do t jet 15:
x = 5;
ptr = &x;
*ptr += 10;

//ptr merr adresen e x


//aty ky pointon ptr (tek x), shto 10

Sidoqoft, duhet t jemi t vetdijshm pr rregullat e prioritetit sepse sht e


mundur kryerja e veprimeve aritmetik jo vetm n vlerat e dereferencuara por
edhe n vet pointert (vlerat e padereferencuara). Kjo aftsi sht nj pasoj e
pafat e rregullave shum liberale t C++-it, t cilat lejojn aritmetikn e
pointerve, duke prfituar nga fakti q pointert n mnyr interne ruhen si
integjer. Megjithat, evitoni prdorimin e aritmetiks s pointerve n tekste.
61

Avni Rexhepi
Pr shembull, dy urdhrat vijues jan shum t ndryshm.
*ptr += 1;
*ptr++;

N urdhrin e par, operatori aplikohet n *ptr, por n t dytin operatori ++


(inkrementimi) aplikohet n ptr. Rezultati i aplikimit t operatorit ++ n ptr do t
jet ai q ptr ndrrohet q t pointoj n lokacion t memories pr nj njsi
memorike m t madhe sesa q kishte m hert.
Nse ptr1 dhe ptr2 jan pointer n tip t njjt, ather
ptr1 = ptr2;

prcakton q ptr1 t pointoj n t njjtin lokacion si ptr2, ndrsa


*ptr1 = *ptr2;

ia ndjan ptr1-shit t dereferencuar, vlern e ptr2-shit t dereferencuar. Figura


1.16 ilustron se si kta dy urdhra jan trsisht t ndryshm. Pr m tepr, kur
prdoret gabimisht forma e gabuar, pasojat mund t mos jen t dukshme
menjher.

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;

sht e vrtet nse t dy pointert pointojn n t njjtin lokacion t memories,


ndrsa
*ptr1==*ptr2;

sht e vrtet nse vlerat e ruajtura n t dy adresat e treguara jan t barabarta.


Prdorimi i forms s gabuar sht gabim i shpesht.
62

Algoritmet dhe strukturat e t dhnave


Krkesa q ptr1 dhe ptr2 t pointojn n t njjtin tip sht pasoj e faktit q
C++ sht strongly typed (me kontroll t lart t tipeve t variablave dhe
lokacioneve t tyre, si dhe kufizim t shndrrimit t brendshm t tipeve, pa
kontroll dhe pa prdorim t operatorve eksplicit t konvertimit t tipeve): tipet
e ndryshme t pointerve nuk mund t przihen pa nj konvertim eksplicit t
tipit, prveq nse shftytzuesi ofron nj konvertim implicit t t dhnave.
S fundi, kur deklarohen pointert, vendosja e * dhe zbraztirave prreth tij
jan t parndsishme pr kompajlerin, prandaj prdoreni stilin q ju plqen.

1.4 Menaxhimi dinamik i memories (Dynamic Memory


Management)
Deri tani, t gjitha variablat q i kemi prdorur jan variabla automatike. Ky
term (i prdorur rrall) tregon se variablat lokale krijohen kur t arrihen n
funksion dhe asgjsohen (shkatrrohen) kur m nuk jan n fushveprimin e
funksionit (p.sh., kur funksioni kthen vlern me return). Ndonjher, objektet
duhet t krijohen n mnyr tjetr. Mnyra tjetr sht alokimi dinamik i
memories.

1.4.1. Operatori new


Objektet mund t krijohen n mnyr dinamike duke thirrur operatorin new
(angl. new - i ri, e re). Operatori new alokon memorien n mnyr dinamike
(gjat ekzekutimit t programit) dhe kthen pointerin pr n objektin e ri t
krijuar. Si rezultat t new kemi pointerin q pointon n objektin e ri t krijuar.
Programi 1.5, ilustron shtjet e prfshira n alokimin dinamik t memories.
Sidoqoft, shembulli sht nj prdorim i varfr i memories dinamike, pasi q
do t duhej t prdoret nj string automatik. N kt rast prdoret vetm sa pr
t ilustruar alokimin dinamik n kontekst t thjesht. Aplikimi m i arsyeshm
do t paraqitet m von (n pikn 1.6.2).
N programin 1.5, rreshti 9 krijon nj string t ri n mnyr dinamike. Vreni se
strPtr sht pointer n tipin string, ashtu q vet string qaset prmes *strPtr, si
sht treguar n rreshtat 10-13. Kllapat jan t nevojshme n rreshtin 11 pr
shkak t rregullave t prioritetit (precedencs).
1
2
3
4
5
6

#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

1.4.2 Pastrimi i mbetjeve dhe fshirja


N disa gjuh programuese, kur objekti m nuk referohet, sht subjekt i
grumbullimit automatik t mbeturinave (angl. garbage collection pastrim i
mbetjeve, mbledhje mbeturinash). Programeri nuk duhet t brengoset pr kt
problem (sht fjala pr mos znien e hapsirs n memorie, nga
variablat/objektet t cilat m nuk jan t nevojshme n program). Mirpo, C++
nuk e ka mekanizmin e grumbullimit automatik t mbeturinave. Kur nj objekt
q alokohet me new, nuk referencohet m tutje, ather duhet t aplikohet
operatori delete (angl. delete-fshije) duhet t aplikohet n objekt (prmes
pointerit). Prndryshe, memoria t ciln ai e konsumon humbet (deri sa
programi t ndalet), gj q njihet si memory leak (angl. leak-rrjedhje, pikim,
humbje, etj.). fatkeqsisht, rrjedhjet e memories jan dukuri e shpesht n shum
programe n C++. Pr fat t mir, shum burime t rrjedhjes s memories mund
t largohen automatikisht, me kujdes, si do t shihet n vazhdim.
Nj rregull e rndsishme sht q t mos prdoret new kur mund t prdoret
variabla automatike. Variabla automatike pastrohet automatikisht (prandaj edhe
quhet ashtu). Nuk duhet prdorur kurr delete n nj objekt q nuk sht krijuar
me new; prndryshe, do t rezultoj me nj shkatrrim gjat kohs s
ekzekutimit. Operatori delete sht ilustruar n rreshtin 15 (t programit 1.5).

1.4.3 Stale Pointert, fshirja e dyfisht dhe problemet tjera


Nj arsye q programert mund t gjinden n telashe gjat prdorimit t
pointerve sht fakti q nj objekt mund t ket disa pointer t cilt pointojn
n t. Shqyrtoni kodin n vijim:
string *s = new string( "hello" ) ; // s pointon n

64

Algoritmet dhe strukturat e t dhnave


//stringun e ri
string *t = s; // t pointon aty, poashtu
delete t; // Objekti nuk sht m

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

Algoritmet dhe strukturat e t dhnave


Referenca duhet t inicializohet kur t deklarohet. Ajo nuk mund t ndryshohet
pr t ju referuar nj variable tjetr sepse nj tentim i ricaktimit t referencs
prmes urdhrit:
cnt = njeObjektTjeter;

do t ja ndante/caktonte objektit emeriGjateiVariables vlern e objektit


njeObjektTjeter. Kjo qasje reflekton me saktsi se si ata prdoren n raste
m t prgjithshme n t cilat fushveprimi i variabls referente sht i
ndryshm prej atij t objektit q referohet. Nj rast i rndsishm sht
prdorimi i variabls referente si parametr formal i funksionit, me rast ajo
vepron si nj alias pr argumentin aktual, me rastin e thirrjes s funksionit. Kjo
sht diskutuar n kontekst t prcjelljes s parametrave t funksioneve, n
rastin e prcjelljes s vektorve (1.2.5).
Le t rishikojm prcjelljen e parametrave.
N programin 1.7 ilustrohet procedura/funksioni swapGabim( ) e cila nuk
funksionon pr shkak t kurfizimeve t thirrjes sipas vlers (prcjelljes sipas
vlers) tek funksionet. Dy alternativat korrekte jan ajo me pointer dhe me
referenc. Thirrja prmes pointerit, q sht C style (n stil t C-s) sht
tradicionale pr gjuhn C, bhet pr t evituar kufizimet e thirrjes prmes vlers.
Thirrja prmes referencs, e cila prdor parametrat referent t C++, sht
opcioni tjetr, trsisht identik pr nga funksionimi.
Dallimet mes tipeve me referenca dhe me pointer jan t prmbledhura si vijon:
- N deklarim t funksionit, parametrat referenc prdoren n vend t
pointerve.
- N definicion t funksionit, parametrat referenc dereferencohen n
mnyr implicite, ashtu q nuk ka nevoj pr operatort * (vendosja e
tyre do t gjeneroj gabim sintaksor).
- N thirrjen e funksionit sapRef, nuk nevojitet &, sepse adresa prcillet
n mnyr implicite mbshtetur n faktin se parametrat formal
korrespondues jan referenca.
- Kodi i cili prfshin prdorimin e parametrave referent sht shum m
i lexueshm.
1
2
3
4
5
6
7
8

#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;
}

Programi 1.7 Call-by-reference kundrejt call-by-pointer.


Referencat jan sikur konstantet pointer n at se vlera t ciln e ruajn sht
adresa e objektit t cilit i referohen. Ato jan ndryshe n at se aplikohet nj
operator automatik i padukshm i dereferencimit n referenc. Ky dallim
prkthehet n lehtsi t notacionit, posaqrisht pasi q mundson q parametrat
t prcillen sipas referencs pa bagazhin shtes t operatorit & n argumentet
aktuale dhe operatorin * i cili shkakton rrmuj n programet dhe funksionet
n stilin e C-s.

68

Algoritmet dhe strukturat e t dhnave


Prndryshe, edhe pointert mund t prcillen sipas referencs. Kjo metod
prdoret pr ti mundsuar funksionit q t ndryshoj vendin ku pointon pointeri
i prcjellur si parametr. Pointeri q prcillet me thirje sipas vlers nuk mund t
ndryshohet q t pointoj n lokacion t ri (sepse parametri formal ruan vetm
nj kopje t vlers s atij vendi).
Nj shtje tjetr me rndsi sht zgjedhja ndrmjet prcjelljes s parametrave
sipas vlers ose sipas referencs. Edhe pse kjo m hert u diskutua n kontekst
t vektorve, kjo vlen pr t gjitha tipet e parametrave.

1.6 Strukturat dhe pointert


Rikujtojm se vargu sht koleksion i objekteve (vlerave, variablave) t tipit t
njjt. Vargu ka dy prparsi kryesore: s pari vargu indeksohet dhe mund t
lvizim npr secilin element t vargut me an t unazave dhe s dyti, kur
prdoren funksionet, mund t prcillet emri i vargut dhe kshtu t prdoret
vetm nj parametrt pr t drguar tr prmbledhjen.
Nj tip tjetr i tipit prmbledhs n C++ sht struktura. Struktura ruan nj
koleksion t objekteve t cilat nuk sht e domosdoshme t jen t tipit t njjt.
Pasi q objektet n koleksion nuk jan t tipit t njjt, nuk mundemi q thjesht
t kalojm me unaza npr to, sikur n rastin e vargjeve.
Secili objekt n struktur sht antar (angl. member) dhe qaset prmes
operatorit pik t antarit (angl. dot member operator). Deklarimi i strukturs
baz bhet prmes prdorimit t fjals s rezervuar struct, emrit t strukturs
dhe lists s antarve t prmbyllur n kllapa t mdha. Pr shembull:
struct Student
{
string emri;
string mbiemri;
int numriStudentit;
double notaMesatare;
}
emri
mbiemri
numriStudentit
notaMesatare

Fig. 1.18 Struktura Student


69

Avni Rexhepi
Figura 1.18. ilustron se struktura Student prbhet prej katr objekteve t
ndryshme. Nse kemi deklarimin,
Student s;

ather, pr notn mesatare do t kemi s.notaMesatare. Programi 1.8


ilustron deklarimin e strukturs, qasjen e antarve t t dhnae dhe prcjelljen e
tyr si parametra t funksionit. Vreni se strukturat zakonisht nuk prcillen sipas
vlers pr shkak se mbingarkimi i prcjelljes sipas vlers mund t jet shum i
shtrenjt (pr shkak t krijimit t kopjeve t parametrave, n kt rast).
Mekanizmi i prcjelljes s parametrave prcaktohet sipas asaj q u theksua tek
diskutimi pr kt shtje (1.2.5).
1 // Shtypi informacionet per studentin
2 void printInfo( const Student &s )
3 {
4
cout << "ID
: " << s.numriStudentit << endl;
5
cout << "Emri
: " << s.emri << " "
6
<< s.mbiemri << endl;
7
cout << "Mesatarja: " << s.notaMesatare << endl;
8 }
9
10 // main i thjeshte.
11 int main( )
12 {
13
Student meri;
14
15
meri.emri = "Meri" ;
16
meri.mbiemri = "Shala" ;
17
meri.notaMesatare = 4.0;
18
meri.numriStudentit = 123456789 ;
19
20
printInfo(meri);
2
22
return 0;
23 }

Programi 1.8 - Programi q ilustron deklarimin e strukturs, qasjen e antarve


t saj dhe prcjelljen e parametrave
Struktura e C++ sht zgjeruar dukshm nga homologia e saj n C, pr t
mundsuar funksionet si antar dhe pr qasjen n antar.

70

Algoritmet dhe strukturat e t dhnave


N diskutimin pr teknikat e avansuara t programimit, hyn edhe deklarimi i
pointerit n struktur, pr t ju qasur antarve t strukturs s pointuar.
Supozojm se kemi:
Student *ptr = &s;

//ptr pointon n sturkuturn s

Ather ne mund ti qasemi nots mesatare prmes (*ptr).notaMesatare.


Kllapat jan t domosdoshme n kt rast, pr t rregulluara prioriteitn, pasi qe
operatori i antarit, duke qen operator postfix, ka prioritet m t lart sesa
operatori prefix i dereferencimit. Meqense prdorimi i kllapave bhet i
mrzitshm, C++ ofron nj postfix operator tjetr plotsues, operatorin ->
(member selection operator operatori pr selektim
antarit), i cili i qaset antarve t strukturs n t ciln pointohet.

Pra, operatori -> prdoret pr t ju qasur antarve t strukturs s pointuar,


prandaj, edhe forma ptr->notaMesatare jep qasjen e njjt si urdhri m
par.

1.6.1 T dhnat ekzogjene kundrejt atyre indigjene dhe kopjimi i


cekt kundrejt atij t thell
C++-i i lejon shfrytzuesit q t definoj operatort n struktura. Pr shembull,
shfrytzuesi mund t shkruaj funksionin me deklarimin:
bool operator<(const Student &lhs, const Student &rhs);

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;

pr t recikluar memorien e alokuar dinamikisht, s ndodhet n telashe serioze.


Ky problem sht i ilustruar n Figurn 1.19, e cila thekson dallimin ndrmjet t
dhnave ekzogjene dhe atyre indigjene. (indigjene-vendse, t brendshme;
ekzogjene-jo vendse, t jashtme).
Meri

emri
s

mbiemri
12345

IDPunetori

12345

Shala

Fig. 1.19 Kopjimi i cekt, ku kopjohen vetm pointert


T dhnat indigjene (t vendit) jan trsisht t prmbajtura nga ana e
strukturs. Pr shembull, n strukturn Student, antart emri dhe mbiemri
jan stringje dhe jan trsisht t vet-prmbajtur. Disavantazh i reprezentimit
t objektit n mnyr indigjene sht se madhsia e objektit sht fikse,
zakonisht sht e madhe dhe prandaj e kushtueshme pr tu kopjuar (kur
prcillet n funksionet, etj).
T dhnat ekzogjene (t jashtme), prkundrazi, qndrojn jasht strukturs dhe
jan t qasura prmes pointerit. Avantazh i t dhnave ekzogjene sht se t
dhnat e zakonshme (prbashkta) mund t bashkndahen (angl. shared) mes
72

Algoritmet dhe strukturat e t dhnave


disa instancave; kur prdoret operatori standard i ndarjes s vlers, kopja sht
vetm kopje e pointerve, jo edhe e vlerave t pointuara. Zakonisht kjo sjellje
sht e dshirueshme. Pr shembull, n Java, kjo sht standarde.
Kopjimi i cekt (angl. shallow copy) nnkupton kopjen e pointerve, e jo t t
dhnave t cilat pointohen. Ngjashm, krahasimet pr barazi pr t dhnat
ekzogjene jan t cekta n mnyr standarde, sepse ato krahasojn vetm
adresat. Edhe pse kopja e cekt sht korrekte n raste t caktuara, lejimi i
kopjimit t cekt kur nuk sht i garantuar mund t drgoj n
shkatrrim/rrmuj.
Kopjimi i thell (angl. deep copy), n t cilin kopjohen vlerat n t cilat
pointohet, n prgjithsi nevojitet pr t alokuar hapsir memorike shtes dhe
pastaj pr t kopjuar pointert e dereferencuar. Kjo krkon rishkrimin e
operatorit t ndarjes s vlers me kopjim (angl. copy assignment operator).
Detajet e implementimit t ksaj procedure jepen m von. Normalisht, duhet t
sigurojm edhe operatorin e krahasimit t thell, pr t implementuar testin e
thell (natyrisht, kur gjejm se jemi duke prdorur s shumti operacionet e
thella, mund t kthehemi n prdorimin e t dhnave indigjene).

1.6.1 Listat jo t vazhduara listat e lidhura


Do t diksutojm nj teknik t prdorur n strukturat e t dhnave. M par u
tregua se duke prdorur vargjet dinamikisht t zgjerueshme, mund t lexojm
nj numr arbitrar t t dhnave hyrse (elementeve hyrse). Kjo teknik ka nj
problem serioz. Supozojm se jemi duke lexuar rekorde 1000-bajtshe dhe kemi
1,000,000 bajta t memories n dispozicion (t lir). Gjithashtu, supozojm se
n nj moment, vargu prmban 400 rekorde dhe sht i mbushur n trsi.
Ather, pr t dyfishuar at, e krijojm nj varg t ri me 800 rekorde, i
kopjojm n t 400 rekordet ekzistuese dhe pastaj i fshijm ato 400 rekordet (t
vjetrat). Problemi sht se n kt hapin e ndrmjetm (kalimtar), kemi n
prdorim t dy: vargun me 400 rekorde dhe vargun me 800 rekorde dhe kshtu
kemi n total 1200 rekorde, q i tejkalon kufijt e memories. N fakt, mund t
mbesim pa memorie pas bashkndarjes s prafrsisht nj t trets s memories
n dispozicion.
Zgjidhja e ktij problemi, sht q t lejohet q lista e rekordeve t ruhet n
fom jo t vazhduar (angl. non-contiguously jo e afrt, jo e puthitur, jo e
vazhduar, sht fjala pr dallim nga rasti kur t gjith antart e vargut, jan n
lokacione t njpasnjshme, t vazhdueshme n memorie). Pr secilin rekord, e
mbajm nj struktur e cila ruan rekordin (vlern) dhe nj pointer next (angl.
next-tjetri, i ardhshmi), pr n strukturn e ardhshme n list. Shembulli

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;

N do pik, ne mund t shtypim listn, duke prdorur iteracionin (unazn)


for( Node *p = first; p != NULL; p = p->next )
printItem( p->item );

dhe n do pik mund t shtojm nj element t ri t fundit x, si n vijim:


//last=iFundit; new=iRi,eRe
last->next = new Node;// Shto nj nyje t re
last = last->next;
// Prshtate t fundit-last
last->item = x;
// Vendose x-in n nyje
last->next = NULL;
// Ky sht i fundit, kshtu q bje
// next=NULL
A0

A1

First
(Fillimi, i/e par-i/a)

A3

A2

Last
(Fundi, i/e fundit)

Fig. 1.20 Lista e lidhur


Kshtu, elementet mund t mos jen n lokacione t njpasnjshme n memorie,
mirpo pr t gjetur nj element t lists, m nuk mundmi me vetm nj asje, si
n rastin e vargut t zakonshm, kur prmes indeksit, secili element mund t
gjindej direkt, me vetm nj qasje. N vend t ksaj, duhet t skenojm (angl.
scan-hetim, krkim, kqyrje etj.) listn prej fillimit e tutje. Dallimi sht i
ngjashm me at t qasjes n t dhnat (p.sh., kngt) n CD (nj qasje) dhe n
shirit (sekuenciale). P.sh., pr t dgjuar kngn e 3, n CD mundemi direkt,
kurse n kasetofon t vjetr, duhet rrotulluar shiritin prej fillimit e deri te knga
e tret.
N ann tjetr, insertimi i nj elementi t ri ndrmjet dy elementeve ekzistuese
krkon shum m pak lvizje t t dhnave n listn e lidhur sesa n nj varg.
P.sh., pr t shtuar nj element t ri n mes t dy antarve ekzistues, duhet
74

Algoritmet dhe strukturat e t dhnave


kopjuar pjesa prapa e vargut, pr tu ruajtur dhe zhvendosur n pozitat pas
insertimit t antarit t ri, kurse, n rastin e lists s lidhur, kjo gj realizohet
vetm duke i ndrruar pointert e elementit para dhe atij pas elementit t ri mes
tyre, q t krijohet renditja e re. Avantazhi i listave t lidhura sht m pak
hapsir e prdorur pr objektet e mdha sesa n teknikn e dyfishimit t vargut.
Dnimi q paguhet sht q qasja n element nuk sht m konstante n koh.
Listat e lidhura do t diskutohen detajisht m von.

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

Prveq ktyre operacioneve, operacionet e iteratorve pr klasat deque dhe


vector jan si vijon:
i1 < i2, i1 <= i2, i1 > i2, i1 >= i2
i1 + n, i1 - n
i1 += n, i1 -= n,
i1[n]

1.7.3 STL Algoritmet


STL ofron afr 70 funksione t prgjithshme, t cilat mund t aplikohen n
kontejner dhe vargje, duke u prfshir n program prmes direktivs #include
<algorithms>.
Kto funksionie (t quajtura edhe algoritme) jan operacione q prdoren shum
shpesh n shumicn e programeve, si p.sh. lokalizimi i nj elementi n
kontejner, insertimi i elementeve, largimi i elementeve, modifikimi i
elementeve, krahasimi i elementeve, gjetja e vlers bazuar n sekuencn e
elementeve, sortimi i elementeve, e kshtu me radh. Pothuajse t gjitha STL
algoritmet prdorin iteratort pr t treguar rangun e elementeve n t cilat ato
operojn. Iteratori i par i referohet elementit t par n rang, i dyti elementit
pas elementit t par. Prandaj, supozohet se sht gjithmon e mundur q t
arrihet poizita e treguar me iteratorin e dyt duke inkrementuar iteratorin e par.
Pr shembull, thirrja e funksioneve:
76

Algoritmet dhe strukturat e t dhnave


random_shuffle(c.begin(), c.end());

i rendit n mnyr t rastit t gjitha elementet e kontejnerit c. Thirrja:


i3 = find (i1, i2, el);

kthen nj iterator i cili tregon pozitn e elementit el n rangun prej i1 deri n


i2. Thirrja:
n = count_if(i1, i2, oddNum);

numron prmes algoritmit count_if elementet n rangun e treguar prmes


iteratorve i1 dhe i2, pr t cilt funksioni me nj argument, i definuar nga
shfrytzuesi, oddNum(), kthen true.
Algoritmet e STL-it jan funksione t cilat jan plotsim pr funksionet e
ofruara nga kontejnert. Sidoqoft, disa algoritme jant t definuara si funksione
antare t klasave, pr t ofruar performans m t mir.

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++;

ngjashm, shembulli vijues, e zvoglon vlern e variabls numriStudentit pr 1,


duke rezultuar n velrn finale 1233.
int numriStudentit = 1234;
numriStudentit--;

Aritmetika e pointerve prdor operatorin e inkrementimit dhe at t


dekrementimit, n mnyr t ngjashme, por pak m ndryshe. Urdhrat vijues
deklarojn dy variabla t prdorura pr t ruajtur numrat e studentve dhe dy
pointer, ku secili pointon n njrn prej ktyre variablave.
int numriStudentit1 = 1234;
int numriStudentit2 = 5678;
int *ptNumriStudentit1;
int *ptNumriStudentit2;
ptNumriStudentit1 = &numriStudentit1;

77

Avni Rexhepi
ptNumriStudentit2 = &numriStudentit2;

Sa do t jet vlera e ruajtur n pointerin ptNumriStudentit1 nse


ptNumriStudentit1 inkrementohet pr 1 duke prdorur urdhrin vijues:
ptNumriStudentit1++;

Kjo sht pak problematike, sepse vlera e ptNurmriStudentit1 sht 0. Nse


ajo inkrementohet pr 1, vlera e re do t duhet t ishte 1. Mirpo, adresa e
memories 2 sht pjesa e dyt e lokacionit t memories s rezervuar pr
numriStudentit1. Kjo do t thot se ptNumriStudentit1 do t pointonte
n mes t vlers s numriStudentit1, gj q nuk ka kuptim.
Ja se ka ndodh n realitet. Kompjuteri prdor aritmetikn e pointerve. Vlerat
inkrementohen dhe dekrementohen n aritmetik t pointerve duke prdorur
madhsin e tipit t t dhnave. Pra, nse adresa e memories prmban nj vler
integer dhe adresa e memories inkrementohet, kompjuteri e shton madhsin
e nj integeri ndaj lokacionit aktual t adress s memories. Kjo i bie, q
lvizja para/prapa bhet me hapin e tipit t pointerit, e secili tip i pointerit ka
madhsin e hapit varsisht prej tipit t variabls n t ciln pointon. Kjo i bie
q pointeri i tipit int, ka hapin 4 bajta (kalon nga 4 bajta), ai i tipit double ka
hapin 8 bajta (kalon nga 8 bajta) dhe ai i tipit karakter ka hapin 1 bajt
(gjegjsisht, lviz me hap prej 1 bajti). N rastin e strukturave, pointeri i
struktors lviz me hapin e madhsis totale t strukturs.
Figura 1.21 paraqet nj varg a, pointerin ptr dhe urdhrin ptr=a. Ktu
prforcohet idea se vlera e ruajtur n a sht vetm lokacioni i memories ku
qndron elementi i zero-t (elementi i pare, me indeks 0) i vargut dhe se
elementet e vargut garantohet t jen t ruajtura n lokacione t njpasnjshme
(konsekutive, t afrta) dhe n rritje t memories. Nse a sht nj varg i
karaktereve, a[1] sht i ruajtur n lokacionin e memories a+1, sepse
karakteret prdorin nj bajt. Pr ket arsye, ++ptr do t rriste pointerin ptr pr
1, duke rezultuar n lokacionin e memories s a[1]. Prandaj, shtimi i nj
integjeri n variabln pointer ka kuptim n nj varg t karaktereve.
Nse a do t ishte varg i integjerve 4-bajtsh, shtimi i 1-shit n ptr do t dukej
se bn q pointri t lviz nj bajt m tutje. Mirpo, pr interpretim m t leht,
thuhet se: ++ptr ia shton adress s pointerit ptr, madhsin e objektit n t
cilin pointon. Ky interpretim bartet n operacionet tjera me pointer. Kshtu,
shprehja x=&a[3] bn q pointeri x t pointoj n a[3]. Nuk ka nevoj pr
kllapa t vogla prreth, si sht thn m hert. Shprehja y=x+4, bn q pointeri
y t pointoj n a[7]. Kshtu, do t mund t prdorim pointert pr t
prshkuar vargun, n vend t metods s zakonshme t iteracionit me indeksat e
vargut.
78

Algoritmet dhe strukturat e t dhnave


a

a[0] a[1]

ptr

a[2] a[3] a[4] a[5]

a[6] a[7] a[8] a[9]

Figura 1.21 Aritmetika e pointerve: x=&a[3]; y=x+4 ;


Nse p sht pointer dhe x sht i tipit integer, g+x vlersohet si adresa g objekte
prtej x-it. Kjo adres sht gjithashtu lokacioni i memories s g[x].

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.

Si prmbledhje, kemi kto operacione t pointerve:


-

Pointerve mund t ju ndahet vlera, t krahasohen pr barazi (jobrazi)


dhe t dereferencohen (n C++ dhe n shumicn e gjuhve
programuese). Operatort jan: =, ==, != dhe *.
N pointer mund t aplikohet operatori i inkrementimit me prefix ose
postfix, t shtohet nj integer dhe t zbritet nj integer ose pointer.
Operatort jan: ++,--, +,-,+= dhe -=.
N pointer mund t aplikojm operatort relacional, por rezultati ka
kuptim vetm nse t dy pointert pointojn n pjest e vargut t njjt
ose s paku njri prej tyre pointon n NULL. Operatort jan: <, <=,
> dhe >=.

79

Avni Rexhepi
-

Mund t testojm me aplikimin e operatorit ! ndaj NULL (sepse NULL


pointeri ka vlern 0).
Mund t indeksojm dhe t fshijm pointert prmes [ ] dhe delete.
Mund t aplikom operatort trivial, si & dhe sizeof, pr t gjetur
informacion pr pointerin (jo pr objektin n t cilin ai pointon).
Mund t aplikojm edhe disa operator t tjer, si ->.

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;

Me variablat e deklaruara, dy urdhrat vijues ua ndajn vlerat pointerit dhe


pointerit t pointerit. N t dy rastet prdoret operatori i adress (&).
Pointerit ptInitial i ndahet adresa e variabls intial1, e cila vendoset n memorie
n adresn 1. Pointerit t pointerit, ptPtIntial, i ndahet adresa e memories s
pointerit ptInitial. Adresa e ptInitial sht adresa 5 e memories. N figurn 1.22
paraqitet meoria e alokuar pas ekzekutimit t ktyre urdhrave.

80

Algoritmet dhe strukturat e t dhnave

Figura1.22 - Variables s pointerit t pointerit i ndahet adresa e memories s


pointerit ptInitial.
Programert e prdorin pointerin n pointer pr ti treguar kompjuterit q t
prdor prmbajtjen e adress s memories n variabln pointer, n t ciln
pointon pointeri n pointer. Kjo sht m leht t sqarohet me nj shembull:
Prmbajtja e variabls intitial1 mund t prdoret duke ju referuar variabls
ptPtInitial. Ja si bhet kjo:
cout << **ptPtInitial;

Urdhri cout i prdorur n kt shembull, bn q t paraqitet prmbajtja e


variabls initial1, edhe pse nuk duket se bn kt. Ky urdhri sht duke i
treguar kompjuterit q t shkoj n adresn e memories t ruajtur n pointerin
ptPtInitial, q pointon n variabln pointer, e cila sht adresa 5 e memories
(Figura 1.23). Prmbajtja e asaj adrese t memories, sht nj adres tjetr e
memories, e q sht adresa 1. Kompjuterit i sht thn t shkoj n adresn e
memories 1 dhe t paraqes prmbajtjen e asaj adrese t memories, e q sht
shkronja D.

Figura 1.23: Dy adresa t memories referohen kur prdoret pointeri n pointer,


pr t paraqitur vlern n ekran.
81

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.

Emri i vargut sht Pointer


Kur alokohet nj varg, kompajleri e shumfishon madhsin n bajta t tipit t
deklaruar me madhsin e vargut (t shnuar brenda [ ]), pr t prcaktuar se sa
memorie duhet rezervuar. N esenc, ky sht prdorimi i vetm i komponents
s madhsis. N fakt, pasi t alokohet vargu, me prjashtime t vogla,
madhsia e tij sht e parndsishme, sepse emri i vargut reprezenton nj pointer
n fillimin e memories s alokuar pr at varg, ashtu si paraqitet n figurn D.1.
Supozojm se kemi deklarimet:
int a[3];
int i;

Kompajleri e alokon memorien si vijon. S pari, tre integjera ndahen/rezervohen


pr vargun dhe referohen prms a[0], a[1] dhe a[2]. Objektet n varg
sht e garantuar se do t ruhen n blloqe t njpasnjshme t memories.
Prandaj, nse a[0] sht ruajtur n lokacionin 1000 dhe integjeri krkon 4
bajta, ather a[1] sht e garantuar se do t jet i lokalizuar n lokacionin
1004 dhe a[2] n lokacionin 1008. N fund, kompajleri alokon memorien pr
objektin tjetr, integjerin i. Nj mundsi sht paraqitur n figurn 1.24., ku i
82

Algoritmet dhe strukturat e t dhnave


alokohet n sllotin e ardhshm n dispozicion (angl. slot-vend i caktuar, ndarje,
vrime, etj).
Pr do i, ne mund t nxjerrim prfundim se a[i] do t ruhet n lokacionet e
memories 1000+4i. Vlera e ruajtur n a sht &a[0]; kjo ekuivalenca sht
gjithmon e garantuar dhe na tregon se n realitet a sht pointer. Vreni se kur
alokohet vargu a, vlera e a sht konstante; nuk krijohet pr t pointeri. Pasi q
ky rast e trajton a si konstant e jo si objekt, &a merr kuptim special (konstantet
normalisht nuk kan adresa) dhe n vetm n kt rast, vlera e &a sht a.
&a[0]

(1000)

a[0]

&a[1]

(1004)

a[1]

&a[2]

(1008)

a[2]

&i

(1012)

i
. . .

konstantet lokale

a=1000

Fig. 1.24. Modeli i memories pr vargjet


Tani, pr t ju qasur elementit a[i], kompajlerit vetm i duhet t merr vlern e
a dhe tia shtoj 4i.
Duke pasur parasysh kt mnyr t manipulimit t vargjeve n C++, mund t
shihet se prse vlejn kufizimet e prmendura pr vargjet dhe si prcillen vargjet
si parametra t funksioneve.
S pari, kemi problemin e verifikimit se a sht indeksi i caktuar brenda rangut.
Kryerja e verifikimit t kufijve do t krkonte q t ruhet madhsia e vargut n
nj parametr plotsues. Sigurisht q kjo sht e mundur, por kjo gj shkakton
kosto shtes t kohs dhe hapsirs. N aplikimet e zakonshme t vargjeve
(stringjet e shkurtra), kjo mbingarkes do t ishte e dukshme. Si sht thn m
par, nse shftyrzuesi dshiron t bj verifikimin e kufijve, mund shkruhet nj
klas dhe t prdoret thuase do t ishte varg i predefinuar (ky sht shablloni
vector). Prandaj, nuk na mbetet q t diskutojm pr vendimin e dizajnerve
t C++ q t mos mandatojn verikimin e rangut, edhe pse kjo munges mund t
shkaktoj probleme serioze. Shqyrtoni fragmentin vijues t kodit, q prdor
deklarimet e mparshme t a dhe i:
83

Avni Rexhepi
for (i=1; i<=3; i++)
a[i]=0;

N kt rast, programeri bn gabimin e zakonshm t qasjes n a[3], duke


harruar se madhsia 3 e vargut prfaqson vetm indeksat prej 0 deri n 2. Kur i
bhet 3, kompajleri e ekzekuton urdhrin a[3]=0 pa e verifikuar nse indeksi
sht valid. Supozojm se memorja sht alokuar, ashtu si sht paraqitur n
figurn 1.24. Efekti i ksaj sht q lokacioni i memories 1012 mbishkruhet me
0 dhe kshtu duke e shkatrruar i-n. rezultati i ksaj, d.m.th., resetimi i i n 0,
shkakton unaz t pafund. Mirpo, nse kompajleri ka vendosur (si bjn disa
kompajler) q t ler lokacionin 1012 t lir dhe t vendos i-n diku tjewtr,
programi duket se punon. Prandaj, gabimet pr nga nj pozit n indekset e
vargjeve mund t drgojn n bug-a q shum vshtir vrehen. N shembullin e
paraqitur, unaza sht e pafund, mirpo i-ja nuk sht ndryshuar direkt
asnjher.
Kufizimi i dyt pr vargun themeor (q mund t prmirsohet vetm prmes
klass s definuar prej shfrytzuesit) sht kopjimi i vargut. Supozojm se a dhe
b jan vargje t tipit t njjt. N shum gjuh programuese, nse vargjet
gjithashtu jan t madhsis s njjt, urdhri a=b do t kryente kopjimin
element pr element t bargut b n vargun a. N C++ ky urdhr sht ilegal,
sepse a dhe b prfaqsojn pointer konstant n fillimin e vargjeve t tyre
respektive, n mnyr specifike t &a[0] dhe &b[0]. Ather a=b sht nj
tentim q t ndryshohet vendi se ku pointon a-ja, e jo kopjimi i vargut b n a.
Ajo q e bn urdhrin ilegal, e jo legal por t gabuar, sht fakti se a-ja nuk
mund t ricaktohet q t pointoj diku tjetr sepse ajo n esenc sht objekt
konstant. Mnyra e vetme pr t kopjuar dy vargje sht q kjo t bhet element
pr element; nuk ka shkurtes. Argumenti i ngjashm tregon se shprehja a==b
nuk vlersohet n true nse dhe vetm nse secili element i a-s i prshtatatet
elementit prkats t b-s. N vend t ksaj, kjo shprehje sht legale. Shprehja
jep true nse dhe vetm nse a dhe b reprezentojn lokacionin e njjt t
memories (d.t.th, i referohen vargut t njjt).
N fund, vargu mund t prdoret si parametr i funksionit dhe rregullat vijojn
logjikisht prej t kutpuarit ton se emri i vargut sht pak m shum sesa nj
pointer. Supozojm se kemi funksionin q pranon si parametr nj varg t tipit
int. ather pamja nga kndvshtrimi i thirrsit dhe t thirrurit jan si vijon:
functionCall(varguAktual) ;
functionCall(int varguFormal[])

// thirrja e funksionit
//deklarimi i funksionit

Vreni q n deklarimin e funksionit, kllapat shrbejn vetm si deklarim i tipit,


n mnyr t njjt si bn edhe int. n thirrjen e funksionit, prcillet vetm emri
i vargut; nuk ka kllapa fare. N pajtim me rregullat e thirrjes/prcjelljes sipas
vlers n C++, vlera e varguAktual kopjohet n varguFormal. Pasi q
84

Algoritmet dhe strukturat e t dhnave


varguAktual prfaqson lokacionin e memories ku i tr varguAktual sht
ruajtur, varguFormal[i] i qaset varguAktual[i]. me fjal tjera, variablat e
prfaqsuar prmes vargut t indeksuar jan t modifikueshme. Prandaj vargu,
kur konsiderohet si aggregate (prmbledhs), prcillet sipas referencs. Pr
m tepr, fardo komponente e madhsis n deklarimin e varguFormal
injorohet dhe madhsia e bargut aktual sht e panjohur. Nse madhsia sht e
nevojshme, ajo duhet t prcillet si nj argument shtes.
Vreni se prcjellja e prmbledhsit sipas referencs nnkupton q funksioni
mund t ndryshoj elementet n varg. Ather, mund t prdoret direktiva const,
pr t tentuar q t mos lejohet ndryshimi i till:
functionCall ( const int varguFormal[ ] );

Tipi char*, pointeri konstant dhe stringu konstant


Nj prdorim i rndsishm i pointerve dhe vargjeve n C++, sht
implementimi i stringjeve. C++ baz ofron prkrahje minimale pr stringjet, t
bazuar trsisht n rregullat e gjuhs C dhe librarit e saj. Rezultati sht shum
minimal pr t qen i dobishm pr nj gjuh moderne programuese, si sht
edhe pr vargjet. Programert n C++ priren t bazohen n klasn e libraris
<string>. Mgjithat, sht mir t dihet se si implementohen stringjet n
librarin baz t C-s, sepse ata formojn bazn pr klasn string.
N C++ dhe n C, stringu sht nj varg i karaktereve. Si rezultat, kur ti
prcillet funksionit, stringu ka tipin char * ose const char *. N shikim t par
mund t supozohet se Nina sht nj varg i katr karaktereve: N, i, n, dhe
a. Problemi me kt supozim sht se nse kt varg ia prcjellni nj
funksionit, ai funksion nuk do ta dij se sa karaktere jan n varg, sepse si sht
treguar, funksioni q pranon nj varg, e pranon vetm pointerin dhe prandaj nuk
ka ide se sa i madh sht vargu aktualisht. Nj zgjidhje pr kt problem sht t
prdoret nj varg pak m i madh me nj shenj/marker t fundit t vargut.
Pr shembull, mund t deklarojm nj varg prej 5 elementeve, duke vendosur
nj zbraztir n pozitn e fundit pr t sinjalizuar se vetm katr pozitat e para
prfaqsojn karaktere t rndsishme. Nse t gjitha funksioniet shkruhen pr
t reflektuar kt marrveshje, ather kemi nj zgjidhje t problemit i cili
krkon nj ndryshim t vogl t gjuhs. Sepse, ne do t mund t dshironim t
prdorim zbraztirn brenda stringut (p.sh., pr t ruajtur adresn e banimit),
ather duhet t zgjedhim nj shenj pr funind (angl. endmarker) q nuk ka
gjasa q t paraqitet tjetrkund n string. N C++ ku karakter special sht null
terminator \0 (shenja e prfundimit NULL). Simboli \ tregon se null
terminator-i gjithmon sht i reprezentuar n mnyr interne si zero, gj q on
nj nj stenografi t shkurtr kur kontrollohet kur shprehja kontrolluese sht e
85

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

Ather urdhrat vijues nuk mund t jen korrekt:


strl = str2;
cond = (strl == str2);

//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;

Figura D.2. Disa funksione n <string>


Funksioni strlen(str) jep gjatsin e strungut str (pa prfshir nullterminator-in); gjatsia e strungut Nina sht 4. N kt dhe t gjitha
funksionet, nse prcillet NULL Pointeri, mund t pritet q programi t
bllokohet. Vreni se kjo qasje sht e ndryshme nga prcjellja e pointerit n
pozitn memorike e cila prmban karakterin \0, i cili prfaqson stringun e
zbrazt t gjatsis 0. Funksioni strcpy(lhs, rhs) kryen ndarjen e vlers s
stringjeve: karakteret e dhna nga rhs kopjohen n vargun e dhn me lhs, deri
sa t kopjohet null-terminatori. (Skurtesat ishin: lhs-left hand side ana e majt,
rhs-right hand side ana e djatht). Nse stringu i prfaqsuar n lhs nuk sht
mjaft i madh sa pr t ruajtur kopjen, nj pjes tjetr e memories mbishkruhet
(duke shkaktuar dmtimin e t dhnave n at pjes). Pra, funksioni strcpy nuk
verifikon se a sht caku mjaft i madh pr t ruajtur kopjen.
Renditja e parametrave lhs dhe rhs mbahet mend leht, duke pasur parasysh q:
strcpy( lhs, rhs )

86

Algoritmet dhe strukturat e t dhnave


sht menduar q t imitoj shprehjen:
lhs = rhs:
Tipi i kthimit t rezultatit char * i mundson funksionit strcpy q t zgjerohet
n form zinxhirore, njsoj si urdhrat: strcpy (a,strcpy (b,c )) q
jan sikur a=b=c.
Funksioni strcat(lhs, rhs) e shton (bashkangjet) kopjen e stringut rhs, n vazhdim
t atij lhs. Njsoj si n rastin e strcpy, sht prgjegjsi e programerit q t
sigurohet q lhs sht duke pointuar n hapsir t mjaftueshme t memories pr
t ruajtur rezultatin. Funksioni strcmp, i krahason dy stringje dhe kthen numr
negativ, zero ose pozitiv, varsisht prej asaj se a sht stringu i par
leksikografikisht m i vogl, i barabart apo m i madh sesa i dyti.
Si sht prshkruar m hert, C++ ofron funksionet e librarive pr stringje por
jo edhe prkrahje nga ana e gjuhs. N fakt, prkrahja e vetme nga gjuha sht
nga string konstantja. Konstantja string sht sekuenc e karaktereve t rrethuara
n thonjza. Null terminatori i shtohet automatiksht. Konstantja string ofron
mekanizm t shkurtr pr t specifikuar sekuencn e karaktereve. Ajo
automatikisht e prfshin null terminator-in si nj karakter t fundit t
padukshm. Cilido karakter (i specifikuar me shenjn speciale \, nse sht e
nevojshme) mund t paraqitet n konstanten string, kshtu q Nina
reprezenton vargun me pes karaktere. Pr m tepr, string konstanta mund t
prdoret si inicializues pr vargun e karaktereve, prandaj:
char namel[]="Nina"; //namel - varg prej 5 karaktereve
char name2[9]="Nina"; //name2 - varg prej 9 karaktereve
char name3[4]="Nina"; //name3 - varg prej 4 karaktereve

N rastin e par, madhsia e vargut t alokuar pr name1 sht e prcaktuar


nmnyr implicite. N rastin e dyt kemi alokuar hapsir t teprt (e cila
nevojitet nse synojm q m von t kopjojm ndonj string m t gjat n
name2). Rasti i tret sht gabim, sepse nuk kemi alokuar hapsir t
mjaftueshme t memories pr null terminatorin. Inicializimi me konstante string
sht prjashtim sepcial. Nuk mund t themi:
char name4[8] = namel; // ILEGALE!

Konstanta string mund t prdoret n cilindo vend ku mund t prdoret objekti


string dhe konstant. Pr shembull, mund t prdoret si parametr i dyt n
strcpy, por jo si parametr i par. Arsye pr kt sht se deklarimi i strcpy nuk
pamundson mudnsin q parametri i par t mund t ndryshohet (n t vrtet,
ne e dijm se sht). Pasi q konstanta string mund t ruhet n memorie
readonly (vetm pr lexim), t mundsuarit q ajo t jet cak i funksionit

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

Deklarimet pr string funksionet indikojn se parametrat jan pointer sepse


emri i nj vargu sht pointer. Parametri i dyt n strcpy sht string konstant,
q nnkupton se cilido string mund t prcillet me garancionin se nuk do t
ndryshohet. Parametri i par sht thjesht string dhe mund t ndryshohet.
Rrjedhimisht, stringu konstant, duke prfshir edhe konstantet e stringjeve, nuk
mund t prcillet n funkson.
Fillestart synojn t ojn ekuivalencn e vargjeve dhe pointerve nj hap m
larg. Rikujtoni se dallimi fundamental ndrmjet vargut dhe pointerit sht se
definicioni i vargut alokon memorie t mjaftueshme pr t ruajtur vargun,
ndrsa pointeri pointon n memorien e cila sht e alokuar tjetrkund. Pasi q
stringjet jan gjithmon vargje t karaktereve, ky dallim aplikohet n stringje.
Gabim i zakonshm sht deklarimi i pointerit kur t nevojitet nj varg.
Shqyrtoni deklarimet:
char name[] = "Nina";
char *name1 = "Nina";
char *name2;

Deklarimi i par alokon pes bajta pr pr name, duke inicializuar n t kopjen e


stringut konstant Nina (duke prfshir edhe null terminatorin). Deklarimi i
dyt thjesht thekson se name1 pointon n karakterin zero t stringut konstant
Nina. N fakt, deklarimi isht i gabuar sepse jemi duke przier tipet e
pointerve: ana e djatht sht const t char*, por ana e majt sht thjesht char
*. Disa kompajler do t ankohen. Aryesja sht se nj urdhr pasues
namel[ 3 ] = 'e';

sht nj tentim pr t ndryshuar konstanten string. Konstanta string supozohet


t jet konstante, ashtu q ky veprim nuk do t lejohej. Mnyra m e leht pr
kompajlerin q t pamundsoj kt veprim sht q t prcjell rregulln q,
nse a sht varg konstant, ather a[i] sht konstante poashtu dhe nuk mund ti
ndahet vler. Nse urdhri
char *name1 = "Nina";

do t lejohej, name1[3] do t lejohej. Duke detyruar const-antsin n


seciln ndarje t vlers, problemi bhet i menagjueshm. (Mund t prdoret
type cast pr const-antsin, por kshtu programeri do t humbiste mbrojtjen e
ofruar nga C++-i). N mnyr legale mund t prdoret:
88

Algoritmet dhe strukturat e t dhnave


const char *name1 = "Nina";

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!";

Nj pasoj e zakonshme e deklarimit t pointerit n vend t vargut sht urdhri


vijues (n t cilin supozojm se name2 sht deklaruar m hert):
strcpy ( name2, name ) ;

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.

Vargu dhe stringu


Vargu sth struktur bazike e t dhnave q prezenton nj grup t elementeve
t ngjashme, q kan qasje sipas indeksit. Struktura e vargut mund t ruhet n
mnyr efektive n kompjuter dhe ofron qasje t shpejt n t gjitha elementet e
tij. Vargjet kan prparsite dhe t metat e tyre.

Prparsit

Ska mbingarkes (overhead) pr element.


Cilido element i nj vargu mund t qaset n koh O(1) prmes indeksit
t tij.

89

Avni Rexhepi

T metat

Vargu si struktur e t dhnave nuk sht trsisht dinamik. Shum


gjuh programuese ofrojn mundsin e alokimit t vargjeve me madhsi
arbitrare (vargje t alokuara n mnyr dinamike), por kur kjo hapsir
t shfrytzohet n trsi (kur mbushet vargu), duhet t alokohet nj varg
me madhsi m t madhe dhe t dhnat e vjetra t kopjohen n t.
Insertimi dhe fshirja e nj elementi t vargut, krkon zhvendosjen (shiftimin) e mesatarisht O(n) elementeve, ku n sht madhsia e vargut.

Vargjet statike dhe dinamike


Ekzistojn dy tipe t vargjeve, t cilat dallojn pr nga mnyra e alokimit. Vargu
statik ka madhsi konstante dhe ekziston gjat tr kohs s ekzekutimit t
aplikacionit. Vargu dinamik (i alokuar n mnyr dinamike) krijohet gjat
ekzekutimit t programit dhe mund t fshihet kur t mos jet m i nevojshm.
Vargjet e alokuara n mnyr dinamike mund t jen shum t mdha, edhe m
t mdha sesa madhsia e memories fizike. Sidoqoft, vargut t alokuar n
mnyr dinamike nuk mund ti ndryshohet madhsia. Mirpo, ju mund ta
rrisni (zgjeroni) vargun si n vijim:
1. Krijoni varg t ri me madhsi m t madhe
2. Kopjoni t dhnat nga vargu i vjetr n t riun
3. Lironi memorien, q ishte e zn me vargun e vjetr

Madhsia fikse dhe vargjet dinamike


Si u tha m hert, vargjeve nuk mund t ju ndryshohet madhsia. N kt rast,
vargu quhet varg me madhsi fikse. Mirpo, ne mund t prdorim nj dredhi
(rreng, trik), pr t konstruktuar nj varg dinamik, t cilit mund ti
ndryshohet madhsia.
Idea sht e thjesht. Alokohet nj hapsir pr vargun dinamik dhe n
mnyr imagjinary e ndajm n dy pjes. Njra pjes prmban t dhnat
(vlerat) dhe pjesa tjetr sht e lir. Kur t shtohet nj element i ri, hapsira e
lir zvoglohet dhe anasjelltas. Kjo qasje rezulton m mbingarkes pr
hapsirn e lir, por ofron t gjitha prparsit e vargjeve dhe mundsin e
ndryshimit t madhsis n mnyr dinamike. Disa definicione lidhur me kt
lloj t vargjeve jan si n vijim.
90

Algoritmet dhe strukturat e t dhnave


Vargu dinamik ka kapacitetin e tij, i cili tregon numrin maksimal t
elementeve q mund ta prmbaj. Gjithashtu, nj varg i till ka madhsin
logjike, e cila tregon sa elemente n t vrtet i prmban vargu. Pr
shembull, dshirojm t gjejm minimumin e vlerave t cilat i jep
shfrytzuesi. Ne alokojm hapsir pr ruajtjen e 15 elementeve, por
shfrytzuesi i jep vetm 5 vlera. N kt rast, kapaciteti i vargut sht 15
elemente, por madhsia logjike sht 5 elemente. Kur vargu dinamike t
mbushet trsisht, ai duhet t zgjerohet duke krijuar nj varg t ri m t madh
dhe t kopjohen elementet nga vargu i vjetr n vargu e ri. Ta keni parasysh,
q kopjimi i vargjeve prkrahet nga hardveri dhe mund t bhet n mnyr
shum efikase.

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;
}

Alokimi dinamik i vargjeve


Supozojm se dshirojm t lexojm nj sekuenc t numrave dhe ta ruajm si
nj varg pr prpunim. Tipari themelor i nj vargu krkon q t deklarojm
madhsin ashtu q kompajleri t mund t alokoj sasin korrekte t memories.
Duhet t bjm kt deklarim para qasjes s par t vargut. Nse nuk kemi ide
se sa elemente mund t priten, zgjedhja e arsyeshme e madhsis s vargut sht
e vshtir. Ather, na hyn n pun alokimi dinamik i vargut, q mundson
zgjerimin/rritjen e tij nse vlersimi fillestar sht shum i vogl. Teknika e
alokimit dinamik t vargjeve (angl. dynamic array allocation) na mudnson t
alokojm nj madhsi arbitrare t vargut dhe pastaj ta rrisim ose zvoglojm at
gjat ekzekutimit t programit.
Mnyra e deritashme e alokimit t vargut ishte:
int al[ SIZE ] ; // SIZE sht konstante e kohs s kompajlimit

Gjithashtu e dijm se mund t prdorim:


int *a2;

si nj varg, prveq se nuk alokohet memorie nga kompajleri pr vargun.


Operatori new na mundson q t marrim memorie nga sistemi, gjat kohs s
ekzekutimit t programit. Ne mund t prdorim shprehjen:
new int [SIZE];

pr t alokuar memorie t mjaftueshme pr t ruajtur SIZE cop objekte t


tipit int. Shprehja vlersohet n adresn ku qndron fillimi i asaj memorie. Ajo
mund ti ndahet vetm nj objekti int *, si n
int *a2 = new int [ SIZE ];

92

Algoritmet dhe strukturat e t dhnave


Si rezultat, a2 sht virtualisht i padallueshm nga a1. Operatori new sht
type-safe q do t thot se
int *a2 = new char[ SIZE ];

do t ishte detektuar si gabim i mosprshtatjes (angl. mismatch error) gjat


kohs s kompajlimit. Ather cili sht dallimi, nse ka ndonj, ndrmjet dy
formave t alokimit t memories pr vargun. Dallimi teknik sht q memoria
pr a1 sht marr nga nj burim tjetr pr dallim nga a2. Sidoqoft, dallimi
sht transparent pr shfrytzuesin. Dallimi i dyt sht q a1 nuk mund t
paraqitet n ann e majt t operatorit t ndarjes s vlers (=) sepse emri i vargut
sht konstante, ndrsa a2 mundet. Dallimi sht gjithashtu relativisht i vogl,
nse do t kishim deklaruar
int * const a2 = new int [ SIZE ];

ky dallim do t zhdukej. sht m e rndsishme, kur prdorim new, SIZE


nuk duhet t jet konstante e kohs s kompajlimit.
1 void f ( int i )
2 (
3 int al[ 10 ];
4 int *a2 = new int [ 10 ] ;
5
6 . . .
7 ST( a1 );
8 g( a2 );
9
10 // N return, tr memorie e shoqruar me a1, lirohet
11 // N kthim, vetm pointeri a2 lirohet;
12 // 10 int-a kan rrjedhur
13 // delete [ ] a2; // Kjo do t zgjidhte problemin e rrjedhjes
14 )
Figura D.3 Dy mnyr t alokimit t vargut njra e rrjedh memorien

a1

a2

Fig.1.25 Restaurimi i memories pr programin n fig. D.3

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.

Kllapa [ ] sht absolutisht e nevojshme ktu, pr t siguruar se t gjitha objektet


n vargun e alokuar do t reciklohen. Pa kllapat [ ], vetm a2[0] mund t
reciklohet, gj q nuk sht ajo ka synohet. Me an t new dhe delete duhet t
menagjojm memorien vet, n vend se kompajleri ta bj kt pr ne. Prse do
t ishim t interesuar pr t vepruar kshtu? Pr arsye se, duke menagjuar
memorien vet, mund t ndrtojm vargje t zgjerueshme (dinamike).
Supozojm se n Fig. D.3 vendosim q pas deklarimit, por para thirrjes s
funksionit g n rreshtat 7 dhe 8, n t vrtet dshirojm 12 int-egjera, n vend
t 10. N rastin e a1 do t ngecim, dhe thirrja e rreshtit 7 nuk do t funksionoj.
Mirpo, me a2, kemi nj alternativ, si n vijim:
int *original = a2; //1. Ruaje pointer-in ne original
a2=new int[12];
//2. Bje a2 te pointoj ne me
//
memorie
for(int i=0;i<10;i++)
// 3. Kopjo te dhenat e vjetra
a2[i]=original[i];
delete[] original;
// 4. Reciklo vargun origjinal

94

shume

Algoritmet dhe strukturat e t dhnave

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.

Reprezentimi intern (i brendshm)


Idea sht e thjesht. Aplikacioni e alokon nj sasi t memories (fizikisht) dhe e
ndan at logjikisht n dy pjes. Njra pjes i prmban t dhnat dhe pjesa
tjetr sht e lir. Fillimisht, e tr hapsira e alokuar sht e lir. Gjat
funksionimit t strukturs s t dhnave, kufiri nrmjet pjess s prdorur dhe
asaj t lir, ndryshon. Nse nuk ka m hapsir t lir pr prdorim, hapsira
zgjerohet duke e krijuar nj varg t ri me madhsi m t madhe dhe duke e
kopjuar prmbajtjen e vjetr n lokacionin e ri. Struktura e vargut dinamik ka
fushat vijuese:

depoja (hapsira pr ruajtje) (storage): hapsira e alokuar n mnyr


dinamike, pr ruajtje t t dhnave;
vlera e kapacitetit (capacity value): madhsia e hapsirs pr
ruajtje;
vlera e madhsis (size value): madhsia e t dhnave reale (vlerave).

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;
}
};

Menaxhimi i kapaciteti: Sigurimi i kapacitetit,


Paketimi(ngjeshja, kompresimi)
Para se t mund t shtojm (insertojm) ose largojm (fshijm) vlera, duhet t
zhvillohet mekanizmi i menaxhimit t kapacitetit. Mekanizmi prbhet prej dy
funksioneve: siguro kapacitetin dhe paketo.

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

verifiko nse kapaciteti aktual nuk sht i mjaftueshm pr ruajtjen e


elementeve t reja;
llogarit kapacitetin e ri prmes formuls: kapacitetiiRi =
(kapacitetiiVjeter* 3) / 2 + 1. Algoritmi krijon rezerv t hapsirs s lir
ashtu q t mos ricaktohet shum shpesh madhsia e hapsirs pr
ruajtje.
Verifiko nse kapaciteti i ri sht i mjaftueshm pr t ruajtur t gjitha
elementet e reja dhe nse jo, rrite at pr t ruajtur sasin e sakt t
elementeve;
aloko hapsirn e re dhe kopjo n t prmbajtjen nga e vjetra;
dealoko (liro) hapsirn e vjetr (n C++);
ndrysho vlern e kapacitetit;

Algoritmet dhe strukturat e t dhnave


Koeficienti i zmadhimit mund t zgjedhet n mnyr arbitrare (por duhet t jet
m i madh se nj). Vler e preferuar sht 1.5 dhe mesatarisht sht vler
optimale.
Shembull. kapaciteti = 6, madhesia = 6, dshirojm t shtojm nj element t ri.

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.

verifiko, nse madhsia sht m e vogl ose baraza me gjysmn e


kapacitetit;
llogarit kapacitetin e ri prmes formuls: kapacitetiiRi = (madhesia * 3) /
2 + 1. Algoritmi l sasin e sakt t hapsirs, thua se kapaciteti i
hapsirs pr ruajtje sht prer pr madhsin dhe pastaj sht thirrur
metoda pr sigurimin e kapacitetit.
Aloko hapsirn e re dhe kopjo n t prmbajtjen nga e vjetra ;
dealoko hapsirn e vjetr (n C++);
ndrysho vlern e kapacitetit.

Shembull. kapaciteti = 12, madhesia = 6, bje paketimin (ngjeshjen,


kompresimin).

97

Avni Rexhepi

Kufiri i poshtm pr madhsin, pas s cils paketimi sht br, mund t


ndryshoj. N shembullin aktual ajo sht 0.5 (gjysma) e vlers s kapacitetit.
Zakonisht, paketimi sht metod private, e cila thirret pas largimit (fshirjes).
Gjithashtu, interfejsi i vargut dinamik ofron metodn e prerjes (angl. trim
shkurtoj majen, prshtas), e cila e zvoglon kapacitetin ashtu q ti prshtatet
sasis s sakt t elementeve n varg. Kjo bhet jasht implementimit, kur jeni
t sigurt se nuk do t shtohen m vlera t tjera, (p.sh., insertimi nga ana e
shfrytzuesit ka prfunduar).
Pjes kodi
Gjuht programuese ofrojn vegla efikase pr kopjim t memories, t cilat jan
prdorur n implementimin vijues, n C++.
//DynamicArray=VarguDinamik, setCapacity=caktoKapacitetin
//newCapacity=kapacitetiiRi, ensureCapacity=siguroKapacitetin
//newStorage=depojaeRe=vendiRiiRuajtjes
//trim=preje,pack=paketoje
#include <cstring>
void DynamicArray::setCapacity(int newCapacity) {
int *newStorage = new int[newCapacity];
memcpy(newStorage, storage, sizeof(int) * size);
capacity = newCapacity;
delete[] storage;
storage = newStorage;
}
void DynamicArray::ensureCapacity(int minCapacity) {
if (minCapacity > capacity) {

98

Algoritmet dhe strukturat e t dhnave


int newCapacity = (capacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
setCapacity(newCapacity);
}
}
void DynamicArray::pack() {
if (size <= capacity / 2) {
int newCapacity = (size * 3) / 2 + 1;
setCapacity(newCapacity);
}
}
void DynamicArray::trim() {
int newCapacity = size;
setCapacity(newCapacity);
}

Funksionet pr qasje n t dhna: Set, Get, InsertAt, RemoveAt


Set(Cakto), Get(Merr), InsertAt (InsertoN), RemoveAt (LargoN).
Struktura e vargut dinamik enkapsulon hapsirn themelore, por interfejsi duhet
t ofroj funksionet e qasjes pr t punuar me t. Mund t shtohen edhe
funksionet pr verifikimin e rangut (kufinjve).

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).

Funksionet Get dhe Set


Pasi t jemi siguruar se indeksi sht prbrenda kufijve t duhur, shkruajm
vlern n hapsirn e ruajtjes ose e lexojm at nga hapsira e ruajtjes.

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

Algoritmet dhe strukturat e t dhnave

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

void DynamicArray::insertAt(int index, int value)


{
if (index < 0 || index > size)
throw "Indeksi jasht kufijve!";
ensureCapacity(size + 1);
int moveCount = size - index;
if (moveCount != 0)
memmove(storage + index + 1, storage
sizeof(int)* moveCount);
storage[index] = value;
size++;
}

102

1),

index,

Algoritmet dhe strukturat e t dhnave

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])

Keni parasysh gjeneralizimin e ktij algoritmi. Ky punon njsoj mir si pr emra


ashtu edhe pr numra, duke pasur t dhn operacionin e duhur t krahasimit
< pr t testuar se cili prej dy elsave (cila prej dy vlerave) duhet t paraqitet
s pari n renditjen e sortuar. Me definicionin e dhn t problemit t sortimit,
mund t verifikohet pa vshtirsi se ky algoritm i rendit n mnyr korrekte t
gjitha instancat e mundshme hyrse.
Duhet pasur parasysh edhe disa shtje praktike:
Algoritmet n dukje t arsyeshme leht mund t jen jokorrekte. Korrektsia e
algoritmit sht veti q duhet demonstruar me kujdes.
Algoritmet mund t kuptohen dhe studiohen n mnyra t pavarur nga
kompjuteri (makina).
Notacioni Big Oh dhe analiza e rastit m t keq jan vegla t cilat n mas t
madhe e thjeshtojn aftsin ton q t krahasojm efikasitetin e algoritmeve.
Krkojm algoritme koha e ekzekutimit t s cilave rriten n mnyr
logaritmike, sepse rritet shum ngadale me rritjen e n-it (numrit t vlerave
hyrse). Modelimi i aplikacionit n terma t algoritmeve dhe strukturave t
definuara mir, sht hapi m i rndsishm n drejtim t zgjidhjes.

RAM modeli i llogaritjes


Dizajni i algoritmit t pavarur prej makins varet nga kompjuteri hipotetik i
quajtur Random Access Machine ose RAM. Sipas ktij modeli t llogaritjes,
jemi prball kompjuterit ku:
104

Algoritmet dhe strukturat e t dhnave


1. Secili operacion i thjesht (+, *, -, =, if, call) merr saktsisht 1 hap
kohor.
2. Unazat dhe nnprogramet nuk konsiderohen operacione t thjeshta. N
vend t ksaj, ata jan nj kompozim (kombinim) i disa operacioneve t
thjeshta nj-hapshe. Nuk ka kuptim q operacioni i sortimit t jet
operacion nj-hapsh, pasi q sortimi i nj milion elementeve do t merr
shum m tepr koh sesa sortimi i 10 elementeve. Koha q krkohet pr
tu ekzekutuar n unaz ose pr tu ekzekutuar nnprogrami varet nga
numri i prsritjeve t unazs ose natyra specifike e nnprogramit.
3. Secila qasje e memories merr saktsisht nj hap kohor dhe kemi aq
memorie sa kemi nevoj. RAM modeli nuk parasysh nse elementi
ndodhet n cache (kesh) ose n disk, gj q e thjeshton analizn.
Sipas RAM modelit, ne masim kohn e ekzekutimit t nj algoritmi (angl. run
time) duke numruar numrin e hapave t cilt i merr pr nj instanc t dhn t
problemit. Duke supozuar se RAM-i ekzekuton nj numr t caktuar t hapave
pr sekond, operacioni i numrimit konvertohet me lehtsi n koh aktuale t
ekzekutimit.
RAM sht model i thjesht i asaj se si funksionon (performon) kompjuteri.
Ankes e zakonshme sht se ky sht shum i thjesht dhe se kto supozime
bjn q konkluzionet dhe analizat t jen tepr t vrazhdta pr tu besuar n
praktik.
Pr shembull, shumzimi i dy numrave merr m shum koh sesa mbledhja e dy
numrave n shumicn e procesorve, gj q thyen supozimin e par t modelit.
Koht e qasjes s memories dallojn shum varsisht prej faktit nse t dhnat
ndodhen n disk apo n cache, duke thyer kshtu supozimin e tret. Prkundr
ktyre ankesave, RAM sht nj model i shklyeshm pr t kuptuar se si do t
performoj nj algoritm n nj kompjuter real. Kshtu, aplikohet nj balans i
mir i prvetsimit t sjelljes themelore t kompjutere duke qen njkohsisht
t thjesht pr t punuar me ta. Modeli RAM prdoret pasi q sht i dobishm
n praktik.
Secili model ka nj rang t madhsis prgjat t cilit ai sht i dobishm. T
marrim pr shembull modelin se toka sht e rrafsht. Mund t argumentohet se
ky model sht i keq, pasi q toka nuk sht e rrafsht. Mirpo, kur t hidhet
themeli i nj shtpie, modeli i toks s rrafsht sht mjaftueshm i sakt dhe
mund t prdoret me besueshmri. Pr m tepr, sht shum m e leht q t
manipulohet modeli i toks s rrafsht q sht i pabesueshm sesa q t
tentohet t mendohet pr nj model sferik kur nuk ka nevoj.
105

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

Algoritmet dhe strukturat e t dhnave


Rasti m i mir, se ju do t dilni si pronar i vendit (e keni fituar tr kazinon),
sht i mundshm por aq i pashpres (pak ka t ngjar t ndodh) saq nuk
duhet t bazohen n t (t keni besim se do t ndodh). Rasti m i keq, q ju do
t humbisni t gjitha n eurot, sht leht i llogaritshm dhe shum me gjas q t
ndodh. Rasti mesatar, q nj person tipik humb 87.32% t parave q i sjell n
kazino, sht vshtir t llogaritet dhe do t thot q sht subjekt i diskutimit.
Mirpo, ka nnkupton n t vrtet mesatar? Njerzit e padijshm humbasin
m shum se t menqurit, kshtu q a jeni m i menqur apo m trullan sesa
personi mesatar dhe pr sa? Ata q luajn n zare humbasin m shum sesa ata
q luajn n rulet. Kshtu, evitojm t gjitha kto kompleksitete dhe marrim
rezultat shum m t dobishm duke marr n konsiderat vetm rastin m t
keq.
Gj me rndsi pr tu kuptuar sht se secili prej ktyre kompleksiteteve
kohore definon funksion numerik, q reprezenton kohn n raport me madhsin
e problemit. Kto funksione jan poaq mir t definuara se edhe funksionet e
tjera numerike. Kompleksitetet kohore jan funksione t komplikuara, sidoqoft.
Pr t thjeshtuar punn me funksionet e tilla t rregullta, na duhet Big-Oh
Notacioni.

ka sht analiza e algoritmit


Algoritmi sht nj bashksi e instruksioneve ose logjiks, e shkruar pr t
realizuar nj detyr t predefinuar. Algoritmi nuk sht nj kod i kompletuar ose
nj program, por sht vetm logjika baz (zgjidhja) e problemit, e cila mund t
shperhet oft si prshkrim logjik i nivelit t lart si pseudokod ose duk
prdorur bllok diagramet.
Algoritmi thuhet se sht efikas dhe i shpejt, nse merr m pak koh pr tu
ekzekutuar dhe konsumon m pak hapsir t memories. Performansa e
algoritmit matet n baz t:
1. Kompleksitetit kohor,
2. Kompleksitetit hapsinor
Kompleksiteti hapsinor
Kompleksiteti hapsinor sht hapsira e memories q krkohet prej algoritmit,
gjat ekzekutimit t tij. Kompleksiteti hapsinor duhet t mirret seriozisht pr
sistemet me shum shfrytzues (angl. multi-user systems) dhe n situatat ku ka
n dispozicion vetm memorie t kufizuar.
Nj algoritm n prgjithsi krkon hapsir pr komponentet vijuese:

107

Avni Rexhepi
-

Hapsira pr instruksione: hapsira e krkuar pr ruajtjen e versionit


ekzekutiv t programit. Kjo hapsir sht fikse, por ndryshon varsisht
prej numrit t rreshtave t kodit n program.
Hapsira pr t dhna: kjo sht hapsira e krkuar pr t ruajtur vlerat e
t gjitha konstanteve dhe variablave.
Hapsira e ambientit punues: kjo sht hapsira e krkuar pr ruajtjen e
informatave t nevojshme pr rikthimin nga funksioni i ndrprer
prkohsisht.

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;
}

Kompleksiteti kohor i ktij algoritmi do t jet linear. Koha e ekzekutimit sht


drejtprdrejt e varur nga N (n proporcion t drejt me N). Kur rritet N, ashtu
rritet edhe koha e ekzekutimit. Kur dyfishohet N, dyfishohet edhe koha e
ekzekutimit.
for (i=1; i<N; i++)
{
for (j=1; i<N; j++)
{
urdhri;
}
}

N kt rast, kompleksiteti kohor i ktij kodi sht kuadratik. Koha e


ekzekutimit t dy unazave sht proporcionale me katrorin e N-it. Kur N
dyfishohet, koha e ekzekutimit rritet pr N*N.
108

Algoritmet dhe strukturat e t dhnave


N prgjithsi, kryerja e veprimeve me secilin element (N) n nj dimension
sht lineare, kryerja e veprimeve me secilin element n dy dimensione sht
kaudratike, ndrsa ndarja e hapsirs punuese prgjysme, sht logaritmike.
Pra, sasia e kohs q e merr pr tu ekzekutuar cilido algoritm pothuajse
gjithmon sht e varur nga sasia e vlerave hyrse t cilat ai duhet ti prpunoj.
Ne presim pr shembull q sortimi i 10,000 elementeve krkon m shum koh
sesa sortimi i 10 elementeve. Koha e ekzekutimit t nj algoritmi pra sht
funksion i madhsis s hyrjes. Vlera e sakt e funksionit varet nga shum
faktor, si jan shpejtsia e kompjuterit, kualiteti i kompajlerit dhe n disa raste
kualiteti i programit. Pr nj program t caktuar n nj kompjuter t caktuar,
mund t vizatojm grafikun e funksionit t kohs s ekzekutimit. Figura 2.1
paraqet grafikun pr disa funksione.

Figura 2.1 Koha e ekzekutimit pr numr t vogl hyrjesh

M shum t dhna do t thot programi merr m shum koh pr tu ekzekutuar.

Lakoret paraqesin katr funksione t zakonshme t hasura n analizn e


algoritmeve: linear, logaritmik, kaudratik dhe kubik. Madhsia hyrse N sht
n rangun prej 1 deri n 100 elemente, ndrsa koha e ekzekutimit n rangun prej
0 deri n 10 milisekonda. Nj vshtrim i shpejt n figurn 2.1 dhe n tabeln
2.1, sygjeron se funksionet kaudratike dhe kubike, kan shkalln shum m t

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

Tabela 2.1 Shkalla e rritjes pr klasat e zakonshme te algoritmeve


Nj shembull sht porblemi i shkarkimit t fajllit nga Interneti (angl. download
shkarkoj posht, transferoj). Supozojm se kemi nj vones fillestare prej 2
sekondash (pr t vendosur lidhjen), pas s cils shkarkimi vazhdon me
shpejtsin 1.6 K/sec. Ather nse fajlli ka N kilobajt, koha e shkarkimit do t
prshkruhet me formuln T(N)=N/1.6+2. Ky sht funksion linear. Shkarkimi i
fajllit me madhsi 80K do t merr prafrsisht 52 sekonda, ndrsa shkarkimi i
fajllit me madhsi t dyfisht (160K) do t marr prafrsisht 102 sekonda, ose
shikuar vrazhde, pothuajse dyfish m shum. Kjo karakteristik, n t ciln koha
sht n esenc n proporcion t drejt me madhsin e hyrjes, sht e algoritmit
linear. N krahasim me funksionet jolineare (kuadratike dhe kubike), funksioni
linear sht shum m efikas.
Nga tabela,mund t shihni se kur hyrja sht e vogl, nuk ka ndryshim t
rndsishm (t dukshm) n vlera, por ather kur vlera hyrse bhet e madhe,
ka dallim/ndryshim t madh. Kjo riforcon at cka pam n grafin n Fig. 2.1.
Pr shkak t ksaj, gjithnj shqyrtohet se ka ndodh kur madhsia e hyrjes
sht e madhe, pr shkak se setet e vogla t hyrjes mund t fshehin ndryshimet
shum dramatike.
T dhnat n ilustrojn edhe faktin se pasi q funksionet me rritje m t shpejt
rriten n shkall/shpejtsi t rrijes kaq domethnse, shum shpejt i dominojn
funksionet me rritje t ndagalshme. Kjo do t thot se nse ne prcaktojm se
kompleksiteti i nj algoritmi sht kombinim i dy prej ktyre klasave, ne shpesh
110

Algoritmet dhe strukturat e t dhnave


do t injorojm t gjitha termat prveq termave me rritje m t shpejt. P.sh.,
nse analizojm nj algoritm dhe gjejm se ai i bn n3 30n krahasime, ne do
ti referohemi ktij algoritmi vetm si algoritm q rritet me shkall/shpejtsi t
n3. Kjo pr arsye se edhe n nj madhsi hyrse prej vetm 100, diferenca
ndrmjet n3 dhe n3-30n sht vetm 0,3%. Kjo ide sht e formalizuar n
klasifikimin e shkallve t rritjes.

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.

O-Notation (Kufiri i eprm/lart)


Big O notacioni e jep kufirin e eprm/lart pr funksionin brenda faktorit
konstant. Shkruajm: f(n) = O(g(n)) (Lexohet si: f sht n Big O t g-s, ose
Big O e f-it, sht g-ja) nse ekzistojn konstantet pozitive n0 dhe c t tilla q n
111

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

-Notation (Rendi i njjt)


Big Teta notacioni e kufizon funksionin n faktor konstant. Themi se f(n) =
(g(n)) nse ekzistonjn konstantet pozitive n0, c1 dhe c2, t tilla q n ann e
djatht t n0 vlera e f(n) gjithmon shtrihet ndrmjet c1g(n) dhe c2g(n)
(inkluzive).
c2g(n)
f(n)
c1g(n)

n0

f(n)=q (g(n))

Fig. 2.3 Big

112

Algoritmet dhe strukturat e t dhnave

-Notation (Kufiri i poshtm/ult)


Big Omega notacioni jep kufirin e poshtm pr funksionin deri n faktorin
konstant. Shkruajm: f(n) = (g(n)) nse ekzistojn konstantet pozitive n0 dhe c
t tilla q n t djatht t n0, vlera e f(n) gjithmon shtrihet n ose mbi cg(n).

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

Rasti-m i keq (Worst-case Complexity)


Koha e ekzekutimit pr hyrje t cilsdo madhsi t dhn do t jet me e
vogl/ult sesa kufiri i eprm prveq mundsis pr disa vlera t hyrjes ku
arrihet maksimumi.

Rastit-mesatar (Average-case Complexity)


Koha e ekzekutimit pr hyrje t cilsdo madhsi t dhn do t jet sa mesatarja
e operacioneve mbi t gjitha instancat e problemit pr nj madhsi t dhn.

Rastit-m i mir (Best-case Complexity)


Kemi edhe rastin m t prshtatshm, q njihet si rasti m i mir (Best-case
complexity), q ka t bj me rastet si: krmimi sekuencial, i vlers q gjendet
113

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

Algoritmet dhe strukturat e t dhnave


asimptotike, e cila mund t z aspektet e prgjithshme t efikasitetit pr t gjitha
hyrjet e mundshme, por jo koht e sakta t ekzekutimit. E dyta sht analiza
empirike e nj implementimi aktual pr t prcaktuar kohn e sakt t
ekezekutimit pr mostra t hyrjeve speciale, por kjo nuk mund parashikoj
performansn e algoritmit n t gjitha hyrjet.
sht edhe aspekti i kompleksitetit t programit, pasi q disa struktura t t
dhnave mund t jen krejtsisht t thjeshta pr tu implementuar, kurse disa t
tjera shum m komplekse. shtja se cila struktura t prdoret mund t varet
nga shtje t cilat nuk kan t bjn fare me shtjen e kohs se ekzekutimit,
por n vend t ksaj me shtjet se cilat struktura t t dhnave jan m
fleksibile, pastaj m t lehtat pr tu implementuar dhe mirmbajtur, etj.
Zakonisht shqyrtohet koha e ekzekutimit, edhe pse ajo q thuhet pr kohn,
mund t vlej edhe pr hapsirn, por hapsira sht m e leht pr tu trajtuar.
Pr nj program t caktuar, koha e tij e ekzekutimit nuk sht nj numr fiks,
por zakonisht nj funksion. Pr seciln hyrje (ose instanc t strukturs s t
dhnave), mund t ket koh tjetr t ekzekutimit. Supozohet se me rritjen e
madhsis hyrse rritet edhe koha e ekzekutimit, kshtu q shpeshher kohn e
ekzekutimit e prshkruajm si funksion t hyrjes/madhsis s strukturs s t
dhnave n, t shnuar si T(n). N duam q nocioni i jon i kohs t jet i
pavarur prej pajisjes (makins, kompjuterit), kshtu q n vend se t maten
sekondat pr CPU (procesor), sht m e zakonishme t maten hapat themelor t
cilt i bn algoritmi (p.sh., numri i urdhrave q ekzekutohen ose numri i
qasjeve n memorie). Kjo nuk do t parashikoj saktsisht kohn e ekzekutimit,
pasi q disa kompajler do t bjn optimizim m t mir se disa t tjer, mirpo
kjo do t ndodh me nj faktor t vogl dhe konstant t kohs s sakt t
ekzekutimit pr shumicn e kohs.
Edhe matja e kohs s ekzekutimit si funksion i madhsis hyrs nuk sht e
definuar mir, sepse pr shembull, mund t jet e mundur q t sortohet nj list
veq e sortuar, gj q ndodh m shpejt sesa nj list me renditje t rasitit. Pr
kt arsye, zakonisht flitet pr kohn e rastit m t keq (angl. orst case) t
ekzekutimit. Prgjat t gjitha hyrjeve t mundshme, cila sht koha maksimale
e ekzekutimit. sht m e arsyeshme t shqyrtohet rasti m i pritshm, kshtu q
nxirret mesatarja e t gjitha hyrjeve t madhsis n. Kjo sht analiza e rastit
mesatar (angl. average case). Rasti m i leht, si p.sh, sortimi i lists s sortuar,
sht rasti m i mir (angl. besta case).

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.

Pasi q kjo sht konstant, vlersojm se T(n) O(n3).


Pra, notacioni O sht i mir pr t caktuar nj kufi t eprm t funksionit.
Vreni se nse T(n) O(n3) sht poashtu edhe O(n4), O(n5), etj., pasi q limiti
do t shkoj n zero. Pr t fituar ndjenjn e shkallve t ndryshme t rritjes, ja
nj prmbledhje e tyre:
T(n) O(1)
T(n) O(log log n)

116

Shum e mir. Kjo do t thot se algoritmi merr koh konstante.


Nuk mund t bni m mir se kaq.
Shum e shpejt. Pr shumicn e synimeve, kjo sht poaq e
shpejt sa koha konstante.

Algoritmet dhe strukturat e t dhnave


T(n) O(log n)

T(n) O((log n)k)

Shum e mir. Kjo quhet koha logaritmike. sht koha e


ekzekutimit t krkimit binar dhe lartsia e pems binare t
balansuar. sht afrsisht m e mira q mund t arrihet pr
strukturat e t dhnave t bazuara n pemt binare. Vini re se
log21000 10, dhe log2,1,000,000 20.
Kjo quhet koha polilogaritmike. Nuk sht e keqe, kur nuk sht
e arritshme koha logaritmike. Kjo do t shkruhet shpesh si:

O(logk n). (k-konstante)


T(n) O(np)

(0<p<1, sht konstante). Kjo sht m e ngadalshme sesa koha


polilogaritmike (pa marr parasysh sa sht e madhe k ose sa
sht e vogl p), por akoma m e shpesjt se koha lineare, e cila
sht e pranueshme pr prdorim t strukturave t t dhnave.
Nj shembull sht O( n )..
Kjo quhet koha lineare. sht pothuajse m e mira q mund t
T(n) O(n)
shpresohet n rastet kur algoritmi duhet ti shikoj t gjitha t
dhnat. (Megjithat, n rastin e strukturave t t dhnave
zakonisht qllimi sht q kjo t evitohet).
Kjo sht e famshme pr arsye se sht koha e nevojshme pr
T(n) O(n log n)
sortimin e nj liste t numrave. Paraqitet edhe n nj numr t
problemve t tjera, poashtu.
2
Koha kuadratike. sht n rregull, nse n sht i rendit t
T(n) O(n )
mijrave, por e padshirueshme kur n kalon n t miliontat.
k
Koha polinomiale. Praktike, nse k nuk sht shum e madhe.
T(n) O(n )
n
T(n) O(2 ), Koha eksponenciale. Algoritmet q marrin kaq shum koh jan
t prshtatshme vetm pr vlerat m t vogla t n-it. (p.sh., n10
O(nn), O(n!)
ose ndoshta n20).

Efikasiteti i algoritmit rastet e ndryshme dhe shembujt


Rendet e zakonshme t algoritmeve dhe shembujt t tyrje jan:

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

Algoritmet dhe strukturat e t dhnave

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

ln n O(1) (Serit harmonike)

i 1

c n 1 1
c 1 (Serit gjeometrike)
c

c 1
i 0
n

Vni re se shumat komplekse shpesh mund t ndahen n terma t thjeshta, t cila


pastaj mund t zgjidhen m leht. Pr shembull:

119

Avni Rexhepi

Shuma e fundit sht me gjas m e rndsishmja pr strukturat e t dhnave.


Pr shembull, supozojm se doni t dini sa nyja ka n pemn komplete 3-are me
lartsi h. (Akoma nuk sht prkufizuar definicioni i pems, por merrni parasysh
figurn vijuese).

Fig. 2.5 Pema 3-are e kompletuar, me lartsi h=2.


Lartsia e pems, h, sht numri maksimal i degve prej rrnjs, deri tek gjethja.
Nj mnyr pr t coptuar kt llogaritje sht q t shikohet pema nivel pas
niveli. N nivelin fillestar (niveli 0), kemi nj nyje, n nivelin 1 kemi 3 nyje, n
nivelin 2 kemi 9 nyje dhe n prgjithsi, n nivelin i kemi 3i nyje. pr t gjetur
numrin total t nyjeve, do t mbledhim prgjat t gjitha niveleve, prej 0 deri n
h. Duke zvendsuar n shprehjen e mparshme n=h, do t kemi:
h

3
i 0

3 h 1 1
3h
3 1

N ann e kundrt, nse dikush do t ju thoshte se keni nj pem 3-are me n


nyje, ju do t mund t prcaktonit lartsin e pems, duke invertuar kt. Pasi q
n = (3(h+1) 1)/2, do t kemi:
3(h+1) = (2n+1)
duke ln t kuptohet se
h = (log3(2n+1) - 1 O(log n).

120

Algoritmet dhe strukturat e t dhnave


Nj fakt tjetr i rndsishm q duhet t mbahet mend lidhur me shumat, sht
se ato mund t prafrohen prmes prdorimit t integraleve.
b

f (i)
i a

xa

f ( x)dx

Me nj shum t dhn t komplikuar, shpesh sht e mundur q t gjindet n


librin e integraleve dhe t prdoret formula prkatse pr t prafruar shumn.

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

Algoritmet dhe strukturat e t dhnave


formuln e dhn na jep: (log3(2 1 + 1)) 1=(log33) 1=0, q ishte vlera e
dshiruar.
Hapi i induksionit: duam t vrtetojm se teorema vlen pr nj vler specifike
t n>1. Vreni se n kt rast nuk mund t aplikojm induksionin standard, pasi
q nuk ka pem komplete 3-are me dy nyje n t (vlera e ardhshme m e madhe
ka 4 nyje).
Do t supozojm hipotezn e induksionit, pr t gjitha vlerat m t vogla n, 1
n<n, H(n) sht dhn me formuln m lart. (Kjo quhet ndonjher
induksioni i fort dhe sht mir t msohet, sepse shumica e provave t
induksionit pr strukturat e t dhnave funksionojn n kt mnyr).
Le t shqyrtojm pemn komplete 3-are me n>1 nyje. pasi q n>1, duhet t
prbhet prej rrnjs dhe plus tri nnpemve t tjera identike, ku secila sht
pem komplete 3-are me n<n nyje. Sa nyje jan n kt nnpem? Pasi q ato
jan identike, nse prjashtojm nyjn rrnj, secila nnpem ka nj t tretn e
numrit t mbetur t nyjeve, kshtu q n=(n-1)/3. Pasi q n<n, mund t
aplikojm hipotezn e induksionit. Kjo na tregon se:
H(n) = (log3(2n + 1)) 1 = (log3(2(n 1)/3 + 1)) 1
= (log3(2(n 1) + 3)/3) 1 = (log3(2n + 1)/3) 1
= log3(2n + 1) log3 3 1 = log3(2n + 1) 2.
Vreni se lartsia e pems s tr sht nj m shum sesa lartsit e
nnpemve, kshtu q H(n) =H(n) + 1. Prandaj, kemi:
H(n) = log3(2n + 1) - 2 + 1 = log3(2n + 1) - 1;
si dshirohej.
Kjo mund t duket nj mnyr shum e komplikuar pr t vrtetuar nj fakt kaq
t thjesht, por induksioni sht teknik e fuqishme pr vrtetimin e fakteve
shum m komplekse t cilat paraqiten n analizn e strukturave t t dhnave.
Duhet pasur kujdes gjat tentimit t vrtetimit me induksion t rasteve q
prfshijn notacionin O(n) (Big-oh notacionin). Ja nj shembull i gabimit t
shpesht.
Teorem. (False!): pr n1, le t jet T(n) e dhn me shumn vijuese:
n

T(n)= i
i 0

Ather, T(n) O(n). (Ne e dijm nga formula e serive lineare se


T(n)=n(n+1)/2 O(n2). Pra, kjo duhet t jet jo e vrtet (false). (A mund ta
123

Avni Rexhepi
lokalizoni
vrtetimin vijues?)

gabimin

Rasti baz: pr n=1, kemi T(1)=1 dhe 1 sht O(1).


Hapi i induksionit: duam t vrtetojm teoremn pr nj vler specifike t n>1.
Supozojm se pr fardo n<n, T(n) O(n). Tani, pr rastin n, sipas
definicionit kemi:
n
n
T(n)= i i n T (n 1) n
i 0
i 0

Tani, pasi q n-1<n, mund t aplikojm hipotezn e induksionit, duke dhn


T(n-1) O(n-1).
Duke vendosur kt prapa, do t kemi:
T(n) O(n-1)+n.
Mirpo, (n - 1) + n 2n - 1 O(n), kshtu q kemi T(n) O(n).
Ather, ku sht gabimi?! Rikujtoni q notacioni asimptotik aplikhet vetm pr
vlera arbitrarisht t mdhaja t n-it (pr n kufitare). Mirpo, prova me induksion
prej vet natyrs s tyre vlejn vetm pr vlera specifike t n-it. Mnyra e duhur
pr ta vrtetuar kt prmes induksionit do t ishte q t dilet me nj shprehje
konkrete, e cila nuk e prfshin O-notacionin.

2.3. Rritja e funksioneve


Shumica e algoritmeve parametrin primar N i cili ka ndikimin m t
rndsishm n kohn e ekzekutimit. Parametri N mund t jet shkalla e
polinomit, madhsia e fajllit q duhet krkuar ose sortuar, numri i karaktereve t
stringut ose ndonj mas tjetr abstrakte e madhsis s problemit q analizohet:
m s shpeshti sht direkt proporcionale m madhsin e bashksis s t
dhnave q procesohet. Kur ka m shum se nj parametr t till (pr shembull
M dhe N n algoritmet pr gjetje n union), shpeshher analiza redukohet
zakonisht n analizn e vetm njrit parametr, duke shprehur njrin parametr
si funksion t tjetrit ose duke e konsideruar nj parametr n koh (duke mbajtur
tjetrin konstant), ashtu q t mund t kufizohemi n analizimin e nj parametri
N, pa humbur n prgjithsim. Qllimi sht q t shprehen krkesat pr resurse
t programit (koha e ekzekutimit dhe hapsira n memorie), n terma t N-it,
duke prdorur formulat matematike sa m thjesht q t jet e mundur dhe q
jan t sakta pr vlera t mdhaja t parametrave. Shumica e algoritmeve kan
zakonisht kohe t ekzekutimit proporcionale me njrin prej funksioneve vijuese:
124

Algoritmet dhe strukturat e t dhnave


1 Shumica e instruksioneve (urdhrave) t shumics s programeve
ekzekutohen nga nj her ose n t shumtn vetm disa her. Nse t
gjitha instruksionet e programit kan kt veti, ather themi se koha
e ekzekutimit t programit sht konstante.
log N Kur koha e ekzekutimit t programit sht logaritmike, programi
bhet pak m i ngadalshm gjersa N rritet. Kjo koh e ekzekutimit
zakonisht paraqitet n programet t cilat zgjidhin nj problem t madh
duke e transformuar n seri t problemeve t vogla, duke prer
madhsin e problemit pr nj faktor konstant n secilin hap. Pr
sfern ton t interesit, mund t konsiderojm kohn e ekzekutimit t
jet m e vogl se nj konstante e madhe. Baza e logaritmit ndryshon
konstantn, por jo shum. Kur N sht 1000, log N sht 3 nse baza
sht 10, ose sht afr 10-shit, nse baza sht 2; kur N sht 1
milion, log N sht vetm dyfishi i vlerave paraprake. Sa hr q N
dyfishohet, log N rritet pr nj konstant, por log N nuk dyfishohet
deri sa N t rritet sa N2.
N Kur koha e ekzekutimit t programit sht lineare, sht zakonisht
rasti kur nj sasi e vogl e procesimit kryhet pr secilin element t
hyrjes. Kur N sht 1 milion, ather aq sht edhe koha e
ekzekutimit. Sa her q N dyfishohet, ashtu bn edhe koha e
ekzekutimit. Kjo situat sht optimale pr nj algoritm q duhet t
procesoj N hyrje (ose t prodhoj N dalje).
N log N Koha e ekzekutimit N log N paraqitet kur algoritmet zgjidhin
problemin duke e ndar at n nnprobleme t vogla dhe duke i
zgjedhur ato n mnyr t pavarur, e pastaj duke i kombinuar
zgjidhjet. Thjesht, themi se koha e ekzekutimit t nj algoritmi t
till sht N log N (sepse mungon ndonj shprehje q do t ishte
kombinim i linear-algoritmik, si p.sh., linearitmik!). Kur N sht 1
milion, N log N sht ndoshta 20 milion. Sa her q N dyfishohet,
koha e ekzekutimit rritet pak m shum se dyfishi (por jo shum).
N2 Kur koha e ekzekutimit t algoritmit sht kuadradike, ai algoritm
sht praktik vetm pr probleme relativisht t vogla. Koha e
ekzekutimit kuadratike n mnyr tipike paraqitet n algoritmet t
cilat procesojn t gjitha iftet e t dhnave (ndoshta n unaz t
dyfisht). Kur N sht 1000, koha e ekzekutimit sht 1 milion. Sa
her q N dyfishohet, koha e ekzekutimit katrfishohet (rritet
katrfish).

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

Algoritmet dhe strukturat e t dhnave


muaj, vite, e kshtu me radh. Tabela 2.2 jep disa shembuj t cilt paraqesin se
si algoritmet e shpejta jan m t prirura t jen t afta ti zgjidhin problemet
sesa komjutert e shpejt, pa u prballur me kohra mizore t ekzekutimit.
lg N
3
7
10
13
17
20

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 :)

Tabela 2.2. Konvertimi n sekonda


Dallimi i madh ndrmjet numrave si 104 dhe 108 sht m i qart kur shqyrtohen
n aspektin e gjatsis s kohs n sekonda dhe kur bhet konvertimi i tyre n
njsi t zakonshme t kohs. sht e kuptueshme se do t prisnim pr
ekzekutimin e nj programi pr 2.8 or, por pak ka gjasa q t pajtohemi me
programin q do t krkonte 3.1 vite pr tu kryer. Pasi q 210 sht prafrsisht
sa 103, kjo tabel do t ishte e dobishme edhe pr fuqit e 2-shit. Pr shembull,
232 sekonda sht prafrsisht 124 vite.
Pr shum aplikacione, gjasa e vetme pr t zgjidhur instanca t problemeve
shum t mdha sht prdorimi i algoritmit efikas. Tabela n vijim paraqet
kohn minimale t nevojshme pr zgjidhjen e problemeve me madhsi t rendit
1 milion dhe 1 miliard, duke prdorur algoritmet lineare (N), kuadratike (N2)
dhe N her logaritmik (N lg N), n kompjutert me shpejtsi t ekzekutimit t 1
milion, 1 miliard dhe 1 bilion instruksione pr sekond. Algoritmi i shpejt na
mundson zgjidhjen e problemeve n kompjuter t ngadalshm, por kompjuteri i
shpejt nuk ndihmon dot kur prdoret algoritmi i ngadalshm.
127

Avni Rexhepi
Operacione
pr sekond

Madhsia e problemit 1 milion

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

Tabela 2.2. Koha pr zgjidhjen e problemeve shum t mdhaja


Paraqiten edhe disa funksione t tjera, si p.sh algoritmi me N2 hyrje q ka koh
t ekzekutimit proporcionale me N3, sht m s shumti algoritm N3/2.
Gjithashtu, disa algoritme kan dy shkall t dekompozimit t nnproblemit, t
cilat ojn n koh t ekzekutimit proporcionale me N log2N. sht e dukshme
nga tabela 2.1 se t dy kto funksione jan shum m t afrta me N log N sesa
me N2.
Funksioni logaritmik luan rol t veant n dizajnin dhe analizn e algoritmeve,
kshtu q sht me rndsi t njihet mir dhe t shqyrtohet detaje. Pasi q
shpesh kemi t bjm me rezultate analitike prbrenda faktorit konstant,
prdorim notacionin log N pa e specifikuar bazn pr logaritmin. Ndryshimi i
bazs prej nj konstante n nj tjetr e ndryshon vlern e algoritmit vetm pr
nj faktor konstant, por disa baza specifike sygjerohen vetvetiu pr kontekste t
venta. N matematike, logaritmi natyral (me baz e=2.71818...) sht shum i
rndsishm dhe shkurtesa e tij speciale sht loge N ln N. Poashtu, logaritmi
binar (me baz 2) sht shum i rndsishm dhe zakonisht prdoret si log2 N
lg N.
Numri m i vogl i plot (integer) m i madh se lg N sht numri i bitave t
nevojshm pr t reprezentuar N-in si numr binar, n mnyr t njjt siq
numri i plot m i vogl (integer) m i madh sesa log10N sht numri i shifrave
t nevojshme pr t reprezentuar N-in si decimal. Urdhri n C++
for (lgN = 0; N > 0; lgN++, N /= 2) ;

sht mnyr e thjesht pr t llogaritur integer-in m t vogl, m t madh sesa


lg N. Mnyra e ngjashme pr llogaritjen e ktij funksioni sht:
for (lgN = 0, t = 1; t < N; lgN++, t += t) ;

ky version thekson q 2n N < 2n+1 kur n sht integer-i m i vogl m i madh


sesa lgN.
Nganjher, e prsrisim logaritmin: e aplikojm at n mnyr t
njpasnjshme n nj numr shum t madh. Pr shembull: lglg 2256 = lg 256 =

128

Algoritmet dhe strukturat e t dhnave


8. Si ilustrohet kt shembull, zakonisht e konsiderojm loglogN si konstant, pr
qllime praktike, pasi q sht shum i vogl, edhe kur N sht shum i madh.
Poashtu, shpeshher hasim nj numr t funksioneve speciale n notacionet
matematike prej analizs klasike, q jan t dobishme n ofrimin e shpjegimeve
koncize (t ngjeshura) t tipareve t programeve. Tabela 2.3 prmbledh
funksionet m t zakonshme:
Funksioni
x
x
lg N
FN

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

Tabla 2.3. Funksionet dhe konstantet sepciale


Algoritmet dhe analizat m s shpeshti kan t bjn me njsi diskret, kshtu
q shpeshher na duhet q pr funksionet speciale vijuese t konvertojm
numrat real n numra t plot (integer):
x: numri i plot m i madh, q sht m i vogl ose baraz me x
x: numri i plot m i madh, q sht m i vogl ose baraz me x

Pr shembull, dhe e jan t dyja t barabarta me 3 dhe lg (N+1) sht


numri i bitave n reprezentimin binar t N-it. Nj prdorim tjetr i rndsishm i
ktyre funksioneve paraqitet kur dshirojm t ndajm prgjysm bashksin
prej N elementeve. Nse N sht tek, kt nuk mund ta bjm saktsisht, por pr
t qen preciz, e ndajm njrn nnbashksi n N/2 dhe tjetrn n N/2.
Nse N sht numr ift, t dy nnbashksit jan me madhsi t barabarta
(N/2 = N/2), mirpo nse N sht tek, ato dallojn n madhsi pr 1 (N/2 +1
= N/2). N C++, kto funksione mund ti llogarisim drejtprdrejt kur jemi
duke operuar me numra integer (p.sh., nse N0, ather N/2 sht dhe N-(N/2)
129

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

Logaritmi natyral ln N sht zona nn lakoren 1/x ndrmjet 1 dhe N; harmoniku


HN sht zona nn funksionin shkall (angl. step function) q definohet duke
vlersuar 1/x n pozitat e numrave t plot ndrmjet 1 dhe N. Ky relacion sht
ilustruar n figurn 2.6.

Figura 2.6. Numrat harmonik


Formula HN ln N++1/(12N), ku = 0.57721... (konstanta e Euler-it) jep nj
prafrim t shklqyeshm t HN. N ann tjetr, pr lg N dhe lg N sht m
mir t prdoret funksioni log i libraris matematikore pr t llogaritur HN sesa t
llogaritet direkt prej definicionit.
Numrat harmonik (seria e harmonikve) jan nj prafrim i zons nn lakoren
y=1/x.
Konstanta e Euler-it definohet si limiti i diferencs ndrmjet seris s
harmonikve dhe logaritmit natyral (pjesa e shkallzuar, prmbi lakore 1/xn):

Seria e numrave:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 ...

t cilt definohen prmes formulave:


FN = FN1 + FN2,

130

pr N 2, me F0 = 0 dhe F1 = 1

Algoritmet dhe strukturat e t dhnave

Njihet si Numrat Fibonacci dhe kan disa tipare shum interesante. Pr


shembull, raporti i dy termave t njpasnjshme, i prafrohet raportit t art:
are knon as the Fibonacci numbers, and they have many interesting properties.
For example, the ratio of to successive terms approaches the golden ratio = (1
+ 5 )/2 1.61803.... Analiza e detajuar tregon se FN sht N/ 5 , e
rrumbullaksuar n numrin e plot m t afrt.
Poashtu, do t kemi rastin e manipulimit t funksionit t njohur t faktorielit, N!.
Ngjashm me funksionin eksponencial, faktorieli paraqitet n zgjidhjet bruteforce t problemeve dhe rritet shum shpejt, sa q zgjidhjet e tilla nuk jan me
interes praktik. Poashtu paraqitet n analizn e algoritmeve sepse prfaqson t
gjitha mnyrat e mundshme t aranzhimit t N objekteve. Pr t prafruar
faktorielin N!, prdoret formula e Stirling-ut:

lg N! N lg N N lg e lg 2N
ose versioni zakonisht i prdorur npr alikacione:

Formula e Stirling-ut tregon se numri i bitave n reprezentimin binar t N! sht


prafrsisht N lg N.
Shum prej formulave t shqyrtuara n analizn e algoritmeve shprehen n
terma t funksioneve t prmendura, prandaj jan me interes.

131

Avni Rexhepi

Algoritmet pr nga teknika dhe qasja


Pr rastet kur natyra e problemit sht e thjesht dhe ka zgjidhje t lehta,
prdoren algoritmet standarde. Pr problemet e komplikuara dhe pr t cilat nuk
ka ndonj zgjidhje standarde, prdoren metodat e optimizimit.
Kur fillohet zgjedhja ideore e algoritmit, varsisht prej problemit, mund t
prdoren qasje t ndryshme. Zgjidhjet ideore t algoritmeve kryesisht jan t
orientuara kah gjetja e zgjidhjeve optimale pr problemin e caktuar, kshtu q
shpesh her flitet pr algoritmet optimizuese. Metodat m t njohura t qasjes
pr algoritmet jan: brute-force (metoda q provon me radh t gjitha rastet),
praj e sundo (angl. Divide and Conquer), metoda e eliminimit ose e turneut
me eliminime (angl. tournament), rekursioni, back-tracking (angl. backprapa,
angl. trackingprcjellja, gjurmimi; kshtu q mund ta quajme rishikimi,
prapaveprimi, prapakthimi, etj), algoritmet dinamike, algoritmet evolutive, etj.

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 n mnyr tipike largon nj element nga problemi,


e zgjidh problemin m t vogl dhe pastaj e prdor zgjidhjen e ktij
problemi m t vogl pr t ja shtuar prsri elementin n mnyrn e
duhur.

Prqaj e sundo n mnyr tipike e ndan problemin n gjysm, e zgjidh


seciln gjysm dhe pastaj i bashkon prapa t dy gjysmat pr t formuar
zgjidhjen e plot.

T dy kto teknika jan t rndsishme pr ti njohur. N veanti, programimi


dinamik sht teknik e keqkuptuar dhe e nnvlersuar.
Shum objekte kan nj renditje t qensore nga e majta n t djatht pr
elementet e tyre, si karakteret n string, elementet e permutacioneve, pikat
prreth poligonit ose gjethet n pemn e krkimit. Pr cilindo problem t
optimizimit n objekte t tilla nga e majta n t djatht, programimi dinamik me
gjas do t drgoj n nj algoritm efikas pr gjetjen e zgjidhjes m t mir. Pa
nj renditje t nnkuptuar nga e majta n t djatht t objekteve, programimi
132

Algoritmet dhe strukturat e t dhnave


dinamik zakonisht sht i dnuar q t krkoj koh dhe hapsir
eksponenciale.
Kur programimi dinamik t jet kuptuar nj her e mir, do t jet m leht q t
krijohen algoritmet e tilla prej zeros sesa t tentohet q t krkohen.
Optimumi global (i gjetur, pr shembull, duke prdorur programimin dinamik)
sht shpeshher dukshm m i ir sesa ndonj zgjidhje e gjetur me heuristik
tipike. Se sa sht i rndsishm ky prmirsim varet prej aplikacionit tuaj, por
kurr nuk mund t jet i dmshm.
Krkimi binar dhe variantet e tij jan algoritme m t sofistikuara prqaj-esundo.

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

Algoritmet dhe strukturat e t dhnave


F(6)

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)

Figura: Pema e llogaritjes pr llogaritjen rekurzive t numrave Fibonacci


Sa koh merr ky algoritm pr t llogaritur Fibonacci[n]?
Pasi q

(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

duke prdorur rastet bazike


T (i, j ) C (i, j ) C ( j,1)

Kjo rekurrenc, edhe pse pak e komplikuar pr tu kuptuar, n fak sht


korrekte. Mirpo, secila zgjidhje parciale sht e prshkruar me nnbashksin e
nyjeve: j1, j2, ..., jk. Pasi q ka 2n nnbashksi t n nyjeve, krkohet koh dhe
136

Algoritmet dhe strukturat e t dhnave


hapsir W(2n), pr t vlersuar kt rekurrenc. Kjo sht e menagjueshme, pasi
W(2n) sht vrtet prmirsim i dukshm n krahasim me t gjitha O(n!) turet e
mundhshme TSP. Megjithat, programimi dinamik sht m s shumti efikas n
objektet e renditura mir.

Programimi linear zgjidhja pr kutin e zez


Mnyra m e leht pr zgjidhjen e nj problemi t optimizimit sht q t
shkruhet specifikacioni pr hapsirn e zgjidhjeve t realizueshme dhe t
funksionit objektiv, e pastaj t prdoren ndonj paket ekzistues softverik pr t
gjetur zgjidhjen optimale.

Algoritmet lakmitare kurr mos shiko prapa


Algoritmet lakmitare (angl. Greedy Algorithms, greed-lakmi) prfshijn ndonj
lloj t optimizimit. Idea e lakmis sht q t fillohet me kryerjen e cilitdo
operacion q kontribon sa nj hap i vetm n drejtim t zgjidhjes prfundimtare
dhe qllimit final. Hapi i ardhshm pastaj do t ishte hapi m i mir q do t
mund t ndrmirrej prej pozits s re, e kshtu me radh. Shembull i mir
prshkrues do t ishte algoritmi i gjetjes s pems minimale t shtrirjes tek
grafet.

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.

Krkimi lokal Mendo globalisht, vepro lokalisht


Algoritmet e optimizimit jan t aplikueshme n disa rrethana t veanta.
Programimi dinamik, krkon struktur speciale pr problemin dhe mund t
krkoj shum hapsir dhe koh. Krkimi sistematik sht zakonisht shum i
ngadalshm pr hyrje t mdha. Algoritmet lakmitare jan t shpejta, por
shpeshher japin vetm zgjidhje t kualitetit t ult. Krkimi lokal sht
procedur iterative gjersisht e aplikueshme. Ai fillon me ndonj zgjidhje t
realizueshme dhe pastaj lviz nga zgjidhja e realizueshme n zgjidhje t
realizueshme me modifikime lokale. Krkimi lokal mban zgjidhjen aktuale t
realizueshme x dhe zgjidhjen m t mir t gjetur deri n at moment x. N
secilin hap, krkimi lokal lviz nga zgjidhja aktuale n nj zgjidhje fqinje. ka
jan zgjidhjet fqinje? Cilado zgjidhje q mund t prfitohet prej zgjidhjes
aktuale, duke br nj ndryshim t vogl n t, sht zgjidhje fqinje.

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.

Kalitja e simuluar t msuarit nga natyra


Nse dshirojm ti ikim zgjidhjeve t padshiruara lokale, duhet t gjindet
mnyra pr t ikur prej tyre. Kjo do t thot q ndonjher duhet t pranohen
lvizjet t cilat zvoglojn vlern objektive. Qllimi sht q t iket prej
optimumeve lokale, ashtu q t lvizet drejt cakut, pr t gjetur optimumet
globale. Nj mnyr e e asthuquajtura kalitja e simuluar (angl. simulated
annealing).
Kjo medot simulon procesin e kalitjes s metaleve, n t cilin substanca nxehet
prmbi temperaturn e saj t shkrirjes dhe pastaj gradualisht ftohet duke
prodhuar rrjetn kristaline, e cila minimizon shprndarjen e probabilitetit t
138

Algoritmet dhe strukturat e t dhnave


energjis s saj. Kjo rrjet kristalore, e prbr nga miliona atome t renditura n
mnyr perfekte, sht shembull i mir i gjetjes natyrore t strukturs optimale.
Megjithat, ftohja e shpejt ose shuarja e prish formacionin kristalor dhe
substanca bhet mas amorfe me gjendje energjie m t lart sesa optimumi.
elsi i formimit t kristalit sht kontrollimi i kujdesshm i shkalls s
ndryshimit t temperaturs.
Algoritmi analog me kt proces fillon me nj hamendsim t rastit t vlerave t
variablave t funksionit t kostos. Nxehja nnkupton modifikimin me rastsi t
vlerave t variablave. Nxehja e lart krkon luhatje t rastit m t mdha.
Funksioni i kostos kthen daljen, t shoqruar me setin e variablave. Nse dalja
zvoglohet, ather seti i variablave zvendson setin e vjetr t variablave.
Edhe nse seti i variablave drgon n kosto m t keqe, mund t pranohet me nj
probabilitet t caktuar.
Variabla kontrolluese vendos hapin e shkalls ashtu q, n fillim t procesit
algoritmi detyrohet t bj ndryshime t mdha n vlerat e variablave. Me koh,
ndryshimet lvizin algoritmin tuje nga optimumi, gj q e detyron algoritmin t
hulumtoj regjione t reja t hapsirs s krkimit. Pas nj numri t caktuar t
prsritjeve, seti i ri i variablave nuk drgon m n kostot e ulta. Algoritmi
ngalet kur T 0. Zvoglimi i T sht i njohur edhe si orari i ftohjes. Jan t
mundshme shum orare t ndryshme t ftohjes. Shpresohet q n kt mnyr t
arrihet n regjionet e zgjidhjeve t mira optimale dhe pastaj n fakt t gjindet
zgjidhje afr optimales, n fazn e temperaturs s ult.
Pragu i pranueshm sht pragu i vlerave t pranueshme t zgjidhjeve.
Tabu lista Tabu lista ose Tabu search (tabu krkimi) na paraqitet n rastet
kur gjat krkimit t zgjidhjeve optimale, arrijm gjithmon n zgjidhjen e
njjt sub-optimale, duke u sillur n cikl t mbyllur. Randomizimi (marrja e
vlerave t rastit) do t paraqiste nj rrugdalje prej optimumeve lokale, duke
mbajtur nj list tabu t elementeve t zgjidhjes evitohet n zgjidhjet e reja,
pr momentin. Tabu listat jan shum t suksesshme dhe mund t prdoren si
teknik baz pr nj variant ta pavarur t zgjidhjes lokale, t quajtur tabu
search (krkimi tabu).
Restartimi sjellje tipike e algoritmeve t rafinuara t krkimit lokal sht
lvizja n nj zon me zgjidhje t realizueshme dhe pastaj hulumtimi i asaj zone,
duke tentuar q t gjindet optimumi lokal gjithnj e m i mir. Mirpo, mund t
ndodh q ekzistojn zgjidhje shum m t mira, n zona t tjera t largta. Disa
ekzekutime t njpasnjshme t pavarura n disa kompjuter t ndryshm mund
t drgojn n nj form t leht t paralelizmit.

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.

Algoritmet me rastsi - Las Vegas dhe Monte Carlo


Algoritmet e randomizuara (me rastsi) ndahen n dy kategori kryesore:
Algoritmet Las Vegas dhe ato Monte Carlo.
Algoritmi Las Vegas gjithmon llogarit prgjigjen korrekte por koha e tij e
ekzekutimit sht variabl e rastit.
Algoritmi Monte Carlo gjithmon ka koh t njjt t ekzekutimit, por ka nj
probabilitet jo-zero t kthimit t prgjigjes jo-korrekte. Probabiliteti q prgjigja
sht jokorrekte sht pothuajse 1/4. Pra, kthen prgjigje, por ndonjher jo t
sakt.

140

Algoritmet dhe strukturat e t dhnave

3. Implementimi i strukturave themelore


Stack dhe Queue
Stack (Steku)
Steku (angl. Stack-mullar, grumbull, raft etj) sht struktura n t ciln
elementet renditen sipas radhs s vendosjes s tyre n list, me fjal t tjera
First In Last Out (FILO) (I pari brenda, i fundit jasht). Kjo struktur sht
rasti i grupit t librave t vendosur nj mbi nj, karikatori i plumbave, nj gyp i
mbullur n njrin skaj, etj. Pra n t gjitha kto raste, kemi qasje vetm n
elementin e fundit t vendosur n list.

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).

Steku prmes vargjeve


Termi Stek mund t duket abstrakt pr tu kuptuar, por n jetn e prditshme e
prdorim gjat lojs me letra, kur bjme palpeta, kur vendosim nj grup librash
nj mbi nj, etj. Steku pra sht nj grup i gjrave t vendosura njra mbi tjetrn
dhe me mundsi t largimit t vetm nj elementi n moment kohor, vetm nga
maja e stekut. Edhe pse e struktur shum e thjesht, steku gjen prdorim t
gjer dhe sht komponente kritike e shum programeve.
Parimi i funksionimit sht LIFO (Last In, First Out). Nuk mund ta merrni nj
element n pjesn e poshtme, pa i larguar s pari nj nga nj t gjitha sipr. Edhe
pse n dukje, struktur joefikase, nse objektiv do t ishte qasja e rastit n
elementet prbrse, pr qllime t tjera, steku del t jet struktur ideale.
Mirpo, nse qllimi sht marrja e gjrave, sipas radhs s vendosjes n stek, si
sht puna me instruksionet e kompjuterit, ather steku sht struktur efikase.
Steku sht nj prej strukturave fundamentale t t dhnave n shkencat
kompjuterike dhe prdoret n shum algoritme dhe aplikacione. Pr shembull,
steku prdoret n: vlersimin e shprehjeve, n mnyr implicite n rekurzion,
pr t verifikuar korrektsin e sekuencs s kllapave, etj.
S pari do t prshkruajm ADT-n e stekut dhe pastaj do t paraqiten
implementimet e ndryshme.

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

Algoritmet dhe strukturat e t dhnave


N stek jan t prcaktuara gjasht veprime standarde. Kemi mundsi t
vendosim nj libr n maje (push; angl. push-goditje, shtytje, hedhje, etj.); t
shikojm cili libr ndodhet n maje (peek, top; angl. peek-shikim vjedhurazi,
prgjim); t largojm librin n maj (pop; angl. pop-nxjerr (kuptimi q i
prshtatet ktij veprimi)); dhe t verifikojm a sht steku i zbrazt (isEmpty).
Gjithashtu, kemi dy operacionet themelore pr krijim (create) dhe asgjsim
(destroy) t stekut.

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

N literatur, operacioni peek quhet edhe top, pr t simbolizuar shikimin n


maje t stekut.

Aksiomat pr stekun

steku i sapokrijuar sht i zbrazt;


pasi t vendoset nj element n stekun e sapokrijuar, ai bhet jo i
zbrazt;
peek kthen elementin e fundit t vendosur n stek;
steku mbetet i paprekur, pas iftit t komandave push dhe pop, t
ekzekutuara njra pas tjetrs.

Pra, t gjitha jan definicione formale. Prmes figurs n vijim ilustrohen


operacione n stek.
Skematikisht, steku mund t paraqitet si vijon:
143

Avni Rexhepi

Fig. 3.1 - Steku


Vargu dhe steku shpeshher duken si koncepte t cilat ngatrrohen n diskutime
t njjta, mirpo n esenc jan dy gjra t ndryshme. Vargu i ruan vlerat n
memories; ndrsa steku vetm e prcjell se cili sht elementi n krye t stekut
(angl. top). Kur nxirret vlera nga steku, ajo n fakt mbetet n memories, sepse
ajo akoma sht element i vargut. Nxjerrja e elementit nga steku, vetm e
ndryshon elementin q ndodhet n krye t stekut.

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

Algoritmet dhe strukturat e t dhnave


Figura 3.2 sht mnyra se si disa programer e prfytyrojn nj varg t
prdorur me stek. Shembulli paraqet nj varg t quajtur stek, me 8 elemente t
vargut. I tr vargu, prmban vlerat t cilat jan t referencuara nga steku. Tri
elemente t vargut, jan vlera t prcaktuara, gjersa t tjerat jan akoma t
zbrazta dhe mund t prdoren kur t vendosen n stek elemente t reja.
Moni sht vlera e par e vendosur n stek. Kjo dihet pr shkak se Moni sht
n fund t stekut. Beni, sht elementi i fundit i futur n stek, sepse Beni sht
n krye t stekut (sht elementi top).

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.

Krijimi i stekut n C++


Steku mund t krijohet n C++ duke definuar klasn Stack dhe duke deklaruar
instancat e klass. Klasa Stack krkon tri atribute dhe disa funksione (member
functions). Do t fillojm me definimin e klases themelore, q prmban vetm
komponentet e nevojshme pr krijimin e stekut.
Klasa do t emrtohet Stack, edhe pse normalisht mund t zgjedhni emrin sipas
dshirs. Klasa prmban pjesn private dhe at publike. Komponentet private
kan qasje vetm prms funksioneve antare t klass.
146

Algoritmet dhe strukturat e t dhnave


N pjsn private jan tri atribute: size (madhsia), top (kreu i stekut) dhe values
(vlerat), t cilat jan t tipit integer. Atributi size ruan numrin e elementeve n
stek, atributi top ruan indeksin e elementit n krye t stekut dhe atributi values
sht pointer n stek, q sht nj varg. Steku n kt shembull sht stek i
numrave t plot, por mund t prdoret vargu i fardo lloji t vlerave, varsisht
prej natyrs s programit.
N fillim, pr qllime thjeshtsie dhe pr tu kuptuar leht, fillojm me vetm
nj funksion antar t klass. Ky funksion, quhet Stack dhe sht konstruktori i
klass. Konstruktor sht funksioni q ka emrin e njejt me vet klasn dhe q
thirret automatikisht kur t krijohet nj instanc e klass.
Brenda konstruktorit ndodhin disa gjra. S pari, konstruktori e merr nj vler
integer si argumet, i cili prcillet kur t deklarohet nj instanc e klass. Kjo
vler integer prcakton numrin e elementeve n stek dhe i ndahet variabls size.
Urdhri i par n konstruktor mund t duket pak i paqart, pasi q duket thua se
vlera e variabls size nga lista e argumenteve po i ndahet vetvetes, por kjo nuk
sht kshtu. N fakt, variaba size nga lista e argumenteve, sht variabl lokale
prbrenda funksionit Stack. Kombinimi: this->size i referohet atributit size t
klass Stack, si n vijim:
this->size = size;

Programert e prdorin pointerin this (angl. this ky, kjo), prbrenda


funksionit t klass pr t ju referuar instancs aktuale t klass. N kt
shembull, pointeri this prdor referencn e pointerit ->, pr ti treguar
kompjuterit q t prdor atributin size t klass. Si dihet, n C++, referenca e
pointerit prdoret (->) kur punohet n mnyr indirekte me antarin e klass
ndrsa operatori dot (.) prdoret kur punohet direkt me antarin e klass.
Kjo i mundson kompajlerit q t bj dallimin ndrmjet variabls s kalss dhe
variabls lokale q ka emr t njjt. Kjo do t thot se vlera e variabls size q
prcillet si argument n funksionin Stack, i ndahet atributit size, duke e br
vlern t disponueshme pr antart e tjer t klass Stack.
Atributi size prdoret n urdhrin e ardhshm. Ky urdhr i bn dy gjra. S
pari, e alokon memorien pr stekun duke prdorur opreratorin new (new
int[size]). Operatori new kthen pointerin pr n lokacionin e rezervuar t
memories. Size sht atributi size i klass dhe prcakton madhsin e vargut.
Vargu sht nj varg i numrave t plot (integer).
Pastaj, pointeri i vargut t integjerave i ndahet atributit values t klass. Atributi
values sht variabl pointer q sht definuar n pjesn private t klass Stack.
Urdhri i fundit n funksionin Stack ia ndan vlern a-1 atributit top. Vlerat e
atributit top sht indeksi i elementit top t stekut. Vlera -1 do t thot q steku
147

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

Algoritmet dhe strukturat e t dhnave


sht funksion i thjesht, i cili krahason vlern e kreut t stekut, atributi top, me
nj m pak sesa vlera e atributit size.
Vlera e atributit top sht -1, kur deklarohet instanca e stekut. Supozojm se
madhsia sht 10 (pra atributi size sht 10). Shprehja e kushtzimit n
urdhrin if t funksionit isFull() prcakton nse vlera top, e cila sht -1, sht
m e vogl se size-1. Pasi q vlera e size sht 10, shprehja e kushtit krahason 1<9. Nse top sht m e madhe ose baraz me 9, ather kthehet true
(plotsohet kushti), prndryshe kthehet false.
Pra, duhet t zbritet 1 nga vlera size, pasi q vlerat e atributit top sht nj
indeks i antarit t vargut. Pasi q indeksat fillojn prej zeros, kemi dhjet
indeksat: 0 deri n 9. Prndryshe, vlera size n fakt sht numri i elementeve t
stekut.
bool isFull()
{
if(top < size-1)
{
return false;
}
else
{
return true;
}
}

//isFull = eshtePlot

Me funksionin e definuar isFull(), vazhdojm definimin e funksionit push( ), si


n shembullin vijues. Funksioni push() shtyen vlern n stek (e vendos vlern e
re n stek). Vlera q shtyhet n stek, prcillet si argument n funksionin push()
dhe i ndahet variabls x (n kt shembull).
Para se t bj ndonj gj tjetr, funksioni push() e kontrollon se a ka vend n
stek, duke e thirrur funksionin isFull(), n shprehjen e kushtzimit if. Kjo
shprehje mund t duket pak e uditshme, pasi paraprihet nga shenja ! (shenja e
negacionit), mirpo kjo sht br pr t siguruar plotsimin e kushtit, pasi q
isFull() kthente false, kur kishte vend n stek. Pra, me logjikn e negacionit,
kthejm true, kur kemi vend n stek, dhe vazhdojm me shtytjen e elementit t
ri n stek.
Brenda urdhrit t kushtit if kemi dy urdhra. I pari, e inkrementon vlern e
atributit top, q sht indeksi i vlers s fundit t vendosur n stek. Nse steku
sht i zbrazt, ather vlera aktuale q sht n fillim -1, e bn vlern top 0, q
edhe sht indeksi i elementit t par t vargut t stekut. Urdhri tjetr n
bllokun e kushtit if ia ndan (prcakton) vlern e prcjellur n funksionin
push(), elementit t ardhshm n dispozicion t vargut.
149

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

Funksioni pop() i klass Stack ka pr detyr t bj ndryshimin e indeksit q


ndodhet n krye t stekut (top) dhe t kthej vlern e vargut korrespondues n
urdhrin q e thrret funksionin pop(). Shembulli vijues, e definon funksionin
pop().
Urdhri i par deklaron nj variabl t tipit integer, t quajtur retVal, e cila ruan
vlern e kthyer nga funksioni pop(). Vlera retVal inicializohet n zero.
Pastaj, thirret n shprehjen e kushtzimit if, thirret funksioni isEmpty(), pr t
prcaktuar nse ka vler n krye t stekut (n top). Vreni, prseri me negacion
(!), prmes logjiks s kundrt, veprohet si n rastin e funkionit push().
150

Algoritmet dhe strukturat e t dhnave


Urdhrat prbrenda urdhrit if duhet t ekzekutohen nse funksioni isEmpty()
kthen false, q do t thot se steku nuk sht i zbrazt.
Brenda kushtit if ndodhin dy hapa. S pari, vlera top i ndahet variabls retVal
duke iu referuar vlers s vargut prmes prdorimit t indeksit t prmbajtur n
atributin top. Pastaj, vlera e atributit top dekrementohet. Vlera retVal ather
kthehet prmes funksionit pop().
int pop()
{
int retVal = 0;
//retVal = Vlera qe kthehet
if(!isEmpty())
{
retVal = values[top];
top--;
}
return retVal;
}

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:

E mban ambientin zhvillimor (angl development environment) m t


pastr dhe m t leht pr tu kuptuar.
Ju mundson q t ju ofroni programerve t aplikacioneve softverike
komerciale vetm interfejsin, pa pasur nevoj tu jepni kodin tuaj
burimor. Ju i ofroni/siguroni programerit header fajllat e juaj, t cilt ata
do ti prdorin pr t kompajluar kodin e tyre (atyre ju duhen vetm
header fajllat pr t komapjluar kodin). Ju e ofroni kodin tuaj burimor n
form t librarive t prekompajluara t cilat referohen nga programet e
programerve t tjer gjat linkimit.

Definicioni i klass prmban prototipet e gjasht funksioneve antare.


Funksioni i par sht quajtur Stack, i cili sht konstruktori q u paraqit n
pjesn paraprake. M par u tregua se konstruktorit i prcillet nj integer i cili
prfaqson madhsin e stekut, size. N versionet reale, programi e vendos nj
vler t nnkuptuar (default) e cila mund t mbishkruhet kur instanca e klass
krijohet n program. Madhsia e nnkuptuar specifikohet duke prdorur
DEFAULT_SIZE, q sht 10 (e caktuar me #define).
Funksioni tjetr sht ~Stack() dhe sht destruktori i klass. Destruktori sht
funksioni i fundit q thirret kur instanca e klass del jasht fushveprimit dhe
vdes. Destruktori gjithmon duhet t ket emrin e njjt m at t klass dhe
t paraprihet nga shenja tilde (~). Sipas definicionit, destruktori nuk mund t
pranoj argumente. Qllimi i destruktorit sht t liroj memorien e prdorur
nga steku ose t bj ndonj lloj tjetr spastrimi,q mund t krkohet.
Funksionet tjera jan ato t njjtat q u paraqitn paraprakisht: isFull(),
isEmpty(), push() dhe pop().
152

Algoritmet dhe strukturat e t dhnave


//stack.h
#define DEFAULT_SIZE 10
class Stack
{
private:
int size;
int top;
int* values;
public:
Stack(int size = DEFAULT_SIZE);
virtual ~Stack();
bool isFull();
bool isEmpty();
void push(int);
int pop();
};

Fajlli stack.cpp sht fajlli i kodit burimor, q prmban implementimin e


funksioneve t klass Stack. sht vendosur n fajll tjetr prej definicionit t
klass sepse sht m leht t lexohet dhe t mirmbahet si dhe pr arsyet e
prmendura m hert.
Fajlli fillon me direktivn preprocesorike #include e cila i tregon kompjuterit q
t vlersoj prmbajtjen e fajllit stack.h para se t kompajloj fajllin stack.cpp
ashtu q ai t dij lidhur me definicionin e klass Stack para se t kompajlohet
programi.
Funksionet antare n fajllin stack.cpp jan t njohura (prve njrit) sepse jan
ato q u paraqitn n pjesn e prparshme. Sidoqoft, emrat e funksioneve n
shikim t par mund t duken t uditshme, sepse t gjitha fillojm me emrin e
klass t pasuar nga simboli :: (katr pika, ose dy dy-piksha). Ky simbol
njihet si scope resolution operator (operatori pr zbrthimin e fushveprimit)
dhe prcakton se n ciln klas sht deklaruar funksioni prkats (gjegjsisht
cils klas i prket funksioni).
Emri i funksionit duhet t paraprihet me operatorin :: nse funksioni definohet
jasht definicionit t klass. Pra, brenda klass deklarohet vetm protoptipi
(paralajmrimi se funksioni ndodhet n kt klas), kurse definicioni i plot i
funksionit (trupi i funksionit me urdhrat prkats) jepet jasht klass.
Mendojeni kt si mnyr pr ti terguar kompjuterit se funksioni i prket klass
prkatse, n kt rast klass Stack.
Funksioni ~Stack() (Destruktori) e liron memorien e prdorur nga steku. Ai e
bn kt duke prdorur operatorin delete dhe duke ju referuar emrit t vargut t
prdorur nga steku. N kt shembull, emri i vargut sht values.
153

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

Algoritmet dhe strukturat e t dhnave


{
if(!isFull())
{
top++;
values[top] = x;
}
}
int Stack::pop()
{
int retVal = 0;
if(!isEmpty())
{
retVal = values[top];
top--;
}
return retVal;
}

N fund, kemi programin stackDemo.cpp, i cili sht programi q krijon


instancn e klass Stack. Urdhri i par krijon stekun n proceson tre-hapsh.
Hapi i par sht prdorimi i operatorit ne pr t alokuar hapsirn n memorie
pr kalsn Stack, duke thirrur konstruktorin e ksaj klase. Operatori ne kthen
lokacionin e memories, t stekut. Hapi i dyt sht deklarimi i pointerit t
quajtur stack. Hapi i fundit sht ndarja e lokacionit t kthyer t memories nga
ana e operatorit ne, pointerit t stekut.
N kt shembull, kemi prdorur madhsin standarde pr stekun, q sht 10
elemente. Konstruktorit Stack() mund tia prcjellim nj vler integer pr t
ndryshuar madhsin e stekut.
Funksioni push() thirret tri her. Seciln her, n stek vendoset vler e
ndryshme. Vreni se n vend t dot operatorit sht prdorur pointeri ->. Kjo
duhet t bhet pr arsye se stack sht pointer n nj instanc t klass dhe jo
vet instanca.
Pjesa e fundit e programit stackDemo.cpp e thrret tri her funksionin pop()
(brenda unazs). Seciln her, vlera prkatse largohet nga kreu i stekut (top)
dhe paraqitet n ekran.
//stackDemo.cpp
void main() {
Stack *stack = new Stack();
stack->push(10);
stack->push(20);
stack->push(30);
for(int i=0; i<3; i++)

155

Avni Rexhepi
{
cout << stack->pop() << endl;
}
}

Implementimi i stekut t bazuar n vargje


Supozojm se kapaciteti i stekut sht i kufizuar n nj vler t caktuar dhe
mbingarkimi (tejmbushja) e stekut do t shkaktoj error (gabim). Megjithat,
duke pasur parasysh idet nga implementimi i vargjeve dinamike, ky kufizim
mund t tejkalohet lehtsisht, prmes mengaxhimit t kapacitetit t vargjeve
dinamike.
Pavarsisht nga kufizimi i kapacitetit, implementimi stekut t bazuar n vargje
prdoret gjersisht n praktik. N nj numr t rasteve, kapaciteti i krkuar i
stekut sht i ditur paraprakisht dhe hapsira e alokuar i prmbush saktsisht
krkesat e detyrs s veant. N rastet tjera, kapaciteti i stekut thjesht tentohet
t prcaktohet q t jet i mjaftueshm. Megjithat nj koncept i till sht
problematik, sepse nj rekurzion i thell mund t shkaktoj tejmbushjen e stekut.

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

Algoritmet dhe strukturat e t dhnave


int *storage;
public:
Stack(int capacity)
{
if (capacity <= 0)
throw string("Kapac. stekut duhet te jete pozitiv ");
storage = new int[capacity];
this->capacity = capacity;
top = -1;
}
void push(int value)
{
if (top == capacity)
throw string("Hapesira e stekut eshte tejmbushur ");
top++;
storage[top] = value;
}
int peek()
{
if (top == -1)
throw string("Steku eshte i zbrazet");
return storage[top];
}
void pop()
{
if (top == -1)
throw string("Steku eshte i zbrazet ");
top--;
}
bool isEmpty()
{
return (top == -1);
}
~Stack()
{
delete[] storage;
}
};

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:

Runtime stack prdoret nga proceset (programet gjat ekzekutimit) pr


t prcjellur funksionet n pun
Pr problemet e krkimit (angl. Search problems)
Pr operacionet undo, redo (zhbrja dhe ribrja e veprimeve,
nprograme t ndryshme), pastaj pr operacionet: back, forward
(lvizja para, prapa, p.sh., gjat shfletimit t eb-faqeve, n shfletuesit e
internetit etj).

Steku prdoret edhe nga compiler-et t cilt i testojne programet pr gabime


sintaksore. Shpeshhere, mungesa e nje simboli t vetm (p.sh., * / ose 1) bn q
kompajleri t kthej nj mori rreshtash te diagnozs pa e identifikuar gabimin
e vrtet.
Prmes stekut, kontrollohet edhe prmbajtja e programit, e urdhrave dhe
funksioneve. Duke i vendosur elementet hapse (kllapa e hapur) n stek, me
lehtsi mund t verifikojm se a ka kuptim simboli mbylls (kllapa e mbyllur).
N mnyr specifike, kemi algoritmin vijues:
1. Krijo nje stek te zbrazet
158

Algoritmet dhe strukturat e t dhnave


2. Lexo simbolet deri n fund t fjallit
a. Nes token-i sht simbol haps, shtyje n stek
b. Nse sht simbol mbylls dhe steku sht i zbrazt, raporto gabim.
c. Prndryshe, trhiqe stekun. Nse simboli i trhequr nuk i
prgjigjet simbolit haps, raporto gabim.
3. N fund t fajllit, nse steku nuk sht i zbrazt, raporto gabim.

Algoritmi i perdorur per te verifikuar simbolet e balansuara sygjeron nj mnyre


pr thirrjen e funksioneve. Problemi sht q kur thirret nj funksion i ri, t
gjitha variablat lokale pr n funksionin e thirrur duhet t ruhen nga sistemi;
prndryshe, funksioni i ri do ti mbishkruante variablat e rutins thirrse.
Pr m tepr, lokacioni aktual ne rutinen thirrse duhet t ruhet ashtu q
funksioni i ri t dije ku t shkoje/kthehet, pasi t ket mbaruar punn. Variablat
n pergjithsi jan caktuar nga kompajleri n regjistra t makins dhe konfliktet
mund t paraqiten.
Arsyeja perse ky problem sht i ngjashm me balansimin e simboleve sht
sepse thirrja e funksionit dhe kthimi i funksionit (return) jan n esenc t njjt
me kllapen e hapur dhe at t mbyllur, prandaj duhet aplikuar idet e njjta.
Nj aplikim tjetr i rndsishm i stekut sht vlersimi i shprehjeve n gjuht
programuese. N shprehjen 1+2*3, n pikn ku haset *, veq e kemi lexuar
operatorin + dhe operandet 1 dhe 2.
A operon * n 2, n 1 dhe 2? Rregullat e prioritetit na tregojne q * operon ne 2,
i cili sht operandi i par s fundi. Pasi t shohim 3, mund t vlersojm 2*3 si
6 dhe pastaj t aplikojm operatorin +.
Ky proces sygjeron se operandet dhe rezultatet intermediate duhet t ruhen ne
stek.
159

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.

N notacionin POSTFIX, operatori vendoset menjhere pas operandve t tij.


INFIX
POSTFIX
a+b
ab+
a+b*c
abc*+
a*b+c
ab*c+
(a + b) * c
ab+c*
N kt rast, pr vlersim mund t prdoret teknika Me laps/dor" (Teknika e
nnvizimit):
1. Skeno shprehjen nga e majta n t djatht, pr t gjetur nj operator.
160

Algoritmet dhe strukturat e t dhnave


2. Lokalizo (nnvizo") dy operandt paraprak dhe kombinoji ata me kt
operator.
3. Prsrit, deri sa t arrihet fundi i shprehjes.

Vlersimi i shprehjeve RPN bhet njsoj edhe prmes stekut:


Prmes algoritmit me stack
1. Inicializo stekun e zbrazt
2. Prsrit sa vijon deri sa t arrihet fundi i shprehjes
a) Merr token-in e ardhshm (const, var, operator) n shprehje
b) Operand push n stack
Operator bj sa vijon
i.
Pop 2 vlera nga stack-u
ii.
Apliko operatorin n dy vlerat (Vrejtje: nse ka mbetur
vetm 1 vler n stack, kjo RPN shprehje sht jo-valide)
iii. Push vlern rezultuese prapa n stack
3. Kur arrihet fundi i shprehjes, vlera e shprehjes sht numri i vetm i
mbetur n stack
Pra, procedura e thjesht sht:
Operand: push
Operator: pop 2 operand, llogarit rezultatin,
push rezultatin prapa n stack.
P.sh., pr shprehjen: 1 2 3 + *

161

Avni Rexhepi

Rreshti - Queue (Kju)


Queue (angl. queue rreshti, radha, radha e pritjes, etj. Lexohet/theksohet: Kju)
sht njsoj si radha e pritjes pr blerjen e biletave ose radha e pritjes pr
kryerjen e pagesave, n dalje t supermarketit. I pari q vjen, renditet n fillim t
radhs, i dyti pozicionohet pas tij e kshtu me radh deri tek klienti i fundit n
radh, n fund t radhs. Klientt, shrbehen sipas radhs me t cilln kan
arritur n rreshtin e pritjes. Kjo sht, i pari q vjen, i pari shrbehet, e njohur si
FIFO (angl. First In, First Out).
I njjti koncept aplikohet edhe n radhn (queue) n programim. Queue sht
nj organizim sekuencial i t dhnave. T dhnat qasen sipas parimit FIFO. Kjo
do t thot q, vlera e par n queue sht e vlera e par q sht e qasshme prej
programit. N fillim do t shohim radhn e thjesht, me madhsi fikse, e cila
realizohet prmes prdorimit t vargut. M von do t shohim mundsin e
realizimit t radhs me prioritet (angl. Priority Queue) e cila realizohet prmes
prdorimit t lists s lidhur. N radhn me prioritet, elementet largohen bazuar
n dy faktor: radha me t ciln jan vendosur n queue dhe prioritetit i
elementit.
Programert prdorin njrin prej llojeve t queue-ve varsisht prej objektivave t
programit, radh e thjesht apo radh me prioritet. Radha e thjesht i organizon
t dhnat n rend, ku elementi i par sht n fillim t rendit dhe elementi i
fundit sht n pjesn e prapme (n fund) t rendit. Secili element procesohet n
radhn n t ciln paraqitet n queue. Elementi i par n rend procesohet i pari,
pasuar nga i dyti, i treti dhe deri sa t procesohet elementi i fundit. Nuk ka
mnyr q nj element t prej rendin dhe t procesohet jasht radhs.
Radha me prioritet sht e ngjashme me radhn e thjesht n at se elementet
organizohen n rend dhe procesohen sekuencialisht. Mirpo, elementet n
radhn me prioritet mund t krcejn n fillim t rendit nse kan prioiritet m
t lart. Prioriteti sht nj vler q i shoqrohet secilit element n radh.
Programi e proceson radhn duke e skanuar at pr elementet me prioritet m t
lart. Kto procesohen t para, pa marr parasysh pozitn n rend. T gjitha
elementet tjetra pastaj procesohen sekuencialisht, pasi t jen procesuar
elementet me prioritet t lart.Tani pr tani do t mirremi me radhn e thjesht.
N botn reale, radht prdoren n programet t cilat procesojn transaksionet.
Transaksioni sht nj bashksi e informacioneve si p.sh., nj formular i
urdhresave. Informacioni pr transaksionin pranohet nga programi dhe pastaj
vendoset n nj radh t pritjes pr tu procesuar nga nj pjes tjetr e programit.
Nse i kthehemi radhs s pritjes pr pages n supermarket, arka e
kompjuterizuar e pagesave sht nj kompjuter q ekzekuton nj program t
162

Algoritmet dhe strukturat e t dhnave


transaksioneve, i cili ndr t tjera, proceson barkodin e secilit produkt t skanuar
n ark.
Nj prej hapave t par t procesimit t barkodit, sht krkimi i mimit. Mund
t ndodh q jan 10 ose m shum arka t pagesave n nj supermarket me
shum klient dhe t gjitha duke krkuar mime t produkteve, n t njjtn
koh. Mirpo, kompjuteri mund t procesoj vetm nj barkod n koh.
Programi q i krkon mimet menagjon krkesat duke prdorur nj radh t
thjesht t pritjes (queue) n t ciln secila krkes e re vendoset n fund t
lists dhe programi proceson barkodin q ndodhet n fillim t radhs s pritjes.
Shum aplikacione prdorin radhn e thjesht t pritjes pr t mirmbajtur
radhn n t ciln procesohen elementet. Kto prfshijn programet t cilat
procesojn bursat, hipotekat dhe ato q procesojn studentt t cilt regjistrohen
pr nj kurs. Radht e pritjes poashtu prdoren n kompjuter pr t menagjuar
shtypjen e dokumenteve n shtyps (printer).

Queue prmes vargjeve


T dhnat e organizuara n radh t pritjes mund t ruhen n nj varg. Queue
prcakton elementin q ndodhet n fillim t radhs dhe at n fund t radhs.
Vargu nuk sht radh e pritjes (queue) dhe as anasjelltas. Pra jan dy gjra t
veanta. Ky sht nj koncept q duhet kuptohet dhe zotrohet si duhet, edhe
pse fillimisht mund t duket e vshtir pr tu kuptuar.
Figura 3.5 sht nj ilustrim se si vargu dhe queue jan t ndryshm por
sidoqoft jan t ndrlidhur s bashku pr ti organizuar t dhnat. Vargu
(realiteti fizik n memorie) vizatohet si bllok i elementeve. Queue (koncepti
logjik) sht vizatuar si rreth. Fushat (kutit) e zbrazta jan lokacionet ku ruhen
vlerat n queue dhe numrat rendor i korrespondojn indeksave t vargut q
sht i shoqruar me queue. N t djatht t rrethit jan paraqitur tri vlera. Vlerat
front (fillimi) dhe back (fundi) ruajn indeksat e fillimit dhe fundit t radhs
(queue). Vlera size sht numri i elementeve n queue, q n kt rast sht 8.

163

Avni Rexhepi

Figura 3.5 - Queue sht i ndryshm nga vargu q prdoret pr t ruajtur t


dhnat q ndodhen n queue.

Enqueue (vendose n radh)


Nj vler vendoset n queue duke kryer procesin enqueue (enkju vendosja n
radh), i cili prbhet prej dy hapave. Hapi i par sht q t identifikohet
elementi i vargut q sht n fund t queue. Mirpo, ky nuk sht
domosdoshmrisht elementi i fundit t vargut. Rikujtoni, queue nuk sht vargu.
Fundi (back) i queue-s llogaritet duke prdorur formuln vijuese:
back = (back+1) % size
Figura 3.6 paraqet se si prdoret formula dhe jep vlerat pr front (fillimi), back
(fundi) dhe size (madhsia) t queue-s. Variablat front dhe back jan vendosur
n zero sepse queue sht i zbrazt dhe size sht 8, sepse vargu ka 8 elemente.

164

Algoritmet dhe strukturat e t dhnave

Figura 3.6 - Procesi enqueue vendos vlern e re n fund (back) t


radhs(queue).
Fusha e ardhshme paraqet formuln q identifikon fundin (back) e queue-s dhe
ia ndan atij vlern 90. N t djatht t ksaj fushe sht formula e njjt ku
emrat e variablave jan zvendsuar me vlerat aktuale. Le t shohim s afrmi
se si sht llogaritur vlera back e radhs s pritjes (queue).
Opercioni i par ndodh brenda kllapave, ku 1 i shtohet vlers s variabls back.
Operatori i modulit prcakton se ku duhet t vendoset elementi i ardhshm n
queue duke br pjestimin e plot dhe duke kthyer mbetjen nga pjestimi i plot.
Edhe pse m par kemi thn se queue sht si radha e pritjes n supermarket,
aktualisht queue sht rrethor. Kjo sht ilustruar n llogaritjen e prdorur pr t
prcaktuar vlern back t queue-s, si n vijim:
(7 + 1) % 8
Kur arrini n elementin e fundit t vargut, me indeks 7, llogaritja kthen 0 (8
pjestuar me 8 sht 1 dhe mbetja 0). Kshtu, pas elementit last n varg, sillemi
dhe arrijm n fillim t vargut, si fund (back) i radhs (queue). Si do t shihet n
vazhdim, para se t vendoset nj element n fund (back) t radhs s pritjes
165

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.

Dequeue (nxjerrja nga queue)


Dequeue sht procesi i largimit t vlers nga fronti (fillimi) i queue-s. sht me
rndsi t kuptohet q vlera largohet prej radhs (queue), jo prej vargut. Vlera
gjihmon mbetet n varg deri sa ajo vler ose t mbishkruhet ose queue t
braktiset. Do ta shohim m von se si t mbishkruhet vlera.
N procesin e largimit jan dy hapa, si sht ilustruar n figurn 3.7. Hapi i par
sht llogaritja e indeksit t elementit t vargut n fillim t radhs, duke
prdorur shprehjen vijuese:
front = (front+1) % 8

Figura 3.7 - Procesi dequeue largon nj element nga front-i i queue.


Vreni se shprehja sht shum e ngjashme me shprehjen e prdorur n procesin
vendosjes n radh, pr llogaritjen e indeksit t elementit t vargut n fund t
166

Algoritmet dhe strukturat e t dhnave


radhs. Operacioni i par n kt shprehje e inkrementon vlern e variabls
front (fillimi). Si mund t shihet n fig. 3.7, variabls front n fillim i sht
ndar vlera zero. Prandaj, rezultati i operacionit t par sht 1. Operacioni i
ardhshm sht aplikimi i operatorit t modulit, i cili sht i njjt me at t
aplikuar n procesin e vendosjes (enqueue). Rezultati i ktij operacioni sht 1,
q do t thot se front-i i radhs sht elementi i vargut q ka indeksin 1. Kjo
vler pastaj i ndahet variabls front. N fillim, e pam se nse ndodhemi n
indeksin 7 t vargut, rezultati i llogaritjes do t ishte 0 ((7+1)%8=0), kshtu q
do t silleshim npr rreth.
Hapi i fundit n procesind dequeue sht prdorimi i vlers s lokalizuar n
front. N mnyr tipike, procesi deque sht funksion (metod) dhe vlera front e
radhs i kthehet urdhrit i cili e thrret funksionin (metodn).
N figurn 3.7, elementi i vargut values[1] sht n front (fillim) t queue-s
(radhs). Vlera e ndar pr kt element sht 90, q ishte vendosur n back
(fund) t radhs (queue-s), nga procesi i mparshm enqueue (i vendosjes n
radh).
Vreni q velra 90 mbetet e caktuar n elementin values[1] t vargut n fig. 3.7,
sepse vlerat e caktuara t vargut, t shoqruara me queue nuk ndikohen kur vlera
largohet nga fronti (fillimi) i queue-s (radhs). Queue prcjell elementet e
vargut t cilat ndodhen n front (fillim) dhe n back (fund) t queue-s, e jo n
fillim dhe n fund t vargut. N kt rast, jemi duke prdorur varg t thjesht me
numra t plot (integer-a) pr t ilustruar principet e implementimit t strukturs
queue. Mund t hasen edhe implementime m komplekse, ku secili element n
varg sht pointer n objektin e klass ose strukturs. N kto raste, duhet t
kujdeseni pr menagjimin e memories gjat kryerjes s operacioneve enque dhe
deque (vendosja n radh dhe largimi nga radha e pritjes).

Queue prmes vargut n C++


Tani q e pam se si punon radha e pritjes (queue) prmes vargut, le t shohim
krijimin e queue n C++.
Programi sht i organizuar n tre fajlla: queue.h, queue.cpp dhe
queueProgram.cpp. Fajlli queue.h, cakton madhsin standarde t vargut dhe
definon klasn Queue. Klasa Queue deklaron atributet size, front dhe back, t
cilat ruajn madhsin e vargut dhe indeksin e elementeve front dhe back t
queue-s (radhs). Klasa Queue gjithashtu deklaron nj pointer q do t pointoj
n varg. Prveq ktyre, klasa Queue definon edhe funksionet antare t cilat
manipulojn queue-t (radht).

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
};

Fajlli queue.cpp prmban implementimin e funksioneve antare pr klasn


Queue. Jan gjasht funksionet t definuara n kt fajll: Queue(), ~Queue(),
isFull(), isEmpty(), enqueue() dhe dequeue().
Funksioni Queue() sht konstruktor, t cilit i prcillet madhsia (size) e vargut
kur t deklarohet nj instanc e klass Queue. Nse konstruktori thirret pa
parametra, ather prdoret madhsia standarde (default), prndryshe, prdoret
vlera e cila i prcillet konstruktorit. Vlera e madhsis s vargut i ndahet
atributit size prmes urdhrit t par n konstruktor.
Urdhri i dyt e prdor operatorin new pr t deklaruar nj varg t numrave t
plot (integer-ave), madhsia e t cilit prcaktohet nga vlera size q i prcillet
konstruktorit. Operatori new kthen pointerin n varg, i cili i ndahet pointerit t
vlerave (values). Dy urdhrat e fundit n konstruktor, inicializon atributet front
dhe back, n zero.
Funksioni ~Queue() sht destruktori dhe prdor operatorin delete pr t
larguar vargun nga memoria, kur instanca e Queue-s del nga fushveprimi
(prdorimi).
Funksioni isFull() (shih Figurn 3.8) prcakton nse ka vend n queue (radh),
duke krahasuar vlern e llogaritur back me vlern e front-it t queue-s
(radhs), si n fig. 3.8. Vreni se shprehja q llogarit back sht shum e
ngjashme me shprehjen e prdorur n procesin enqueue dhe t dyja prodhojn
rezultat t njjt. Queue sht i mbushur (angl. full), kur indeksi back sht 1
prapa front. Vendosja e nj elementi tjetr n queue do t mbishkruante
elementin front dhe do t korruptonte (prishte) queue-n (radhn e pritjes).
Operatori i modulit prdoret prsri pr ta br kt circular queue (radh
168

Algoritmet dhe strukturat e t dhnave


rrotulluese, rrethore, qarkore), ashtu q kur jemi n elementin 7 n back,
elementi i ardhshm n t cilin duhet t shikohet sht elementi 0.

Figura 3.8 - Funksioni isFull() prcakton (kontrollon) nse ka vend pr nj


tjetr element n fund (back) t queue-s.
Funksioni isFull() thirret nga funksioni enqueue() para se t tentohet vendosja e
vlers n back (fund) t queue-s (radhs). Funksioni isFull() kthen true nse
nuk ka m vend n queue ose false nse ka vend t lir, n dispozicion.
Funksioni isEmpty() prcakton (shiko Figurn 3.9) nse queue sht i zbrazt
duke krahasuar variablat back dhe front. Nse ato kan vlera t njjta,
kthehet true; prndryshe, kthehet false. Funksioni isEmpty() thirret brenda
funksionit dequeue() para se ai t tentoj t largoj elementin front nga queue
(radha).

169

Avni Rexhepi

Figura 3.9 - Funksioni isEmpty() prcakton (kontrollon) nse queue ka vlera.


Funksioni enqueue() e vendos nj element n fund t radhs. Funksionit
enqueue() i prcillet vlera q duhet t vendoset n radh. Mirpo, para se t
bhet kjo gj, thirret funksioni isFull(), pr t kontrolluar nse ka vend n radh.
Vreni n shembullin vijues se funksioni isFull() thirret si shprehje e
kushtzimit (kusht) n urdhrin if. Poashtu, vreni se operatori i negacionit e
ndryshon n t kundrt vlern bool-ane t kthyer nga isFull(). Do t thot, nse
ka vend t lir n radh, nga funksioni isFull() kthehet false. Shperhja e
kushtzimit n urdhrin if e rrotullon logjikn n true ashtu q urdhrat t
ekzekutohen urdhrat brenda urdhrit if, pr t vendosur elementin e ri n fund
t radhs.
Funksioni dequeue() e largon nj element nga radha e pritjes dhe kthen (return)
at element n urdhrin e programit i cili e thrret funksionin dequeue().
Mirpo, prbrenda funksionit dequeue(), n shprehjen e kushtzimit t urdhrit
if, thirret funksioni isEmpty(), si n kodin vijues.
Operatori i negacionit (!-not) e rrotullon logjikn e kthyer nga funksioni
isEmpty(). Funksioni isEmpty() kthen false nse radha nuk sht e zbrazt.
Operatori e kthen at n true, duke u mundsuar urdhrave brenda kushtit if

170

Algoritmet dhe strukturat e t dhnave


q t largojn elementin front nga radha dhe tia kthejn at urdhrit q e
thrret funksionin dequeue().
//queue.cpp
#include "queue.h"
Queue::Queue(int size)
{
this->size = size;
values = new int[size];
front = 0;
back = 0;
}
Queue::~Queue()
{
delete[] values;
}
bool Queue::isFull()
{
if( (back+1) % size == front)
{
return true;
}
else
{
return false;
}
}
bool Queue::isEmpty()
{
if(back == front)
{
return true;
}
else
{
return false;
}
}
void Queue::enqueue(int x)
{
if(!isFull())
{
back = (back+1) % size;
values[back] = x;
}

171

Avni Rexhepi
}
int Queue::dequeue()
{
if(!isEmpty())
{
front = (front+1) % size;
return queue[front];
}
return 0;
}

Programi queueProgram.cpp sht vendi ku ndodhin veprimet. Ktu deklarohet


dhe manipulohet instanca e klass Queue. Si mund t shihet n shembullin
vijues, urdhri i par i programit prdor operatorin new pr t deklaruar
instancn e klass Queue dhe pr t caktuar madhsin (size) n 8 elemente.
Operatori new kthen nj pointer q i ndahet nj pointeri n instanc t klass
Queue.
Tre urdhrat vijues e thrrasin tri her funksionin enqueue(), pr t vendosur
vlerat 10, 20 dhe 30 n queue, respektivisht. Programi prfundon me thirrjen e
funksionit dequeue() tri her, pr t paraqitur prmbajtjen e queue-s (radhs s
pritjes). Figura 3.10 paraqet queue-n (radhn) dhe vargun, pas thirrjes s fundit
t funksionit enqueue().

Figura 3.10 - Queue dhe vargu pas thirrjes s fundit t funksionit enqueue().
172

Algoritmet dhe strukturat e t dhnave

//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.

Figura 4.1: Studentt jan ulur n renditje t rastit


Le t themi se profesori duhet t vendos emrat e studentve n renditje
alfabetike, ashtu q t mund ti gjej m leht emrat n list. Nj opcion sht q
studentt t ndryshojn vendet dhe t ulen me radh sipas alfabetit. Mirpo, kjo
mund t jet situat problematike, ns sht n pyetje nj numr i madh i
studentve n nj klas.
Nj opcion tjetr sht q studentt t mbesin t ulur n vendet e tyre, por t
krijohet nj list e numrave t ulseve, t cilat i korrespodojn renditjes
alfabetike t studentve. Lista do t dukej dika si: 3, 1, dhe 2 (ashtu si sht
paraqitur n fig. 4.1). Studenti n ulsen 3, sht i pari n listn alfabetike, i
pasuar nga studenti n ulsen 1, e kshtu me radh. Vreni se ky opsion nuk
shkakton shqetsime pr klasn.
Supozojm se dshirojm t rirendisim studentt, sipas madhsis (gjatsis).
Prsri, kemi mundsi q t mos i lvizim studentt npr klas, mirpo do t
174

Algoritmet dhe strukturat e t dhnave


krijojm nj list tjetr t numrave t ulseve t cilat reflektojn gjatsin e secilit
student. Tani lista sht: 2, 3 dhe 1 (fig. 4.1). Lista mund t lexohet prej fundit
kah fillimi, pr renditjen prej gjatsis m t vogl kah ajo m e madhe dhe
anasjelltas, prej t gjats kah e shkurta.
Kur t krijohet nj her lista, profesori mundet thjesht t kaloj me radh npr
list, pr t par se cila ulse e ka studentin e ardhshm. Pr ti pyetur studentt
n baz t renditjes alfabetike, profesori do t prdorte listn alfabetike, pr t
par q studenti n ulsen 3 sht i pari, i pasuar nga ai n ulsen 1. Mund t
fillohet edhe nga ana e kundrt dhe pastaj t krkohet studenti paraprak n list.
Ky lloj i listave n programim njihet si list e lidhur, sepse secili element n list
sht i lidhur me elementin paraprak dhe at t ardhshm. Kjo do t thot q,
ulsja e studentit aktual sht e lidhur me ulsen e studentit t prparshm dhe
at t atij t ardhshm, n list.
Prderi sa msohen listat e lidhura, sht me rndsi t kihet parasysh situata
n botn reale, sepse prndryshe mund t fitohet ideja se lista e lidhur sht nj
koncept abstrakt q ka pak prdorim n botn reale. N fakt, listat e lidhura
luajn rol kritik n aplikacionet t cilat u mundsojn shum kompanive dhe
qeverive, menagjimin dinamik t t dhnave.
Kemi dy versione t listave t lidhura: lista e lidhur njfish (angl. single link, ose
singly linked list) dhe lista e lidhur dyfish (angl. doble link, ose doubly linked
list). Lista e lidhur njfish i mundson programit q t lviz npr list n nj
drejtim (kahje), i cili zakonisht sht prej fillimit t lists, kah fundi i lists, apo
si thuhet ndryshe, lvizje para. Lista e lidhur dyfish, i mundson programit q t
lviz npr list n t dy drejtimet (kahjet), apo si thuhet ndryshe, lvizje para
dhe prapa.
Edhe pse sht thn q nj hyrje (element, nyje), n listn e lidhur prmban
t dhnn (vlern) dhe pointert pr n elementin e prparshm dhe at t
ardhshm n list, ky sht nj thjeshtim i tepruar. T dhnat pr t cilat e kemi
fjaln, zakonisht jan nj bashksi e t dhnave, si p.sh, t dhnat e klientit. T
dhnat e klientit mund t jen: ID e klientit, emri, mbiemri, adresa, qyteti, shteti,
kodi postal, etj. Programert e quajn kt record (rekord). Kjo do t thot se
nj element n list, mund t prmbaj disa t dhna. N shembullin n vijim,
sidoqoft, do t ruhet vetm nj vler numerike e tipit integer, ashtu q t
fokusohemi n principet e puns s listave t lidhura. N realitet, pr seciln
nyje n list mund t shtohen atribute shtes sipas dshirs ose sipas nevojs.
Programert e zgjedhin listn e lidhur ndaj vargut, sepse lista e lidhur mund t
zgjerohet (rritet, zgjatet) ose ngushtohet (zvoglohet, shkurtohet) pr nga
madhsia gjat kohs s ekzekutimit. Nj element i ri mund t vendoset n fund
t lists s lidhur thjesht duke ia ndar (caktuar) nj referenc (pointer) pr n
175

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

Algoritmet dhe strukturat e t dhnave


Ather, elementet e vargu, kopjohen n lokacionin e ri. Ky proces ka koston e
vet.
Nse ndryshohet madhsia e lists s lidhur, sistemi operativ vetm ndryshon
referencat (pointert) pr elementin e prparshm dhe at t ardhshm n list,
q sht numr shum m i vogl i hapave sesa ndryshimi i madhsis s vargut.

Struktura e lists s lidhur


Secili element n listn e lidhur quhet node (angl. node nyje). Paramendojeni
nyjn si nj element me tri nnelemente. Njri prmban vlern, e cila mund t
jet nj atribut ose shum atribute. Tjetri pointon n nyjen e prparshme dhe i
fundit pointon n nyjen e ardhshme. Kur vendoset nj element i ri n listn e
lidhur, alokohet nyja e re dhe caktohen pointert pr nyjen e prparshme dhe at
t ardhshme.
N C++ nyja krijohet duke prdorur objektin struktur (struct) ose klas (class).
N shembullin n vazhdim, pr krijimin e nyjes do t prdoret struktura, e cila si
dihet sht tip i t dhnave i definuar prej shfrytzuesit. Nyja sht paraqitur n
figurn 4.2.

Figura 4.2 - Nyja prmban pointerin pr nyjen e prparshme dhe pointerin pr


n nyjen e ardhhsme n listn e lidhur, si dhe prmban t dhnat e shoqruara
me nyjen aktuale.
struct Node
{
int data;
Node* previous;
Node* next;
};

struct Nyje
{
int vlera;
Nyje* ePerparshme;
Nyje* eArdhshme;
};

Mund t duket e uditshme sepse n kt shembull, struktura n dy prej


atributeve, ka pointerin n vet strukturn. Elementi i par deklaron nj integer
177

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.

Lista e lidhur njfish ndaj lists s lidhur dyfish


Lista e lidhur dyfish quhet edhe bidireksionale (angl. bidirectional-dy
drejtimshe, figura 4.3), sepse secila nyje prmban pointer n nyjen e
prparshme dhe t ardhshme n listn e lidhur. Kjo i mundson programere q
t prshkojn listn e lidhur n t dy drejtimet duke ju referuar nyjes s
prparshme dhe asaj t ardhshme. Lista mund t transformohet n list t lidhur
njfish (Fig. 4.3) duke pasur vetm nj pointer n struktur, q prmban
adresn e nyjes s ardhshme. N mnyr tipike, nyja e lists s lidhur njfish i
referohet vetm nyjes s ardhshme, e jo edhe asaj t prparshme, edhe pse asgj
nuk e ndalon q t krijohet vetm referenca prapa, duke prdorur vetm
pointerin pr nyjen e prparshme.

178

Algoritmet dhe strukturat e t dhnave

Ose:

Figura 4.3 - Listat e lidhura. Lista e lidhur dyfish prmban dy pointera,


ndrsa ajo e lidhur njfish vetm nj pointer, pr n nyjen e ardhshme.
Deklarimi vijues sht afrsisht i njjt me t prparshmin, prveq se nyja sht
e lidhur vetm n nj drejtim, sepse mungon pointeri n nyjen e prparshme.
Kjo do t thot se mund t lvizet vetm teposht lists s lidhur, e jo n t dy
drejtimet.
struct Node
{
int data;
Node* next;
};

struct Nyje
{
int vlera;
Nyje* eArdhshme;
};

Pr shembull, pr krijimin e nj nyjeje t vetme, mund t veprojm si vijon:


179

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

Nse e konsiderojm nj nyje t


vetme si list me vetm nj antar,
pasi q pr listn zakonisht
definohen edhe dy pointer t cilt e
prcjellin fillimin dhe fundin e lists,
do t kishim situatn kur t dy
pointert pointojn n nyjen e njjt
(t vetme), si n vijim:

fillimi

10

Nse insertojm edhe nj nyje t re, me vlern 20, do t kemi:


fundi (eFundit)

fillimi (ePara)

180

10

20

eArdhshme

eArdhshme

Algoritmet dhe strukturat e t dhnave


Pjesa e kodit pr krijimin e dy nyjeve do t ishte si vijon:
...
nyje *n,*ePara=NULL,*eFundit=NULL;
n=new nyje;
//krijojm nyjen e re (t parn)
n->vlera=10;
//tek nyja e par, vlera = 10
n->eArdhshme=NULL;
//pointeri pr n nyjen e ardhshme (NULL)
ePara=n;
//Pointer i nyjes s par t lists=nyja e par
eFundit=n;
// Pointer i nyjes s fundit t lists=nyja e par
n=new nyje;
// Shtojm nj nyje t re (t dytn)
eFundit->eArdhshme=n;
//nyja e ardhshme e t pars=nyja e re
n->vlera=20;
//vlera = 20
n->eArdhshme=NULL;
//pointeri pr n nyjen e ardhshme (NULL)
eFundit=n;
//Fundi i lists bhet nyja e re (e dyta)
//Pointer i nyjes s fundit t lists=nyja e re (e dyt)
...

N mnyr t ngjashme, mund t shtojm nyje t reja, duke krkuar hapsir t


re nga memoria, me operatorin ne dhe duke marr pointerin n pr at
lokacion t memories. Pasi t shtohet nyja e re, vetm ndryshojm pointert, q
t pointojn n nyjet adekuate.
Pr t shtypur antart e lists, fillojm nga nyja e par dhe pastaj prmes
pointerve, kalojm me radh npr nyjet tjera. Zakonisht, kt e bjm prmes
nj funksioni, i cili e ka argument (parametr) adresn e nyjes s par dhe pastaj
vazhdon prej nyjes n nyje, deri n fund t lists (duke verifikuar a sht
pointeri pr nyjen e ardhshme i ndryshm prej 0, gjegjsisht NULL, (n!=0) ).
Deri sa vlen kushti, q pointeri pr nyjen e ardhshme nuk sht null, do t thot
kemi nyjet tjetr n vazhdim:
...
void ShtypeListen(nyje *ePara)
{
nyje *n=ePara;
while (n!=0)
//Gjersa ka nyje t tjera n vazhdim
{
cout << Vlera
: << n->vlera << endl;
cout << eArdhshme: << n->eArdhshme<< endl;
n=n->eArdhshme;
//kalojm n nyjen e ardhshme
}
cout << endl;
}
...

Pr t krijuar m shum nyje, mund t prdorim unaz si n shembullin vijues:

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

Algoritmet dhe strukturat e t dhnave


cout << "------------------------------------------\n";
ShtypeListen(ePara);
system("Pause");
return 0;
}
void ShtypeListen(nyje *ePara)
{
nyje *n=ePara;
while (n!=0)
{
cout<<setw(10)<<n<<setw(5)<<n->vlera<<setw(15)<<n->eArdhshme<<endl;
n=n->eArdhshme;
}
cout << endl;
}

Klasa linked list


N parim, pr menagjimin e lists s lidhur, n C++ krijohet klasa LinkedList.
Definicioni i klass LinkedList prbhet prej dy antarve t t dhnave dhe
gjasht funksioneve antare, si sht paraqitur n shembullin n vijim. Dy
antart e t dhnave jan pointer pr n instancat e strukturs Node (Nyja) e
cila u definua m hert. Pointeri i par, front (fillimi) i referohet nyjes s par n
listn e lidhur. Pointeri i dyt, back (fundi), i referohet nyjes s fundit n listn e
lidhur.
Gjasht funksionet shrbejn pr manipulimin e lists s lidhur. Funksioni i par
sht konstruktori i klass LinkedList dhe thirret kur t deklarohet instanca e
klass. Pas konstruktorit sht destruktori. Pr tia kthyer memorien sistemit
operativ, prmes prdorimit t operatorit delete, thirret destruktori. Nse nuk
thirret operatori delete, ather destruktori nuk thirret asnjher dhe
aplikacioni shkakton rrjedhje t memories (memory leak).
Funksioni appendNode() (angl. append shto, bashkangjit) e vendos nyjen e re
n fund t lists s lidhur. Funksioni appendNode() krkon nj integer q
prfaqson vlern aktuale t nyjes (sepse n kt shembull, tham q elementi i
lists s lidhur sht nj numr i plot, integer).
Dy funksionet e ardhshme paraqesin prmbajtjen e lists s lidhur. Funksioni
displayNodes() paraqet listn e lidhur n renditjen natyrale (prej fillimit kah
fundi). (angl. display paraqes, shfaq). Funksioni displayNodesReverse()
paraqet listn e lidhur n renditje t kundrt.
Funksioni i fundit sht destroyList() (angl. destroy-shkatrro, rrno, asgjso)
dhe thirret pr t larguar instancn e lists s lidhur nga memoria.

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
};

Konstruktori dhe destruktori i klass LinkedList


Konstruktori LinkedList sht funksioni i cili thirret (ekzekutohet automatikisht)
kur deklarohet ndonj instanc e klass LinkedList. Qllimi i konstruktorit sht
q t inicializoj pointert front dhe back, si sht treguar n definicionin
vijues. T dy pointerve u ndahet velra NULL, e cila prdoret nga funksioni
appendNode() pr t kontrolluar (prcaktuar) nse lista e lidhur sht e zbrazt.
M von do t shohim se si bhet kjo.
LinkedList()
{
front = NULL;
back = NULL;
}

Destruktori sht funksioni q thirret kur t fshihet instanca e klass, prmes


prdorimit t operatorit delete. N shembullin n vijim, destruktori prmban
nj urdhr i cili e thrret funksionin destroyList().
Funksioni destroyList() e fshin prmbajtjen e lists s lidhur por nuk e fshin
vet listn e lidhur (strukturn e saj). Do t thot, i largon t gjitha nyjet nga lista
e lidhur. Funksioni destroyList() i reseton (rivendos) pointert front dhe
back n NULL, duke treguar se lista sht e zbrazt. Destruktori sht
prgjegjs pr dealokimin e memories q sht alokuar pr listn e lidhur. N
kt rast, t gjitha nyjet.

184

Algoritmet dhe strukturat e t dhnave


Mund t pyesni veten, e prse definohen dy funksione pr kryerjen e parimisht
t njjts pun? Kjo pr arsye q t mundesohet zbrazja e lists ashtu q t
resetohet prmbajtja e lists s lidhur pa e shkatrruar instancn e klass
LinkedList.
~LinkedList()
{
destroyList();
}

Shtimi i nyjes n list


Funksioni appendNode() e vendos nj nyje t re n fund t lists s lidhr. Jan
disa hapa q duhet kryer pr t shtuar nyjen n list. Kta hapa jan paraqitur n
definicionin vijues t funksionit appendNode():
void appendNode(int data)
{
Node* n = new Node(data);
n->data=data;
if(back == NULL)
{
back = n;
front = n;
}
else
{
back->next = n;
n->previous = back;
back = n;
}
}

Funksioni appendNode() krkon nj argument, t quajtur data, i cili sht vlera


aktuale pr nyjen. Argumenti i prcillet instancs s strukturs Node. Si ju
kujtohet nga pjesa paraprake, vlera e prcjellur n strukturn Node i ndahet
elementit t t dhnave (data) t nyjes.
Urdhri i par n funksionin appendNode(), deklaron nj instanc t strukturs
Node duke prdorur operatorin new, i cili kthen pointerin n instanc, i cili n
ann tjetr i ndahet variabls pointer t quajtur n.
Kur t krijohet nyja e re, funksioni appendNode() e pozicionon nyjen e re n
listn e lidhur. S pari, ai e kontrollon nse lista e lidhur sht e zbrazt, duke
krahasuar nyjen back me NULL. Kjo pasi nyja back caktohet n NULL kur
185

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.

Figura 4.4 - Funksioni appendNode() ndryshon cilat jan nyjet e pointuara n


listn e lidhur
186

Algoritmet dhe strukturat e t dhnave


Hapi i par ia ndan adresn e memories s nyjes s re antarit next t nyjes
back, i cili sht paraqitur n bllokun e dyt t memories n figurn 4.4.
Hapi i dyt, i ndan adresn e memories s nyjes back antarit previuous (i
prparshmi) t nyjes s re (new). Kjo i lidh t dy nyjet.
Hapi i tret, e zvendson adresn e memories s nyjes back n listn e lidhur
me adresn e memories s nyjes s re (new). Kjo e vendos nyjn e re n n
fund t lists s lidhur.

Paraqitja e lists s lidhur


Funksioni displayNodes(), paraqet seciln nyje t lists s lidhur, duke filluar
me nyjen n fillim t lists s lidhur dhe duke prfunduar me nyjen n fund t
lists, si n vijim:
void displayNodes()
{
cout << "Nyjet:";
Node* temp = front;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->next;
}
}

Funksioni displayNodes() fillon me paraqitjen e fjals Nyjet: n ekran dhe


pastaj deklaron pointerin pr n nyje, i cili inicializohet me nyjen q paraqitet n
fillim (front) t lists s lidhur.
Para se t tentoj t paraqes vlern q i sht ndar nyjes, funksioni
displayNodes() kontrollon nse ka nyje n fund (back) t lists s lidhur. E bn
kt, duke prcaktuar nse nyja e pointuar nga pointeri temp sht NULL.
Ns po, lista e lidhur sht e zbrazt dhe aty nuk ka asgj pr t shfaqur. Nse
jo, funksioni vazhdon dhe paraqet t dhnat e ndara pr nyjen q ndodhet n
fund t lists s lidhur.
Pastaj paraqitet nj zbraztir, e prcjellur me vlern (t dhnn) q i sht ndar
nyjes. Funksioni displayNode() prdor antarin next t nyjes pr tia ndar
pointerin n nyjen e ardhshme pointerit temp.
Ky proces prfundon pasi t paraqitet nyja n fund t lists s lidhur, pasi q
antari next i saj sht NULL.

187

Avni Rexhepi

Rrotullimi i lists s lidhur


Funksioni displayNodesReverse() paraqet prmbajtjen e lists s lidhur n
renditje t kundrt, duke filluar nga nyja n fund t lists s lidhur dhe duke
vazhduar deri sa t paraqitet nyja e par. Shembulli vijues tregon se si realizohet
kjo:
void displayNodesReverse()
{
cout << "Nyjet n renditje t kundert:";
Node* temp = back;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->previous;
}
}

Mund t vrehet se funksioni dispalyNodesReverse() sht afrsisht i njjt me


funksionin displayNodes(), t prshkruar m hert. Mirpo, jan dy dallime t
rndsishme ndrmjet ktyre dy funksioneve antare. Funksioni
displayNodesReverese() ia ndan pointerin n nyjen n fund t lists, pointerit
temp, duke br q nyja n fund t lists t paraqitet e para. Funksioni
displayNodes() e cakton pointerin back n pointerin temp, duke shkaktuar
paraqitjen e nyjes s fundit n listn e lidhur.
Dallimi tjetr ndrmjet funksioneve displayNodesReverse() dhe atij
displayNodes() sht se n funksionin displayNodesReveres(), antari i
prparshm (previuous) i nyjes prdoret pr t prcaktuar nyjen e ardhshme pr
tu paraqitur. Kjo mundson paraqitjen e nyjeve n renditje t kundrt (inverse).
Figura 4.5 ilustron se si rrotullohet lista e lidhur.

Figura 4.5 - Antari i prparshm (previuos) i secils nyje, e rrotullon listn e


lidhur.

188

Algoritmet dhe strukturat e t dhnave

Asgjsimi i lists s lidhur


Funksioni destroyList() i largon nyjet nga lista e lidhur pa e larguar vet listn e
lidhur, si n shembullin vijues. Secila nyje deklarohet n mnyr dinamike duke
prdorur operatorin new, si sht treguar m hert. Kjo mundson largimin e
nyjes prmes prdorimit t operatorit delete.
void destroyList()
{
Node* temp = back;
while(temp != NULL)
{
Node* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}

Funksioni destroyList() fillon duke deklaruar pointerin e prkohshm (temp),


t cilit i ndahet pointerin n nyjen e cila ndodhet n fund (back) t lists s
lidhur. Mirpo, para se nyja t largohet, funksioni e prcakton (kontrollon) nse
ka nyje n fund t lists s lidhur, duke testuar nse pointeri temp sht NULL.
Nse sht kshtu, ather funksioni destroyList() supozon se nuk ka nyje n
listn e lidhur. Nse pointeri temp nuk sht NULL, ather funksioni vazhdon
me fshirjen e nyjes.
Deklarohet nj tjetr nyje e prkohshme (temp2) dhe i ndahet pointeri n nyjen e
pointuar nga nyja e prkohshme, temp. Kjo bhet sepse pointerit temp i
caktohet (ndahet) nyja tjetr (next) q duhet t fshihet nga lista e lidhur n
urdhrin e ardhshem.
Pointeri pr n nyjen tjetr (next) ndodhet n antarin tjetr (next) t nyjes s
prkohshme (temp), e cila pastaj i ndahet pointerit temp. Kjo do t thot se
temp2 pointon n nyjen n fund t lists s lidhur dhe temp tani pointon n
nyjen q ndodhet menjher para (previous) nyjes n fund (back) t lists s
lidhur. Ather nyja e pointuar nga temp2 fshihet, si sht ilustruar n
Figurn 4.6

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.

Listat e lidhura n C++


Tani q i njohim pjest e lists s lidhur dhe mnyrn e krijimit dhe manipulimit
t saj prmes prdorimit t klass, do t bashkojm t gjitha pjest n nj
aplikacion t C++-it i cili prdor listn e lidhur.
Lista e lidhur organizohet n tre fajlla. I pari sht header fajlli i cili prmban
definicionin e struktors s nyjes (Node) dhe definicionin e klass LinkedList.
Fajlli i dyt sht kodi burimor i cili prmban implementimin e funksioneve t
klass LinkedList. Fajlli i fundit sht fajlli i aplikacionit i cili prmban kodin
q krijon dhe prdor klasn LinkedList.
190

Algoritmet dhe strukturat e t dhnave


Le t fillojm me header fajllin LinkedList.h. Ky fajll prmban dy
komponente, definicionin e strukturs Node dhe definicionin e klass
LinkedList, t cilat programert i quajn specifikacioni i klas.
Do t vreni se t dy komponentet jan diskutuar n detaje n pjesn paraprake.
Gjithashtu do t shihni se definicioni i klass LinkedList nuk prmban
implementimin e funksioneve antare. N vend t ksaj, prmban prototipet e
funksioneve antare t cilat jan implementuar n fajllin e kodit burimor.
Mbajtja e specifikacioneve dhe implementimeve n fajlla t ndar, t header
fajllit dhe atij burimor, sht praktik e zakonshme. Pjesve t programit t cilat
prdorin klasn, ju interesojn vetm funksionet interfejs, t definuara n header
fajll; atyre nuk ju intereson implementimi. Kjo mundson q ju t
parakompajloni kodin tuaj burimor n module t librarive ashtu q shfrytzuesit
e ksaj klase t ken nevoj vetm pr header-t dhe modulet.
//LinkedList.h
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};
class LinkedList
{
private:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
};

Definicionet e funksioneve antare t klass LinkedList jan t prmbajtura n


fajllin LinkedList.cpp, si sht treguar n kodin n vijim. Fajlli fillon me
urdhrin preporcesorik (#include) q i tregon preprocesorit q ti referohet
prmbajtjes s fajllit LinkedList.h gjat paraprocesimit. Fajlli LinkedList.h
prmban definicionin e klass LinkedList dhe definicionin e strukturs Node, t
191

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

Algoritmet dhe strukturat e t dhnave


void LinkedList::displayNodesReverse()
{
cout << "Nyjet n renditje t kundert:";
Node* temp = back;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->previous;
}
}
void LinkedList::destroyList()
{
Node* temp = back;
while(temp != NULL)
{
Node* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}

Fajlli i fundit sht aplikacioni n C++ (i quajtur LinkedlistDemo.cpp), i cili


prdor listn e lidhur. Aplikacioni sht shum i shkurtr n krahasim me kodin
e prdorur pr t definuar strukturn Node dhe klasn LinkedList.
//LinkedListDemo.cpp
#include <iostream>
using namespace std;
void main()
{
LinkedList * list = new LinkedList();
list->appendNode(10);
list->appendNode(20);
list->appendNode(30);
list->displayNodes();
list->displayNodesReverse();
delete list;
}

Aplikacioi fillon me deklarimin e instancs s klass LinkedList. Si ju kujtohet


prej pjess m hert, konstruktori i inicializon pointert front dhe back.
Instanca deklarohet duke prdorur operatorin new. Operatori new ktehn
pointerin pr n lokacionin e memories s instancs. I njjti urdhr deklaron
193

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

Listat e lidhura nj-fish (Singly-linked list)


Lista e lidhur sht struktur dinamike e t dhnave shum e rndsishme.
Parimisht, ka dy lloje t listave t lidhura: singly-linked list (lista e lidhur
njfish, vetm n nj kah/an) dhe doubly-linked list (lista e lidhur dyfish).
N listn e lidhur njfish, secili element prmban nj t dhn (vler) dhe
lidhjen pr tek elementi tjetr (i ardhshm), e cila mundson mbajtjen e
strukturs. N ann tjetr, secila nyje n listn e lidhur dyfish, vlern, lidhjen
tek elementi i ardhshm dhe lidhjen tek elementi i prparshm. Lista e lidhur
mund t jet struktur e t dhnave n t ciln bazohet zbatimi i stack-ut,
radhs s pritjes (angl. queue) ose lists s sortuar.

194

Algoritmet dhe strukturat e t dhnave

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.

Opearcionet (Veprimet) n listn e lidhur njfish


Implementimi konkret i operacioneve n listn e lidhur njfish varet nga
qllimi pr t cilin prdoren.

Lista e lidhur njfish, reprezentimi i brendshm


Secila nyje e lists s lidhur njfish prmban informacionet vijuese:

vlera (e dhna e shfrytzuesit);


lidhja pr n elementin e ardhshm (e dhn ndihmse)

Skematikisht, mund t paraqitet si n vijim:

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

Algoritmet dhe strukturat e t dhnave


//SinglyLinkedList=ListaeLidhurNjefish
class SinglyLinkedList
{
private:
SinglyLinkedListNode *head;
SinglyLinkedListNode *tail;
public:
SinglyLinkedList() {
head = NULL;
tail = NULL;
}
}

Prshkimi i lists s lidhur njfish


Supozoni se kemi nj list me disa nyje. Prshkimi (angl. traversal prshkimi,
bredhja, kalimi me radh) sht operacion bazik, i cili paraqitet si pjes e
pothuajse do operacioni (veprimi) n listn e lidhur njfish. Pr shembull,
algoritmi mund t prshkoj listn e lidhur njfish pr t gjetur nj vler, pr t
gjetur pozitn pr insertim, etj. Pr list t lidhur njfish, sht i mundur vetm
prshkimi prpara.

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

Pr disa algoritme, prcjellja (ndjekja, gjurmimi) i nyjes s prparshme sht


esencial, por pr disa sht e panevojshme. Megjithat ktu sht paraqitur rasti
i zakonshm (i prgjithshm) dhe algoritmi konkret mund t prshtatet pr t
plotsuar krkesat individuale.
Pjes kodi
Edhe pse kemi dy klasa pr listn e lidhur njfish, klasa SinglyLinkedListNode
prdoret vetm pr ruajtje. I tr algoritmi implementohet n klasn
SinglyLinkedList.
//traverse=prshko, current=aktuale/momentale
int SinglyLinkedList::traverse()

198

Algoritmet dhe strukturat e t dhnave


{
int sum = 0;
SinglyLinkedListNode *current = head;
SinglyLinkedListNode *previous = NULL;
while (current != NULL)
{
sum += current->value;
previous = current;
current = current->next;
}
return sum;
}

Lista e lidhur njfish Operacioni i shtimit (insertimit)


Insertimi n listn e lidhur njfish ka dy raste speciale. Ato jan insertimi i nyjs
s re para koks (krejt n fillim t lists) dhe pas bishtit/fundit (krejt n fund t
lists). Pr kt qllim, i prdorim dy pointer : head (koka) dhe tail (bishti),
prmes s cilve prcjellim fillimin dhe fundin e lists. N t gjitha rastet tjera,
nyja e re insertohet ndrmjet nyjeve n list, kshtu q ka paraardhs dhe
pasardhs n list.

Rasti i lists s zbrazt


Kur lista sht e zbrazt, gj q tregohet prmes kushtit (head==NULL),
insertimi sht trsisht i thjesht. Algoritmi i vendos t dyja, fillimin dhe
fundin, q t pointojn n nyjen e re. (angl. new node Nyja e re).

199

Avni Rexhepi

Shtimi i nyjes n pozit t par


N kt rast, nyja e re (new node) insertohet para nyjes s par aktuale (para
koks).

Kjo mund t bhet n dy hapa:


1. Azhuro lidhjen next (i ardhshmi) t nyjes s re, pr t pointuar n
nyjen aktuale t filllimit (kokn).

2. Azhuro lidhjen (pointerin) e koks, q t pointoj n nyjen e re (new


node).

200

Algoritmet dhe strukturat e t dhnave

Shtimi i nyjes n fund


N kt rast, nyja e re insertohet menjher pas nyjes s fundit aktuale.

Kjo mund t bhet n dy hapa:


1. Azhoro lidhjen e ardhshme (pointerin next) t nyjes s fundit aktuale,
pr t pointuar n nyjen e re.

2. Azhuro lidhjen e nyjes s fundit (bishtit) q t pointoj n nyjen e re.

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.

Nj insertim i till mund t bhet n dy hapa:


1. Azhoro lidhjen e nyjes s prparshme, q t pointoj n nyjen e re.

2. Azhuro lidhjen e nyjes s re, q t pointoj n nyjen e ardhshme.

202

Algoritmet dhe strukturat e t dhnave


Pjes kodi
T gjitha rastet e paraqitura m sipr mund t implementohen n nj funksion
me dy argumente, t cilat jan: nyja pas (prapa) s cils duhet t insertohet nyja
e re dhe nyja e re. Pr operacionin add first (shto t parin), argumentet jan
(NULL, newNode). Pr operacionin add last (shto t fundit), argumentet jan
(tail, neNode). Megjithat, kto operacione specifrike (shto t parin dhe shto t
fundit) mund t implementohen vemas, n mnyr q t evitohen verifikimet e
panevojshme.
//addLast=shtoTFundit, newNode=nyjeeRe
void SinglyLinkedList::addLast(SinglyLinkedListNode *newNode)
{
if (newNode == NULL)
return;
else {
newNode->next = NULL;
if (head == NULL)
{
head = newNode;
tail = newNode;
} else
{
tail->next = newNode;
tail = newNode;
}
}
}
//addFirst=shtoTParin, newNode=nyjeeRe
void SinglyLinkedList::addFirst(SinglyLinkedListNode *newNode)
{
if (newNode == NULL)
return;
else {
if (head == NULL)
{
newNode->next = NULL;
head = newNode;
tail = newNode;
} else
{
newNode->next = head;
head = newNode;
}

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;
}
}
}

Lista e lidhur njfish Operacioni i largimit (fshirjes)


Jan katr raste t cilat mund t paraqiten gjat fshirjes (largimit) t nyjes dhe
jan t ngjashme me rastet e insertimit. Kemi katr situatat e njjt, por me
renditje t kundrt t veprimeve. Vreni, se algoritmi i largimit prfshin
shkatrrimin e nyjes s fshir, veprim i cili mund t jet i panevojshm n
gjuht programuese me mbledhje automatike t mbetjeve (si p.sh., Java).

Lista ka vetm nj nyje


Kur lista ka vetm nj nyje, gj q tregohet nga kushti se koka pointon n t
njjtn nyje sikur edhe fundi (bishti), largimi sht krejt i thjesht. Algoritmi e
eliminon nyjen e pointuar nga koka (ose bishti) dhe i vendos t dyja, edhe
kokn edhe bishtin, n NULL.

204

Algoritmet dhe strukturat e t dhnave

Largo nyjen e par (fillimin)


N kt rast largohet nga lista nyja e par (nyja aktuale kok).

Kjo mund t bhet n dy hapa:


1. Azhuro lidhjen e koks q t pointoj n nyjen e ardhshme pas koks.

2. Elimino nyjen e larguar

205

Avni Rexhepi

Largo nyjen e fundit


N kt rast, largohet nga lista nyja e fundit (bishti aktual). Ky operacion sht
pak m i ndrlikuar sesa largimi i nyjes s par, sepse algoritmi s pari duhet t
gjej nyjen e cila i paraprin nyjes s fundit (bishtit).

Kjo mund t bhet n tre hapa:


1. Azhuro lidhjen (linkun) e bishtit q t pointoj n nyjen para bishtit
(nyjen e parafundit). Pr ta gjetur at, s pari duhet t prshkohet lista,
duke filluar nga fillimi (koka).

206

Algoritmet dhe strukturat e t dhnave


2. Vendose lidhjen e bishtit t ri pr tek nyja e ardhshme, n NULL.

3. Elimino nyjen e larguar.

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.

2. Elimino nyjen e larguar.

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

Algoritmet dhe strukturat e t dhnave


{
if (head == NULL)
return;
else
{
SinglyLinkedListNode *removedNode;
removedNode = head;
if (head == tail)
{
head = NULL;
tail = NULL;
} else
{
head = head->next;
}
delete removedNode;
}
}
void SinglyLinkedList::removeLast()
{
if (tail == NULL)
return;
else
{
SinglyLinkedListNode *removedNode;
removedNode = tail;
if (head == tail)
{
head = NULL;
tail = NULL;
}
else
{
SinglyLinkedListNode *previousToTail = head;
while (previousToTail->next != tail)
previousToTail = previousToTail->next;
tail = previousToTail;
tail->next = NULL;
}
delete removedNode;
}
}

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:

Fig. 4.7 Dritarja gjat ekzekutimit


210

Algoritmet dhe strukturat e t dhnave


// ListaeLidhurShembull.cpp
#include "stdafx.h"
#include<iostream>
using namespace std;
class nyje
//Klasa nyje definon nyjen
{
public :
int vlera;
//vlera,e dhna
nyje *pNeA;
// pNeA=pointeri ne Nyjen e Ardhshme
// (prmban adresn e nyjes s ardhshme)
};
class listaeLidhur //Klasa listaeLidhur definon listen e lidhur
{
public :
nyje *fillimi; //pointeri per fillimin e listes, nyja e pare ne liste
listaeLidhur() //Konstruktori i listes se lidhur
{
fillimi=NULL;
};
void insertoNeFillim();
void insertoNdermjet();
void insertoNeFund();
int largo();
void paraqit();
};
int main()
{
int n=0,a;
listaeLidhur lista;
//lista - e tipit listaeLidhur
do {
cout<<"\n************** M E N Y J A **************\n";
cout<<"\n 1.Inserto ne fillim\n 2.Inserta ne mes\n 3.Inserto ne
fund\n 4.Fshije nyjen\n 5.Paraqiti elementet";
cout<<"\n 6.Dalja\n";
cout<<"\n*****************************************\n";
cout<<" Zgjedhe opcionin: ";
cin>>a;
switch(a)
{
case 1: lista.insertoNeFillim(); break;
case 2: lista.insertoNdermjet(); break;
case 3: lista.insertoNeFund(); break;
case 4: lista.largo(); break;
case 5: lista.paraqit(); break;
case 6: n=1; break;
default : cout<<"\nVlere e gabuar. Perseriteni zgjedhjen \n";
break;
}
} while(n!=1);

211

Avni Rexhepi
system(Pause);
return 0;
}

void listaeLidhur :: insertoNeFund()


{
nyje *temp; //(temp-e Prkohshme); Pointeri per nyjen ndihmese
// (nyje e perkohshme)
if(fillimi==NULL)
{
fillimi=new nyje [1];
fillimi->pNeA=NULL;
cout<<"\n Shtype vleren per t'a insertuar ne liste: ";
cin>>fillimi->vlera;
}
else
{
temp=fillimi;
while(temp->pNeA!=NULL)
temp=temp->pNeA;
temp->pNeA= new nyje [1];
temp=temp->pNeA;
cout<<"\n Shtype vleren per t'a insertuar ne liste: ";
cin>>temp->vlera; temp->pNeA=NULL;
}
}
void listaeLidhur :: insertoNdermjet()
{
nyje *temp,*eArdhshme; //Pointeret per nyjen ndihmese dhe e ardhshme
int x;
pozita:
temp=fillimi;
cout<<"\n Jepe elementin fqinje (pas se cilit insertohet nyja e re):";
cin>>x;
while(temp->vlera!=x)
{
temp=temp->pNeA;
if (temp==NULL)
{
cout<<x<<" nuk ekziston ne liste\n";
goto pozita;
}
}
eArdhshme=temp->pNeA;
temp->pNeA=new nyje [1];
temp=temp->pNeA;
temp->pNeA=eArdhshme;
cout<<"\n Shtype vleren per t'a insertuar ne liste: ";
cin>>temp->vlera;
}

212

Algoritmet dhe strukturat e t dhnave


void listaeLidhur :: insertoNeFillim()
{
nyje *temp;
temp=new nyje [1];
temp->pNeA=fillimi;
fillimi=temp;
cout<<"\n Shtype vleren per t'a insertuar ne liste: ";
cin>>temp->vlera;
}
int listaeLidhur :: largo()
{
nyje *temp,*ePerparshme=NULL; //Pointeret, per nyjen ndihmese dhe
nyjen e perparshme
int x;
if(fillimi==NULL)
{
cout<<"\n Lista e zbrazet -- Nuk ka elemente per t'u fshire \n";
return 0;
}
fshirja:
temp=fillimi;
cout<<"\n Shtype vleren per t'a fshire: ";
cin>>x;
while(temp->vlera!=x)
{
temp=temp->pNeA;
if (temp==NULL)
{
cout<<x<<" nuk ekziston ne liste\n";
goto fshirja;
}
}
if(fillimi->vlera==x && fillimi->pNeA==NULL)
{
delete fillimi;
fillimi=NULL;
cout<<"\n Fshirja e suksesshme\n";
return 0;
}
if(fillimi->vlera==x)
{
temp=fillimi->pNeA;
delete fillimi;
fillimi=temp;
cout<<"\n Fshirja e suksesshme \n";
return 0;
}
temp=fillimi;

213

Avni Rexhepi
while(temp->vlera!=x)
{
ePerparshme=temp;
temp=temp->pNeA;
}
ePerparshme->pNeA=temp->pNeA;
cout<<"\n Fshirja e suksesshme \n";
return x;
}

void listaeLidhur :: paraqit()


{
nyje *temp;
temp=fillimi;
if (fillimi!=NULL)
{
cout<<"\n Elementet ne listen e lidhur jane: ";
while(temp->pNeA!=NULL)
{
cout<<""<<temp->vlera<<" ";
temp=temp->pNeA;
}
cout<<temp->vlera<<endl;
}
else
cout<<"Lista eshte e zbrazet!\n";
}

214

Algoritmet dhe strukturat e t dhnave

Lista e lidhur n STL


N STL ekziston libraria <list> q sht kontejner pr listn e lidhur dyfish.
Lista e STL-it mund t insertoj elementet, t shtoj elementet n fillim m
shpejt sesa vektort, sepse lista nuk ka nevoj pr zhvendosjen e elemeneteve
t tjera. Listat poashtu jan efikase me rastin e shtimit t elementeve n fund
sepse kan pointerin e brendshm (built-in) pr n elementin e fundit n list.
Funksionet e klass list q prdoren pr listat e STL-it, jan:

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

Algoritmet dhe strukturat e t dhnave


// Paraqiti vlerat perseri
for (iter = ListaIme.begin(); iter != ListaIme.end();
iter++)
cout << *iter << " ";
cout << endl;
}

Rezultati:
0 10 20 30 40 50 60 70 80 90
90 80 70 60 50 40 30 20 10 0

Steku prmes lists s lidhur


M hert e kemi par krijimin e stekut prmes vargjeve. Mirpo, prdorimi i
vargjeve paraqet problemin e pamundsis s prshtatjes s madhsis s stekut
gjat ekzekutimit t programit. Zgjidhja pr kt do t ishte prdorimi i lists s
lidhur pr krijimin e stekut. N vazhdim do t shohim si t prdoret lista e lidhur
pr t krijuar stekun.

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.

Figura 4.8 - Nyja prmban pointerin n nyjen e prparshme dhe at t


ardhshme n listn e lidhur dhe prmban t dhnn (vlern) e shoqruar me
nyjen aktuale.
Nyja definohet n C++ prmes prdorimit t strukturs, e cila sht tip i t
dhnave i definuar prej shfrytzuesit. Struktura vijuese e definon nyjen:
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};

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

Algoritmet dhe strukturat e t dhnave


funksione antare, t definuara n klasn LinkedList. Antart e t dhnave jan
pointer n instancat e strukturs Node. Pointeri i par quhet front dhe ai i
referohet nyjs s par n listn e lidhur. Pointeri i dyt quhet back dhe i
referohet nyjs s fundit n listn e lidhur.
T dy kta pointer, front dhe back deklarohen n pjesn e mbrojtur (angl.
protected) t klass, sepse klasa lista LinkedList trashgohet nga klasa
StackLindekList, pr t ciln do t flasim n vazhdim. Klasa StackLinkedList
prdor pointert front dhe back.
Gjasht funksionet antare t klass, manipulojn listn e lidhur. Kto funksione
jan
konstruktori,
destruktori,
appendNode(),
displayNodes(),
displayRevereseNodes() dhe destrotyNodes(). Kto u paraqitn n pjesn e
mparshme.
N vazhdim kemi definicionin e klass LinkedList. Do t shihni se sht
afrsisht i njjt me definicionin e klass Linkedlist t paraqitur m par, por me
nj dallim t leht. Gjersa m par pointert front dhe back ishin t definuar
n pjesn private t klass, tani ata jan n pjesn e mbrojtur (protected), sepse
klasa StackLinkedList do ti prdor ata:
class LinkedList
{
protected:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
};

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();
};

Konstruktori dhe destruktori StackLinkedList


Konstruktori dhe destruktori i klass StackLinkedList mund t duken t
uditshm n fillim, sepse t dy jan t zbrazt dhe nuk ka instruksione/urdhra
t specifikuara n trupin e tyre, si vijon:
StackLinkedList()
{
}
~StackLinkedList()
{
}

Konstruktori sht i zbrazt sepse para konstruktorit t klass StackLinkedList


thirret konstruktori i klass LinkedList. Ju kujtohet se klasa StackLinkedList e
trashgon klasn LinkedList. Konstruktori i klass LinkedList inicializon
pointert front dhe back n NULL. Prandaj, nuk ka asgj tjetr pr t br
nga ana e konstruktorit t klass StackLinkedList.
Ngjashm, destruktori i klass LinkedList thirret para destrkutorit t klass
StackLinkedList. Destruktori i klass LinkedList e fshin tr memorien e
shoqruar me nyjet e lists s lidhur. Prandaj, destruktori i klass
StackLinkedList poshtu nuk ka pr t br asgj.

Vendosja/shtyrja e nyjes n stekun-list e lidhur


N pjesn pr stekun sht sqaruar se vlerat vendosen/shtyhen n krye t stekut
dhe largohen/trhiqen nga kreu i stekut (top). Kto veprime quheshin push dhe
pop. Hapat e njjt ndodhin edhe gjat prdorimit t lists s lidhur pr stekun,
220

Algoritmet dhe strukturat e t dhnave


por n vend t vendosjes s vlers n inkeksin e ardhshm n dispozicion t
vargut, ajo vendoset n fund (back) t lists s lidhur.
Duhet t definoni funksionin push() t klass StackLinkedList i cili thirret sa
her q t shtohet nj vler n stek. Mbani mend q n realitet jeni duke shtuar
nj nyje n listn e lidhur dhe jo thjesht vetm vler. Vlera sht e prmbajtur
prbrenda nyjes (n nyje).
Pr t shtyar nj nyje n stek, prdoren hapat e njjt si n rastin e shtimit t
nyjes n listn e lidhur. Kjo do t thot q funksioni appendNode() i klass
LinkedList mund t prdoret pr t vendosur nyjen e re n stek. Prandaj, krejt
ka nevojitet sht q nga ana e funksionit push(), t thirret funksioni
appendNode(). Pasi q funksioni appendNode() sht publik, ai do t mund t
thirrej edhe direkt, sa pr t vendosur nyjen n stek, por vendosja e funksionit
push() n klasn e stekut e bn kt m intuitive pr prdoruesit e ksaj klase.
Kjo poashtu ndihmon n fshehjen e implementimit t brendshm, pradaj
prdorimi i klass sht pak m i drejtprdrejt.
Si ju kujtohet, funksioni appendNode() krkon nj argument (parametr), i cili
sht e dhna (vlera) q i ndahet nyjes s re. Duhet t definohet funksioni push()
q t pranoj kt vler t njjt si argument t tij, pr t prcjellur kt t dhn
n funksionin appendNode(). Kjo sht ilustruar n shembullin vijues. Funksioni
push() kkron nj integer t prcjellur si argument. Ajo vler integer pastaj i
prcillet funksionit appendNode() prmbrenda trupit t definicionit t funksionit
push().
void push(int x)
{
appendNode(x);
}

Nxjerrja e nyjs nga steku-list e lidhur


sht e nevojshme t definohet edhe funksioni pr nxjerrjen (trheqjen) e nyjs
prej stekut, q do t quhet pop(). Pasi q jemi duke prdorur listn e lidhur si
stek, funksioni pop() duhet t largoj nyjen prej fundit t lists s lidhur.
Fatkeqsisht, nuk sht e mundur q thjesht t thirret funksioni i klass
LinkedList pr t nxjerrur nyjen prej stekut, sepse klasa LinkedList nuk e ka t
definuar funksionin i cili e largon nyjen nga lista e lidhur. Sikur t kishim pasur
funksionin e klass baz pr largim t nyjes s fundit removeBack(), do t
mund t thirrej ky funksion pr t nxjerrur (duke e larguar) nyjen nga lista. N
kt rast, do t duhet t definohet funksioni pop() n klasn StackLinkedList pr
t br kt gj. Kjo do t siguroj qasjen last in, first out, n stek.

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

Algoritmet dhe strukturat e t dhnave


}
delete temp;
return retVal;
}

S pari, duhet t prcaktohet/kontrollohet nse ka ndonj vler n stek, duke


thirrur antarin isEmpty(). Ky funksion kthen vlern Bool-eane true nse steku
sht i zbrazt dhe at false nse nuk sht i zbrazt. Nga figura 4.9 mund t
shihet se ka dy nyje n stek dhe kshtu steku nuk sht i zbrazt. Pr kt arsye,
urdhri return n kushtin if nuk do t ekzekutohet.
Funksioni pop() i referohet atributit back t klass LinkedList. sht me
rndsi t mbahet mend q atributi back i referohet kreut t stekut (pra atributit
top t stekut). Nyjet do t largohen nga fundi (back) i lists s lidhur, pr t
kryer operacionin pop. Prandaj, vlera front sht Node2 (Nyja 2).
Vlera e Node 2, i ndahet variabls retVal, e cila sht vlera e kthyer nga
funksioni pop(), nse ka nyje n stek (steku nuk sht i zbrazt). Kjo bn q
vlera t trhiqet (nxirret) nga steku.
Pastaj, adresa e nyjs back, q sht Node 2, i ndahet pointerit t prkohshm
temp. Nyja n t ciln pointon pointeri i prkohshm largohet nga memoria me
operatorin delete n fund t funksionit pop().
Pastaj, prcaktohet nse nyja n fund t stekut ishte nyja e vetme n listn e
lidhur. Kjo bhet duke par nse atributi previous i nyjes sht NULL. Nse
pointeri previous n fund t lists sht NULL, kjo tregon se ka vetm nj nyje
n listn e lidhur.
Duhet t jeni t kujdesshm kur analizoni funksionin pop(). Mbani mend q
fundi i lists s lidhur (back) sht kreu i stekut (top) dhe se fundi i stekut sht
fillimi i lists s lidhur.
Nse funksioni pop() sht duke larguar nyjen e vetme nga steku, ather
atributet front dhe back t klass LinkedList do t vendosen n NULL, duke
treguar se nuk ka nyje t mbetura n listn e lidhur, pas ekzekutimit t funksionit
pop().
Sidoqoft, nse ka s paku nj nyje n stek, ekzekutohen urdhrat prbrenda
urdhrit else, si n figurn 4.9. Urdhri i par prbrenda urdhrit else e
cakton atributin previous t atributit back si back i ri (si fundi i ri). N
figurn 4.9, atributi previous sht 1. Kjo tregon se Node 1 vjen para Node 2.
Ather Node 1 caktohet si fundi i ri i stekut.
Mbani mend q nuk ka nyje tjetr (t ardhshme, next) n stek sepse gjithmon
jeni duke punuar me fundin (back) e lists s lidhur. Prandaj, duhet tia ndani

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.

Kontrollimi a sht i zbrazt steku


Funksioni pop() duhet t kontrolloj nse steku sht i zbrazt ose do t tentoj
t largoj nyjen q nuk ndodhet n stek. Funksioni pop() e kontrollon a sht
steku i zbrazt duke thirrur funksionin isEmpty(), i cili duhet t definohet si
pjes e klass StackLinkedList.
Funksioni isEmpty() sht funksion i thjesht i cili kontrollon a sht steku i
zbrazt duke shikuar nse vlera e atributit front t klass LinkedList sht
NULL. Nse sht, ather kthehet vlera bool-eane true, prndryshe kthehet
false. Nse steku sht i zbrazt, t dy atribuetet, front dhe back jan
NULL, por mjafton t testohet vetm njra prej tyre.
bool isEmpty()
{
if(front == NULL)
{
return true;
}
else
{
return false;
}
}

Steku si list e lidhur n C++


Tani q dihen komponentet, duhet t krijohet steku-list e lidhur. N kt pjes
do t fokusohemi n bashkimin e tyre n nj aplikacion funksional n C++. Disa
programer i organizojn komponentet e stekut-list e lidhur n pes fajlla:
LinkedList.h, LinkedList.cpp, StackLinkedList.h, StackLinkedList.cpp dhe
StackLinkedListDemo.cpp. t gjith kta fajlla bashkohen sbashku n kohn e
kompajlimit pr t krijuar fajllin ekzekutiv.
Header fajlli LinkedList.h sht fajlli q prmban definicionin e strukturs
Node adhe definicionin e klass Linkedlist. LinkedList.cpp sht kodi burimor i
cili prmban implementimin e funksioneve antare t klass LinkedList, t cilat
jan paraqitur m par (n kapitullin 6).
224

Algoritmet dhe strukturat e t dhnave


Fajlli StackLinkedList.h sht header fajlli i cili prmban definicionin e klass
StackLinkedList. StackLindekList.cpp sht fajlli i kodit burimor q prmban
implementimin e funksioneve t klass StackLinkedList.
StackLinkedListDemo.cpp prmban aplikacionin dhe sht vendi ku deklarohet
instanca e klass StackLinkedList dhe ku thirren funksionet antare.

Funksionet LinkedList Header Fajlli dhe LinkedList


Fajlli LinkedList.h dhe fajlli LinkedList.cpp jan treguar n kodin vijues. Kto
duhet t duken t njohura sepse jan t njjtat q jan deklaruar m par (n
kapitullin 6). Mirpo ka nj prjashtim. Atributet front dhe back t definuara
n klasn LinkedList n fajllin LinkedList.h jan definuar n pjesn (seksionin)
e mbrojtur (protected) t definicionit t klass. Ata paraqiteshin m par n
pjesn private. Klasa StackLinkedList duhet t ju qaset ktyre variablave, ashtu
q ato vendosen nn specifikimin protected pr t qen t dukshme pr nnklasn.
Pr shpjegimet e plota t tyre, referojuni pjess ku jan paraqitur m par.
//LinkedList.h
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};
class LinkedList
{
protected:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
};

//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

Algoritmet dhe strukturat e t dhnave


}
void LinkedList::destroyList()
{
Node* temp = back;
while(temp != NULL)
{
Node* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}

Fajlli header StackLinkedList dhe fajlli burimor StackLinkedList


Fajlli StackLinkedList.h prmban definicionin e klass StackLinkedList, si n
vijim. Nn fajllin StackLinkedList.h ndodhet fajlli StackLinkedList.cpp i cili
prmban definicionet e funksioneve antare.
Definicioni i secilit funksion sht sqaruar n pjesn Klasa StackLinkedList.
//StackLinkedList.h
class StackLinkedList : public LinkedList
{
public:
StackLinkedList();
virtual ~StackLinkedList();
void push(int);
int pop();
bool isEmpty();
};
//StackLinkedList.cpp
StackLinkedList.h
StackLinkedList::StackLinkedList()
{
}
StackLinkedList::~StackLinkedList()
{
}
void StackLinkedList::push(int x)
{
appendNode(x);
}

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

Algoritmet dhe strukturat e t dhnave


Funksioni displayNodes() sht antar i klass LinkedList dhe sht sqaruar m
par.
Funksioni pop() thirret pastaj pr t larguar nyjen e fundit n stek, e cila pastaj
paraqitet n ekran (shih figurn 4.10). Programi pastaj thrret operatorin delete
pr t larguar stekun nga memoria.

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 prmes lists s lidhur


Gjat prdorimit t queue-s (radhs s pritjes), mund t ndodh q nuk ka vend
t mjaftueshm pr vendosjen e t gjitha t dhnave t cilat duhet t prpunohen.
Pr t evituar kt problem, pr krijimin e radhs s pritjes, mund t prdoren
listat e lidhura.
sht thn m hert se queue sht organizim sekuencial i t dhnave, ku t
dhnat (vlerat) kan qasje vetm n parimin First In, First Out (I pari brenda, i
pari jasht), q sht e ngjashme me radhn e pritjes pr blerjen e biletave pr
koncert ose pr kryerjen e pagess, tek arkat e pagesave n supermarket.
N pjesn ku sht shpjeguar queue u krijua prmes prdorimit t vargut, pr
ruajtjen e t dhnave. Si ju kujtohet, vargu ishte i ndar nga queue. Vlerat i
ndahen elementeve t vargut. Vet queue prbhet prej dy variablave t quajtura
front dhe back. Secila pointon n elementin e vargut q ndodhet n fillim
(front) t radhs (queue-s) ose n fund (back) t radhs (queue-s). Kur largohet
e dhna (vlera) nga fillimi i radhs, programi e ndryshon vlern e variabls
front, pr t pointuar n elementin e ardhshm t vargut. Mirpo, e dhna
(vlera) e larguar nga radha (queue) akoma mbetej n varg. Kjo do t thot q e
dhna (vlera) nuk largohet nga memoria.
sht edhe nj problem serioz me prdorimin e vargjeve pr t ruajtur radht
(queues): kur t shkruhet programi, duhet t dihet madhsia e vargut. Vargu
mund t ruaj vetm nj numr t caktuar t elementeve n cilindo moment
kohor. Programert evitojn kt problem duke prdorur listn e lidhur n vend
t vargut, pr t krijuar radhn e pritjes (queue-n), pasi si sht thn m hert,
lista e lidhur mund t rritet ose zvoglohet gjat kohs s ekzekutimit bazuar n
nevojat e programit.

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

Algoritmet dhe strukturat e t dhnave


prparshme dhe n nyjen e ardhshme (Figura 4.11). Pjesa e kodit n vijim sht
nyje e tipit t definuar prej shfrytzuesit. N kt pjes do t prdoret struktura
vijuese e tipit t definuar prej shfrytzuesit, pr t krijuar queue-list e lidhur.

Figure 4.11 - Secila nyje pointon nyjen e prparshme dhe at t ardhshme.


Emri i strukturs s definuar prej shfrytzuesit sht Node dhe prdoret n
klasn LinkedList pr t deklaruar instancat e nyjes. Tre urdhrat e fundit n
struktur deklarojn nj integer q ruan vlern aktuale dhe deklarojn dy
pointer pr t ju referuar nyjes s prparshme dhe nyjes s ardhshme n listn e
lidhur.
Seciln her q krijohet nj nyje, strukturs s definuar prej shfrytzuesit i
prcillet vlera pr nyjen. Pointert n nyjen e prparshme dhe t ardhshme
caktohen n NULL, q tregon se nuk ka nyje t prparshme ose t ardhshme.
NULL zvendsohet me pointert prkats kur t shtohet nyja e re n list.
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme

231

Avni Rexhepi
struct Node
{
int data;
Node* previous;
Node* next;
};

Klasa LinkedList krijon dhe menagjon listn e lidhur. Rikujtoni se klasa


LinkedList identifikon nyjen q ndodhet n fillim t lists s lidhur (front) dhe
nyjen q ndodhet n fund t lists (back).
Pr m teprt, klasa LinkedList definon funksionet antare t cilat menagjojn
listn e lidhur. Kto jan funksionet e definuara n pjesn ku u shpjeguan listat e
lidhura (kapitullin 6): konstruktori, destruktori, appendNode(), displayNodes(),
displayRevereseNodes() dhe destroyList(). Ja definicioni i klass LinkedList q
do t prdoret pr t krijuar queue-list e lidhur.
class LinkedList
{
protected:
Node* front;
Node* back;
public:
LinkedList();
~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
};

Zakonisht struktura e nyjes dhe klasa LinkedList vendosen n header fajllin e


njjt, LinkedList.h, si mnyr pr t mbajtur at t organizuar. Programert
pastaj prdorin direktivn preprocesorike #include pr t prfshir LinkedList.h
n programin q prdor listn e lidhur.
Komponenta e fundit e queue-list e lidhur sht definicioni i klass
QueueLinkedList. Kjo klas trashgon klasn LinkedList dhe pastaj definon
funksionet antare t cilat jan t dizajnuara n mnyr specifike pr t
menagjuar queue-n (radhn).
Edhe pse duket se do t ishte mir q t kombinohen klasat LinkedList dhe
QueueLinkedList n nj klas, duke i mbajtur t gjitha n nj vend sa her q
nevojitet t krijohet lista e lidhur, kjo gj do t shkaktonte prsritje t kodit, gj
q programert e evitojn sa her t jet e mundur.

232

Algoritmet dhe strukturat e t dhnave


Pr shembull, definicionet e nyjes dhe klasa LinkedList do t lokalizoheshin n
dy vende. Nse nevojitet prmirsim (angl. upgrade) i cilit do definicion, do t
duhet t mbahen mend t gjitha vendet ku ato jan definuar brenda kodit. Qasje
m e mir sht q t vendoset secili definicion n fajllin e vet (p.sh.,
LinkedList.h, QueueLinkedList.h), ashtu q kodi nuk do t prsritet.
N vijim paraqitet definicioni i klass QueueLinkedList q do t prdoret pr t
krijuar queue-n. Ky definicion ruhet n fajllin QueueLinkedList.h. Klasa
QueueLinkedList ka pes funksione: konstruktori, destruktori, enqueue(),
dequeue() dhe isEmpty().
//QueueLinkedList.h
#include "LinkedList.h"
class QueueLinkedList : public LinkedList
{
public:
QueueLinkedList();
virtual ~QueueLinkedList();
void enqueue(int);
int dequeue();
bool isEmpty();
};

Konstruktori dhe destruktori i klass QueueLinkedList jan t zbrazt, si do t


shihet n kodin vijues. Konstruktori n mnyr tipike inicializon antart e nj
instance t klass. N rast t queue-list e lidhur, inicializimi bhet nga
konstruktori i klass LinkedList, i cili thirret para konstruktorit t klass
QueueLinkedList, prandaj kjo do t thot se nuk ka asgj pr t br nga ana e
konstruktorit t klass QueueLinkedList.
Destruktori n mnyr tipike e liron memorien e prdorur nga instanca e klass.
Lista e lidhur e prdorur pr queue largohet nga destruktori i klass LinkedList, i
cili poashtu thirret para se t thirret destruktori i klass QueueLinkedList.
Prandaj, as pr destruktorin e klass QueueLinkedList, nuk ka asgj pr t br.
QueueLinkedList::QueueLinkedList()
{
}
QueueLinkedList::~QueueLinkedList()
{
}

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

Algoritmet dhe strukturat e t dhnave

Figura 4.12 - Nyja e re shtohet n queue n fund t lists s lidhur.


Hapi i par n kt proces ia cakton referimin n nyjen e re, pointerit next t
nyjes front. Nyja front sht Node 2 dhe i ndahet pointeri n Node 3 si vler
e nyjes s ardhshme n listn e lidhur. Kjo e bn Node3 fund (back) t lists s
lidhur.
Hapi i dyt ia ndan referimin n Node2 si vler t nyjs s prparshme
(previous) n Node3. Kjo do t thot se programi shikon n vlern e nyjes s
prparshme t Node3 pr t ditur se cila nyje vjen para Node3 n listn e lidhur.
Hapi i fundit sht t caktohet Node3 si vler e re e antarit back t klass
LinkedList.
void enqueue(int x)
{
appendNode(x);
}

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.

Figura 4.13 paraqet mnyrn e funksionimit t funksionit dequeue(). Vrehet se


jan tri nyje n queue, kshtu q funksioni isEmpty() kthen vlern bool-eane
false, duke br q programi t largoj nyjen e fillimit front nga queue.

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

Algoritmet dhe strukturat e t dhnave


Pastaj, nyjes s fillimit (front) i ndahet referimi n variabln pointer temp.
Operatori delete e prdor variabln temp m von n funksion, pr t
larguar nyjen e fundit (back) nga memoria.
N vazhdim, funksioni prcakton nse ka nyje tjetr n queue duke kontrolluar
vlern e antarit next t nyjes front. Nse vlera e antarit next sht NULL,
ather nuk ka nyje t tjera n queue. N kt rast, antart front dhe back t
klass LinkedList caktohen n NULL, duke treguar se queue (radha) sht e
zbrazt.
Mirpo, nse antari next i nyjes front nuk sht NULL, vlera e antarit
next t nyjes front i ndahet antarit front t klass LinkedList. N kt
shembull, Node2 sht nyja e ardhshme pr Node1. Node2 bhet front i ri pr
queue.
Vreni se antari i prparshm i Node2 sht caktuar n Node1. Mirpo,
Node1 nuk ekziston m!. Prandaj, antari i prparshm (previuous) duhet t
caktohet n NULL, sepse nuk ka nyje t prparshme (previuous). Node2 sht
fillimi (front) i queue-s (radhs s pritjes).
Nyja temp pastaj fshihet nga memoria. Rikujtoni se nyja temp sht pointeri
q pointon n Node1, dhe Node1 nuk ekziston m n memories. Urdhri i fundit
kthen vlern e variabls retVal, e cila sht vlera (e dhna) q ishte ruajtur n
Node1.
int 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;
}

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;
}
}

Queue-list e lidhur n C++


Tani q u kuptua se si krijohet queue duke prdorur listn e lidhur, le t
prmbledhim t gjitha pjest pr t krjijuar nj queue funksional n C++.
Programert e organizojn aplikacionin n disa fajlla, t cilt prmbajn pjes t
veanta t aplikacionit.
N rastin e demo queue aplikacionit t ilustruar n vazhdim, jan pes
konponente t veanta: fajlli drejtues QueueLinkedListeDemo.cpp, header fajlli
i cili prmban definicionin e klass s nyjes dhe klass LinkedList
(LinkedList.h), fajlli i cili prmban implementimin e funksioneve antare t
klass LinkedList (LinkedList.cpp), header fajlli i cipi prmban definicionin e
klass QueueLinkedList (QueueLinkedList.h) dhe fajlli i cili prmban
implementimin e funksioneve antare t klass QueueLinkedList
(QueueLinkedList.cpp).
Aplikacioni quhet QueueLinkedListDemo dhe prdor listn e lidhur pr t
krijuar queue-n, si sht paraqitur n kodin vijues. Aplikacioni fillon me
deklarimin e isntancs s klass QueueLinkedList, duke prdorur operatorin
new. Pastaj deklaron pointerin n nj instanc t QueueLinkedList. Pointeri
sht quajtur queue, t cilit i ndahet referenca n instancn e krijuar nga
operatori new.

238

Algoritmet dhe strukturat e t dhnave


Pastaj thirret tri her funksioni enqueue(). Seciln her nj nyje e re vendoset n
queue. Queue i paraqitur n Figurn 4.14 ilustron queue-n pas thirrjes s fundit
t funksionit enqueue().

Figura 4.14 - Queue, pas vendosjes s tri vlerave.


Pastaj thirret funksioni dequeue(), pr t larguar nyjen e par nga queue dhe pr
t paraqitur vlerat n ekran. Figura 4.15 paraqet queue-n pas thirrjes s
funksionit dequeue().

Figura 4.15 - Queue pas thirrjes s funksionit dequeue()


Urdhri i fundit n program, e largon queue-n nga memoria.
Secila komponent tjetr ve sht par dhe diskutuar n pjesn e prparshme.
//QueueLinkedListDemo.cpp
#include <iostream>
using namespace std;
void main(){
QueueLinkedList* queue = new QueueLinkedList();
queue->enqueue(10);
queue->enqueue(20);
queue->enqueue(30);
cout << queue->dequeue() << endl;
delete queue;
}
//LinkedList.h
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};
class LinkedList

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

Algoritmet dhe strukturat e t dhnave


temp = temp->next;
}
}
void LinkedList::displayNodesReverse()
{
cout << "Nyjet ne renditje te kundert:";
Node* temp = back;
while(temp != NULL)
{
cout << " " << temp->data;
temp = temp->previous;
}
}
void LinkedList::destroyList()
{
Node* temp = back;
while(temp != NULL)
{
Node* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}
//QueueLinkedList.h
#include "LinkedList.h"
class QueueLinkedList : public LinkedList
{
public:
QueueLinkedList();
virtual ~QueueLinkedList();
void enqueue(int);
int dequeue();
bool isEmpty();
};
//QueueLinkedList.cpp
#include "QueueLinkedList.h"
QueueLinkedList::CQueueLinkedList()
{
}
QueueLinkedList::~CQueueLinkedList()
{
}
void QueueLinkedList::enqueue(int x)

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;
}
}

Funksionet plotsuese pr stek dhe queue: Insert, Delete, Peek, Find


Sikur n rastin e msimit t programimit, ku n fillim msohen vetm gjrat
elementare e pastaj kalohet n programe m komplekse, edhe n rastin e listave
t lidhura, pasi u sqaruan funksionet themelore pr pun me listat e lidhura,
sht radha t shohim edhe funksionet plotsuese, t fuqishme, t cilat nevojiten
pr t ndrtuar aplikacione t kompletuara. Do t shohim funksionet pr
insertim, nxjerrje, fshirje dhe krkim t cilat do t mundsojn prdorimin e
stekut dhe queue-s npr aplikacione.
242

Algoritmet dhe strukturat e t dhnave

Klasa e zgjeruar LinkedList


Pr t rritur efikasitetin e klass LinkedList dhe pr ta br m t leht pr
prdorim, do t rrisim funksionalitetin e klass s definuar m par. Si sht
thn m par, klasa LinkedList krijon instanc t strukturs Node q sht
definuar n header fajllin LinkedList.h. Struktura Node ka tri elemente: t
dhnn (vlern) e ruajtur n nyje, pointerin pr n nyjen e ardhshme dhe
pointerin pr n nyjen e prparshme n listn e lidhur.
Klasa LinkedList q u prdor n pjesn e prparshme prmban dy antar t t
dhnave (dy vlera) dhe gjasht funksione antare. Antart e t dhnave jan
referenca (pointer) pr n nyjen q ndodhet n fillim (front) t lists dhe pr
nyjen q ndodhet n fund (back) t lists s lidhur.
Klasa LinkedList ha funksionet t cilat e shtojn nyjen n listn e lidhur dhe
paraqesin t dhnat (vlerat) e nyjeve n renditje natyrale dhe t kundrt, si dhe
funksionin pr asgjsim t lists s lidhur. Si plotsim, klasa LinkedList ka edhe
konstruktorin dhe destruktorin.
Kt antar (vlera dhe funksione) jan skeleti i nevojshm pr operim t klass
LinkedList. Tani do t ndrmarrim disa hapa plotsues pr t rritur
funksionalitetin e klass LinkedList duke e br m t prdorshme dhe m t
dobishme pr pun me aplikacionet me lista t lidhura.
Plotsimi i par sht definimi i antarit t ri t t dhnave, t quajtur size.
Antari size sht nj numr i plot (integer) q prfaqson numrin e nyjeve q
ndodhen n listn e lidhur. (Pordoret indeksi, por size prcakton nse sht
lista e zbrazt ose nse sht prcjellur indeksi jo valid). Kjo vler mund t
prdoret sa her q aplikacioni ka nevoj t dij madhsin e lists s lidhur.
Pastaj, duhet t definohen funksionet plotsuese, i pari prej t cilave do t jet
funksioni removeNode() (angl. remove node largo nyjen). Funksioni
removeNode() largon nyjen dhe e rilidh listn e lidhur. Ky funksion sht
protected (i mbrojtur) sepse prdoret vetm n mnyr interne (prbrenda).
Shfrytzuesi i ksaj klase nuk do t dinte pointert n nyjet individuale. Ky
sht funksion i prshtatshm pr largimin e nyjeve.
Nj funksion tjetr i dobishm q do t definohet, sht funksioni
removeNodeAt(), i cili e largon nyjen q ndodhet n nj lokacion t caktuar t
lists s lidhur. Radha n t ciln paraqiten nyjet e lists referohet si index
order (renditja e indeksuar). Pozita e nyjes n listn e lidhur referohet si index-i
i nyjes. Indeksi prcillet funksionit removeNodeAt() pr t specifikuar nyjen e
cila do t largohet nga lista e lidhur. Ather funksioni removeNodeAt() e
largon nyjen dhe i rivendos lidhjet n listn e lidhur. Nyja n fillim (front) t
243

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

Algoritmet dhe strukturat e t dhnave


N vijim do t paraqitet header fajlli i riprpunuar LinkedList.h, i cili prmban
definicionet e strukturs s nyjes dhe klass s zgjeruar LinkedList. Vreni se
antari size dhe funksioni removeNode() jan t vendosur n zonn e qasjes s
mbrojtur (protected) t definicionit t klass. Kjo pr arsye se asnjri nuk
prdoret drejtprdrejt nga aplikacioni. N vend t ksaj, ata prdoren nga
funksionet antare t klass LinkedList dhe nga funksionet antare t klasave q
e trashgojn klasn LinkedList.
T gjitha funksionet jan vendosur n pjesn zonn me qasje publike t
definicionit t klass LinkedList dhe jan n dispozicion pr qasje direkte nga
aplikacioni. M von do t paraqiten detajet pr mnyrn e funksionimit t
funksioneve antare.
//LinkedList.h
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};
class LinkedList
{
protected:
Node* front;
Node* back;
int size;
void removeNode(Node* node);
public:
LinkedList();
virtual ~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
void removeNodeAt(int);
int findNode(int);
void deleteNode(int);
void insertNodeAt(int,int);
int peek(int);
int getSize();
};

245

Avni Rexhepi

Funksionet removeNode(), removeNodeAt(), dhe deleteNode()


Largimi i nyjes nga lista e lidhur sht operacion i ndrlikuar. S pari duhet t
zgjidhet nyja (t largohen lidhjet e nyjes) nga lista e lidhur. Mirpo, duke br
kt, prishen lidhjet, sepse m nuk ka asgj q e lidh nyjen e prparshme
(previuos) dhe nyjen e ardhshme (next), sepse nyja q largohet ishte lidhja
ndrmjet tyre. Kjo do t thot q, pas largimit t nyjes, duhet t lidhen mes veti
nyja e prparshme dhe nyja e ardshme, e nyjes q largohet.
Klasa LinkedList mund t zgjerohet pr t prmbajtur tri funksione t cilat e
largojn nyjen nga lista e lidhur dhe pastaj i lidhin mes veti nyjen e prparshme
dhe nyjen e ardhshme t saj. Kto funksione jan: removeNode(),
removeNodeAt() dhe deleteNode().
Funksionit removeNode() i prcillet referenca (pointeri) pr n nyjen q duhet
larguar nga lista e lidhur dhe thirret nga funksioni removeNodeAt() dhe
funksioni deleteNode(). Funksioni removeNode() nuk mund t thirret direkt nga
aplikacioni sepse sht antar i mbrojtur i klass.
Funksioni removeNodeAt() e prdor indeksin e nyjes pr t lokalizuar nyjen q
duhet t largohet. Kur t gjindet nyja, referenca e saj (pointeri n t) i prcillet
funksionit removeNode(). Ngjashm, funksioni deleteNode() e prdor vlern e
nyjes pr t lokalizuar nyjen. Kur t gjindet nyja, funksioni deleteNode() e
nxjerr referencn e nyjes (pointerin), i cili pastaj i prcillet funksionit
removeNode().
Pr shembujt n kt pjes, do t prdoret lista e lidhur e paraqitur n figurn
4.16, e cila ka pes nyje: NodeA deri n NodeE, repsektivisht. Secila nyje e
mban pozitn n listn e lidhur dhe secila pozit identifikohet nga nj indeks.
Vlerat e indeksave fillojn prej zeros dhe jan paraqitur prmbi emrin e secils
nyje (n figurn 4.16).

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

Algoritmet dhe strukturat e t dhnave


vijues. Referenca pr n nyjen q do t largohet (pointeri) i prcillet funksionit
removeNode(). Funksioni removeNode() duhet t vendos se cili prej katr
proceseve duhet t prdoret pr t larguar nyjen.
Procesi i par q duhet t prcaktohet (kontrollohet, verifikohet) nga funksioni
removeNode() sht nse nyja sht e vetmja nyje n listn e lidhur. Ky e bn
kt prcaktim duke vlersuar nse nyjet: e prparshme (previous) dhe e
ardhshme (next) jan NULL. Nse jan, nyja q fshihet sht e vetmja nyje n
list. Nyja pastaj largohet duke ia ndar vlern NULL antarve front dhe
back t klass LinkedList. Si ju kujtohet, funksionet q i nxjerrin t dhnat
nga lista e lidhur gjithmon verifikojn antart front dhe back pr t
prcaktuar nse jan t dy NULL. Ns po, ather funksioni e di se lista e
lidhur nuk ka asnj nyje.
Nse nyja nuk sht e vetmja nyje n listn e lidhur, funksioni removeNode()
duhet t verifikoj pastaj nse nyja q largohet sht n fillim (front) t lists s
lidhur. Ai e kontrollon kt duke verifikuar antarin e prparshm (previuous) t
nyjes. Nse nyja ndodhet n fillim (front) t lists s lidhur, ather antari
previuous sht NULL dhe funksioni removeNode() i ndrmerr hapat vijues
pr t larguar nyjen:
1. Nyja n t ciln pointohet nga antari antari next i nyjes s fshir
(larguar), i ndahet antarit front t lists s lidhur. Kjo e bn at
front (fillim) t lists s lidhur.
2. Antarit previous t nyjes q tani sht n fillim (front) t lists s
lidhur i caktohet vlera NULL, pr t treguar se nuk ka nyje t
prparshme, sepse sht larguar nyja e prparshme e saj. Kjo bhet si n
vijim. Mund t duket pak konfuze, por sht e leht t kuptohet nse e
ndani kt urdhr:
node->next->previous = NULL;

3. Le t themi se largohet NodeD. Nyja e ardhshme pr t sht NodeE.


Tani, zvendsoni emrat pr termat n kt shprehjeje:
NodeD->NodeE->previous = NULL;

sht e qart se antari previous i prket nyjes NodeE.


N procesin e tret, funksioni removeNode() e kontrollon (prcakton) nse nyja
q largohet sht n fund (back) t lists s lidhur. Kt e bn duke krahasuar
vlern e antarit next (t ardhshm) me NULL. Nse antari next sht
NULL, ather nyja q largohet sht nyja e fundit n listn e lidhur.
247

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;

Le t themi se largohet nyja NodeC. Nyje e prparshme (previuous) sht


NodeB. Tani zvendsoni emrat pr termat n kt urdhr:
NodeC->NodeB->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;

Zvendsoni node, previuos dhe next me emrat e nyjeve aktuale, pr t


kuptuar m mir veprimin:
NodeC->NodeB->next = NodeC->NodeD;

Tani q NodeB sht lidhur me NodeD, duhet t lidhet edhe NodeD me NodeB:
node->next->previous = node->previous;

Prsri, zvendsoni emrat e nyjeve:


NodeC->NodeD->previous = NodeC->NodeB;

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

Algoritmet dhe strukturat e t dhnave


Hapi i fundit sht q t prshtatet vlera e antarit size (madhsia) e klass
LinkedList, pr t reflektuar nj nyje m pak n listn e lidhur. Kjo do t bhet
prmes dekrementimit t vlers size.
Figura 4.17 paraqet listn e lidhur pas ekzekutimit t funksionit removeNode().
Vreni se NodeC nuk sht m n listn e lidhur dhe vlerat e indeksave jan
prshtatur pr t reflektuar numrin e ri t nyjeve n listn e lidhur.

Figura 4.17: Lista e lidhur pas largimit t NodeC

void 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;
}
delete node;
size--;
}

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

Algoritmet dhe strukturat e t dhnave


Node* temp_node = front;
for(int i=0; i<index; i++)
{
temp_node = temp_node->next;
}
removeNode(temp_node);
}

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

Algoritmet dhe strukturat e t dhnave

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;

N kt pik, nse nyja e re nuk sht insertuar as n fillim (front) e as n fund


(back) t lists s lidhur, ather funksioni insertNodeAt() supozon se nyja e re
do t insertohet diku n pjesn ndrmjet, t lists s lidhur.
Ky proces fillon me deklarimin e pointerit t quajtur temp dhe n fillim duke i
ndar atij nyjen n fillim (front) t lists s lidhur. Pastaj, funksioni e gjen
nyjen n indeksin e dhn. Kjo nyje zhvendoset (lvizet) djathtas (pr t krijuar
vendin pr nyjen e re). sidoqoft, nuk caktoeht n previous, caktohet n
poiztn e indeksit dhe nyja n at pozit lvizet djathtas. Kjo bhet duke prdoru
unazn for. Pr secilin iteracion, nyja e ndar antarit next t nyjes temp i
ndahet nyjes temp. Kjo tingllon uditshm, por do t jet e qart kur t
shiqohet se ka po ndodh.
Le t themi se jan pes nyje n listn e lidhur, si sht paraqitur n figurn
4.16. Nyja e fillimt (front) sht NodeA dhe vlera filestare e temp sht
NodeA. Le t themi se dshironi t insertoni nyjen e re NodeN n pozitn me
indeks 2.
254

Algoritmet dhe strukturat e t dhnave


Para iteracionit t par, front=NodeE. Gjat iteracionit t par, ja se ka
ndodh:
temp = temp->next
temp = NodeA->NodeB
temp = NodeB

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;

Pr sqarim m t leht, le t ndrrojm nyjet pr pointert:


new_node->next = NodeC;
new_node->previous = NodeC->NodeB;
NodeC->NodeB->next = new_node;
NodeC->previous = new_node;

Figura 4.18 paraqet listn e lidhur pas insertimit t nyjes s re NodeN, n


pozitn me indeksin 2 n list.

Figura 4.18: Nyja e re e quajtur NodeN sht vendosur n pozitn me indeks 2


n listn e lidhur.

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

Algoritmet dhe strukturat e t dhnave

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;
}

Klasa e zgjeruar LinkedList


Pasi u pa se si funksionojn zgjerimet individuale t klass LinkedList, ta
shohim n vazhdim aplikacionin n trsi. Aplikacioni sht ndar n tre fajlla:
demo.cpp, LinkedList.h dhe LinkedList.cpp. t tre fajllat jan paraqitur n kodin
n vijim. Fajllat LinkedList.h dhe LinkedList.cpp mund t prdoren me fajlla
specifik pr stek dhe queue, q jan par m hert.
Fajlli demo.cpp prmban aplikacionin n C++ i cili prdor klasn e zgjeruar
(plotsuar) LinkedList, pr t krijuar dhe manipuluar listn e lidhur. Fajlli
LinkedList.h prmban definicionet e nyjes dhe klass LinkedList. Fajlli
LinkedList.cpp prmban definicionet e funksioneve antare t klass
LinkedList.
N fajllin demo.cpp zhvillohet aksioni. Aplikacioni fillon me deklarimin e
instancs s klass LinkedList dhe pastaj ndarjen e instancs pointerit t quajtur
list.
Pastaj, thirret pes her funksioni appendNode(), i cili sht funksion origjinal i
klass LinkedList dhe shton nyje t re n listn e lidhur. Lista e lidhur e krijuar
pas thirrjes s fundit t funksionit appendNode() duket si ajo n krye t figurs
4.19

258

Algoritmet dhe strukturat e t dhnave

Figura 4.19 - N krye ndodhet lista e lidhur para se t largohet nyja.


N mes sht lista pas thirrjes s funksionit removeNodeAt(3).
N fund, lista pas thirrjes s funksionit deleteNode(20).
Kur t jet krijuar lista e lidhur, aplikacioni thrret funksionin removeNodeAt(3)
pr t larguar nyjen e lokalizuar n indeksin 3.
Pastaj, aplikacioni e thrret funksionin findNode(20), pr t lokalizuar indeksin
e nyjs q prmban vlern 20 (e dhna e saj, sht 20). Bazuar n listn e
paraqitur n figurn 4.19, funksioni findNode(20) kthen vlern e indeksit 1.
Pastaj thirret funksioni deleteNode(20) thirret dhe largon nga lista e lidhur nyjn
e cila ka vlern 20, si element i t dhns s saj. Lista e lidhur e paraqitur n
fund t figurs 4.19 ilustron gjendjen pas thirrjes s funksionit deleteNode(20).
Pastaj, n listn e lidhur insertohet nj nyje e re, duke thirrut funksionin
insertNodeAt(1,35). Ky funksion inserton nj nyje t re n indeksin 1 n listn e
lidhur dhe ia ndan vlern 35. Figura 4.20 paraqet listn e lidhur pas thirrjes s
funksionit insertNodeAt(1,35).
N vazhdim, thirret funksioni peek(3) pr t nxjerr (lexuar, shikuar) vlewrn e
nyjs n pozitn me indeks 3, n listn e lidhur. Bazuar n listn e paraqitur n
figurn 4.20, funksioni peek(3) do t kthej 50 si vler (e dhn) e nyjs n
pozitn me indeks 3.
259

Avni Rexhepi

Figura 4.20 - Lista e lidhur pas thirrjes s insertNodeAt(1).


Funksioni i fundit q thirret sht funksioni getSize(), i cili kthen madhsin e
lists s lidhur. Si shihet n fig. 4.20, lista e lidhur ka katr nyje, prandaj
funksioni getSize() kthen vlern 4.
Urdhri i fundit n aplikacionin demo prdor operatorin delete pr t larguar
instancn e klass LinkedList nga memoria.
//demo.cpp
#include <iostream>
using namespace std;
//LinkedList.h
// Node=Nyje; data=vlera;
// previous=ePerparshme; next=eArdhshme
struct Node
{
int data;
Node* previous;
Node* next;
};
class LinkedList
{
protected:
Node* front;
Node* back;
int size;
void removeNode(Node* node);
public:
LinkedList();
virtual ~LinkedList();
void appendNode(int);
void displayNodes();
void displayNodesReverse();
void destroyList();
void removeNodeAt(int);
int findNode(int);
void deleteNode(int);
void insertNodeAt(int,int);

260

Algoritmet dhe strukturat e t dhnave


int peek(int);
int getSize();
};
//LinkedList.cpp
//#include "LinkedList.h" (Nese i bartim ne header fajll te veant)
LinkedList::LinkedList()
{
front = NULL;
back = NULL;
size = 0;
}
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;
}
size++;
}
void LinkedList::displayNodes()
{
cout << "Elementet: ";
Node* temp = front;
while(temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}

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

Algoritmet dhe strukturat e t dhnave


}
delete node;
size--;
}
void LinkedList::removeNodeAt(int index)
{
if(index < 0 || index > size-1)
{
return;
}
Node* temp_node = front;
for(int i=0; i<index; i++)
{
temp_node = temp_node->next;
}
removeNode(temp_node);
}
int LinkedList::findNode(int data)
{
int index = 0;
Node* temp_node = front;
while(temp_node != NULL)
{
if(temp_node->data == data)
{
// kthe indeksin e nyjes
return index;
}
else
{
temp_node = temp_node->next;
index++;
}
}
return -1;
}
void LinkedList::deleteNode(int data)
{
Node* temp_node = front;

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

Algoritmet dhe strukturat e t dhnave


new_node->next = temp;
new_node->previous = temp->previous;
temp->previous->next = new_node;
temp->previous = new_node;
}
size++;
}
int LinkedList::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;
}
int LinkedList::getSize()
{
return size;
}
//Programi kryesor
int main()
{
LinkedList* list = new LinkedList();
list->appendNode(10);
list->appendNode(20);
list->appendNode(30);
list->appendNode(40);
list->appendNode(50);
list->displayNodes();
list->removeNodeAt(3);
list->displayNodes();
list->displayNodesReverse();
int index = list->findNode(20);
list->deleteNode(20);
list->insertNodeAt(1, 35);
list->displayNodes();

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;
}

Listat kundrejt vargjeve


Vargjet jan m t mira n qasjen e indeksuar, gjersa listat kan fuqin e tyre n
manipulimet e sekuencave n pozita arbitrare. T dy qasjet realizojn
operacionet e duhura pr stek dhe queue n mnyr efikase. Mirpo, vargjet jan
m efikase n aspekt t keshit (angl. cash), ndrsa listat ofrojn garanca pr
performansn e rastit m t keq.
Lista e lidhur njfish mund t garoj me listn e lidhur dyfish n shumicn e
aspekteve. Avantazhi i vetm i vargjeve ciklike ndaj vargjeve t pakufizuara
sht se ato mund t implementojn operacionet pushFront dhe popFront n
mnyr efikase.
Efikasiteti hapsinor sht poashtu shtje jotriviale. Lista e lidhura jan shum
kompakte, nse elementet jan shum m t mdhaja sesa pointert. Pr tipe t
elementeve t vogla, vargjet jan zakonisht m kompakte, sepse nuk ka
mbingarkim pr pointert. Kjo sht sigurisht e vrtet nse madhsit e
vargjeve jan t njohura paraprakisht ashtu q t mund t prdoren vargjet e
kufizuara. Vargjet e pakufizuara, kan nj pazar" ndrmjet efikasitetit
hapsinor dhe kopjimit t mbingarkimit gjat realokimit.

Radha me prioritet - Priority queue ADT


N praktik shpesh kemi t bjm me prioritete. Pr shembull, n listn e
obligimeve ditore, secila detyr ka nj rndsi (pesh) t caktuar. sht m me
rndsi t drgohet n servis vetura q ka nevoj pr riparim, sesa t shikohet
nj film i ri. Prveq shembujve nga jeta e prditshme, edhe detyrat e ndryshme
n kompjuter, punojn sipas prioritetit. Nj shembull i prmendur shpesh sht
algoritmi i Dijkstra-s pr shtegun m t shkurtr (Dijkstras Shortest Path
Algorithm). ADT e radhs s pritjes na mundson t punojm ne objektet t cilat
kan t shoqruar nj prioritet t caktuar.
N aplikacione kemi iftin (priority, item) (angl. priority-prparsia, prioritet;
angl. item-send, artikull, element), ku elementi sht nj e dhn ndihmse me
266

Algoritmet dhe strukturat e t dhnave


t ciln sht shoqruar prioriteti. Pr t ruajtur thjeshtsin, lm anash
prioritetet dhe marrim n konsiderim q pr elementet e1, e2: e1 < e2 do t
thot q e1 ka prioritet m t lart se e2.

Operacionet

PriorityQueue create()
krijon radhn e zbrazt t prioritetit

boolean isEmpty(PriorityQueue pq)


tregon nse radha e prioritetit pq sht e zbrazt

insert(PriorityQueue pq, Item e)


inserton elementin e n radhn e prioritetit pq

Item minimum(PriorityQueue pq)


tregon elementin minimal n radhn e prioritetit pq
Parakusht: pq nuk sht zbrazt

removeMin(PriorityQueue pq)
largon elementin minimumal nga radha e prioritetit pq
Parakusht: pq nuk sht zbrazt

destroy(PriorityQueue pq)
asgjson radhn e prioritetit pq

Radha e pritjes implementohet tek pirgu binar (binary heap).

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

Algoritmet dhe strukturat e t dhnave

Prparsit dhe t metat e rekursionit


Prparsia kryesore e rekursionit sht thjeshtsia e programimit. Gjat
prdorimit t rekursionit, programeri mundet t lr anash pr nj koh
problemin e tr dhe t koncentrohet n zgjidhjen e rastit aktual, rastit baz.
Pastaj, duke u kthyer prapa tek problemi i tr, zvhillohen rastet baz (sepse
sht e mundur t ket m shum se nj rast baz) dhe pika hyrse pr rekursion.
N ann tjetr, rekursioni ka disavantazhin serioz t prdorimit t sasis s
madhe t memories. Pr m tepr, pr shumicn e gjuhve programuese,
rekursioni prdor stekun pr ruajtjen e gjendjeve t t gjitha thirrjeve rekurzive
aktuale. Madhsia e stekut mund t jet shum e madhe, por megjithat e
kufizuar. Prandaj, rekursioni shum i thell mund t rezultoj me tejngarkim t
stekut (Stack Overflo). Pr t zgjidhur kt problem rekursioni mund t
simulohet, duke prdorur unazat dhe stekun.
Rekurzioni sht koncept themelor n matematik dhe n shkencat
kompjuterike. Definicioni i thjesht sht se programi rekurziv n gjuh
programuese sht ai i cili e thrret vetveten (njsoj si funksioni rekurziv n
matematik q sht ai funksion i cili sht i definuar n terma t vetvetes).
Programi rekurziv nuk mund ta thrras vetveten prgjithmon ose prndryshe
nuk do t ndalonte kurr (njsoj si funksioni rekurziv q nuk mund t definohet
n terma t vetvets prgjithmon, sepse do t bhej qarkor, cirkular), kshtu
q elementi tjetr prbrs themelor sht q duhet t ket nj kusht t ndalimit
kur programi do t pushoj s thirruri vetveten (si kur funksioni matematik nuk
sht i definuar n terma t vetvetes). T gjitha llogaritjet praktike mund t
shprehen n korniz rekurzive.
Studimi i rekurzionit sht i ndrlidhur me studimin e strukturave rekurzive t
njohura si pem (angl. tree). Pemt do t prdoren edhe pr t ndihmuar n
kuptimin e analizimin e programeve rekurzive edhe si struktura eksplicite t t
dhnave. Lidhja ndrmjet programeve rekurzive dhe pemve prdoret ashtu q
pemt prdoren pr t kuptuar programet rekurzive dhe programet rekurzive
prdoren pr t ndrtuar pemt. Gjithashtu ndrlidhja fundamentale mes pemve
dhe rekurzionit prdoret pr analiz t algoritmeve. Rekurzioni ndihmon n
zhvillimin e algoritmeve dhs strukturave t t dhnave elegante dhe efikase pr
t gjitha llojet e aplikacioneve.
N kt pjes do t analizojm programet rekurzive dhe strukturat e t dhnave,
si pajisje praktike. Do t diskutohet relacioni ndrmjet rekurrencs matematike
dhe programeve t thjeshta rekurzive, skemn themelore rekurzive t njohur si
divide and coquer (praj dhe sundo), e cila prdoret pr zgjidhjen e shum
problemeve. Pastaj do t shqyrtohet qasja e prgjithshme e implementimit t
programeve rekurzive e njohur si programimi dinakim, e cila ofron zgjidhje
269

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);
}

Relacionet e rekurrencs jan funksione t definuara n mnyr rekurzive


(Shiko shtojcn pr rekurrencn, n fund t librit). Relacioni i rekurrencs
definon funksionin domeni i t cili sht integer-a jonegativ ose sipas ndonj
vlere fillestare ose rekurzivisht n terma t vet vlerave t veta n integer-a m t
vegj. Ndoshta m i njohuri prej tyre sht funksioni i faktorielit, i cili definohet
si relacion i rekurrencs:
N!=N *(N 1)!,

pr N 1 me 0! = 1.

Ky definicion i prgjigjet direkt funksionit rekurziv n C++, q sht nj unaz e


thjesht, q kruen llogaritjen e njjt:
for (t = 1, i = 1; i <= N; i++)

270

Algoritmet dhe strukturat e t dhnave


t *= i;

Si do t shihet, gjithmon sht e mundshme q programi rekurziv t


transformohet n program jorekurzivm, q kryen llogaritjen e njjt. N ann
tjetr, pashtu mund t shprehim pa unaz secilin llogaritje q prfshin
prdorimin e unazave, duke prdorur rekurzionin.
Rekurzioni prdoret pasi q shpesh mundson shprehjen e algoritmeve
komplekse n form kompakte, pa sakrifikuar efikasitetin. Pr shembull,
implementimi rekurziv i funksionit t faktorielit shmanjg nevojn pr variabla
lokale. Kostoja e implementimit rekurziv mbahet nga mekanizmat e programit t
cilt prkrahin thirrjet e funksioneve, t cilat jan ekuivalente me stekun e
brendshm. Shum gjuh programuese moderne kan krijuar me kujdes
mekanizmat pr kt detyr. Prkundr avantazheve, shum leht mund t
ndodh q shkruhet funksioni rekurziv i cili sht jashtzakonisht joefikas,
prandaj duhet kujdes i madh dhe ushtrime t shumta pr t evituar
implementimet e vshtira.
Nse argumenti N sht tek, funksioni e thrret vetvent me argumentin 3N+1;
nse N sht ift me argumentin N/2. Me metodn e induksionit nuk mund t
vrtetojm se ky program ngalet, sepse jo t gjitha thirrjet rekurzive thrrasin
prdorimin me argument m t vogl se ai i dhn.
Programi 5.2. Nj program rekurziv jo i sigurt
int puzzle(int N)
{
if (N == 1) return 1;
if (N % 2 == 0)
return puzzle(N/2);
else return puzzle(3*N+1);
}

N rastin e faktorielit, programi 5.1 ilustron karakteristikn themelore t


programeve rekurzive: thirrja e vetvetes, me vler m t vogl t argumentit dhe
ka kushtin e ndrprerjes (ndalimit) n t cilin rezultati llogaritet direkt. Pr t
qen t bindur n saktsi, mund t prdorim metodn e induksionit, pr vrtetuar
se programi funksionon ashtu si synohet:

Ai e llogarit 0! (bazn).
Nn supozimin se llogarit k! pr k < N (hipoteza induktive), ai e
llogarit N!.

Duke arsyetuar n kt mnyr, mund t ofrojm nj shteg t shpejt t


zhvillimit t algoritmeve t cilat zgjidhin probleme komplekse.
271

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:

Duhet t zgjidhin rastin baz n mnyr eksplicite.


Secila thirrje rekurzive duhet t prfshij vlera m t vogla t
argumeteve.

Kto jan kushte t nnkuptojn q duhet t kemi prova valide induktive pr


secilin funksion rekurziv q shkruhet dhe shrbejn si udhzues i dobishm n
zhvillimin e zgjidhjeve t problemeve.
Nj prej algoritmeve m t vjetra t njohura, q daton para m shum se 2000
vitesh, sht metoda rekurzive e gjetjes s plotpjestuesit m t madh t
prbashkt pr dy numra t plot (integer), i definuar nga matematikani i lasht
Euklidi.
Programi 5.3. Algoritmi i Euclid-it
//gcd-greatest common divisor=pjestuesi me i madh i //perbashket
int gcd(int a, int b)
{
if (b == 0) return a;
return gcd(b, a % b);
}

Ku:
//funksioni gcd()
int gcd(int a, int b)
{
int temp;
while (b != 0)
{
temp = b;
b = a % b;
a = temp;
}
return a;
}

272

Algoritmet dhe strukturat e t dhnave


Duke pasur parasysh kushtet e prmendura, analizojm programet 5.2 dhe 5.3.
Programi 5.2 sht nj shembull interesant q ilustron nevojn pr nj argument
induktiv. sht funksion rekurziv q then rregulln e thirrjes rekurzive me
prfshirje t vlers m t vogl t argumentit, kshtu q nuk mund t prdorim
induksionin matematik pr ta kuptuar at. N t vrtet, nuk sht e ditur nse
llogaritja e tij prfundon pr do N, nse nuk ka kufizime pr madhsin e N-it.
Pr numra t plot t vegjl, q mund t shprehen si int, mund t verifikojm q
programi prfundon (si n Fig. 5.1), mirpo pr numra t mdhnj (p.sh., 64
bitsh), nuk e dijm nse ky program shkon n unaz t pafund.
Kjo sekuenc e ndrthurur e thirrjes s funksioneve n fund ndalet, por nuk
mund ta provojm (vrtetojm) se funksioni rekurziv n programin 5.2 nuk ka
ndrthurje t thell arbitrare pr disa argumente. Preferohen programet rekurzive
t cilat gjithmon thrrasin vetveten me argumente m t vogla.

Figura 5.1. Shembull i zinxhirit rekurziv t thirrjeve


Programi 5.3 sht implementim kompakt i algoritmit t Euklidit, pr gjetjen e
plotpjestuesit m t madh t prbashkt t dy numrave t plot. sht i bazuar n
vrojtimin q pjestuesi m i madh i prbashkt pr dy numra t plot x dhe y, ku
x>y, sht i njjt me pjestuesin m t madh t prbashkt t y dhe x%y (% moduli; mbetja nga plotpjestimi i x me y). Nj numr t, i pjeston dy numrat x
dhe y nse dhe vetm nse t pjeston t dy, y dhe x%y, sepse x sht i barabart
me x%y plus shumfishi i y. thirrjet rekurzive t bra pr nj shembull t ktij
programi, jan paraqitur n figurn 5.2. Pr algoritmin e Euklidit, thellsia e
rekurzionit varet nga tiparet aritmetike t argumenteve t tij (sht e njohur t
jet logaritmik).
Kjo sekuenc e ndrthurrur e thirrjeve t funksionit ilustron operacioniet e
algoritmit t Euklidit q zbulon se 314159 dhe 271828 jan relativisht numra
primar.

273

Avni Rexhepi

Figura 5.2. Shembull i algoritmtit t Euclid-it


Programi 5.4 sht nj shembull me thirrje t shumfishta rekurzive. sht nj
vlersues tjetr i shprehjeve, q kryen llogaritjet n shprehjet me prefiks dhe i l
vend rekurzionit n stekun eksplicit. N vazhdim do t kemi disa shembuj t
programeve rekurzive dhe ekuivalenteve t tyre me stek. Do t analizojm
relacionin specifik ndrmjet disa programeve t tilla.
Pr t vlersuar shprehjet me prefiks, ose i konvertojm numrat nga ASCII n
binar (n unan while n fund) ose kryejm operacionin e treguar nga karakteri
i par n shprehje n t dy operandet, t vlersuar rekurzivisht. Funksioni sht
rekurziv, por e prdor vargun global q prmban shprehjen dhe nj indeks t
karakterit aktual n shprehje. Indeksi avanson pas secils nnshprehje t
vlersuar.
Programi 5.4. Programi rekurziv pr vlersim t shprehjeve me prefiks.
char *a; int i;
int eval()
{ int x = 0;
while (a[i] == ' ') i++;
if (a[i] == '+')
{ i++; return eval() + eval(); }
if (a[i] == '*')
{ i++; return eval() * eval(); }
while ((a[i] >= '0') && (a[i] <= '9'))
x = 10*x + (a[i++]-'0');
return x;
}

N figurn 5.3 sht paraqitur operacioni i programit 5.4 n nj shembull t


shprehjes me prefiks. Thirrjet e shumfishta rekurzive maskojn serit
komplekse t llogaritjeve. Sikur shumica e programeve rekurzive, ky program
kuptohet m s miri induktivisht: duke supozuar se punon si duhet pr shprehje
274

Algoritmet dhe strukturat e t dhnave


t thjeshta, mund t bindemi se punon si duhet edhe pr shprehje komplekse. Ky
program sht shembulli thjesht i analizatorit rekurziv, q mund t prdoret
p.sh., edhe pr konvertimin e programeve nga C++ n kod makine.

Figura 5.3. Shembull i vlersimit t shprehjes me prefiks


Kjo sekuenc e ndrthurrur e thirrjeve t funksioneve ilustron operacionin e
vlersimit t algoritmit rekurziv t shprehjeve prefiks, n nj shembull t
shprehjes. Pr thjeshtsi, jan paraqitur argumentet e shprehjes. Vet algoritmi
asnjher nuk e vendos shtrirjen (gjatsin) e argumentit string t tij, por ai
merr at q i nevojitet nga pjesa e prparme e stringut.
Nj prov induktive precize se programi 5.4 e vlerson shprehjen si duhet sht
shum m e vshtir pr tu shkruar sesa provat pr funksionet me argumente
integjer, q jan diskutuar m par, e do t hasen edhe programe rekurzive dhe
struktura t t dhnave t cilat jan edhe m t komplikuara se kjo. Prandaj, n
prputhje me rrethanat, nuk synohet qllimi idealistik i orfrimit t provave
komplete induktive ose korrektsi pr secilin program rekurziv. N kt rast,
aftsia e programit pr t ditur se si t ndaj operandt q i prgjigjen
operatorve t dhn duke fillimisht misterioze (ndoshta pasi q nuk mund t
shohim menjher se si t bhet kjo ndarje n nivelin m t lart), por n fakt
sht llogaritje e drejtprdrejt (sepse rruga q duhet ndjekur n seciln thirrje t
funksionit sht padyshim e prcaktuar nga karakteri i par n shprehje).
N parim, mund t zvendsom unazn for me nj program rekurziv
ekuivalent. Shpeshher, programi rekurziv sht mnyr m natyrale pr t
shprehur llogaritjen sesa unaza for, kshtu q mund t prfitojm nga
mekanizmi i siguruar nga gjuha programuese q prkrah rekursionin. Mirpo,
ka nj kostro t fshehur, t ciln duhet ta kemi parasysh. Si u tha m par, kur
275

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

Algoritmet dhe strukturat e t dhnave


Programi 5.5. Shembuj t funksioneve rekurzive pr listat e lidhura
//count=numro, link=lidhje, traverse=pershko, remove=largo
int count(link x)
{
if (x == 0) return 0;
return 1 + count(x->next);
}
void traverse(link h, void visit(link))
{
if (h == 0) return;
visit(h);
traverse(h->next, visit);
}
void traverseR(link h, void visit(link))
{
if (h == 0) return;
traverseR(h->next, visit);
visit(h);
}
void remove(link& x, Item v)
{
while (x != 0 && x->item == v)
{ link t = x; x = x->next; delete t; }
if (x != 0) remove(x->next, v);
}

Disa ambiente programuese n mnyr automatike e detektojn dhe eliminojn


rekurzionin e fundit, kur veprimi i fundit sht thirrje rekurzive e funksionit,
sepse ai nuk sht domosdoshmrisht i nevojshm n mnyr strikte pr t
shtuar thellsin e rekurzionit, n raste t tilla. Ky prmirsim do t
transformonte n mnyr efektive funksinet pr numrim, prshkim dhe largim
n programin 5.5, n unaza, por nuk aplikohet n funksionin pr prshkimin n
drejtim t kundrt.
N vazhdim do t algoritmet rekurzive Praj e sundo, t cilat prezentojn
paradigma themelore t llogaritjeve, si dhe do t shqyrtohen strukturat e t
dhnave t cilat shrbejn si baz pr nj numr t madh t algoritmeve.

277

Avni Rexhepi

5.2. Algoritmet praj-e-sundo


Shum algoritme rekurzive prdorin dy thirrje rekurzive, secila prej t cilave
operon n afrsisht gjysmn e hyrjes (vlerave hyrse). Kjo skem rekurzive
ndoshta sht instanca m e rendsishme e paradigms s mirnjohurs divide
and conquer (praj e sundo), pr dizajnin e algoritmeve, e cila shrben edhe si
baz pr shum algoritme t rndsishme.
Si shembull, merrni parasysh gjetjen e maksimumit n mesin e N elementeve t
ruajtura n vargun: a[0], a[1],...a[N-1]. Kjo mund t kryhet thjesht
duke kaluar nj her npr tr vargun, si vijon:
for (t = a[0], i = 1; i < N; i++)
if (a[i] > t) t = a[i];

Zgjidhja rekurzive praj-e-sundo e dhn n vazhdim (Programi 5.6) sht


poashtu nj algoritm i thjesht (trsisht i ndryshm) pr t njjtin problem dhe
zakonisht prdoret pr t ilustruar konceptin praj-e-sundo.
Ky definicion e ndan vargun a[l], ..., a[r] n a[l], ..., a[m] dhe a[m+1],
... , a[r], gjen maksimumi n t dy pjest (rekurzivisht) dhe kthen elementin
m t madh prej tyre si maksimumi i tr vargut. Nse madhsia e vargut sht
ifte, t dy pjest jan t ndara n pjes t barabarta, prndryshe njra pjes ka
nj element m shum.
Programi 5.6. Divide-and-conquer pr gjetje t maksimumit
Item max(Item a[], int l, int r)
{
if (l == r) return a[l];
int m = (l+r)/2;
Item u = max(a, l, m);
Item v = max(a, m+1, r);
if (u > v) return u; else return v;
}

Qasja praj-e-sundo m s shpeshti prdoret pasi q ofron zgjidhje m t


shpejt sesa algoritmet e thjeshta iterative, por sht poashtu edhe e rndsishme
pr ekzaminim t afrt t mnyrs s kuptimit t natyrs s disa llogaritjeve
fundamentale.
Figura 5.4 paraqet thirrjet rekurzive t bra n programin 5.6. Edhe pse struktura
duket e komplikuar, ska arsye pr shqetsim, pasi q pr t vrtetuar se
programi funksionon bazohemi n induksion, kurse pr analiz t performanss
prdoret relacioni i rekurrencs.
278

Algoritmet dhe strukturat e t dhnave


Kjo sekuenc e thirrjeve t funksionit ilustron dinamikn e gjetjes s
maksimumit me algoritm rekurziv.

Figura 5.4. Qasja rekurzive pr gjetjen e maksimumit


Si zakonisht, vet kodi sygjeron vrtetimin me induksion, se ai kryen llogaritjen
e duhur:

Ai e gjen maksimumin e vargut me gjatsi 1 n mnyr eksplicite dhe


menhher.
Pr N>1, e ndan vargun n dy vargje me gjatsi m t vogl se N, e
gjen maksimumin e dy pjesve sipas hipotezs induktive dhe kthen
vlern m t madhe prej ktyre dy vlerave, e cila duhet t jet vlera
maksimale e tr vargut.

Pr m tepr, mund t prdoret struktura rekurzive e programit, pr t kuptuar


karakteristikn e performanss s tij.

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.

Zgjidhja TN = N 1 sht e drejtprdrejt, prmes induksionit. Nse madhsit


mbledhen deri n vlern m t vogl se N, prova se numri sht m i vogl se N1 e prcjell t njjtin argument induktiv.
Programi 5.6 sht reprezentues pr shum algoritme praj-e-sundo me
struktur saktsisht t njjt, por rastet tjera mund t dallojn n dy aspekte
primare. S pari, programi 5.6 kryen sasi konstante t puns n seciln thirrje t
funksionit, kshtu q koha e tij totale e ekzekutimit sht lineare. Agloritmet
tjera praj-e-sundo mund t kryejn m shum pun n seciln thirrje t
funksionit (si do t shihet n raste t tjera), kshtu q prcaktimi i kohs totale
t ekzekutimit krkon analiz m t ndrlikuar. Koha e ekzekutimit t
algoritmeve t tilla varet nga mnyra precize e ndarjes n pjes. S dyti,
programi 5.6 sht reprezentues i algoritmeve praj-e-sundo pr t cilat pjest
mblidhen pr t formuar trsin. Algoritmet tjera praj-e-sundo mund t
ndahen n pjes m t vogla t cilat prbjn m pak se problemi i tr ose pjes
t cilat mbulojn njra tjetrn, pr t prbr m shum se problemi i tr. Kto
algoritme prap se prap jan algoritme t rregullta rekurzive sepse secila pjes
sht m e vogl se trsia, por analizimi i tyre sht m i vshtir.
Shembulli i algoritmit t krkimit binar sht algoritm praj-e-sundo, q e
ndan problemin n dy pjes dhe pastaj punon vetm me njrn pjes.
N figurn 5.5 paraqitet prmbajtja e brendshme e stekut t mirmbajtur nga
ambienti programues, pr t prkrahur llogaritjen n figurn 5.4. Modeli i
parqitur sht idealistik, por jep pasqyr t dobishme t strukturs s llogaritjes
praj-e-sundo. Nse programi ka dy thirrje rekurzive, steku aktual intern
prmban nj vler q i korrespondon thirrjes s par gjersa ai funksion sht
duke u ekzekutuar (i cili prmban vlerat e argumenteve, variablave lokale dhe
adresn pr kthim), pastaj nj vler t ngjashme q i korrespondon thirrjes s
dyt t funksionit gjersa ai sht duke u ekzekutuar. Alternativa e paraqitur n
figurn 5.5 sht q t vendosen t dy vlerat n stek prnjher, duke mbajtur t
gjitha nn-detyrat e mbetura q t bhen n mnyr eksplicite n stek. Ky
aranzhim prvijon qartazi llogaritjen dhe vendos bazat pr skemat e
prgjithshme llogaritse, si ato q do t analizohen n pjest 5.6 dhe 5.8.

280

Algoritmet dhe strukturat e t dhnave


Kjo sekuenc sht nj reprezentim idealistik i prmbajtjes s stekut intern gjat
llgoaritjes s njjt pr figurn 5.4. Fillohet me indeksat e majt dhe t djatht t
tr nnvargut n stek. Secili rresht paraqet rezultatet e trheqjes (pop) s dy
indeksave dhe nse ata nuk jan t barabart, duke shtyer (push) katr indeksa,
t cilt ndajn nnvargun e majt dhe t djatht pasi nnvargu i trhequr t
ndahet n dy pjes. N praktik, sistemi mban adresat e kthimit (return) dhe
variablat lokale n stek, n vend se t bhet ky reprezentim specifik i puns q
duhet br, por ky model mjafton pr t prshkruar llogaritjen.

Figura 5.5. Shembull i dinamiks s stekut intern


Figura 5.6 paraqet strukturn e algoritmit praj-e-sundo pr gjetje t
maksimumit. sht struktur rekurzive: nyja n krye prmban madhsin e
vargut n hyrje, struktura pr nnvargun e majt sht vizatuar n ann e majt
dhe ajo pr nnvargun e djatht n ann e djatht. Kjo sht struktur e pems
dhe do t diskutohet n detaje n vazdhim. Pemt jan t dobishme pr t
kuptuar strukturat e programve q prfshijn thirrjet funksioneve t
ndrthurrura, n veanti t programeve rekurzive. Poashtu n figurn 5.6 sht
paraqitur pema e njjt, por me seciln nyje t shnuar me vlern kthyese pr
thirrjen prkatse t funksionit.

281

Avni Rexhepi

Figura 5.6. Struktura rekurzive pr alroritmin gjeje maksimumin (find-themaximum)


Algoritmi praj-e-sundo e ndan problemin e madhsis 11 n nj t madhsis
6 dhe nj t madhsis 5, pastaj problemin e madhsis 6 n dy probleme t
madhsis 3, e kshtu me radh, deri sa t arrij n problemet e madhsis 1.
Secili rreth n kto diagrame reprezenton nj thirrje t funksionit rekurziv, pr
nyjet e lidhura me to, ndrsa katrort paraqesin thirrjet pr t cilat prfundon
rekurzioni.
Asnj diskutim pr rekurzionin nuk do t ishte i kompletuar pa prmendur
problemin antik t kullave t Hanoit. Kemi tre kunja (tri shtylla) dhe N disqe, t
cilat vendosen n shtylla. Disqet jan me madhsi t ndryshme dhe fillimisht
jan t vendosur n njrn shtyll, sipas radhitjes prej m t madhit (disku N) n
fund deri tek m i vogli (disku 1) n krye. Detyra sht q t lvizet steku i
disqeve djathtas, nj nga nj, duke ju prmbajtur rregullave vijuese: 1-vetm nj
disk mund t lvizet n nj moment kohor; dhe 2-disku i madh nuk mund t
shkoj mbi t voglin.
Legjenda thot se kur monarkt e nj tempulli n Hanoi t prfundojn lvizjen e 64
disqeve t arta n tri shtulla diamanti, do t vije fundi i bots.
Duke pasur parasysh se problemi mund t paraqitet me fardo numri t disqeve,
n
numri i lvizjeve t nevojshme pr zgjidhje t problemit t kullave t Hanoit sht 2 -1,
ku n sht numri i disqeve.
Sikur legjenda t ishte e vrtet dhe sikur t mund t bhej nj lvizje e diskut pr
64
sekond, ather numri m i vogl i lvizjeve do t krkonte 2 -1 sekonda, q i bie
prafrsisht 585 miliard vite ose 18,446,744,073,709,551,615 lvizje pr t prfunduar
(ska arsye pr panik :) ).

282

Algoritmet dhe strukturat e t dhnave


Programi 5.7 jep zgjidhjen rekurzive t problemit. Ai specifikon se cili disk
duhet t lvizet n seclin hap dhe n cilin drejtim (+ do t thot lviz nj
shtyll djathtas, duke vazhduar ciklin n skajin e majt, nse lvizet nga shtylla
e skajit t djatht; ndrsa - do t thot lviz nj shtyll m majtas, duke
vazhduar ciklin n shtylln m t djatht kur kemi arritur skajin e majt).
Rekurzioni sht i bazuar n iden vijuese: pr t lvizur N disqet nj shtyll n
t djatht, s pari lvizen N-1 disqet n krye nj shtyll n t majt, e pastaj
zhvendosim diskun N nj shtyll n t djatht, e pastaj lvizen N-1 disqet nj
shtyll n t majt (n diskun N). Vrtetimi i zgjidhjes mund t bhet prmes
induksionit. N figurn 5.7 jan paraqitur lvizjet pr N=3 dhe thirrjet rekurzive
pr N=5. Modeli sht evident dhe mund t analizohet n detaje.

Figura 5.7. Kullat e Hanoit, rasti me 3 disqe


N rastin me 5 disqe, zhvendosen katr disqet e eprme, e pastaj lvizet disku i
5, pastaj zhvendosen katr disqet e eprme pr nj pozit majtas , e kshtu me
radh. Sekuenca e thirrjeve t funksionit paraqet rastin pr tri disqe. Sekuenca e
lvizjeve sht: +1 -2 +1 +3 +1 -2 +1, e cila paraqitet katr her n zgjidhje
(p.sh, 7 lvizjet e para)

283

Avni Rexhepi

Figura 5.7. Kullat e Hanoit, rasti me 5 disqe

Sekuenca e thirrjeve t funksionit


Struktura rekurzive e zgjidhjes tregon numrin e lvizjeve t krkuara.
Vetia 5.2. Algoritmi rekurziv praj-e-sundo pr problemin e kullave t
Hanoit, prodhon zgjidhjen q ka 2N 1 lvizje
Si zakonisht, sht e drejtprdrejt nga kodi se numri i lvizjeve plotson
rekurrencn. N kt rast, rekurrenca q potsohet nga numri i lvizjeve t
284

Algoritmet dhe strukturat e t dhnave


diskut sht e ngjashme me formuln 2.5:
TN = 2TN

+ 1, pr N 2 me T1 = 1.

Mund t verifikojm rezultatin e dhn drejtprdrejt me inuksion: kemi T(1)=21


1=1; dhe nse T(k)=2k 1 pr k < N, ather T(N) = 2(2N 1 1) + 1 = 2N
1.
Metoda e thjesht, jorekurzive, e cila do t jepte zgjidhjen e njjt me lehtsi.
Zgjidhja sht relevante pr shum probleme praktike.
I zhvendosim disqet n t djatht (rekurzivisht) duke zhvendosur t gjitha disqet,
(prveq atij t fundm) n t majt, e pastaj e lvizim diskun e fundm n t
djatht. Pastaj (rekurzivisht) zhvendosim kulln prapa n diskun e fundm.
Program 5.7. Zgjidhja e problemit t kullave t Hanoit
void hanoi(int N, int d)
{
if (N == 0) return;
hanoi(N-1, -d);
shift(N, d);
hanoi(N-1, -d);
}

Pr t kuptuar zgjidhjen e problemit t kullave t Hanoit, mund t shqyrtohet


edhe detyra m e thjesht e vizatimit t vizave ndarse t vizores. N secilin
inch t gjatsis, vizorja ka nj shenj n 1/2 inch, nj pak m t shkurtr n 1/4
inch, edhe m t shkurtr n 1/8 inch, e kshtu me radh. Detyra sht t
shkruhet programi i cili vizaton kto shenja n fardo rezolucioni t dhn,
duke supozuar se kemi n dispozicion funksionin mark(x,h), pr t br shenjn
h njsi t gjat n pozitn x.
Nse do t duhej rezolucioni 1/2 inch, reshkallzojm ashtu q detyra do t ishte
t vendoset shenja n do pik ndrmjet 0 dhe 2n (pa prfshir pikat skajore).
Prandaj, shenja n mes duhet t jet n njsi e lart, shenjat n mes t gjysms
s majt dhe t djatht duhet t jen (n-1) njsi t larta, e kshtu me radh.
Programi 5.8 sht algoritm i drejtprdrejt praj-e-sundo pr t realizuar kt
objektiv, ndrsa figura 5.8 paraqet at gjat operimit n nj shembull t vogl.
Thn rekurzivisht, idea e ktij funksioni sht si vijon. Pr t vendosur shenjat
n nj interval, s pari e ndajm at n dy pjes t barabarta. Pastaj, bjm
shenjat (e shkurtra) n gjysmn e majt (rekurzivisht), shenjn e lart n mes
dhe pastaj shenjat (e shkurtra) n gjysmn e djatht (rekurzivisht). Thn
iterativisht, figura 5.8 ilustron q funksioni bn shenjat m radh, nga e majta n
285

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.

Figura 5.8. Thirrjet e funksionit pr vizatim t vizores


Kjo sekuenc e thirrjeve t funksionit formon llogaritjen pr vizatimin e vizores
me gjatsi 8, duke rezultuar me shenjat n gjatsit: 1, 2, 1, 3, 1, 2, dhe 1.
Pr t vizatuar shenjat n vizore, vizatojm shenjat n gjysmn e majt, pastaj
shenjn m t gjat n mes, pastaj shenjat n gjysmn e djatht. Programi sht i
parapar pr prdorim me r-1 t barabart me fuqi t 2-shit, veti t ciln e ruan
n thirjet e tij rekurzive.
Programi 5.8. Algoritmi praj-e-sundo pr vizatim t vizores
//rule=vendos, mark=shenjo
void rule(int l, int r, int h)
{ int m = (l+r)/2;
if (h > 0)

286

Algoritmet dhe strukturat e t dhnave


{
rule(l, m, h-1);
mark(m, h);
rule(m, r, h-1);
}
}

Shihet se sekuenca e gjatsive sht saktsisht e njjt si sekuenca e lvizjeve


t disqeve n problemin e kullave t Hanoit. Prov e thjesht se jan identike
sht fakti se programet rekurzive t tyre jan t njjta. Thn ndryshe, pr t
vendosur se cili disk duhet t lvizet, mund t prdoren shenjat n vizore.
Pr m tepr, t dy zgjidhjet jan varianta t skems praj-e-sundo t
shembullit n programin 5.6. T tri rastet zgjidhin problemin e madhsis 2n
duke e ndar n dy probleme me madhsi 2n-1. Pr gjetjen e maksimumit, kemi
zgjidhje me koh lineare me madhsin e hyrjes; pr vizoren dhe kullat e
Hanoit, kemi zgjidhje me koh lineare me madhsin e daljes. Pr kullat e
Hanoit, normalisht mendojm pr zgjidhjen si t jet e kohs eksponenciale,
sepse e masim madhsin e problemit n terma t numrit t disqeve, n.
Vizatimi i shenjave n vizore prmes programit rekurzive sht i leht, por a ka
ndonj mnyr edhe m t thjesht pr t llogaritur gjatsin e shenjs s i-t,
pr nj i t dhn? Figura 5.9 paraqet nj proces t thjesht llogarits q jep
prgjigje n kt pyetje. Numri i i-t i shtypur, n t dy problemet, at t vizores
dhe at t kullave t Hanoit, nuk sht asgj tjetr prveq numrit t bitave 0 t
njpasnjshm, q e pasojn reprezentimin binar t i-s. Kjo veti mund t
provohet prmes induksionit me ant t korrespondencs me formulimin praje-sundo t procesit t shtypjes s tabels s numrave n-bitsh. Shtypni tablen e
numrave (n-1) bitsh, secili i paraprair me bitin 0, e pastaj tabeln e numrave
(n-1) bitsh t paraprir me bitin 1.

287

Avni Rexhepi

Figura 5.9. Numrimi binar dhe funksioni i vizores


Llogaritja e funksionit t vizores sht ekuivalent me numrimin e numrave t
zerove pasuese n numrat ift N-bitsh.
Pr problemin e kullave t Hanoit, implikimi i korrespondencs me numrat nbitsh sht algoritm i thjesht pr detyrn. Nj grumbull mund t lvizet nj
shtyll djathtas duke prsritur dy hapat vijues (deri n prfundim):
1. Lvize diskun e vogl n t djatht nse n sht tek (n t majt
nse n sht ift).
2. Bje lvizjen e vetme t lejuar q nuk e prfshin diskun e vogl.
Kjo i bie q, pasi t lvizet disku i vogl, dy shtyllat tjera prmbajn dy disqe,
njri me i vogl se tjetri. Lvizja e vetme e lejuar q nuk prfshin diskun e
288

Algoritmet dhe strukturat e t dhnave


vogl sht q t lvizet disku m i vogl n at m t madh. Secila lvizje tjetr
prfshin diskun m t vogl pr t njtjn arsye se secili numr tjetr sht tek
dhe secila shenj tjetr n vizore sht m e shkurtra.
Prov formale prmes induksionit, se secila lvizje tjetr n zgjidhjen pr kullat
e Hanoit prfshin diskun e vogl (duke filluar dhe mbaruar me lvizje t tilla)
sht instruktive: Pr n=1, ka vetm nj lvizje, e cila prfshin diskun e vogl,
kshtu q rregulla vlen. Pr n>1, supozimi se rregulla vlen pr n-1 implikon
faktin se ajo vlen pr n sipas konstruksionit rekurziv: zgjidhja e par pr n-1
fillon me lvizje t diskut t vogl dhe zgjidhja e dyt pr n-1 mbaron me lvizje
t diskut t vogl, kshtu q zgjidhja pr n fillon dhe mbaron me lvizje t
diskut t vogl. E vendosim nj lvizje q nuk e prfshin diskun e vogl
ndrmjet dy lvizjeve t cilat e prfshijn diskun e vogl (lvizja q e prfundon
zgjidhjen e par pr n-1 dhe lvizja q e fillon zgjidhjen e dyt pr n-1), ashtu q
rregulla q secila lvizje tjetr prfshin diskun e vogl, ruhet.
Pr dallim nga programi 5.8, vizoren mund ta vizatojm edhe duke vizatuar s
pari t gjitha shenjat n gjatsin 1, apstaj n gjatsin 2, e kshtu me radh.
Variabla t bart gjatsin e sjenjave dhe variabla j bart numrin e shenjave
ndrmjet dy thirrjeve sukcesive t gjatsis t. Unaza a jashtme for
inkrementon t-n dhe ruan vetin j=2t-1. Unaza e brendshme for vizaton t
gjitha shenjat n gjatsin t.
Programi 5.9. Programi jorekurziv pr vizatim t vizores
//rule=vendos, mark=shenjo
void rule(int l, int r, int h)
{
for (int t = 1, j = 1; t <= h; j += j, t++)
for (int i = 0; l+j+i <= r; i += j+j)
mark(l+j+i, t);
}

Programi 5.9 sht nj mnyr alternative e vizatimit t vizores q sht e


inspiruar nga korrespondenca (ndrlidhja) me numrat binar (shiko figurn 5.10).
Ktij versioni t algoritmit i referohemi se implementimi bottom-up (prej
posht, te lart). Nuk sht rekurziv, por sigurisht sht i sygjeruar nga algoritmi
rekurziv. Ndrlidhja mes algoritmeve praj-e-sundo dhe reprezentimit binar t
numrave shpeshher jep mendjeprehtsi pr analzin dhe zhvillimin e
versioneve t prmirsuara, si jan qasjet prej posht, te lart. Kjo qasje
mund t shqyrtohet pr t kuptuar dhe mundsisht pr t prmirsuar secilin
algoritm praj-e-sundo.

289

Avni Rexhepi

Figura 5.10. Vizatimi i vizores n renditjen posht-lart.


Pr t vizatuar vizoren n mnyr jorekurzive, i alternojm vizatimin e shenjave
me gjatsi 1 dhe kalimin e pozicioneve, pastaj alternojm vizatimet e shenjave t
gjatsis 2 dhe kalimet e pozicioneve t mbetura, pastaj alternojm vizatimet e
shenjave me gjatsi 3 dhe kalimet e pozitave t mbetura, e kshtu me radh.
Qasja prej posht-te lart prfshin riaranzhimin e radhs s llogaritjes kur
jemi duke vizatuar vizoren. Figura 5.11 paraqet nj shembull tjetr, ku
rirregullohet renditja e tri thirrjeve t funksioneve n implementimin rekurziv.
Kjo reflekton llogaritjen rekurzive n mnyrn e prshkruar hern e par: Vizato
shenjat e mesit, pastaj gjysmn e majt, pastaj gjysmn e djatht. Modeli i
vizatimit t shenjave sht kompleks, por sht rezultat i thjesht ndrrimit t dy
urdhrave n programin 5.8 dhe relacioni mes figurave 5.8 dhe 5.11 sht i
ngjashm me at q kan mes veti shprehjet aritmetike me prefiks dhe postfiks.
Kjo sht sekuenca e cila tregon rezultatin e vizatimit t shenjave para thirrjeve
rekurzive, n vend se ndrmjet tyre:

290

Algoritmet dhe strukturat e t dhnave

Figure 5.11. Funksioni pr vizatim t vizores (versioni preorder)


Vizatimi i shenjave n radhn si n firgurn 5.8 mund t jet m i preferuar ndaj
riaranzhimit t llogaritjeve t bra n programin 5.9 si sht treguar n figurn
5.11, sepse mund t vizatojm nj vizore arbitrarisht t gjat, nse imagjinojm
pajisjen pr vizatim e cila thjesht lviz n shenjn e ardhshme n rrotullim t
vazhdueshm. Ngjashm, pr t zgjidhur problemin e kullave t Hanoit, jemi t
kufizuar n prodhimin e sekuencs s lvizjes s disqeve n renditjen n t ciln
ato kryhen. N prgjithsi, programet rekurzive varen nga nnproblemet q
zgjidhen n renditje t veant (t caktuar). Pr llogaritjet tjera, si p.sh.,
programi 5.6, renditja n t ciln zgjidhet problemi sht e parndsishme. Pr
llogaritje t tilla, kufizimi i vetm sht se duhet t zgjidhen nnproblemet para
se t mund t zgjidhet problemi kryesor. T kuptuarit se kur e kemi
fleksibilitetin e rirenditjes s llogaritjeve, jo vetm q sht sekreti i suksesit n
dizajnim t algoritmeve, por ka edhe efekte direkte praktike n shum kontekste.
Pr shembull, jo shtje sht shum kritike kur shqyrtohet implementimi i
algoritmeve n procesor paralel.
Qasja prej posht-te lart i korresondon metods s prgjithshme t dizajnit t
algoritmeve ku kemi pr t zgjidhur problemin s pari duke zgjidhur
nnproblemet e thjeshta, e pastaj duke i kombinuar kto zgjidhje pr t zgjidhur

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.

Figura 5.12. Fraktal dy dimensional


Modelet gjeometrike t definuara n mnyr rekurzive, si kto n figurn 5.12
quhen fraktale. Nse prdoret ndonj figur fillestare m e komplikuar dhe
prfshihen thirrje rekurziva me t komplikuara (posaqrisht duk prfshir
funksione t definuara rekurzivisht n rrafshin real dhe kompleks), mund t
zhvillohen modele shum komplekse. Nj shembull tjetr sht ylli i Koch-ut i
demonstruar n figurn 5.13, i cili sht i definuar rekurzivisht, si vijon: Ylli
Koch i rendit 0 sht thjesht nj kodr, e cila prsritet pastaj me zevendsim
n secilin segment. Pra, ylli i rendit n sht yll i rendit n-1, ku secili segment
sht i zvendsuar me yllin e rendit 0, me shkallzim t duhur.

Figure 5.13. Fraktali rekurziv i Koch-ut


Sikur vizatimi i vizores dhe zgjidhja e kullave t Hanoit, kto algoritme jan
linear n numrin e hapava, por ai numr sht eksponencial n thellsin
maksimale t rekurziont. Ato mund t ndrlidhen drejtprdrejt edhe me
numrimin n sistemin numerik t caktuar.
Kto probleme jan mjaft interesante dhe ndrlidhja e tyre me numrat binar
sht befasuese, mirpo interesi kryesor sht se ofrojn nj kuptim t
paradigms themelore t dizajnit t algoritmeve, t ndarjes prgjysm dhe
zgjidhjes s dy gjysmave n mnyr t pavarur, q sht nj prej teknikave
kryesore.
292

Algoritmet dhe strukturat e t dhnave

Algoritmet themelore praj-e-sundo


Krkimi binar dhe sorti merge (shpjegohet n pjesn e sortimeve) jan
algoritme prototipike praj-e-sundo, t cilat ofrojn performans optimale t
garantuar pr krkim dhe sortim. Rekurrenca tregon natyrn e llogaritjeve
praj-e-sundo pr secilin algoritm. Krkimi binar e ndan problemin
prgjysm, e bn nj krahasim dhe pastaj bn thirrjen rekurzive pr njrn prej
gjysmave. Sorti merge e ndan problemin n gjysm, pastaj punon n t dy
gjysmat rekurzivisht, e pastaj bn N krahasime. Shum algoritme t tjera
zhvillohen me kto skema rekurzive.

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

Quicksort (q shpjegohet n pjesn e sortimeve) dhe pema e krkimit binar


(poashtu shpjegohet m von) paraqesin variacione t rndsishme t skems
themelor praj-e-sundo, ku problemi ndahet n nnprobleme t madhsis
k-1 dhe N-k, pr nj vler t k, e cila prcaktohet nga hyrja. Pr vler
hyrse t rastit, kto algoritme e ndajn problemin n nnprobleme q jan
mesatarisht sa gjysma e madhsis (si n rastin e sortit merge ose krkimit
binar).
Variacionet tjera n skemn themelore e q ia vlen t konsiderohen jan: ndarja
n pjes me madhsi t ndryshme, ndarja n m shum se dy pjes, ndarja n
pjes q mbulojn njra tjetrn. Kto bjn sasi t ndryshme t puns n pjesn
jorekurzive t algoritmit. N prgjithsi, algoritmet praj-e-sundo prfshijn
punn pr ndarjen e hyrjes n pjes ose bashkimin e rezultatit t procesimit t dy
pjesve t pavarura t zgjidhura t hyrjes, ose pr t ndihmuar gjrat pasi t jet
procesuar gjysma e hyrjes. Do t thot, mund t ket kod para, pas apo ndrmjet
thirrjeve rekurzive. Natyrisht, variacionet e tilla drgojn n algoritme m
komplekse sesa krkimi binar dhe sorti merge dhe jan m t vshtira pr tu
analizuar.
293

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

Algoritmet dhe strukturat e t dhnave

Figura 5.14. Struktura e algoritmit rekurziv pr numrat Fibonacci


N ann tjetr, sht e leht t llogariten N numrat e par Fibonacci, n nj koh
proporcionale me N, duke prdorur vargun:
F[0] = 0; F[1] = 1;
for (i = 2; i <= N; i++)
F[i] = F[i-1] + F[i-2];

Numrat rriten eksponencialisht, kshtu q vargu sht i vogl pr shembull,


F45=1836311903 sht numri m i madh Fibonacci q mund t reprezentohet si
integer 32 bitsh, kshtu q vargu me madhsi 46 kryen pun.
Kjo teknik jep nj mnyr t drejtprdrejt pr t fituar zgjidhjet numerike pr
fardo relacioni t rekurrencs. N rastin e numrave Fibonacci, mund t lm
anash fare vargun dhe t prcjellim vetm dy vlerat e prparshme. Pr shum
rekurrenca t tjera t hasura zakonisht, duhet t mirmbajm vargun me t gjitha
vlerat e njohura.
Rekurrenca sht funksion rekurziv me vlera integer. Diskutimi paraprak i
paragrafit t mparshm drgon n konkluzionin se mund t vlersojm fardo
funksionin t till duke llogaritur t gjitha vlerat e funksionit n renditjen q
fillon prej m t voglit, duke prdorur n secilin hap vlerat e llogaritura
paraprakisht, pr t llogaritur vlern aktuale. Ksaj teknike i referohemi si
teknika bottom-up dynamic programming (programimi dinamik prej posht
295

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.

Figura 5.15. Programimi dinamik top-down, pr llogaritje t numrave


Fibonacci
Kjo figur e thirrjeve rekurzive t prdorura pr t llogaritur F8 prmes
implementimit t programimit dinamik top-down t algoritmit rekurziv,
ilustron se si ruajtja e vlerave t llogaritura zvoglon koston prej asaj
eksponenciale (fig. 5.14) n at lineare.
Programi 5.10. Numrat Fibonacci (implementimi rekurziv)
Ky program, edhe pse kompakt dhe elegant, nuk sht i prdorshm pasiq q
merr koh eksponenciale pr t llogaritur FN. Koha e ekzekutimit pr t
llogaritur FN+1, sht e gjat 1.6 her sa koha e ekzekutimit pr t llogaritur
FN. Pr shembull, pasi q 9 > 60, nse vrejm se kompjuteri po merr
296

Algoritmet dhe strukturat e t dhnave


prafrsisht nj sekond pr t llogaritur FN, e dijm se do t merr m shum se
nj minut pr t llogaritur FN+9, dhe m shum se nj or pr t llogaritur FN+18.
int F(int i)
{
if (i < 1) return 0;
if (i == 1) return 1;
return F(i-1) + F(i-2);
}

Si shembull m kompleks (m i komplikuar), shqyrtoni knapsack problem


(problemin e ants s shpins).
Problemi Knapsack (angl. knapsack ant
e shpins), sht problem i optimizimit
kombinatorik. Pr nj bashksi t dhn t
elementeve, secili me nj pesh dhe vler t
caktuar, prcaktoni numrin e elementeve t
cilat do t prfshihen n koleksion (do t futen
n ant), ashtu q pesha totale sht m e
vogl ose baraz se kufiri i lejuar dhe vlera
sht sa m e madhe q t jet e mundur.
Prezentohet edhe si versioni n vijim: Hajni ka
gjetur nj thesar n nj sef, por n antn e tij,
ka vend pr nj pjes t vogl t elementeve,
kshtu q ai duhet t mbush antn, me
elementet q kan vlern m t madhe.

Pra, duhet gjetur kombinimi i elementeve, pr t maksimizuar vlern totale t


elementeve. Pr shembull, me elementet e paraqitura n figurn 5.16, anta
sht me madhsi 17, mund t mirren 5 A-ja (por jo gjasht), pr nj total t
vlers prej 20, ose nj D dhe j E, pr nj total t vlers prej 24, ose ndonj
kombinim tjetr. Qllimi sht q t gjindet nj algoritm efikas i cili disi do t
gjej maksimumin n mesin e t gjitha opsioneve t mundura, pr fardo
bashksie t elementeve dhe kapaciteti t ants.

297

Avni Rexhepi

Figura 5.16. Shembull i Knapsack


Nj instanc e problemit knapsack konsiston n kapacitetin e ants dhe
bashksin e elementeve me madhsi t ndryshme (dimensioni horizontal) dhe
vlerave (dimensioni vertikal). Kto figura paraqesin katr mnyra t ndryshme
pr t mbyshur antn me madhsi/kapacitet 17, dy prej t cilave drgojn n
vlern m t lart.

298

Algoritmet dhe strukturat e t dhnave

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

Figura 5.18a. Struktura rekurzive e algoritmit knapsack


Kjo pem paraqet strukturn e thirrjeve rekurzive t algoritmit t thjesht
rekurziv knapsack, n programin 5.12. Numri n seciln nyje reprezenton
kapacitetin e mbetur n ant. Algoritmi vuan problemin e njjt themelor t
performanss eksponenciale pr shkak t rillogaritjes masive pr nnproblemet
me prputhje, t cilat u shqyrtuan n llogaritjen e numrave Fibonacci (Fig. 5.14).
Ashtu si n bri n llogritjen e numrave Fibonacci, teknika e ruajtjes s vlerave
t njohura e redukon koston e algoritmit knapsack, nga eksponenticale n lineare
(Figura 5.18).

Figura 5.18b. Programimi dinamik top-down pr algoritmin knapsack


Sipas dizajnit, programimi dinamik eliminon t gjitha rillogaritjet n cilindo
program rekurziv, subjekt vetm t kushtit q mund tia lejojm vetes q t
ruajm vlerat e funksionit pr argumentet m t vogla sesa thirrja aktuale (n
pyetje).
Duke ruajtur vlerat q llogariten n nj varg statik (vlerat e t cilit inicializohen
n 0, n C++), evitojm rillogaritjen n mnyr eksplicite. Ky program llogarit
FN n koh proporcionale me N, n kontrast t zymt me kohn O(N) t
prdorur nga programi 5.10.
Programi 5.11. Numrat Fibonacci (Programimi dinamik)
//known=i/e njohur
int F(int i)
{ static int knownF[maxN]; //knownF = F e ditur
if (knownF[i] != 0) return knownF[i];
int t = i;
if (i < 0) return 0;
if (i > 1) t = F(i-1) + F(i-2);
return knownF[i] = t;
}

300

Algoritmet dhe strukturat e t dhnave


Vetia 5.3. Programimi dinamik redukton kohn e ekzekutimit t
funksioneve rekurzive, q t jet s shumti sa koha e krkuar pr t
vlersuar funksionin pr t gjitha argumentet m t vogla ose barazi me
argumentin e dhn, duke trajtuar koston e thirrjes rekurzive si konstant.
Pr problemin knapsack, kjo veti implikon se koha e ekzekutimit sht
proporcionale me NM. Prandaj, ne mund t zgjidhim problemin knapsack m
leht kur kapaciteti nuk sht i madh; pr kapacitete shum t mdha, krkesat
kohore dhe hapsinore mund t jen jashtzakonisht t mdha.
Edhe programimi dinamik bottom-up (prej posht-te lart), mund t aplikohet
pr problemin knapsack. N t vrtet, mund t prdorim qasjen bottom-up
seciln her q prdorim qasjen top-down, edhe pse duhet t kemi kujdes q t
sigurohemi q vlerat e funksionit llogariten n renditjen e duhur, ashtu q secila
vler q na duhet, t jet llogaritur kur t na duhet. Pr funksionet me argumente
t vetme integer, si kto t dyja q u shqyrtuan, thjesht vazhdojm n renditje
rritse t argumenteve; pr funksione m t komplikuara rekurzive, prcaktimi i
renditjes s duhur mund t jet sfid.
Pr shembull, nuk duhet t kufizohemi n funksione rekurzive me nga nj
argument t vetm integer. Kur kemi funksione me argumente t shumfishta
integer, mund t ruajm zgjidhjet e nnproblemeve t vogla n vargje
shumdimensionale, nga nj pr secilin agrument. Situatat tjera nuk prfshijn
fare argumente integer, por m par prdorin formulim diskret abstrakt t
problemit, q lejon dekompozimin e problemeve n probleme m t vogla.
Si edhe n rastin e numrave Fibonacci, mos e prdorni kt zgjidhje t
problemit (kt program), sepse do t merr koh eksponenciale dhe prandaj nuk
do t kompletoj ekzekutimin edhe pr probleme t vogla. Mirpo, sidoqoft,
paraqet zgjidhje kompakte e cila mund t prmirsohet leht (shiko programin
5.13). Ky kod supozon se elementet jan struktura me madhsi dhe vler, t
definuar prmes:
typedef struct { int size; int val; } Item;

dhe kemi nj varg me N elemente, t tipit Item. Pr secilin element t


mundshm, llogarisim (rekurzivisht) vlern maksimale q do t mund t arrihej
duke prfshir at element, e pastaj duke marr maksimumin e t gjitha atyre
vlerave.
Programi 5.12. Problemi Knapsack (implementimi rekurziv)
//knap=knapsack, space=hapesira, cap=kapaciteti
int knap(int cap)
{ int i, space, max, t;

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;
}

N programimin dinamik top-down, i ruajm vlerat e njohura; n


programimin dinamik bottom-up, i parallogarisim ato. N prgjithsi
preferojm programimin dinamik top-down ndaj atij bottom-up, sepse

sht transformim mekanik i zgjidhjes natyrale t problemit.


Radha e llogaritjes s nnproblemeve kujdeset pr vetveten.
Mund t mos kemi nevoj q t llogarisim prgjigjet pr t gjitha
nnproblemet.

Aplikacionet e programimit dinamik dallojn n natyrn e nnproblemeve dhe


n a sasin e informacioneve t nevojshme pr tu ruajtur lidhur me
nnproblemet.
Pik kritike e cila nuk mund t lihet anash sht se programimi dinamik bhet
joefektif kur numri i vlerave t mundshme t funksioneve t cilat mund t
nevojiten sht aq i madh sa q nuk mund ti lejojm vetes (t kemi
komoditetin) e ruajtjes (top-down) ose parallogaritjes (bottom-up) s t gjitha
vlerave. Pr shembull, nse M dhe madhsit e elementeve jan madhsi 64bitshe ose numra floating-point (me presje t lvizshme) n problemin
knapsack, ne nuk do t jemi n gjendje ti ruajm vlerat duke i indeksuar n nj
varg. Ky dallim shkakton m shum sesa bzdi t vogl paraqet vshtirsi
fundamentale. Nuk ka zgjidhje t mir t njohur pr probleme t tilla ka arsye t
besohet se zgjidhjet e tilla nuk ekzistojn.
Programimi dinamik sht teknik e dizajnit t algoritmeve q sht e
prshtatshme pr problemet e avansuara. Programimi dinamik top-down sht
teknik baz pr zhvillimin e implementimeve efikase t algoritmeve rekurzive
dhe sht vegl pr kdo q mirret me dizajnin dhe implementimin e
algoritmeve.
Ky modifikim mekanik i kodit t programit 5.12 redukon kohn e ekzekutimit
prej asaj eksponenciale n at lineare. Thjesht i ruajm vlerat e funksionieve t
cillat llogariten, pastaj i thrrasim vlerat e ruajtura kurdo q na duhen (duke
prdorur vlerat rezerv pr t reprezentuar vlerat e panjohura), n vend se t
bhen thirrjet rekurzive. Ruajm indeksin e elementit, ashtu q t mund t
302

Algoritmet dhe strukturat e t dhnave


rikonstruktojm prmbajtjen e ants pas llogaritjes, nse dshirojm q:
itemKnon[M] t jet n ant, prmbajtja e mbetur sht e njjt sikur pr
knapsack-un optimal t madhsis M-itemKnon[M].size ashtu q
itemKnon[M-items[M].size] sht n ant, e kshtu me radh.
Programi 5.13. Poblemi Knapsack (programimi dinamik)
// space=hapsira; maxKnown=max_i_njohur;
// unknown=i_panjohur; itemKnown=elementi_i_njohur
int knap(int M)
{ int i, space, max, maxi = 0, t;
if (maxKnown[M] != unknown) return maxKnown[M];
for (i = 0, max = 0; i < N; i++)
if ((space = M-items[i].size) >= 0)
if ((t = knap(space) + items[i].val) > max)
{ max = t; maxi = i; }
maxKnown[M] = max; itemKnown[M] = items[maxi];
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

Pemt prdoren pr t prshkruar tiparet dinamike t algoritmeve.


Ndrtojm dhe prdorim struktura eksplicite t t dhnave t cilat jan
realizim konkret i pemve.

Veq kemi par shembuj t ktyre prdorimeve. Ne dizajnojm algoritmet pr


problemet e lidhjes t cilat jan t bazuara n strukturn e pems dhe
shpjegojm strukturn e thirrjeve t algoritmeev rekurzive prmes strukturs s
pems.
Pemt i hasim shpesh n jetn e prditshme dhe koncepti themelor sht i
njohur. Pr shembull, njerzit ruajn t dhnat pr paraardhrsit dhe pasardhsit
prmes pems familjare (trungut familjar) dhe terminologjia e prgjithshme vjen
pikrisht prej ktij prdorimi. Shembull tjetr sht skema e organizimit t
garave sportive, ose skema organizative e ndonj organizate t madhe. Ky
prdorim prkujton dekompozimin hierarkik q i karakterizon algoritmet praje-sundo. Shembull tjetr sht pema e analizs gramatikore t fjalis, n pjest
prbrse t saj; pemt e tilla jan t lidhura ngusht me procesimin e gjuhve
programuese. Figura 6.1 paraqet nj shembull tipik t pems nj q e
prshkruan strukturn e librit.

Figura 6.1 - Pema


Pr secilin entitet ka nj nyje. secila nyje sht e lidhur me nyjet pasuese
prmes lidhjeve dhe ato paraardhse.
N aplikacionet kompjuterike, nj prej prdorimeve m familjare t strukturs s
pems sht organizimi i sistemit t fajllave. Fajllat i ruajm npr folder-a t
cilt jan t definuar rekurzivisht si sekuenc e folderave dhe fajllave.
304

Algoritmet dhe strukturat e t dhnave


Definicioni rekurziv prseri reflekton natyrn rekurzive t dekompozimit dhe
sht identik me definicionin e nj tipi t zakonshm t pems.

Fig.6.2 - Rreprezentimi i strukturws sw pemws si bashkwsi tw ndwrthurrura,


kllapa tw ndwrthurrura, zhvendosje e shkallwzuar dhe si pemw.

+
/

a
b

d
c

*
e

Fig.6.3 - Paraqitja nw formw tw pemws, e shprehjes: (a+(b/c)*(d-e*f)

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

Figura 6.4 - Llojet e pemve


N figurn 6.4 jan paraqitur disa shembuj t pemve: pema binare, pema
ternare, me rrnj dhe e lir. Kjo figur ilustron shum koncepte themelore t
cilat do t diskutohen dhe definohen.
Pema sht bashksi (koleksion) jo i zbrazt i nyjeve dhe degve, q i plotson
disa kushte t caktuara. Nyja sht objekt i thjesht q mund t ket emr dhe
mund t bart edhe informata t tjera t shoqruara. Dega sht nj lidhje
ndrmjet dy nyjeve. Shtegu n pem sht nj list e degve t ndryshme n t
cilin nyjet e njpasnjshme lidhen prmes degve n pem. Nj tipar definues i
pems sht se ekziston saktsisht nj shteg q i lidh cilatdo dy nyje. Nse ka
m shum se nj shteg ndrmjet ndonj ifti t nyjeve ose nse nuk ka shteg
ndrmjet ndonj ifti t nyjeve, ather kemi t bjm me graf, jo me pem. N
306

Algoritmet dhe strukturat e t dhnave


ann tjetr, mund t thuhet se pema sht rast specifik i grafit. Nj bashksi e
pemve t ndara, quhet pyll (angl. forest).
Pema e rrnjzuar (ose pema me rrnj) sht ajo pem n t ciln e
prcaktojm nj nyje si rrnt t pems. N shkencat kompjuterike, normalisht
termi pem i referohet pems me rrnj dhe prdoret termi pem e lir pr t ju
referuar strukturs s prgjithshme t pems. N pemn me rrnj, cilado nyje
sht rrnj e nnpems q prbhet prej saj dhe prej nyjeve nn t.
Ekziston saktsisht nj shteg ndrmjet rrnjs dhe secils nyje tjetr n pem.
Definicioni nuk prfshin drejtimin (kahjen) n deg. Normalisht, i mendojm
degt si t jen t gjitha t drejtuara prej rrnjs teposht ose t gjitha t
drejtuara kah rrnja, varsisht prej aplikacionit. Zakonisht, pema me rrnj
vizatohet si pem e prmbysur, me rrnjn n maje (edhe pse fillimisht duket si
jo e natyrshme) dhe flasim pr nyjen y si nyje q ndodhet nn nyjen x (dhe
x mbi y) nse nyja x sht n shtegun prej y kah rrnja (do t thot, y
sht lidhur me x prmes degs q nuk kalon npr rrnj). Secila nyje (prveq
rrnjs) ka saktsisht nj nyje prmbi vetn, e cila quhet prind. Nyjet
drejtprdrejt nj nyje quhen fmijt e saj. Terminologjia familjare prdoret edhe
pr nivelet si gjyshrit ose vllezrti/motrat pr nyjet e nivelit t njjt, me
prind t njjt.
Nyjet t cilat nuk kan fmij, quhen gjethe ose nyje fundore. Nyjet t cilat
kan s paku nj fmij, ndonjher quhen edhe nyje jo-fundore. N pemt t
cilat prdoren pr t prezentuar strukturn e thirrjeve t algoritmeve rekurzive,
nyjet jofundore (rratht) paraqesin thirrjet e funksioneve me thirrje rekurzive,
ndrsa nyjet fundore (katrort) paraqesin thirrjet e funksioneve pa thirrje
rekurzive.
N disa aplikacione, sht shum me rndsi mnyra n t ciln jan renditur
fmijt e secils nyje, kurse n t tjerat nuk sht me rndsi. Nj pem e
renditur sht pema me rrnj n t ciln renditja e fmijve n seciln nyje
sht e prcaktuar (specifikuar). Pemt e renditura jan reprezentim natyral:
p.sh., fmijt i vendosim sipas nj radhe t caktuar kur e vizatojm pemn. N t
vertet n shum reprezentime konkrete natyrale ka renditje t nnkuptuar dhe
ky prcaktim sht shum i rndsishm kur shqyrtohet prfaqsimi i pemve n
kompjuter.
Nse secila nyje duhet t ket nj numr t caktuar t fmijve, q paraqiten n
nj renditje specifike, ather kemi t bjm me pemn M-are. N pemn e till,
sht e zakonshme t definohen nyjet eksterne speciale t cilat nuk kan fmij.
Pastaj, nyjet eksternale mund t sillen (veprojn) si nyje t rrejshme pr
referenc nga nyjet t cilat nuk kan numrin e specifikuar t fmijve. N
veanti, pema m e thjesht M-are sht pema binare. Pema binare sht pem e
renditur e cila prbhet nga dy lloje t nyjeve: nyjet eksterne q nuk kan fmij
307

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.

Pemt binare dhe M-are


Pemt e renditura
Pemt me rrnj
Pemt e lira

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;

q n fakt sht kodi n C++ pr definicionin 5.1. Nyjet prbhen prej


elementeve dhe ifteve t pointerve pr n nyje, t cilve ju referohemi si
lidhje. Kshtu p.sh., operacionin abstrakt t lvizjes n nnpemn e majt e
implementojm me referencn me pointer si: x = x->l. (l pr left, angl. leftmajtas).

308

Algoritmet dhe strukturat e t dhnave

Figura 6.5 - Reprezentimi i pems binare


Ky reprezentim standard mundson implementim efikas t operacioneve t cilat
prdoren pr t lvizur npr pem, nga rrnja e teposht, por jo pr operacionet
t cilat prdoren pr t lvizur te lart pems, nga fmija tek prindi. Pr
algoritmet t cilat krkojn operacione t tilla, mund t shtojm nj lidhje t
tret n seciln nyje, e cila do t pointoj n prindin. Kjo alternativ sht e
ngjashme me at t lists s lidhur dyfish. Sikur n rastine listave t lidhura
dyfish, nyjet e pems i mbajm n nj varg dhe i prdorim indeksat n vend t
pointerve, si lidhje pr situatat e caktuara.
Pr shkak t mundsive t ndryshme t reprezentimit, do t mund t zhvillonim
tipin abstrakt t t dhnave (ADT) t pems binare, i cili do t enkapsuloj
operacionet e rndsishme t cilat do t dshirojm ti kryejm dhe q ndan
prdorimin dhe implementimin e ktyre operacioneve. Megjithat, nuk do ta
prdorim kt qasje, sepse

M s shpeshti prdoret reprezentimi me dy lidhje (dy pointera)


Pemt prdoren pr t implementuar ADT t niveleve m t larta dhe
fokusi mbetet tek to
Prdorim algoritme efikasiteti i t cilave varet nga reprezentimi i veant
fakt q mund t humbet n nj ADT.

Kshtu, reprezentimi binar i paraqitur n figurn 6.5 sht fundamental. Pr


listat e lidhura, operacionet elementare ishin insertimi dhe largimi i nyjeve. Per
reprezentimin standard t pemve binare, operacionet e tilla nuk jan
elementare, pr shkak t lidhjes s dyt. Nse dshirojm t largojm nj nyje
nga pema binare, duhet t bashkrendojm problemin themelor se do t duhet t
trajtojm dy fmij pasi t jet larguar nyja, por vetm nj prind. Kto jan tri
operacione natyrale t cilat nuk e kan kt vshtirsi: insertimi i nyjes n fund
(zvendso lidhjen Null me lidhjen pr nyjen e re), largimi i gjethes (zvendso
lidhjen e saj me Null) dhe kombinimi i dy pemve duke krijuar rrnj t re me
lidhje t majt n njrn pem dhe me lidhje t djatht n pemn tjetr. Kto
operacione prdoren gjersisht gjat manipulimit t pemve binare.

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.

Figura 6.6. Reprezentimi i pems


Reprezentimi i pems duke mbajtur listn e lidhur t fmijve t secils nyje
sht ekuivalent me reprezentimin e saj si pem binare. Diagrami lart, n t
djaht, paraqet reprezentimin e pems majtas lart, prmes lists s lidhur t
fmijve, me listn e implementuar n lidhjet e djathta t nyjeve dhe me seciln
lidhje t majt t nyjes duke pointuar n nyjen e par n listn e lidhur t
310

Algoritmet dhe strukturat e t dhnave


fmijve t saj. Diagrami n t djatht posht, paraqet nj version t riaranzhuar
t diagramit lart dhe qartazi reprezenton pemn binare n skajin e majt posht.
Pra, mund t shqyrtojm pemn binare si prfaqsues t pems.
Vetia 6.1. Ekziston nj lidhje nj-me-nj ndrmjet pemve binare dhe pyjeve t
renditura.
Kjo lidhje sht paraqitur n figurn 6.6. Mund t paraqesim fardo pylli si
pem binare, duke br lidhjen e majt t secils nyje q t pointoj n fmijn
m t majt t saj dhe lidhjen e djatht t secils nyje q t pointoj n
vllaun/motrn n t djatht.
Definicion 6.4. Pema me rrnj (ose pema e parenditur) sht nj nyje (e quajtur
rrnj) e lidhur me nj multiset t pemve me rrnj (multiseti i till quhet nj
pyll i parenditur).
Pema ku renditja n t ciln shqyrtohen fmijt e nyjes nuk sht e rndsishme
sht pem e parenditur. Pema e parenditur mund t definohet edhe si pem t
parenditura t prbra nga nj bashksi e relacioneve prind-fmij ndrmjet
nyjeve. Kjo zgjidhje duket t ket nj lidhje t vogl me strukturat rekurzive q
jan duke u shqyrtuar, por ndoshta sht reprezentim konkret q sht m i sakti
pr nocionin abstrakt.
Mund t zgjedhim q pemn e parenditur, n kompjuter ta reprezentojm
prmes nj peme t renditur, duke ditur se shum pem t ndryshme t renditura
mund t reprezentojn t njjtn pem t parenditur. Me t vrtet, problemi i
kundrt i prcaktimit nse dy pem t ndryshme t renditura reprezentojn ose
jo t njjtn pem t parenditur (problemi i izomorfizmit t pems) sht i
vshtir pr tu zgjidhur.
Tipi m i prgjithshm i pems sht ai ku nuk ka nyje rrnj t dalluar. Pr t
definuar si duhet pemn e parrnj, t parenditur ose t lir, duhet t jepet
definicioni i grafit.
Definicion 6.5. Grafi sht nj bashksi e nyjeve s bashku me bashksin e
degve t cilat i lidhin iftet e nyjeve t ndryshme (me m s shumti nj deg q
lidhe cilindo ift t nyjeve).
Mund t prfytyrojm fillimin nga nj nyje dhe vazhdimin npr nj deg deri
tek nyja e lidhur n t, e pastaj duke vazhduar npr nj rrug prej asaj nyje, n
nj nyje tjetr, e kshtu me radh. Sekuenca e degve t cilat drgojn prej nj
nyje n nj nyje tjetr n kt mnyr, pa asnj nyje q paraqitet dy her, quhet
shteg i thjesht (angl. simple path). Grafi sht i lidhur nse ka nj shteg t
thjesht q lidh cilindo ift t nyjeve. Shtegu q sht i thjesht, por n t cilin
nyja e par dhe e fundit jan e njjta nyje quhet cikl (fillimi dhe mbarimi n t
njjtn nyje).
311

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:

G ka N 1 deg dhe asnj cikl.


G ka N 1 deg dhe sht i lidhur.
Saktsisht nj shteg i thjesht lidhe secilin ift t nyjeve n graf.
G sht i lidhur, por nuk mbetet i lidhur nse largohet cilado nyje.

Cilido prej ktyre kushteve sht i nevojshm dhe i mjaftueshm pr t provuar


(vrtetuar) tri t tjerat. Formalisht, duhet t zgjedhim njrin prej tyre q t
shrbej si definicion i pems s lir, mirpo joformalisht lejojm q t gjitha s
bashku t shrbejn si definicion.
Ne reprezentojm pemn e lir thjesht si koleksion t degve. Nse zgjedhim
q t reprezentojm pemn e lir si nj pem t parenditur, t renditur soe edhe
binare, duhet t pranojm se, n prgjithsi, ka shum mnyra t ndryshme pr
t reprezentuar seciln pem t lir.
Abstraksioni i pems paraqitet si nevoj shpeshher dhe dallimet e diskutuara n
kt pjes jan t rndsishme, sepse njohja e abstraksioneve t ndryshme t
pemve sht shpeshher esenciale n gjetjen e algoritmeve efikase dhe
strukturave gjegjse t t dhnave, pr problemin e dhn. Shpesh punojm
drejtprdrejt me reprezentimin konkret t pemve pa pasur kujdes pr
abstraksionin e veant, por shum her prfitojm nga puna me abstraksionin e
duhur t pems, e pastaj duke shqyrtuar reprezentimet konkrete. Ka shum
shembuj t ndryshm.
N vazhdim do t paraqesim nj numr t tipareve themelore matematike t
pemve, t cilat do t jen t dobishme n dizajnin dhe analizn e algoritmeve t
pemve.

Tiparet matematike t pemve binare


Pasi q m s shpeshti prdoren pemt binare, do t fokusohemi n tiparet
matematike t tyre. T kuptuarit e tipareve themelore sht baz pr t kuptuarit
e karakteristikave t performanss s algoritmeve t ndryshme t cilat hasen jo
vetm n prdorimin e pemve binare si struktur eksplicite e t dhnave, por
gjithashtu edhe pr algoritmet rekurzive praj-e-sundo dhe aplikacioneve t
tjera t ngjashme.
Vetia 6.2. Pema binare me N nyje interne ka N+1 nyje eksterne.
Kjo veti provohet prmes induksionit: pema binare pa asnj nyje interne ka nj
nyje eksterne, kshtu q vetia vlen pr N=0. Pr N>0, cilado pem binare me N
312

Algoritmet dhe strukturat e t dhnave


nyje interne ka k nyje interne n nnpemn e saj t majt dhe N-1-k nyje interne
n nnpemn e saj t djatht, pr ndonj k ndrmjet 0 dhe N-1, pasi q rrnja
sht nj nyje interne. Sipas hipotezs induktive, nnpema e majt ka k+1 nyje
eksterne dhe nnpema e djath ka N-k nyje eksterne, pr nj total prej N+1
nyjesh.
Vetia 6.3. Pema binaer me N nyje interne ka 2N lidhje/deg: N-1 lidhje pr n
nyjet interne dhe N+1 nyje pr n nyjet eksterne.
N cilndo pem me rrnj, secila nyje, prveq rrnjs, ka nj prind unik, dhe
secila deg lidh nj nyje me prindin e saj, kshtu q jan N-1 lidhje q lidhin
nyjet interne. Ngajshm, secila prej N+1 nyjeve eksterne ka nj lidhje, pr n
prindin e saj unik.
Karakteristika e performanss s shum algoritmeve varet jo vetm n numrin e
nyjeve n pemn e shoqruar, por edhe n tiparet e ndryshme strukturale.
Definicion 5.6. Niveli i nyjes n pem sht pr nj m i lart se niveli i prindit
t saj (me rrnjn n nivelin 0). Lartsia e pems sht maksimumi i niveleve t
nyjeve t pems. Gjatsia e shtegut t pems sht shuma e t gjitha niveleve t
t gjitha nyjeve t pems. Gjatsia e shtegut intern t pems binare sht sa
shuma e niveleve t t gjitha nyjeve interne t pems. Gjatsia e shtegut
eksternal t pems binare sht sa shuma e niveleve t t gjitha nyjeve eksterne
t pems.
Mnyr e prshtatshme pr t llogaritur gjatsin e shtegut t pems sht q t
mblidhen, pr do k, produkti i k dhe numrit t nyjeve n nivelin k.
Kto madhsi gjithashtu kan definicione t thjeshta rekurzive t cilat rrjedhin
drejtprdrejt prej definicioneve rekurzive t pemve dhe pemve binare. Pr
shembull, lartsia e pems sht 1 m e madhe sesa maksimumi i lartsis s
nnpemve t rrnjs s saj dhe gjatsia e shtegut t pems me N nyje sht
shuma e gjatsive t shtigjeve t nnpemve t rrnjs s saj plus N-1.
Madhsit gjithashtu ndrlidhen drejtprdrejt me analizn e algoritmeve
rekurzive. Pr shembull, pr shum llogaritje rekurzive, lartsia e pems
korresponduese sht saktsisht thelsia maksimale e rekurzionit ose madhsia
e stekut t nevojshm pr t prkrahur llogaritjen.
Vetia 6.4. Gjatsia e shtegut eksternal t cilsdo pem binare me N nyje interne
sht 2N m e madhe sesa gjatsia e shtegut intern.
Kjo mund t provohet (vrtetohet) me induksion, por nj prov alternative (q
poashtu funksionon edhe pr vetin 5.6) sht instruktive. Vreni se cilado pem
binare mund t konstruktohet prmes procesit vijues: filloni me pemn binare q
prbehet prej nj nyjeje eksterne. Pastaj, prsritni N her si n vijim: zgjedhni
nj nyje eksterne dhe zvendsojeni me nj nyje t re interne me dy nyje
313

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 ,

pasi q jan N+1 nyje eksterne. Ky inekuacion implikon vetin e shpallur:


gjatsia e rastit m t mir (angl. best-case) sht saktsisht e barabart me lg
N, e rrumbullaksuar n numrin e plot m t afrm.

Figura 6.7. Tri pem binare me nga 10 nyje interne


Pema binare majtas, ka lartsin 7, gjatsin e shtegut intern 31 dhe gjatsin e
shtegut ekstern 51. Pema trsisht e balansuar (n mes) me 10 nyje interne ka
lartsin 4, gjatsin e shtegut intern 19 dhe gjatsin e shtegut ekstern 39 (asnj
pem binare me 10 nyje nuk ka vlera m t vogla pr kto madhsi). Pema e
degjeneruar (n skajin e djatht) me 10 nyje interne, ka lartsin 10, gjatsin e
shtegut intern 45 dhe gjatsin e shtegut ekstern 65 (asnj pem binare me 10
nyje nuk ka vlera m t mdhaja pr kto madhsi).
Vetia 6.6. Gjatsia e shtegut intern t pems binare me N nyje interne sht s
paku N*lg(N/4) dhe m s shumti N(N-1)/2.
Rasti m i keq (angl. worst-case) dhe rasti m i mir (angl. best-case) arrihen pr
t njejtat pem si ato t referuara n diskutimin e vetis 5.8 dhe t paraqitur n
figurn 6.7. Gjatsia e shtegut intern t rastit m t keq sht: 0+1+2+...+(N
314

Algoritmet dhe strukturat e t dhnave


1)=N(N1)/2. Pema e rastit m t mir ka (N+1) nyje eksterne n lartsin jo

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)

Fig. 6.8 Pema binare


Nse prcillet rruga e kaluar ather mund t shihet se seciln nyje e vizitojm
nga tri her (n seciln nyje hyjm nga tri her): n rrug e sipr, kur
vizitojm nyjen hern e par (Preorder), n kthim nga fmija i majt (Inorder)
dhe n kthim nga fmija i djatht (Postorder).

Preorder

Postorder
Inorder

Fig. 6.9 Prshkimi i nyjeve t pems


Algoritmet e ndryshme, bjn kryerjen e operacioneve n momente t ndyshme,
duke zgjedhur kryerjen e operacioneve n seciln nyje, n ndonjrn prej
vizitave t nyjes. Kjo zgjedhje ka ndikim n mnyrn e kryerjes s
operacioneve, paraqitjen dhe rezultatet. P.sh., prshkimi i pems nga figura 5.x
dhe regjistrimi i karaktereve n seciln nyje n renditjen me t ciln hasen,
fitojm renditjet vijuese:

316

Algoritmet dhe strukturat e t dhnave

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

Fig. 6.10 Prshkimi i pems shprehjet


Kto tri forma t shprehjes rezultojn me mnyrn e paraqitjes s tyre:
-

prshkimi preorder rezulton me notacionin prefix;


prshkimi postrorder gjeneron notacionin postifx, dhe
prshkimi inorder, rezulton me notacionin konvencional, infix (edhe
pse pa kllapat e nevoshme pr t qartsuar prioritetet e operatorve)

Prshkimi rekurziv i pems


Funksioni rekurziv n vijim e merr si argument nj lidhje pr n pem dhe e
thrret funksionin visit me seciln prej nyjeve t pems si argument. Kshtu si
sht, funksioni implementon prshkimin preorder (rendi paraprak, ose
pararendja); nse e lvizim thirrjen e funksionit visit ndrmjet thirrjeve
rekurzive, do t kemi prshkimin inorder (n rend); dhe nse e lvizim thirrjen
e funksionit visit pas thirrjeve rekurzive, do t kemi prshkimin postorder
(rendi pasues, pasrendja).
Programi 6.1- Prshkimi rekurziv i pems
// traverse=prshko,visit=vizitol
// l=left(majt), r=right(djatht)
void traverse(link h, void visit(link))
{
if (h == 0) return;
visit(h);
traverse(h->l, visit);
traverse(h->r, visit);
}

Fillojm me shqyrtimin e procesit pr pemt binare. Pr listat e lidhura, kishim


dy opcione themelore: proceso nyjen dhe pastaj prcjelle lidhjen (n t cilin rast
mund ti vizitonim nyjet me radh) ose prcille lidhjen e pastaj proceso nyjen
(n t cilin rast do t mund t vizitonim nyjet n renditjen e kundrt). Pr pemt
317

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:

Preorder, ku vizitojm nyjen, pastaj vizitojm nnpemn e majt dhe at


djatht
Inorder, ku vizitojm nndegn e majt, pastaj vizitojm nyjen, pastaj
vizitojm nndegn e djath
Postorder, ku vizitojm nndegn e majt dhe nndegn e djath, e pastaj
vizitojm nyjen

Kto funksione mund ti implementojm me lehtsi me nj program rekurziv,


si sht paraqitur n programin 6.1, i cili sht prgjithsim direkt i programit
5.5 pr prshkimin e lists s lidhur. Pr t implementuar prshkimin n
renditjet tjera, bjm permutacion t thirrjeve t funksionit n programin 6.1, n
mnyrn e duhur. Figura 6.12 paraqet renditjen n t ciln vizitohen nyjet n nj
pem t marrur si shembull, pr seciln renditje. Figura 6.11 paraqet sekuencn
e thirrjeve t funksionit q ekzekutohet kur thirret programi 6.1, n pemn
shembull t figurs 6.12.
Kjo sekuenc e thirrjeve t funksionit konstituon prshkimin preorder pr
pemn n figurrn 6.12.

318

Algoritmet dhe strukturat e t dhnave

Figura 6.11 - Thirrjet e funksionit n prshkimin Preorder

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

Figura 6.12. Renditjet e prshkimit t pems


320

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

Algoritmet dhe strukturat e t dhnave


Kto sekuenca paraqesin rendin n t cilin vizitohen nyjet me rastin e prshkimit
t pems, pre-order (majtas), inorder (n mes) dhe post-order (djathtas).
Procesi i njjt rekurziv n t cilin jan t bazuara metodat e ndryshme t
prshkimit t pems ve sht hasur m par, n programet rekurzive praj-esundo (shih figurn 5.8 dhe at 5.11) dhe n shprehjet aritmetike. Pr shembull,
kryerja e prshkimit preorder i prgjigjet vizatimit t shenjave n vizore s
pari, e pastaj thirrjeve rekurzive (shih figurn 5.11); kryerja e prshkimit
inorder i prgjigjet lvizjes s disqeve t mdha n zgjidhjen e problemit t
kullave t Hanoit n mes t thirrjeve rekurzive t cilat i lvizin t gjitha t tjerat;
kryerja e prshkimit postorder i prgjigjet vlersimit t shprehjeve postfiks, e
kshtu me radh. Kto korrespondenca na japim pamje t drejtprdrejt n
mekanizmat prapa prshkimit t pems. Pr shembull, ne e dijm se secila nyje
tjetr n prshkimin inorder sht nj nyje eksterne, pr t njjtn arsye si
secila lvizje tjetr n problemin e kullave t Hanoit prfshin diskun e vogl.
sht e dobishme t shqyrtohet edhe implementimi jorekurziv i cili e prdor
nj stek eksplicit. Pr thjeshtsi, fillojm duke shqyrtuar nj stek abstrakt i cili
mund t mbaj elementet ose pemt, t inicializuar me pemn q duhet
prshkuar. Pastaj, hyjm n unaz, ku trheqim (pop) dhe procesojm vlern e
kreut (top) n stek, duke vazhduar gjersa steku t jet i zbrazt. Nse entiteti i
trhequr sht nj element, e vizitojm at; nse entiteti i trhequr sht pem,
ather kryejm sekuencn e veprimeve push q varet nga renditja e
dshiruar:

Pr preorder, e shtyejm (push) nndegn e djatht, pastaj t majtn dhe


pastaj nyjen.
Pr inorder, e shtyejm s pari nndegn e djatht, pastaj nyjen dhe
pastaj nndegn e majt.
Pr postorder, e shtyejm n stek nyjen, pastaj nndegn e djatht dhe
pastaj nndegn e majt.

Ky funksion jorekurziv i bazuar n stek, sht funksionalisht ekuivalent me


homologun e tij rekurziv (programin 6.1).
Programi 6.2 - Prshkimi preorder (jorekurziv)
//traverse=pershko, link=lidhje, s-steku
void traverse(link h, void visit(link))
{ STACK<link> s(max);
s.push(h);
while (!s.empty())
{
visit(h = s.pop());
if (h->r != 0) s.push(h->r);

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.

Figura 6.13 - Prmbajtja e stekut pr algoritmet e prshkimit t pems


Kto sekuenca tregojn se prmbajtja e stekut pr prshkimin e pems preorder
(majtas), inorder (qendr) dhe postorder (djathtas), pr nj model t idealizuar t
llogaritjes, t ngjashm me at q u prdor n figurn 5.5, ku vendosim nj
element dhe dy nndegt e tij n stek, n renditjen e treguar.
Skema e prshkruar sht konceptuale dhe prmbledh t tri metodat e
prshkimit, por implementimet q prdoren n praktik jan pak m t thjeshta.
Pr shembull, pr preorder, nuk kemi nevoj q t shtyejm nyjet n stek (ne
vizitojm rrnjn e secils pem q e trheqim (pop)) dhe prandaj ne mund t
prdorim nj stek t thjesht i cili prmban vetm nj tip t elementeve (deg t
pems) siur n implementimin jorekurziv n programin 6.2. Steku i sistemit q
prkrah programin rekurziv prmban adesat e kthimit dhe vlerat e
argumenteve, m par sesa elementet ose nyjet, por sekuenca aktuale n t ciln
i bjm llogaritjet (vizitojm nyjet) hst e njjt pr metodat rekurzive dhe ato
t bazuara n stek.

322

Algoritmet dhe strukturat e t dhnave

Prshkimi me renditje t nivelit (angl. Level-order traversal)


Shkmbimi i strukturs themelore t t dhnave n prshkimin preorder
(programi 6.2) nga steku n queue (radh), transformon prshkimin n levelorder (prshkim me renditje t nivelit).
Programi 6.3. Prshkimi Level-order
//put=vendose (inserto ne queue), get=merre(nxjerrja nga queue)
void traverse(link h, void visit(link))
{ QUEUE<link> q(max);
q.put(h);
while (!q.empty())
{
visit(h = q.get());
if (h->l != 0) q.put(h->l);
if (h->r != 0) q.put(h->r);
}
}

Strategjia e katrt natyrale e prshkimit sht thjesht t vizitohen nyjet n pem


ashtu si paraqiten n faqe, duke lexuar prej lart te posht dhe prej t majts kah
e djathta. Kjo metod quhet prshkimi level order(angl. level-nivel) sepse
nyjet n secilin nivel, paraqiten sbashku, me rend. Figura 6.14 paraqet mnyrn
se si vizitohen nyjet e pems (nga figura 6.12). n renditjen sipas nivelit (levelorder).

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

Figura 6.14 - Prshkimi level-order


Kjo sekuenc ilustron rezultatin e vizitimit t nyjeve n pem n renditjen prej
lart te posht dhe prej t majts kah e djathta.
324

Algoritmet dhe strukturat e t dhnave


Mrrekullisht, mund t prfitojm prshkimin level-order duke zvendsuar
queue-n pr stekun n programin 6.2, siq sht paraqitur n programin 6.3. Pr
prshkimin preorder, prdorim struktur t t dhnave LIFO (Last In Firs Out
stek), ndrsa pr renditje level-order prdorim struktur t t dhnave FIFO
(Firs In First Out queue). Kto programe meritojn studim t kujdesshm,
sepse reprezentojn qasjet e organimzimit t puns q mbetet pr tu br q
dallojn n nj mnyr esenciale. N veanti, level-order nuk i korrespondon
implementimit rekurziv q ndrlidhet me strukturn rekurzive t pems.
Preorder, postorder dhe level-order jan t definuara mir edhe pr rastin e
pyjeve. Pr ti br definicionet konsistente, mendoni pr pyllin si nj pem me
rrnj imagjinare. Pastaj, rregulla preorder sht vizito rrnjn, pastaj seciln
nnpem, rregulla postrorder sht vizito seciln nnpem, pastaj rrnjn.
Rregulla level-order sht e njjt si pr pemt binare. Implementimet direkte t
ktyre metodave jan prgjithsime t drejtprdrejta t programeve t prshkimit
preorder t bazuara n stek (programet 6.1 dhe 6.2), pr pemt binare q sapo u
shqyrtuarn. Shqyrtimi i procedurs m t prgjithsuar t implementimeve bhet
n pjesn e prshkimit t grafit.

Algoritmet rekurzive t pems binare


Algoritmet e prshkimit t pems ilustrojn faktin themelor se jemi t drejtuar
n shqyrtimin e algoritmeve rekurzive pr pemt binare, pr shkak t vet
natyrs s ktyre pemve si struktura rekurzive. Shum detyra pranojn
algoritmet direkte rekurzive praj-e-sundo, t cilat n esenc i prgjithsojn
algoritmet e prshkimit. Pema procesohet duke procesuar rrnjn dhe
(rekurzivisht) nndegt e saj; llogaritjen mund ta bjm para, ndrmjet apo pas
thirrjeve rekurzive (ose ndoshta n t trijat).
Shpeshhere na duhet t gjejm vlerat e parametrave t ndryshm struktural pr
pemn, kur na sht dhn vetm nj lidhje pr n pem. Pr shembull,
programi 6.4 prmban funksionet rekurzive pr llogaritjen e numrit t nyjeve n
pem dhe lartsin e pems s dhn. Funksionet pasojn drejtprdrejt ngavetia
6.6. Asnjri prej ktyre funksioneve nuk varet prej radhs n t ciln procesohen
thirrjet rekurzive: ata procesojn t gjitha nyjet n pem dhe kthejn prgjigjen e
njjt, nse pr shembull, i ndrrojm renditjet e thurrjeve rekurzive. Nuk
llogariten aq leht t gjith parametrat. Pr shembull, programi pr llogaritje
efikase t gjatsis s shtegut intern t pems binare sht m sfidues.
Ne mund t prdorim procecura t thjeshta rekurzive si jan kto pr t msuar
tiparet themelore strukturale t pemve.

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;
}

Nj funksion tjetr q sht shum i dobishm kurdo q shkruajm programe t


cilat procesojn pemt sht ai q shtyp ose vizaton pemn. Pr shembull,
programi 6.5 sht procedur rekurzive q shtyp pemn (nyjet e pems) n
formatin e paraqitur n figurn 6.15. Mund t prdorim skemn e njjt
rekurzive pr t vizatuar reprezentime m t hollsishme t pemve.

Figura 6.15. Shtypja e pems (inorder dhe preorder)


Rezultati majtas rezulton nga prdorimi i programit 6.5 n pemn e shembullit
nga figura 6.12 dhe shfaq strukturn e pems n mnyr t ngjashme me
reprezentimin grafik t cilin jemi duke e shfrytzuar, t rrotulluar pr 90 shkall.
Rezultati djathtas sht prej programit t njjt por me urdhrin e shtypjes t
zhvendosur n fillim; shfaq strukturn e pems n formatin e zakonshm t
prvijimit.

326

Algoritmet dhe strukturat e t dhnave


Programi 6.5 sht nj prshkim inorder nse shtypim elementin para thirrjeve
rekurzive, fitojm prshkimin preorder, i cili sht ilustruar n figurn 6.15. Ky
format sht i afrm me at q mund ta prdorim pr shembull pr shtypjen e
trungut familjar ose pr t listuar fajllat e sistemit t fajllave t bazuar n pem
ose pr t br nj prmbledhje (prvijim) t dokumentit t shtypur. Pr
shembull, brja e prshkimit preorder n pemn n figurn 6.1 jep versionin e
tabels s prmbajtjes s librit.
Shembulli i par i programit q ndrton nj struktur eksplicite t pems binare
sht i shoqruar me aplikacionin pr gjetje t maksimumit. Qllimi i jon sht
q t bjm nj gar (turne, dyluftim): pema binare n t ciln secili element n
nyjen interne sht kopje e elementit m t madh prej dy fmijve. N veanti,
elementi n rrnj sht kopja e elementit m t madh n gar. Elementet n
gjethe (nyjet q nuk kan fmij) prbjn t dhna me interes dhe pjesa tjetr e
pems sht struktur e t dhnave q na mundson gjetjen e elementit m t
madh, n mnyr efikase.
Programi 6.5. Funksioni pr shtyjen e shpejt t pems
//printnode=shtype nyjen, item=elementi, show=paraqite
void printnode(Item x, int h)
{ for (int i = 0; i < h; i++) cout << " ";
cout << x << endl;
}
void show(link t, int h)
{
if (t == 0) { printnode('*', h); return; }
show(t->r, h+1);
printnode(t->item, h);
show(t->l, h+1);
}

Ky program rekurziv prcjell lartsin e pems dhe prdor kt informat pr


zhvendosje (dhmbzim) t shtypjes s reprezentimit t pems, q mund t
prdoret pr t debug-uar programet pr procesim t pemve (shih figurn
6.1). Supozohet se elementet n nyje jan t tipit Item, pr t cilin operatori
<< sht definuar prmes mbingarkimit (angl. overloading).
Programi 5.19 sht program rekurziv i cili ndrton turneun prej elementeve n
nj varg. sht nj zgjedrim i programit 5.6, prdor strategjin praj-e-sundo:
pr t ndrtuar turneun pr nj element t vetm, ne krijojm (dhe kthejm)
gjethen q prmban nj element. Pr t ndrtuar turneun pr N>1 elemente,
prdorim strategjin prqaj e sundo: ndaji elementet n gjysm, ndrto turnet
pr seciln gjysm dhe krijo nyje t er me lidhje pr n t dy turnet dhe menj
element q sht kopja e elementit m t madh n rrnjt e t dy turneve.
327

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.

Figura 6.16. Pema eksplicite pr gjetjen e maksimumit (turneut)


Kjo figur paraqet strukturn eksplicite t pems q konstruktohet nga programi
6.6, prej hyrjes: A M P L E. Elementet e t dhnave ndodhen n gjethe. Secila
nyje interne ka kopjen e elementit m t madh nga dy fmijt e saj, kshtu q
prmes induksionit, elementi m i madh ndodhet n rrnj.
Programi 6.6. Konstruktimi i turneut.
//item=elementi/nyja; l-left, r-right
struct node
{ Item item; node *l, *r;
node(Item x)
{ item = x; l = 0; r = 0; }
};
typedef node* link;
link max(Item a[], int l, int r)
{ int m = (l+r)/2;
link x = new node(a[m]);
if (l == r) return x;
x->l = max(a, l, m);
x->r = max(a, m+1, r);
Item u = x->l->item, v = x->r->item;
if (u > v)
x->item = u; else x->item = v;

328

Algoritmet dhe strukturat e t dhnave


return x;
}

Ky funksion rekurziv, e ndan vargun a[l], ... , a[r] n dy pjes: a[l], .


. ., a[m] dhe a[m+1], ..., a[r], ndrton turneun pr t dy pjest
(rekurzivisht) dhe bn turneun pr vargu e plot duke prcaktuar lidhjet n nyjen
e re pr n turnet e ndrtuara rekurzivisht dhe duke caktuar elementin e saj n
m t madhin nga rrnjt e dy turneve t ndrtuara rekurzivisht.
Implementimet e bazuara n pem pr shum ADT t queue-ve jan t
rndsishme. Poashtu shum algoritme jan t bazuara n pemt binare t
krkimit dhe sfit pr implementimin dhe prdorimin e strukturave t tilla sht
q t sigurohet se algoritmi mbetet efikas edhe pas nj serie t gjat t
insertimeve, largimeve dhe operacioneve t tjera.
Si shembull t dyt i programit q ndrton pemn binare sht modifikimi i
programit pr vlersimin e shprehjeve me prefiks (Programi 5.4), pr t
konstruktuar pemn q reprezenton shprehjen me prefiks, n vend se vetm t
vlersohet ajo (shih figurn 6.17). Programi 6.7 prdor skemn e njjt
rekurzive sikur programi 5.4, por funksioni rekurziv kthen lidhjen pr n pem,
e jo vlern. Pr secilin karakter n shprehje krijojm nj nyje t re t pems:
nyjet q i korrespondojn operatorve kan lidhjet (pointert) tek operandt e
tyre dhe gjethet prmbajn variablat (ose konstantet) t cilat jan hyrjet pr
shprehjen.

Figura 6.17. Pema e analizs


Pema sht konstruktuar nga programi 6.7 pr shprehjen prefiks: * + a * * b c +
d e f. Kjo sht nj mnyr natyrale pr t reprezentuar shprehjen: secili operand
n gjethe (t ciln e paraqesim si nyje eksterne) dhe secili operator duhet t
aplikohet n shprehjet e reprezentuara nga dega e majt dhe e djath e nyjes q
prmban operatorin.
Programet prkthyese si jan kompajlert shpesh prdorin reprezentim t till
intern t pemve pr programet, sepse pemt jan t dobishme pr shum
qllime. Pr shembull, mund t imagjinojm operand q i korrespondojn
variablave t cilat marrin vlerat dhe mund t gjenerojm kod t makins pr t
329

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;
}

Duke prdorur strategjin e njjt q u prdor pr t vlersuar shprehjet prefix


(programi 5.4), ky program krijon pemn e analizs prej shprehjes prefix. Pr
thjeshtsi, supozojm se operandt jan karaktere t vetme. Secila thirrje e
funksionit rekurziv krijon nj nyje t re me karakterin e ardhshm prej hyrjes, si
shenj (angl. token). Nse tokeni sht operand, kthejm nyjen e re; nse sht
operator, caktojm pointert e majt dhe t djatht pr n pemn e ndrtuar
(rekurzivisht) pr t dy argumentet.

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

Algoritmet dhe strukturat e t dhnave

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).

Figure 6.18. Thirrjet e funksionit tek Depth-firstsearch


Kjo sekuenc e thirrjeve t funksioneve prbn krkimin thellsia-s-pari pr
grafin n figur. Pema e cila ilustron strukturn e thirrjeve rekurzive, quhet
pema e krkimit thellsia-s-pari.

331

Avni Rexhepi

Figura 6.19. Krkimi Depth-first dhe krkimi breadth-first


332

Algoritmet dhe strukturat e t dhnave


Krkimi thellsia-s-pari (majtas) lviz prej nyjes n nyje, duke u kthyer prapa
n nyjen e prparshme pr t tentuar mundsin e ardhshme sa her q ka
provuar seciln mundsi n nyjen aktuale. Krkimi Gjersia-s-pari (angl.
Breadth-first search), shterron t gjitha mundsit n nj nyje, para se t lviz
n tjetrn.
Dallimi ndrmjet krkimit thellsia-s-pari dhe prshkimit t prgjithshm t
pems (programi 6.1) sht se duhet t kujdesemi n mnyr ekslicite q t mos
vizitojm nyjet t cilat ve jan vizituar. N pem, asnjher nuk hasim n nyje
t tilla. Vrtet, nse grafi sht pem, krkimi rekurziv thellsia-s-pari duke
filluar nga rrnja sht ekuivalent me prshkimin preorder.
Vetia 6.7. Krkimi Depth-first krkon koh proporcionale me V + E n
grafin me V nyje dhe E deg, duke prdor reprezentimin me list t
fqinjsis.
N reprezentimin me list t fqinjsis, ka nj nyje t lists q i korrespondon
secils deg n graf dhe nj pointer t koks s lists, q i prgjigjet secils nyje
n graf. Krkimi thellsia s pari, i prekt t gjitha, m s shumti nga nj her.

Fig. 6.20 - Lista e fqinjsis pr grafin nga figura 6.19.

Krkimi thellsia s pari


Programi 6.8. Krkimi thellsia-s-pari (Depth-first search).
//visit=vizito; visited=vizituar
void traverse(int k, void visit(int))
{ visit(k); visited[k] = 1;
for (link t = adj[k]; t != 0; t = t->next)
if (!visited[t->v]) traverse(t->v, visit);
}

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

Algoritmet dhe strukturat e t dhnave

Figura 6.21. Dinamika e stekut pr krkimin thellsia s pari.


Mund t mendojm pr stekun q prkrah krkimin thellsia-s-pari sikur
prmban nyjen dhe referencn (pointerin) n listn e saj t fqinjsis (t treguar
me nyjen e rrethuar, - si n ann e majt). Prandaj, fillojm me nyjen 0 n stek,
me referimin (me pointer) n nyjn e par n listn e saj, nyja 7. Secili rresht
tregon rezultatin e trheqjes (pop) nga steku, shtyrjes (push) s pointerit pr n
nyjn e ardhshme n list pr nyjet q jan vizituar dhe shtyrjes (push) s nj
vlere n stek pr nyjet q nuk jan vizituar. Alternativisht, mund t mendojm
pr procesin sikur thjesht shtyhen (push) n stek t gjitha nyjet fqinje t cilsdo
nyje t pavizituar (ana e djatht).
Prndryshe, ashtu si vepruam pr prshkimin e pems, mund t konsiderojm se
steku prmban vetm lidhjet pr n nyje. Me stekun e inicializuar n nyjen
fillestare, hyjm n unaz ku vizitojm nyjen n krye (top) t stekut (nse nuk
sht vizituar), pastaj shtyejm (push) n stek t gjitha nyjet fqinje me t. Figura
335

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.

Krkimi gjersia s pari


Pr t vizituar t gjitha nyjet e lidhura me nyjen k n graf, e vendosim nyjen
k n queue-n FIFO (Firs In Firs Out), pastaj hyjm n unaz, ku marrim nyjen
e ardhshme nga queue dhe nse ajo nuk sht vizituar, e vizitojm dhe shtyjm
t gjitha nyjet e pavizituar n listn e saj t fqinjsis, duke vazhduar gjersa
queue t jet i zbrazt.

336

Algoritmet dhe strukturat e t dhnave


Programi 6.9. Krkimi gjersia-s-pari (Breadth-first search)
//traverse=prshko,kalo npr; visited=vizituar
void traverse(int k, void visit(int))
{
QUEUE<int> q(V*V);
q.put(k);
while (!q.empty())
if (visited[k = q.get()] == 0)
{
visit(k); visited[k] = 1;
for (link t = adj[k]; t != 0; t = t->next)
if (visited[t->v] == 0) q.put(t->v);
}
}

Algoritmi n paragrafin paraprak sht i rndsishm sepse do t mund t


prdornim fardo ADT t queue t prgjithsuar (gjeneralizuar) dhe akoma t
vizitojm seciln nyje n graf (dhe t gjenerojm pemn e shtrirjes). Pr
shembull, nse prdorim queue n vend t stekut, ather kemi krkimin
thellsia-s-pari (angl. Breadth-first search), i cili sht i ngjashm me
prshkimin level-order (renditja n nivel) n pem. Programi 6.9 sht nj
implementim i ksaj metode (funksioni); nj shembull i algoritmit n veprim
sht ilustruar n figurn 6.22.

Figura 6.22. Dinamikat e queue-s Breadth-firstsearch


Fillojm me nyjen 0 n queue, pastaj e marrim 0, e vizitojm dhe vendosim
nyjet n listn e saj t fqinjsis 7 5 2 1 6, n at renditje n queue. Pastaj
marrim 7, e vizitojm dhe vendosim nyjet n listn e saj t fqinjsis, e kshtu
337

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.

Figura 6.23. Pemt e prshkimit t grafeve


N figurn 6.23 jan paraqitur rrugtimet e pjesrishme gjat krkimit npr nj
graf shum t madh e kompleks (majtas), t krkimit thellsia-s-pari (depthfirst search; n qendr) dhe atij gjersia-s-pari (breadth-first search; djathtas).
Thellsia s pari gjarpron nga nyja n nyje, kshtu q shumica e nyjeve jan t
lidhura vetm me dy t tjera (t prparshmen dhe t ardhshmen). N ann tjetr,
krkimi gjersia-s-pari prfshin, pushton npr graf, duke vizituar nyjet e
lidhura me nyjen e dhn, para se t shkoj tutje, kshtu q disa nyje jan t
lidhura me shum t tjera.

338

Algoritmet dhe strukturat e t dhnave

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.

Figura 6.24 - Pema binare


N programim, pema binare prdoret si model pr krijimin e strukturs s t
dhnave pr t koduar logjikn e marrjes s vendimeve komplekse. Le t themi
se nj deg sht nj bashksi e urdhrave t programit. N fund, programi
vlerson nj shprehje binare. Dihet se shprehja binare jep vlern bool-eane true
ose false. Bazuar n vlersim, programi vazhdon tutje npr njrn deg.
Secila deg ka bashksin e vet t urdhrave.
Koncepti i pems binare prdore logjikn Bool-eane, t implementuar n
urdhrin if. Mirpo, pema binare sht shum m shum sesa urdhri if.

Elementet e pems binare


N programim jan prcaktuar terma t ndryshme ndaj atyre t cilt prdoren
zakonisht kur flitet pr pemn.
339

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

Algoritmet dhe strukturat e t dhnave


Nyja fmij gjithashtu referohet edhe si nyje e majt ose nyje e djatht, varsisht
prej ans n raport me nyjen aktuale. Nse nyja aktuale nuk ka ndonj nyje
fmij, ather nyja aktuale referohet si nyje gjethe. Nyja gjethe sht e
lokalizuar n fund t pems, njsj si gjethet e pems, q ndodhen n pikn
fundore t pems.

Thellsia dhe madhsia


Pema binare prshkruhet duke prdorur dy parametra mats: thellsia (angl.
depth) dhe madhsia (angl. size), si n figurn 6.26. Thellsia e pems paraqet
numrin e niveleve n pem. Niveli i ri krijohet seciln her q nyja aktuale
degzohet n nyje fmij. Pr shembull, nj nivel krijohet, kur rrnja degzohet
n nyjet fmij.

Figura 6.26 - Numri i niveleve t pems definon thellsin, ndrsa numri i


nyjeve definon madhsin e pms.
Madhsia e pems sht numri i nyjeve n pem. Pr shembull, n figurn 6.26,
niveli i par ka vetm nj nyje, q sht nyja rrnj. Niveli i dyt ka m s
shumti dy nyje, t cilat jan nyjet fmij t rrnjs. Niveli i tret mund t ket
deri n katr nyje. Madhsia e pems binare llogaritet duke prdoru formuln:
madhesia 2 thellesia (size 2 depth )

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.

Prse prdoret pema binare?


Pema binare prdoret n programim pr t gjetur shpejt t dhnat e ruajtura n
seciln nyje t pems binare. Le t themi se duhet t gjeni ID-n e studentit n
listn me nj milion student. Sa sht numri maksimal i krahasimeve t
nevojshme pr t gjetur ID-n e studentit?
Nse krkimi bhet n mnyr sekuenciale n listn prej nj milion studentve,
do t mund t bhen maksimalisht nj milion krahasime. Nse krkimi bhet n
mnyr t rastit, duke zgjedhur nj ID nga lista dhe pastaj duke e kthyer prsri
n list, nse nuk sht ajo q krkohet.
Mirpo, nse t dhnat ruhen n strukturn e pems binare, ather do t
nevojiten vetm 20 krahasime pr t gjetur ID-n. Kjo pr arsye t mnyrs s
organizimit t t dhnave n pemn binare. E dhna (vlera) e ruajtur n nyjen e
majt sht m e vogl sesa vlerat n t gjitha nyjet e ans s djatht, pr seciln
nyje aktuale.
Kjo mund t tinglloj si konfuze, mirpo prms ilustrimit, koncepti do t jet i
qart. Supozojm se kemi nj list prej pes ID-ve t studentve: : 101, 102,
103, 104 dhe 105. Kto ID t studentve jan ruajtur n nj pem binare, kshtu
q ID-ja n qendr sht rrnja, ID-t m t vogla se nyja aktuale (rrnja)
vendosen n nyjen e fmijs s majt dhe ato m t mdha n ann e djatht, si
n figurn 6.27.

342

Algoritmet dhe strukturat e t dhnave

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

Algoritmet dhe strukturat e t dhnave

Krijimi i pems binare


Pr t krijuar pemn binare s pari definohet struktura e saj. Struktura do t
emrtohet Metadata sepse prshkruan t dhnat q prdoren n pemn binare,
e termi metadata i refereohet t dhnave t cilat i prshkruajn t dhnat, si ajo
se si ID-ja e studentit e gjen emrin e studentit (ose t dhnat tjera t studentit).
N mnyr tipike Metadat i referohet ifteve emr/vler ose n rastin ton,
ifteve els/vler.
Secila instanc e strukturs s t dhnave sht nyje n pemn binar dhe
prmban katr elemente t dhnash. Dy elementet e para jan elsi dhe vlera.
N kt shembull, t dyja jan vargje karakteresh. Madhsia e ktyre vargjeve
prcaktohet nga direktiva preprocesuese #define, n fillim t shembullit.
Madhsia e vargut caktohet duke prdorur direktivn preprocesorike sepse
pastaj me lehtsi mund t ndryshohet vlera e saj n nj vend t vetm, pa pasur
nevoj t lokalizohet do pozit brenda kodit ku prdoret vlera e madhsis s
vargut.
Dy elementet tjera t strukturs metadata jan pointert e quajtur left dhe
right (majt dhe djatht). Secili prej tyre pointon n metadata struktur. Me
fjal tjera, ata pointojn n nyjen e ardhshme n t majt dhe nyjen e ardhshme
n t djatht t nyjs aktuale. Kjo i mundson aplikacionit q t bj dy gjra. S
pari, aplikacioni mund t lviz n nivelin tjetr t pems. Gjithashtu mundet ti
aset edhe elsit edhe vlers s nyjes n nivelin e ardhshm.
N shembull, struktura e prdor vet definicionin e saj, pra struktura e prdor
vetveten pr t inicializuar elementet e strukturs. elsi dhe vlera e tij i
prcilln strukturs Metadata kur t deklarohet nj struktur metadata, d.m.th.,
kur t insertohet nyja e re n pemn binare. T dy kto vlera prcillen si pointer
char sepse jan vargje.
Aplikacioni kopjon elsin dhe vlern n elementet e t dhnave t instancs s
strukturs Metadata duke prdorur funksionin strcpy(), i cili kopjon parametrin e
dyt n parametrin e par. Vreni se n kt shembull prdoret operatori this, i
cili i tregon kompajlerit se dshironi ti referoheni elementit t t dhns s ksaj
instance t strukturs n vend se parametrit q i sht prcjellur.
Dy elementet e fundit t t dhnave pr tu inicializuar jan pointert left (i
majt) dhe right (i djatht). T dy kta caktohen n NULL sespe nyja e re nuk
ka fmij kur t krijohet. M von do t definohen funksionet q shtojn nyjen
fmij dhe zvendsojn vlern NULL me pointerin n nj nyje aktuale.
#include <string>
#define SIZE_KEY 32
#define SIZE_VALUE 256
typedef struct Metadata

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;

Prveq definimit t strukturs do t definohet edhe klasa BinarySearchTree.


Klasa BinarySearchTree definon t dhnat dhe funksionet t cilat krijojn dhe
manipulojn nyjen. Si sht ilustruar n kodin vijues, definicioni i klass sht i
organizuar n dy pjes, at private dhe at publike. Aplikacioni mund ti qaset
vetm t dhnave dhe funksioneve publike; antart e definuar n pjesn private
mund t qasen vetm prmes funksioneve t klass. N pjesn private t klass
BinarySearchTree jan deklaruar dy antar privat: size dhe root. Antari
size sht nj integer q ruan numrin e nyjeve n pem, kurse root sht
pointer n nj instanc t strukturs metadata. Me fjal t tjera, root sht nyja e
par n pem.
Pjesa private prmban poashtu edhe nnt funksione antare:
addNode()
getNode()
removeAllNodes()
processNodesInOrder()
getTreeDepth()
containsNode()
removeNode()
removeRootNode()
moveLeftMostNode()

//shtoNyje()
//merrNyje()
//largoTeGjithaNyjet()
//procesoNyjetMeRadhe()
//merrThellesineePemes()
//permbaneNyjen()
//largoNyje()
//largoNyjenRrenje()
//levizNyjenMeTeMajte()

Kto funksione prdoren nga funksionet e definuara n pjesn publike t klass,


pr t manipuluar nyjet e pems. M von do t paraqiten t gjitha kto
funksione.
Pjesa publike prmban edhe konstruktorin dhe destruktorin dhe disa funksione
t cilat i mundsojn aplikacionit q t krijoj dhe largoj nyjet dhe t
manipuloj nyjet e pems. Funksionet do t paraqiten t kompletuara m von.
//BinarySearchTree=Pema e kerkimit Binar
BinarySearchTree();

346

Algoritmet dhe strukturat e t dhnave


~BinarySearchTree()
add()
remove()
removeAll()
get()
contains()
displayInOrder()
getSize()
getDepth()
class BinarySearchTree
{
private:
int size;
METADATA* root;
bool addNode(METADATA** current_node,
METADATA* new_node);
bool getNode(METADATA* current_node,
char* key, char* value);
void removeAllNodes(METADATA* node);
void processNodesInOrder(METADATA* node);
int getTreeDepth(METADATA* node);
bool containsNode(METADATA* node, char* key);
bool removeNode(METADATA** node, char* key);
void removeRootNode(METADATA** node);
void moveLeftMostNode(METADATA** node,
METADATA* root);
public:
BinarySearchTree();
virtual ~BinarySearchTree();
bool add(char* key, char* value);
bool remove(char* key);
void removeAll();
bool get(char* key, char* value);
bool contains(char* key);
void displayInOrder();
int getSize();
int getDepth();
};

347

Avni Rexhepi

Konstruktori dhe Destruktori


Konstruktori i klass BinarySearchTree inicializon antarin root me vlern
NULL. root sht pointer n nj instanc t strukturs metadata dhe pointon n
nyjen rrnj (root) pr pemn binare t krkimit, kur t jet shtuar nyja n pem.
Konstruktori poashtu inicializon antarin size n zero. Kjo do t thot se nuk
ka nyje n pem. Antari size inkrementohet seciln her q shtohet nj nyje
n pem dhe dekrementohet seciln her q largohet nj nyje nga pema.
Destruktori largon t gjitha nyjet nga pema dhe liron hapsirn e zn t
memories. Destruktori nuk i largon nyjet drejtprdrejt, por e thrret funksionin
removeAll() i cili n fakt mirret me fshirjen e nyjeve dhe lirimin e memories.
BinarySearchTree()
{
root = NULL;
size = 0;
}
~BinarySearchTree()
{
removeAll();
}

Funksionet add() dhe addNode()


Nyja shtohet n pem duke thirrur funksionin add() t klass BinarySearchTree
(si do t shihet n pjesn e kodit n vijim. Funksioni add() krkon dy
parametra, pointerin pr n elsin e nyjes s re dhe nj pointer tjetr pr n
vlern e nyjes. Kta pointer do t emrtohen key (elsi) dhe value (vlera).
Para se t shtohet nyja n pem, funksioni add() i verifikon pointert key dhe
value me dy teste. S pari, sigurohet q ata nuk kan vler NULL. Pastaj,
teston pr t qen i sigurt q asnjri prej tyre nuk sht m imadh sesa
madhsia e vargut t alokuar pr key. Kt e bn duke krahasuar gjatsin e
key dhe gjatsin e vlers (value) pr n vlern gjegjse t definuar n
direktivn preprocesorike #define. Nse ndonjri prej ktyre testeve dshton,
ather funksioni add() kthen vlern Bool-eane False n urdhrin n
aplikacion i cili e ka thirrur funksionin add().
Nse key dhe value jan vlera valide, ather funksioni add() vazhdon me
krijimin e nyjes s re. S pari, ai deklaron nj instanc t strukturs metadata
dhe i prcjell key dhe value n instanc. M par u tha se key dhe value
bhen vlerat iniciale pr elementet gjegjse t t dhnave (antart) t strukturs
metadata.
348

Algoritmet dhe strukturat e t dhnave


Hapi i fundit n procesin e shtimit t nyjes s re n pem sht thirrja e
funksionit addNode(). Funksioni addNode() sht i definuar n pjesn private t
klass BinarySearchTree dhe sht prgjegjs pr vendosjen e nyjes s re n
pem.
bool 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);
}

Funksioni addNode(), q do t paraqitet n vazhdim, i krkon dy argumente.


Argumenti i par sht pointer n pointerin i cili pointon n nyjen aktuale.
Arguementi tjetr sht pointeri n nyjen e re. procesi i shtimit t nyjes s re n
pem fillon me kontrollimin nga funksioni addNode() nse nyja e re e prcjellur
sht NULL. Kur vlera e current_node (nyja aktuale) sht NULL, ather
sht arritur n nj gjethe n pem. Ktu sht vendi ku ndodh shtimi i nyjes s
re. T gjitha nyjet shtohen si nyje gjethe. Nse kjo sht nyja e par q shtohet
n pem, ather gjethja sht edhe rrnj. Nyja e re caktohet n pointerin e
current_node dhe antari size inkrementohet. Kjo e shton nyjen e re n
pem. Funksioni addNode() kthen vlern Bool-eane True, duke treguar se
operacioni ishte i suksesshm. Duhet t prcillet pointeri pr n pointer si
argument i par sepse do t ndryshoni vlern n at nyje. Ajo ka prcillni n t
vrtet sht adresa e pointerit n prind. Pointeri n prind ndryshohet q t
pointoj n kt nyje t re q sht duke u shtuar n pem.
Nse nyja aktuale (current node) nuk sht NULL, ather hapi i ardhshm sht
q t gjindet vendi se ku do t shtohet nyja e re n pem. Ky porces sht i
komplikuar, pasi q nyja e re duhet t lokalizohet n pozitn ku do t jet m e
madhe ose m e vogl sesa prindi i saj.
Funksioni addNode() krahason elsin (key) e nyjes aktuale (current node) me
elsin e nyjes s re (ne node) duke prdorur funksionin strcmp(). Nse vlera e
kthyer nga funksioni strcmp() sht m e vogl se zero, ather elsi i nyjes s
re sht m i vogl sesa ai i nyjes aktuale. Pastaj funksioni addNode() thirret
prsri rekurzivisht, por kt her pointeri n nyjen e majt t nyjes aktuale
prcillet si argument i par n funksionin addNode(). Si ju kujtohet, argumenti i
par konsiderohet nga funksioni addNode() si nyja aktuale. N kt rast, nyja e
majt e nyjes aktuale konsiderohet si nyje aktuale. Argumenti i dyt sht nyja e
re. Vreni se funksioni addNode() thirret rekurzivisht deri sa t gjej vendin pr
349

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;
}
}
}

Funksionet remove(), removeNode(), dhe removeRootNode()


Largimi (fshirja) e nyjes nga pema sht nj proces shum-hapsh q fillon kur
aplikacioni e thrret funksionin remove(), si n pjen vijuese t kodit. Funksioni
remove() krkon elsin (key) e nyjes q duhet t largohet. Pastaj ai e thrret

350

Algoritmet dhe strukturat e t dhnave


funksionin removeNode(), i cili sht antar privat i klass BinarySearchTree
dhe prandaj nuk mund t thirret drejtprdrejt nga aplikacioni.
Funksioni removeNode() i krkon dy parametra. I pari sht referenca n nyjen
aktuale q sht duke u vlersuar, q sht vendi ku fillon krkimi. Krkimi
fillohet duke prcjellur rrnjn e pems, pastaj thirrjet pasuese do t prcjellin
rrnjt e nnpemve. Gjithnj pr pemn duhet t mendoni si bashksi e
nnpemve secila nyje sht rrnj pr t gjitha nyjet nn t. Parametri i dyt
sht elsi (key) i pranuar nga funksioni remove().
bool remove(char* key)
{
return removeNode(&root, key);
}

Funksioni removeNode(), i paraqitur n pjesn e kodit n vijim, prdor vlern e


prcjellur nga ana e funksionit remove() pr t lokalizuar nyjen e cila do t
fshihet. Para se t filloj krkimi, funksioni removeNode() kontrollon
(prcakton) nse nyja rrnj e prcjellur tek ai nga ana e funksionit remove()
sht NULL. Kjo mund t jet rrnja e pems nse kjo sht thirrja e par e
funksionit ose mund t jet rrnj e nnpems. Nse sht NULL, ather
kthehet vlera Bool-eane false sepse nyja pr tu larguar nuk sht gjetur.
Nse nyja rrnja nuk sht NULL, krkimi vazhdon. Objektiv i funksionit
removeNode() sht q t gjindet elsi i nyjes n pem i cili prputhet
(prshtatet) me elsin e prcjellur nga ana e funksionit remove(). Kur t jet
gjetur njher, referenca pr n nyjen q prmban elsin i prcillet funksionit
removeRootNode(), i cili n fakt largon nyjen nga pema. Funksioni
removeRootNode(0 mund t largoj rrnjn e pems ose rrnjn e nnpems.
Krkimi fillon duke krahasuar elsin e nyjes rrnj t prcjellur nga funksioni
remove() me elsin e prcjellur nga funksioni remove(). Nse elsat
prputhen, ather nyja rrnj i prcillet funksionit removeRootNode() ku nyja
largohet. Antari size dekrementohet pr t reflektuar largimin e nyjes nga
pema. Pastaj nga funksioni removeNode() kthehet vlera Bool-eane true.
Nse nuk ka prputhje, ather funksioni removeNode() prcakton nse elsi i
nyjes rrnj sht m i vogl se elsi i prcjellur prej funksionit remove(). Nse
sht kshtu, ather funksioni removeNode() krahason elsin e nyjes s majt
me at t elsit t prcjellur prej funksionit remove(). Funksioni removeNode()
thirret rekurzivisht deri sa t gjindet prshtatja, me rast thirret funksioni
removeRootNode(0 dhe i prcillet referenca pr n nyjen e prshtatur
(prputhur).
Nse elsi sht m i madhe se elsi i nyjes rrnj, ather ai krahasohet me
elsin e nyjes s djatht. Prsri, funksioni removeNode() thirret rekurzivisht
351

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;
}
}

Funksioni removeRootNode() sht funksioni i cili n fakt largon nyjen nga


pema. Termi root node (nyje rrnj) ndonjher mund t shkaktoj konfuzion
sepse intuitivisht supozohet se sht fjala pr rrnjn e tr pems, mirpo n
realitet secila nyje mund t jer rrnj pr t gjitha nyjet nn t. Edhe nse nyja
sht gjethe, ajo akoma sht rrnj e nnpems (n kt rast, t zbrazt). Pra,
ndoodh q ajo sht e vetmja nyje e nnpems. Prandaj, n emrin e ktij
funksioni prdoret fjala Node (rrnja).
Funksioni removeRootNode() krkon nj argumen, i cili sht pointeri n
pointerin pr nyjen q largohet. Procesi i largimit fillon me deklarimin e
pointerit n strukturn metadata. N pjesn vijuese t kodit, ky pointer
emrtohet temp.
Para se t largoj nyjen, funksioni removeRootNode() kontrollon nse nyja ka
fmij t djaht dhe fmij t majt. Nse t dy fmijt jan NULL, ather nuk
ka asnj fmij dhe thirret operatori delete pr t liruar memorien e shoqruar
me kt nyje. pastaj pointeri n nyjen prind vendoset n NULL, sepse kjo nyje
fmij sht larguar. Vreni se nse kjo nyje do t ishte nyja e vetme e pems,
352

Algoritmet dhe strukturat e t dhnave


funksioni do t caktonte rrnjn e pems n NULL, gj q ka kuptim pasi q
pema do t ishte e zbrazt.
Nse njri prej fmijve nuk sht NULL, ather do t fmija i djatht do t
krahasohej me NULL. Ky do t ishte rasti nse nyja q fshihet nuk ka fmij t
djatht. N kt rast, ndryshohet pointeri n nyjen prind n t majt t nyjes q
sht duke u larguar. Nyja rrnj caktohet pr n pointerin temp pr t
mbajtuar mend lokacionin e nyjes q sht duke u larguar nga pema. Pastaj
referenca pr n nyjen e majt caktohet pr n nyjen prind. Operatori delete
pastaj e largo nyjen q referohet prmes pointerit temp pr t liruar memorien
e shoqruar me nyjen q sht duke u fshir.
Nse nyja e djatht nuk sht NULL, ather funksioni removeRootNode()
kontrolon nse nyja e majt sht NULL. Kjo prcjell logjikn e njjt me
rastin e mparshm, prveq se nyja q sht duke u fshir nuk ka fmij t
majt, kshtu q pointeri n nyjen prind caktohet pr n nyjen n t djath t
asaj q sht duke u fshir. Referenca pr ny nyjen rrnj caktohet pr n
pointerin temp. referenca pr n nyjen e djatht pastaj caktohet pr n nyjen
prind. Operatori delete e liron memorin e shoqruar me nyjen q sht duke u
larguar.
Skenari i fundit dhe m i komplikuari sht nse nyja q sht duke u larguar ka
t dy fmijt, edhe nyjn fmij t majt edhe at t djatht. N kt rast,
funksioni removeRootNode() thrret funksionin moveLeftMostNode() (lvize
nyjen m t majt) dhe i prcjell atij adresn e nyjes s djatht.
void removeRootNode(METADATA** root)
{
METADATA* temp;
if((*root)->left == NULL && (*root)->right == NULL)
{
delete(*root);
*root = NULL;
}
else if((*root)->right == NULL)
{
temp = *root;
*root = (*root)->left;
delete(temp);
}
else if((*root)->left == NULL)
{
temp = *root;
*root = (*root)->right;
delete(temp);

353

Avni Rexhepi
}
else
{
moveLeftMostNode(&((*root)->right), *root);
}
}

Objektiv i funksionit moveLeftMostNode() sht t gjej nyjen e cila do t


zvendsoj nyjen aktuale t nnpems. Pr t arritur kt qllim, duhet t
lvizni nj her n t djatht dhe pastaj t shkoni teposht pems sa m larg (sa
m thell) q t jet e mundur n t majt, deri sa t gjendet nyja m e vogl n
t djatht. Lvizja n t djatht ndodh kur funksioni moveLeftMostNode()
thirret pr her t par. Pastaj lvizni majtas thirrjet pasuese t funksionit. Kur t
jet gjetur vlera m e vogl n ann e djatht, nyja q prmban vlern m t
vogl bhet nyja e re rrnj.
Le t shohim se si funksionon kjo duke lvizuar (ecur) npr definicionin vijues
t funksionit moveLeftMostNode(). Ky funksion krkon dy argumente, nyjen
aktuale q sht duke u vlersuar dhe nyjn e cila do t zvendsohet. Mbani
mend, ky funksion do t kopjoj elsin dhe vlern (t dhnn) prej nyjes m t
vogll n nnpemn e djatht, n nyjn e cila sht duke u larguar. Kjo
zvendson nyjen q sht duke u larguar me njrn nga nyjet gjete dhe pastaj
nyja gjethe fshihet.
Nse referenca pr n nyjen q sht duke u lvizur nuk sht NULL dhe
pointeri i majt i nyjes q sth duke u lvizur sht NULL, ather keni gjetur
nyjen e cila do t lvizet prpjet deri n pozitn e nyjes q sht duke u larguar.
Deklarohet nj pointer dhe caktohet pr n nyjen q lvizet. Pastaj, elsi dhe
vlera e nyjes kopjohen n elsin dhe vlern e nyjes rrnj. Nyja rrnj n kt
rast sht nyja q sth duke u larguar nga pema. Pasi q jeni duke lvizur
fmijn m t majt t nnpems s djatht, ky fmij m i majt mund t ket
fmij n t djatht t tij. Vlera e pointerit n prindin e nyjes q sth duke u
lvizur caktohet n pointerin e djatht t asaj q sht duke u lvizur. Kjo i
mban kto nn-nyje t paprekura. Prfundimisht, operatori delete e largon
nyjen.
Nse nuk sht gjetur nyja m e majt e nnpems s djatht, ather thirret
prsri funksioni moveLeftMostNode(). Kt her, nyja e fmijs s majt
prcillet si nyje q duhet t lvizet n pozitn rrn. Pozita rrnj n kt rast
sht nyja q sht duke u larguar.
void moveLeftMostNode(METADATA** node, METADATA* root)
{
if(*node != NULL && (*node)->left == NULL)
{

354

Algoritmet dhe strukturat e t dhnave


METADATA* temp = *node;
strcpy(root->key, (*node)->key);
strcpy(root->value, (*node)->value);
*node = (*node)->right;
delete(temp);
}
else
{
moveLeftMostNode(&((*node)->left), root);
}
}

Funksionet removeAll() dhe removeAllNodes()


M par u pan disa funksione t cilat largojn nj nyje nga pema. Ka raste kur
do t nevojitet q t largohen t gjitha nyjet nga pema. Pr t br kt, duhet t
thirret funksioni removeAll() (largo t gjitha).
Funksioni removeAll() i paraqitur n pjesn vijuese t kodit, kryen dy
operacione. S pari, ai thrret funksionin removeAllNodes() (largo t gjitha
nyjet), i cili sht i definuar n pjesn private t klass BinarySearchTree. Ky
sht funksioni i cili n fakt largon t gjitha nyjet nga pema. Operacioni i dyt
sht q t resetohen (rivendosen (angl. reset)) antart root dhe size t
klass BinarySearchTree. Antari root caktohet n NULL, pr t treguar se
nuk ka asnj nyje n pem. Antari size caktohet n zero, pr t treguar se
pema sht e zbrazt. Funksioni removeAll() poashtu thirret edhe nga
destruktori.
void removeAll()
{
removeAllNodes(root);
root = NULL;
size = 0;
}

Funksioni removeAllNodes() krkon nj argument, i cili sht pointeri pr n


nyjen rrnj. Prderi sa nyja rrnj nuk sht NULL, funksioni
removeAllNodes() thrret vetveten seciln her, duke i prcjellur s pari nyjen e
fmijs s majt dhe pastaj nyjen e fmijs s djatht si nyje rrnj. Renditja e
ktyre thirrjeve sht me rndsi. Rikujtoni q nyje rrnj sht ose nyja rrnj e
tr pems ose rrnja e nnpems (nndegs). Para se t largohet prindi duhet t
largohen t gjitha nyjet fmij. I adresoheni pems s majt, pastaj pems s
djaht, e pastaj kur ktheht tek thirrsi, sht e sigurt q t fshihet nyja aktuale
(nyja rrnj) sepse t gjith fmijt do t jen fshir. Sikur me t gjitha
355

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;
}
}

Funksionet get() dhe getNode()


Funksioni get() (merr) i klass BinarySearchTree thirret prbrenda aplikacionit
sa her q dshironi t lexoni/trheqni (angl. retrieve-trheq, rikthej) vlern e
nyjes. Pr t trhequr vlern, duhet ti siguroni funksionit get() elsin pr
krkim dhe variabln e cila do t ruaj vlern, kur t gjindet elsi.
Kjo gj funksionon n mnyrn si sht ilustruar n pjesn vijuese t kodit, ku i
prcillni funksionit get() dy argumente. Argumenti i par sht pointeri q i
referohet elsit pr krkim. N kt shembull, elsi sht nj string/tekst/fjal.
Prandaj, funksionit i prcillet nj pointer n char, i cili si mund t ju kujtohet,
pointon n karakterin e par t stringut. Argumenti i dyt sht poashtu nj
pointer i tipit char. Ky pointon n elementin e par t vargut t karaktereve t
cilat funksioni get() i prdor pr t ruajtur vlern e nyjes q sht e shoqruar
me elsin e krkimit.
Le t marrim si shembull q elsi i krkimit sht ID e studentit 1234 dhe
vlera e shoqruar me kt els sht Ben Shpati. Ju i prcillni funksionit get()
1234 dhe ai kopjon vlern Ben Shpati n vlern e vargut t karaktereve,
nse elsi 1234 sht gjetur n ndonj nyje t pems. Pastaj e prdorni vlern
e vargut t karaktereve npr aplikacionin tuaj.
Funksioni get() sht i definuar n pjesn publike t klass BinarySearchTree
dhe prandaj sht i qasshm pr aplikacionet. Mirpo, funksioni get() thjesht
vetm e thrret funksionin getNode(), i cili sht i definuar n pjesn private t
356

Algoritmet dhe strukturat e t dhnave


klass BinarySearchTree. Funksioni getNode() kthen vlern Bool-eane true
nse elsi i krkuar gjindet; prndryshe kthehet vlera Bool-eane false. Vlera e
kthyer prej tij poashtu bhet vler e kthyer prej funksionit get().
bool get(char* key, char* value)
{
return getNode(root, key, value);
}

Funksioni getNode() sht vendi ku ndodhe i tr veprimi. Ktu bhet krkimi


dhe vlera e nyjes kopjohet n vlern e vargut. Si sht ilustruar n vijim,
funksioni getNode() krkon tri argumente. Argumenti i par sht pointeri q i
referohet nyjes rrnj. Nyja rrnj sht pozita startuese e krkimit dhe
zakonisht sht nyja m e lart e pems, por mund t jet cilado nyje. Argumenti
i dyt sht pointeri q i referohet elsit t krkimit, i cili n kt shembull
sht pointer i tipit char. Argumenti i tret sht pointeri n variablan q e
ruan vlern e nyjes e cila prmban elsin e krkimit. T dyja, elsi dhe vlera
e variabls jan t njjta me ato q i prcillen funksionit get().
Funksioni getNode() fillon procesimin duke verifikuar nyjen rrnj. Nse nyja
rrnj sht NULL, ather argumenti i vlers caktohet n string t zbrazt (e
cakton karakterin e par n NULL) dhe nga ana e funksionit getNode() kthehet
vlera Bool-eane false pr t terguar se elsi nuk sht gjetur n pem.
Nse rrnja nuk sht NULL, ather vazhdon krkimi. Funksioni getNode()
thirret n mnyr rekurzive. Seciln her q thirret, ai krahason elsin q
krkohet me elsin e nyjes rrnj. Nse ata prputhen, ather vlera e nyjes
rrnj kopjohet n vlern e variabls dhe nga funksioni kthehet vlera Bool-leane
true.
Nse elsi i krkimit nuk prshtatet me elsin e rrnjs, ather funksioni
getNode() prcakton nse elsi sht m i vogl ose m i madhe se elsi i
nyjes rrnj. Varsisht prej rezultatit t ktij krahasimi, funksioni getNode() e
thrret vetveten dhe prdor ose fmijn e majt ose at t djatht t rrnjs si
argument pr nyje rrnj t funksionit getNode(). Ky proces vazhdon deri sa ose
elsi i krkimit prpythet me elsin e nyjes rrnj (aktuale) ose nyja rrnj
sht NULL, duke treguar se elsi q krkohet nuk ekziston n pem. Ky tip i
krkimit sht ajo ku vjen n shprehje fuqia e pemve binare. Vreni q seciln
her q thirret funksioni, duke br nj krahasim n els, eliminohet nga
krkimi gjysma e nyjeve t mbetura, kshtu q do t jeni n gjendje q t gjeni
elsin shum shpejt, edhe n bashksi shum t madhe t t dhnave.
bool getNode(METADATA* node, char* key, char* value)
{
if(node == NULL)
{

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);
}
}
}

Funksionet contains() dhe containsNode()


M hert sht theksuar se elsi n pem duhet t jet unik. Nuk mund t keni
dy elsa me vler t njjt t elsit. Mos e przieni vlern e elsit me vlern e
ruajtur n nyje. Vlera els sht vlera e vet elsit.
Para se t shtohet nj nyje e re n pem, duhet t prcaktoni nse elsi i nyjes
s re ve ekziston n pem. sht e mundur q t konstruktohet pema binare e
cila lejon elsa duplikat, mirpo ky nuk sht zbatim (implementim) i
zakonshm. N kt rast, kemi definuar rregulln q t gjith elsat e pems
duhet t jen unik.
Pr t prcaktuar nse elsi veq ekziston n pem, thirret funksioni contains()
(angl. contains-prmban) q sht antar i klass BinarySearchTree, i cili
sht paraqitur n vazhdim. Funksioni contains() krkon nj argument, pointerin
q i referohet elsit. Ai kthen vlern Bool-eane true nse elsi ekziston,
prndryshme kthen vlern false.
Funksioni contains() sht funksion i thjesht dhe ka vetm nj urdhr. Ky
urdhr thrret funksionin containsNode(), antar i klass. Funksioni
containsNode() krkon pemn pr elsin e krkimit dhe kthen vlern Bool-eane
true nse elsi sht gjetur, prndryshe kthen vlern Bool-eane false, e cila
vler pastaj prdoret si vler kthyese prej funksionit contains().
358

Algoritmet dhe strukturat e t dhnave


Funksioni contains() sht definuar n pjesn publike t klass
BinarySearchTree, ndrsa funksioni containsNode() sht definuar n pjesn
private t klass s njjt.
bool contains(char* key)
{
return containsNode(root, key);
}

Funksioni containsNode(), si sht treguar n vazhdim, krkon dy argumente


(dy pointer). Argumenti i par sht pointeri q i referohet nyjes rrnj. Nyje
rrnj mund t jet cilado nyje, por n mnyr tipike rrnj sht nyja e par e
pems sepse dshironi q t krkoni elsin duke filluar nga maja e pems.
Argumenti i dyt sht pointeri q i referohet elsit (key), i cili sht elsi i
njjt q i prcillet funksionit contains().
Procesi fillon me prcaktimin (kontrollimin) nse pointeri i rrnjs sht NULL.
Nse pointeri sht NULL, ather elsi nuk ekziston dhe kthehet vlera Booleane false; prndryshe elsi krahasohet dhe krkimi vazhdon.
S pari, funksioni containsNode() krahason elsin me elsin e nyjes rrnj.
Nse ka prputhje (prshtatje), ather kthehet vlera Bool-eane true dhe
krkimi mbaron. Nse jan t ndryshm, ather funksioni containsNode()
prcakton nse elsi (key) sht m i vogl se ai i nyjes rrnj. Nse po, ather
funksioni containsNode() e thrret vetveten dhe prdor nyjen e fmijs s majt
t rrnjs, si nyje e re rrnj.
Nse elsi nuk sht m i vogl se elsi i rrnjs, ather funksioni
containsNode() e prcakton nse elsi sht m i madh se ai i rrnjs. Nse po,
ather funksioni containsNode() e thrret vetveten duke prdorur fmijn e
djatht t rrnjs si nyje e re rrnj.
Funksioni containsNode() thirret rekurzivisht deri sa ose t gjindet elsi ose
deri sa vlera e rrnjs sht NULL, duke treguar se keni arritur n fund t pems
pa e gjetur elsin.
bool containsNode(METADATA* node, char* key)
{
if(node == NULL)
{
return false;
}
else
{
if(strcmp(key, node->key) == 0)
{
return true;

359

Avni Rexhepi
}
else if(strcmp(key, node->key) < 0)
{
return containsNode(node->left, key);
}
else
{
return containsNode(node->right, key);
}
}
}

Funksionet displayInOrder() dhe processNodesInOrder()


Prmbajtja e pems mund t shfaqet (paraqitet) duke e thirrur funksionin
displayInOrder() (paraqiti me radh), antar i klass BinarySearchTree. Si
tregon edhe emri, funksioni displayInOrder() sht funksion publik i cili paraqet
elsin dhe vlern e t gjitha nyjeve t majta, t pasuara nga t gjitha nyjet e
djathta, pr seciln nyje n pem.
Si sth treguar n vijim, funksioni displayInOrder() ka nj urdhr, i cili thrret
funksionin processNodesInOrder() (proceso nyjet me radh), funksion antar i
klass BinarySearchTree. Funksioni processNodesInOrder() sht i definuar n
pjesn private t klass prandaj nuk sht n dispozicion pr alikacionin
(aplikacioni nuk ka qasje n pjesn private!).
Funksionit processNodesInOrder() duhet ti prcillet nj argument, i cili sht
pointeri q i referohet nyjes rrnj. Nyje rrnj n mnyr tipike sht nyja e
par e pems, mirpo mund t filloni paraqitjen e prmbajtjes s pems nga
cilado nyje, duke e prcjellur at si argument t funksionit
processNodesInOrder().
void displayInOrder()
{
processNodesInOrder(root);
}

Definicioni i funksionit processNodesInOrder(0 sht paraqitur n vijim. Do t


vreni se ky sht nj funksion rekurziv dhe thirret shum her n mnyr q t
shtyp nyjet q ndodhen n degn e majt dhe at t djatht t pems.
Procesimi fillon duke prcaktuar (kontrolluar) nse rrnja sht NULL. Nse po,
jeni n fund t pems. Nse nuk sht NULL, ather funksioni
processNodesInOrder() thirret prsri dhe i prcillet fmija i majt i nyjes rrnj.
Ather paraqitet n ekran elsi dhe vlera e nyjes.
360

Algoritmet dhe strukturat e t dhnave


Kjo vazhdon deri sa t paraqiten n ekran elsat dhe vlerat e t gjitha nyjeve
majtas. Procesi i njjt prcillet pr t paraqitur fmijt djathtas t nyjes rrnj.
Pr cilndo nyje t dhn, s pari do t shtypen t gjitha nyjet majtas, pastaj
shtypet vet nyja, e pastaj t gjitha nyjet djathtas.
void processNodesInOrder(METADATA* node)
{
if(node != NULL)
{
processNodesInOrder(node->left);
cout << "key: " << node->key << "\tvalue: " << node->value
<< endl;
processNodesInOrder(node->right);
}
}

Funksionet getSize(), getDepth() dhe getTreeDepth()


M hert sht thn se pema matet pr nga numri i nyjeve dhe pr nga numri i
niveleve. Numri i nyjeve n pem thirret madhsia e pems (angl. size) dhe
numri i niveleve t pems sht thellsia e pems (angl. depth). Kemi definuar
funksionet antare t cilat mund t prdoren pr t prcaktuar madhsin dhe
thellsin e pems.
Funksioni i par quhet getSize() (merre madhsin), i cili sht paraqitur n
vazhdim. Ky funksion thjesht kthen vlern e antarit size t klass
BinarySearchTree. Funksionet t cilat shtojn dhe largojn nyjet e prshtasin
vlern e antarit size ashtu q antari size i klass gjithmon reflekton
numrin aktual t nyjeve n pem.
int getSize()
{
return size; }

Funksioni getDepth() (merre thellsin), prcakton numrin e nivele n pem. Ky


funksion thrret funksionin antar getTreeDepth() (merre thellsin e pems)
dhe ia prcjell atij referencn pr n nyjen rrnj e cila prdoret si nivel startues
kur llogaritet thellsia e pems. Ai kthen nj integer q prfaqson numrin e
niveleve n pem.
Funksioni getDepth() dhe funksioni getSize() jan t dy t definuar n pjesn
publike t klass BinarySearchTree. Funksioni getTreeDepth() sht i definuar
n pjesn private.
int getDepth()
{
return getTreeDepth(root);
}

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

Algoritmet dhe strukturat e t dhnave

Pema binare n C++


Tani q i pam pjest e klass BinarySearchTree, le ti bashkojm n nj
aplikacion punues. Aplikacioni do t organizohet n tre fajlla:
BinaryTreeDemo.cpp, BinarySearchTree.h dhe BinarySearchTree.cpp. Fajlli
BinarySearchTree.cpp sht fajlli i aplikacionit q prmban kodin i cili krijon
dhe manipulon pemn. BinarySearchTree.h prmban definicionin e strukturs
s prdorur pr t ndrtuar nyjen dhe definicionin e klass BinarySearchTree.
BinarySearchTree.cpp prmban definicionin e funksioneve antare t klass
BinarySearchTree. T gjitha jan listuar n kodin vijues. Mbetet vetm ta
provoni se si aplikacioni krijon dhe manipulon pemn, e cila ka ID-t (elsat)
dhe emrat (vlerat).
//BinaryTreeDemo.cpp
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <stdio.h>
#include "BinarySearchTree.h"
int main()
{
BinarySearchTree* tree = new BinarySearchTree();
char key[SIZE_KEY];
char value[SIZE_VALUE];
int i;
cout << "Shtimi i tre elesave dhe tri vlerave ne peme." << endl;
for(i=0; i<3; i++)
{
if (i==0)
{
strcpy(key,"345");
strcpy(value,"Beni");
}
if (i==1)
{
strcpy(key,"123");
strcpy(value,"Meri");
}
if (i==2)
{
strcpy(key,"999");
strcpy(value,"Suzi");
}
if (!tree->contains(key))

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;
}

Aplikacioni fillon me dekarimin e instancs s klass BinarySearchTree dhe ia


cakton at referencs s quajtur tree (pema). Pastaj, deklarohen dy vargjet e
char dhe nj int. vargjet char quhen key (elsi) dhe value (vlera) dhe
madhsia e ktyre vlerave caktohet duke prdoru makron e definuar n fajllin
BinarySearchTree.h. Vargjet ruajn nj ID dhe emrin i cili i ndahet nyjes n
pem. Variabla e tipit int kontrollon unazn for.
364

Algoritmet dhe strukturat e t dhnave


Pastaj, unaza for shton seciln ID dhe emr n pem. Pr seclin iteracion
(kalim, prsritje), thirret funksioni strcpy() pr t kopjuar stringun q prmban
ose ID-n ose emrin q duhet kopjuar n vargje.
Kur t jet kopjuar bashksia e stringjeve n vargje, aplikacioni thrret
funksionin contains() (prmban) pr t kontrolluar nse elsi ve ekziston n
pem. Rikujtoni q secli els duhet t jet unik. Funksioni contains() kthen
vlern Bool-eane true nse elsi sht n pem. Me logjikn e prmbysjes (e
kundrt, e invertimit) me operatorin ! not trajtojm vlern Bool-eane true si
false. Kjo do t thot q urdhrat prbrenda urdhrit if nuk do t
ekzekutohen nse elsi ve ekziston n pem.
Nse elsi nuk ekziston n pem, ather aplikacioni paraqet elsin (key) dhe
vlern (value) n ekran, para se t thrras funksionin add() (shtoje) pr t
vendosur n pem elsin dhe vlern, si n vijim:
Shtimi
Shtimi
Shtimi
Shtimi

i
i
i
i

tre elesave dhe tri vlerave ne peme.


nyjeselesi/key: 345 vlera: Beni
nyjeselesi/key: 123 vlera: Meri
nyjeselesi/key: 999 vlera: Suzi

Figura 6-29 ilustron elsat dhe vlerat t organizuara n pem.

Figura 6.29 - pa marr parasysh radhn me t ciln shtohen t dhnat n pem,


nyja fmij e majt sht m e vogl sesa nyja prind dhe nyja e djaht fmij
sht m e madhe sesa nyja prind.
Nse elsi ve ekziston n pem, ather paraqitet porosia n ekran q tregon
se elsi sht els duplikat.
Pasi t vendosen n pem t tri ID-t dhe emrat, aplikacioni manipulon nyjet n
pem. Manipulimi i par sht thirrja e funksionit displayInOrder(), i cili i
paraqet elsat dhe vlerat e secils nyje, si n vijim:
Pershkimi *In order* i pemes:
elesi: 123 vlera: Meri

365

Avni Rexhepi
elesi: 345 vlera: Beni
elesi: 999 vlera: Suzi

Pastaj aplikacioni paraqet thellsin dhe madhesin e pems duke thirrur


funksionet getDepth() dhe getSize(). Rezultati paraqitet n ekran, si n vijim:
Thellesia e pemes para largimit te nyjeve: 2
Madhesia e pemes para largimit te nyjeve: 3

Rikujtoni q thellsia e pems sht numri i niveleve t pems. N kt


shembull, jan dy nivele. Niveli i par prmban nyjen rrnj dhe niveli i dyt
prmban nyjen fmij t majt dhe nyjen fmij t djatht.
Pastaj, aplikacioni i trheq (lexon) vlerat e shoqruara me elsin 123 duke
thirrur funksionin get(). Funksioni get() kthen vlern Bool-eane true nse
elsi sht gjetur; prndryshe, kthehet vlera Bool-eane false. Nse elsi sht
gjetur, ather paraqitet n ekran vlera, si n vijim. Rikujtoni q emri i
shoqruar me elsin 123 i caktohet vlers varg prmes funksionit get().
Leximi i nje vlere nga pema:
Vlera: Meri

Pastaj, aplikacioni largon nyjen q prmban elsin 123. S pari thirret


funksioni contains() pr t prcaktuar nse pema prmban elsin me vler
123. Ns po, ather kthehet vlera bool-eane true, prndryshe kthehet vlera
bool-eane false. Pasi q ekziston nyja me elsin 123, thirret funksioni
remove() dhe i prcillet stringu 123 pr t larguar nyjen.
Funksioni displayInOrder() thirret prsri, pr t paraqitur pemn pasi t jet
larguar nyja. Ajo ka paraqitet n ekran sht si n vijim.
Largimi i nje nyjeje nga pema:
Pershkimi *In order* i pemes:
elesi: 345 vlera: Beni
elesi: 999 vlera: Suzi

Vreni se nyja 123 m nuk ekziston n pem (Figura 6.30).

Figura 6.30 - Nyja e majt fmij sht larguar nga pema: pema akoma ka
thellsin 2 nivele.
366

Algoritmet dhe strukturat e t dhnave


N fund, aplikacioni thrret funksionet getDepth() dhe getSize() pr t paraqitur
thellsin dhe madhsin e pems pasi t jet larguar nyja. Rezultati si vijon:
Thellesia e pemes pas largimit te nyjeve: 2
Madhesia e pemes pas largimit te nyjeve: 2

Aplikacioni prfundon largimin e pems duke thirrur operatorin delete.


Rikujtoni q destruktori i klass BinarySearchTree e thrret funksionin
removeAllNodes() i cili i paraqet elsat dhe vlerat q largohen. Ja se ka do t
paraqitet n ekran:
Largohet nyja elesi (key): 999
Largohet nyja elesi (key): 345

Suzi
Beni

Fig.6.31 Rezultati i ekzekutimit

367

Avni Rexhepi

Krkimi tek pema binare


Pema binare (angl. Binary Tree) sht struktur e t dhnav e prdorur
gjersisht. Karakteristik e pems binare, e cila e dallon nga pema e
zakonshm, sht se secila nyje ka m s shumti dy fmij. Prdorim m i
shpesht i pems binare sht si struktur bazike pr pemn binare t krkimit
(angl. Binary search tree). Secila pem binare ka grupet vijuese t nyjeve:

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).

N vijim kemi nj shembull t pems binare.

Shembull i pems binare

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

Algoritmet dhe strukturat e t dhnave


-

Preorder (root-left-right rrnja-nyja e majtnyja e djatht);


Postorder (left-right-root nyja e majtnyja e djathtrrnja); dhe,
Inorder (left-root-right nyja e majt-rrnja-nyja e djatht)

Implementimi themelor sht n pemn e krkimit binar (angl. Binary


search tree (BST)).

Pema e krkimit binar - Binary search tree


Fillimisht duhet theksuar se pema binare e krkimit (BST) sth struktur
dinamike e t dhnave, q do t thot se madhsia e saj sht e kufizuar vetm
nga sasia e mmories s lir (free memory) n sistemin operativ dhe numri i
elementeve mund t ndryshoj gjat ekzekutimit t programit. Prparsi
kryesore e pems binare t krkimit sht krkimi i shpejt, gjersa shtimi i
elementeve sht mjaft i lir. Definicionet themelore t pems binare t
krkimit jan dhn n vijim.
Pema binare e krkimit sht struktur e t dhnave, e cila e plotson krkesat
vijuese:

sht pem binare;


secila nyje ka vler;
rendi total sht i definuar n kto vlera (do dy vlera mund t
krahasohen m njra tjetrn);
nnpema/nndega e majt (left subtree) e nyjes prmban vetm vlerat
m t vogla sesa vlera e nyjes;
nnpema/nndega e djatht (right subtree) e nyjes prmban vetm
vlerat m t mdha sesa vlera e nyjes;

Vni re se definicioni i ktill nuk lejon duplikate.

Shembull i pems binare t krkimit

369

Avni Rexhepi

Pr ka prdoret pema binare e krkimit?


Pema binare e krkimit prdoret pr t konstruktuar strukturn e t dhnave
map (angl. map-hart, plan, skem). N praktik, t dhnat shpeshher mund t
jen t lidhura (shoqruara, bashkuara) me nj vler unike, els unik (angl.
unique key). Pr shembull, n adresarin e telefonave, nj els i till sht
numri i telefonit. Ruajtja e t dhnave t tilla n pemn binare t krkimit
mundson krkim m t shpejt sipas elsit pr ndonj rekord, sesa sikur t
dhnat t ishin ruajtur n ndonj list t parenditur. Gjithashtu, pema binare e
krkimit mund t shfrytzohet pr t konstruktuar strukturn e t dhnave set
(grup, set, bashksi), e cila lejon ruajtjen e nj koleksioni t parenditur t velrave
unike dhe t kryej operacione me koleksione t tilla.
Performansa e pems binare t krkimit varet nga lartsia e saj. Pr t mbajtur
pemn t balansuar dhe pr t minimizuar lartsin e saj, idea e pems binare t
krkimit sht avansuar n pemt e balansuara t krkimit (AVL tree, RedBlack tree, Splay tree, etj).

Operacionet n pemn binare t krkimit

Shtimi i nj vlere t re
Krkimi pr ndoj vler
Largimi (fshirja) e vlers
Marrja me radh e vlerave nga pema binare e krkimit

Pema binare e krkimit reprezentimi i brenshm


Sikur do struktur tjetr dinamike e t dhnave, pema binare e krkimit krkon
ruajtjen e disa t dhnave ndihmse t nevojshme pr t mbajtur strukturn e saj.
Secila nyje e pems binare prmban informatat vijuese:

Vlern (e dhn e shfrytzuesit-users data);


Lidhjen pr tek fmija i majt (e dhn ndihmse-auxiliary data) ;
Lidhjen pr tek fmija i djatht (e dhn ndihmse-auxiliary data) ;

Varsisht nga madhsia e t dhnave t shfrytzuesit, mund t ndryshoj


mbingarkimi i memories, por n prgjithsi sht plotsisht i arsyeshm. N disa
implementime, nyja mund t ruaj lidhjen pr tek prindi, por kjo varet nga
algoritmi t cilin programeri dshiron ta aplikoj pr pemn binare t krkimit
(BST). Pr operacionet themelore, sikur shtimi, largimi dhe krkimi, lidhja pr
tek prindi nuk sht e novojshme. Ajo sht e nevojshme pr implementimin e
iteratorve.

370

Algoritmet dhe strukturat e t dhnave


Me pamje t prshtatur pr reprezentim t brendshm, shembulli i dhn ne
fillim ndryshon:

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

Pema binare e krkimit shtimi i vlers


Shtimi i nj vlere n pemn binare mund t ndahet n dy faza:

krkimi pr vendin pr ta vendosur elementin e ri;


insertimi i elementit t ri n kt vend.

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.

Tani, le t prshkojm algoritmin. N kt rast dhe n pothuajse secilin


operacion n pemn binare t krkimit, shftytzohet rekurzioni. Duke filluar nga
rrnja,
1. verifiko, a mos jan t barabarta vlera n nyjen aktuale dhe vlera e re.
Nse po, sht gjetur duplikati. Prndrsyhe,
2. nse vlera e re sht m e vogl, ather vlera e nyjes:
o nse nyja aktuale nuk ka fmij t majt, vendi pr insertim sht
gjetur;
o prndryshe, trajtoje fmijn e djatht me t njjtin algoritm.

Shembull
372

Algoritmet dhe strukturat e t dhnave


Inserto 4 n pemn e treguar m lart.

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

Algoritmet dhe strukturat e t dhnave


else if (value > this->value)
{
if (right == NULL)
{
right = new BSTNode(value);
return true;
} else
return right->add(value);
}
return false;
}

Pema binare e krkimit operacioni i krkimit


Krkimi i vlern s pemn binare sht i ngjashm me operacionin e
shtimit/insertimit. Algoritmi i krkimit e prshkon pemn n thellsi (angl. in
depth), duke zgjedhur rrugn e duhur prmes vetis s pems binare t krkimit
dhe krahason vlern e secils nyje t vizituar me at q jemi duke e krkuar.
Algoritmi ndalet n dy raste:

nyja me vlern e duhur sht gjeturl;


algoritmi nuk ka rrugdalje pr t vazhduar tutje.

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

Algoritmet dhe strukturat e t dhnave


else
return root->search(value);
}
bool BSTNode::search(int value) {
if (value == this->value)
return true;
else if (value < this->value) {
if (left == NULL)
return false;
else
return left->search(value);
} else if (value > this->value) {
if (right == NULL)
return false;
else
return right->search(value);
}
return false;
}

Pema binare e krkimit largimi i nyjes


Operacioni i largimit/fshirjes n pemn binare sht m i kompletuar sesa
insertimi dhe krkimi. N parim, ai mund t ndahet n dy faza:

krkimi i nyjes q duhet larguar;


nse nyja sht gjetur, ekzekuto algoritmin e largimit/fshirjes

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

2. Nyja q duhet larguar e ka nj fmij.


N kt rast, nyja largohet/pritet nga pema dhe algoritmi e lidh fmijn e vetm
(me nnpemn e tij) drejtprdrejt me prindin e nyjes s larguar.
Shembull. Largimi i 18 nga pema binare.

378

Algoritmet dhe strukturat e t dhnave

3. Nyja q duhet larguar i ka dy fmij.


Ky sht rasti m i komplikuar. Pr ta zgjidhur, s pari le t shohim nj veti t
dobishme t pems binare. Do t prdorim iden q i njjti set i vlerave mund t
paraqitet si pem t ndryshme binare. Pr shembull pemt vijuese:

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

gjeni vlern minimale n nnpemn e djatht (19 n kt


shembull);
zvendsoni 5 me 19
lidheni/vareni 5 si fmij t majt

Qasja e njjt mund t prdoret pr t larguar nyjen, e cila ka dy fmij:


o
o
o

gjeni vlern minimale n nnpemn e djatht;


zvendsoni vlern e nyjes q duhet larguar me minimumin e
gjetur. Tani, nndega e djatht prmban nj duplikat!
apliko largimin/fshirjen n nndegn e djatht pr t larguar
duplikatin

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.

Gjeni elementin m t vogl (minimumin) n nndegn e djatht t nyjes q


duhet larguar. N kt shembull sht 19.

380

Algoritmet dhe strukturat e t dhnave

Zvendsoni 12 me 19. Vreni se vetm vlera sht ndryshuar, jo nyjet. Tani


kemi dy nyje me vler t njjt.

Largoni 19 nga nndega e majt.

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

Algoritmet dhe strukturat e t dhnave


{
if (value < this->value)
{
if (left != NULL)
return left->remove(value, this);
else
return NULL;
} else if (value > this->value) {
if (right != NULL)
return right->remove(value, this);
else
return NULL;
} else {
if (left != NULL && right != NULL) {
this->value = right->minValue();
return right->remove(this->value, this);
} else if (parent->left == this) {
parent->left = (left != NULL) ? left : right;
return this;
} else if (parent->right == this) {
parent->right = (left != NULL) ? left : right;
return this;
}
}
}
int BSTNode::minValue()
{
if (left == NULL)
return value;
else
return left->minValue();
}

Pema binare listimi i vlerave me radh


Pr t konstruktuar algoritmin pr listimin me radh t vlerave t pems binare,
le t rikujtojm tiparet e pems binare t krkimit:

nndega e majt e nyjes prmban vetm vlerat m t vogla sesa vlera e


rrnjs;
nndega e djatht e nyjes prmban vetm vlerat m t mdha sesa vlera
e rrnjs.

Algoritmi duket si n vijim:

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

Algoritmet dhe strukturat e t dhnave

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).

Pema binare komplete


Thuhet se pema binare sht komplete nse t gjitha nivelet e saj, prveq
ndoshta nivelit m t fundit, jan komplete. Megjithat, niveli i poshtm jo i
kompletuar nuk mund t ket vrima, q do t thot se duhet t jet i mbushur
nga nyja m e majt dhe prpjet deri n ndonj nyje n mes. Shikoni ilustrimet
n vijim.

Shembull korrekt i pems binare komplete

Shembull jo korrekt, niveli i mesm sht i pakompletuar

Shembull jo korrekt, niveli i fundit ka "vrim"


386

Algoritmet dhe strukturat e t dhnave

Lartsia e pems binare komplete sht e rendit O(log n).

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.

Tipari heap pr pirgun min


Pr seciln nyje n pirg, vlera e nyjes sht m e vogl ose baraz sesa vlerat e
fmijve.

Tipari heap pr pirgun max


Pr seciln nyje n pirg, vlera e nyjes sht m e madhe ose baraz sesa vlerat e
fmijve.

387

Avni Rexhepi

Pr thjeshtsi, n vazhdim do t marrim n konsiderim vetm pirgun min.

Pirgu binar - Reprezentimi i brendshm i bazuar n


vargje
Pirgu sht pem binare dhe prandaj mund t ruhet n kompjuter duke prdorur
listat. Kjo ofron prparsi t ndryshme; nj prej tyre sht mundsia pr t
ndryshuar me lehtsi numrin e elementeve n pirg. N ann tjetr, secila nyje
ruan dy lidhje ndihmse, gj q nnkupton kosto shtes t memories. Si u tha m
par, pirgu sht pem binare komplete, gj q on n iden e ruajrjes s tij duke
prdorur nj varg. Duke prdorur reprezentimin e bazuar n vargje, mund t
zvoglojm koston e memories gjersa navigimi i pems mbetet mjaft i thjesht.
Sa pr algoritmin heapsort, implementimi i bazuar n vargje sht n njfar
mnyre i natyrshm.

Pasqyrimi i pirgut n varg


Pirgu ruhet (vendoset, deponohet) n varg nivel pas niveli. Niveli m i lart
prmban vetm rrnjn. Ajo pasqyrohet n elementin e par t vargut (me
indeksin 0). Fmijt e rrnjs pasqyrohen n elementin e dyt dhe t tret, e
kshtu me radh. Pirgu sht pem binare komplete, gj q garanton se nyjet e
pirgut i zn vendet n varg n mnyr kompakte, duke e br pasqyrimin
plotsisht efikas.

388

Algoritmet dhe strukturat e t dhnave

Nj pasqyrim i ktill i prgjigjet formulave vijuese:


Left(i) = 2 * i + 1

Right(i) = 2 * i + 2

Parent(i) = (i - 1) / 2

Pra, mund t shihet se navigimi npr pirg t pasqyruar n varg, n t vrtet


sht shum i leht.
Pjes kodi
class BinaryMinHeap {
private:
int *data;
int heapSize;
int arraySize;
int getLeftChildIndex(int nodeIndex) {
return 2 * nodeIndex + 1;

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;
}
};

Insertimi i elementit n pirg


N vazhdim do t shohim veprimet bazike n strukturn e pirgut. Kjo quhet
sifting (angl. Sift shosh, sit, analizim) apo shoshitje, por haset n literatur
edhe me termat trickle (pikon, rrjedh), heapify (pirgzoj), bubble
(flusk), percolate (filtroj, kulloj), etj.

Algoritmi i insertimit
Algoritmi i prgjithshm pr insertimin e elementit t ri n pirg, sht si vijon:

390

Algoritmet dhe strukturat e t dhnave


1. Shto elementin e ri n fund t vargut;
2. Gjersa sht e prishur rregulla e pirgut, shoshit prpjet elementin e ri.
Shoshitja bhet si vijon: krahaso vlern e nyjes me vlern e prindit. Nse
nuk jan n renditje t duhur, shkmbje pozitat.

Shembull
Inserto -2 n pirgun n vijim:

Inserto elementin e ri n fund t vargut:

N rastin e prgjithshm, pas insertimit, afr elementit t ri (nyjes s re)


zakonisht prishet (thyhet) rregulla e pirgut:
391

Avni Rexhepi

Pr t rivendosur rregulln e pirgut, algoritmi e shoshit prpjet elementin e ri,


duke br shkmbimin e pozitave me prindin e tij:

Tani, rregulla e pirgut sht e thyer n nyjen rrnj:

392

Algoritmet dhe strukturat e t dhnave

Vazhdo me shoshitje:

Rregulla e pirgut sht e plotsuar, shoshitja prfundon.

393

Avni Rexhepi
Pirgu fillestar

Pirgu pas insertimit t -2

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

Algoritmet dhe strukturat e t dhnave


siftUp(heapSize - 1);
}
}

Largimi i minimumit nga pirgu


Operacioni i largimit prdor iden e ngjashme me at t insertimit. Vlera e
rrnjs, q sht minimumi sipas rregulls s pirgut, zvendsohet me vlern e
fundit t vargut. Pastaj vlera e re shoshitet teposht, gjersa t arrij n pozitn e
duhur.

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:

Kopjo vlern e fundit t vargut n rrnj dhe zvoglo madhsin e pirgut pr 1:

395

Avni Rexhepi

Tani rregulla e pirgut sht thyer tek nyja rrnj:

Rrnja ka dy fmij. Shkmbe vlern e rrnjs me vlern m t vogl prej


fmijve:

396

Algoritmet dhe strukturat e t dhnave

Rregulla e pirgut sht thyer n nyjen 1:

Rivendose rregulln e pirgut:

397

Avni Rexhepi

Nyja 3 nuk ka fmij. Shoshitja sht kompletuar.


Pirgu fillestar

Pirgu pas largimit tw minimumit

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

Algoritmet dhe strukturat e t dhnave


Pjes kodi:
void BinaryMinHeap::siftDown(int nodeIndex)
{
int leftChildIndex, rightChildIndex, minIndex, tmp;
leftChildIndex = getLeftChildIndex(nodeIndex);
rightChildIndex = getRightChildIndex(nodeIndex);
if (rightChildIndex >= heapSize)
{
if (leftChildIndex >= heapSize)
return;
else
minIndex = leftChildIndex;
}
else
{
if (data[leftChildIndex] <= data[rightChildIndex])
minIndex = leftChildIndex;
else
minIndex = rightChildIndex;
}
if (data[nodeIndex] > data[minIndex])
{
tmp = data[minIndex];
data[minIndex] = data[nodeIndex];
data[nodeIndex] = tmp;
siftDown(minIndex);
}
}
void BinaryMinHeap::removeMin()
{
if (isEmpty())
throw string("Pirgu eshte i zbrazet");
else {
data[0] = data[heapSize - 1];
heapSize--;
if (heapSize > 0)
siftDown(0);
}
}

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

Algoritmet dhe strukturat e t dhnave


s kushtit t balansimit, do t shqyrojm pyetjen se a sht ky kusht mjaft i fort,
pr t garantuar q lartsia e pems AVL me n nyje do t jet O(log n). Pr t
vrtetuar kt, le t themi se N(h) paraqet numrin minimal t nyjeve q mund t
jen n nj pem AVL me lartsi h. Mund t gjenerohet rekurrenca pr N(h).
shte e qart, q N(0)=1. N prgjithsi N(h) do t jet 1 (pr rrnjn) plus
N(hL) dhe N(hR), ku hL dhe hR jan lartsit e dy nnpemve. (L: pr left-majt;
R pr Right-djatht). Pasi q pema e tr ka lartsi h, njra prej nnpemve
duhet t ket lartsin h-1, le t themi hL. pr t br nnpemn tjetr sa m t
vogl q t jet e mundur, e minimizojm lartsin e saj. Lartsia e saj mund t
jet jo m e vogl se h-2, pa e thyer kushtin AVL. Prandaj, kemi rekurrencn
N(0) = 1
N(h) = N(h1) + N(h 2) + 1
Kjo rekurrenc nuk sht e definuar mir pasi q N(1) nuk mund t llogaritet
prej ktyre rregulave, kshtu q e shtojm rastin plotsues N(1)=2. Kjo
rekurrenc duket shum e ngjashme me rekurrencn Fibonacci (F(h) = F(h1)=F(h-2)). N fakt, sht argumentuar se:

1 5

N (h)

Vlera (1 5 ) / 2 1.618 sht raporti i art i famshm (faktori i art).


Prandaj, duke invertuar kt, gjejm se lartsia e rastit m t keq pr pemt AVL
me n nyje sht prafrsisht logn , ku sht raporti i art. Nga kjo del se
pema AVL me lartsi H, ka s paku (afrsisht) H+3/ 5 nyje. prandaj, thellsia
sht m s shumti logaritmike. Pr lartsin e pems AVL plotsohet:
H < 1.44 log (N+2)-1.328
Kshtu q lartsia e rastit m t keq sht prafrsisht 44% m shum sesa
minimumi i mundshm pr pemt binare.
Mbetet t paraqitet se si t kryhen insertimet dhe fshirjet n pemt AGL dhe si t
restaurohet kushti i balansit AVL pas secilit insertim ose fshirje.

401

Avni Rexhepi
Rasti LL Rotacioni djathtas
(Left Left Case Right Rotation)

Rasti RR Rotacioni majtas


(Right Right Case Left Rotation)

Rasti LR
(Left Right Case)

Rasti RL
(Right Left Case)

Rotacioni majtas (Left Rotation)

402

Rotacioni djathtas (Right Rotation)

Algoritmet dhe strukturat e t dhnave

Rotacioni djathtas (Right Rotation)

Rotacioni majtas (Left Rotation)

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

Fig.6.32 Rotacion i njfisht


N ann tjetr, supozojm se nipi (fmija i fmijs) i rnd sht nnpema e
djatht e nnpems s majt (prsri nnpema e majt e fmijs s djatht sht
simetrike). N kt rast, rotacioni i njfisht nuk do t jet i mjaftueshm pr t
prmirsuar jobalansin. Mirpo, dy rotacione do ta arrijn kt. Shikoni figurn
vijuese. N veanti, bhet rotacioni i majt n nipin nipin e majt-t djatht
(fmija i djatht i fmijs s majt) dhe pastaj rotacion t djatht n fmijn e
majt, pr t rivendosur balansin. Ky operacion quhet rotacion i dyfisht
(angl. double rotation). (N figur paraqiten faktort e balansit pr t treguar
vlerat e mundshme, t cilat duhet t azhurohen).

Fig. 6.33 Rotacion i dyfisht majtas-djathtas


Pra, n prgjithsi rastet e mundshme t ribalansimit jan:
404

rotacion i njfisht i majt, L


rotacion i njfisht i djatht, R

Algoritmet dhe strukturat e t dhnave


-

rotacion i dyfisht (majt-djatht) LR


rotacion i dyfisht (djatht-majt) RL

Vetm pr nyjet q ndodhen n rrugn/shtegun prej rrnjs deri tek pika e


insertimit, mund t ndryshohen faktort e balansimit.
Fshirja
Fshirja/largimi sht i ngjashm me insertimit n at se fillohet me aplikimin e
algoritmit t fshirjes pr pemn binare t pabalansuar. Kjo prfshin tri raste:
gjethen, fmijn e vetm dhe dy fmij. N rastin e dy fmijve, duhet t gjindet
elsi zvendsues. Pasi t jet kryer fshirja, kalojm npr pem (duke u kthyer
nga thirrjet rekurzive), duke azhuruar faktort e balansimit (ose lartsit) gjat
kalimit. Sa her q haset ndonj nyje e pabalansuar, aplikohet rotacioni i
nevojshm, pr t sanuar situatn.
Supozojm se duhet t fshihet nj els (nyje, vler) nga nnpema e majt dhe si
rezultat i ksaj lartsia e nnpems sht zvogluar pr nj, por kjo ka shkaktuar
q ndonj para-ardhs (nyje para-ardhse), t thyej kushtin e balansit. Jan dy
raste t mundshme. S pari, nse faktori i balansit pr fmijn e djatht sht ose
0 ose +1 (nuk peshon majtas), mund t bhet rotacion i njfisht, si n figurn
vijuese. Ka disa mundsi t faktorve t balansimit.

Fig. 6.34 Rotacion i njfisht pr fshirje


N ann tjetr, nse fmija i djatht ka faktor t balansit -1 (peshon majtas)
ather duhet t bjm rotacion t dyfisht, si n figurn vijuese.

405

Avni Rexhepi

Fig. 6.35 Rotacion i dyfisht pr fshirje


N vijim do t paraqitet nj rast i kompletuar, si n figur. S pari fshihet nyja
1. Kjo shkakton q nyja 2 t jet e pabalansuar. Ather, kryhet rotacion i
njfisht i majt n nyjn 2. Mirpo, tani rrnja bhet e pabalansuar. Tash, bhet
rotacion i dyfisht djathtas-majtas, n rrnj.

Fig. 6.36 Shembull i fshirjes n pemn AVL


406

Algoritmet dhe strukturat e t dhnave


Fshirja e zvarritur
Numri i rasteve t ndryshme t fshirjes sht shum i madh. Ndonjher mund
t prdoret nj alternativ e fshirjes, e quajtur fshirja e zvarritur ose fshirje
prtace. Idea sht q t mos shkohet n procesin e fshirjes s plot t nyjeve,
por pr seciln nyje t shtohet nj informacion plotsues, nj vler bool-eane e
cila tregon se a sht nyja e gjall apo e vdekur. Kur t fshihet nj els
(vler), thjesht e deklarojm t vdekur, por e lm n pem. Nse bhet nj
tentim pr t insertuar t njejtin els prsri, ather nyja bhet prsri e
gjall. Natyrisht, pas nj seri t gjat t fshirjeve dhe insertimeve, sht e
mundur q pema t ket shum nyje t vdekura. Pr t prmirsuar kt, n
mnyr periodike kryhet faza e pastrimit t mbeturinave (angl. garbage
collection phase), e cila e prshkon pemn, duke zgjedhur vetm elementet e
gjalla dhe pastaj duke ndrtuar pemn e r AVL, vetm me kto elemente.
Shembull i ndrtimit i pems s balansuar
Pr t gjetur lartsin maksimale h pr t gjitha pemt e balansuara me n nyje,
le t shqyrtojm lartsin h dhe le t provojm t ndrtojm pemn e balansuar
me numrin maksimal t nyjeve. Kjo strategji rekomantohet pasi q si n rastin e
lartsis minimale, vlera mund t prfitohet pr disa vlera specifike t n-it. Le t
jet pema me lartsi h, e emrtuar Th. Qartazi, T0 sht pema e zbrazt, ndrsa
T1, sht pema me nj nyje t vetme (rrnjn). Pr t konstruktuar pemn Th pr
h>1, do t sigurojm nyjen me dy nndeg t cilat prsri kan numr minimal
t nyjeve. Rrjedhimisht, nnpemt jan poashtu T-ja. sht evidente se nj
nnpem duhet t ket lartsin h-1 dhe tjetra pastaj lejohet t ket lartsin pr
nj m t vogl, h-2. N figurn vijuese jan paraqitur pemt me lartsi 2, 3 dhe
4. Pasi q principi i kompozimit t tyre i prngjan numrave Fibonacci, kto
quhen Pemt Fibonacci. Kto pem definohen si:
1. Pema e zbrazt sht Pema-Fibonacci me lartsi 0.
2. Nj nyje e vetme sht Pem-Fibonacci me lartsi 1.
3. Nse Th-1 dhe Th-2 jan Pem-Fibonacci me lartsit h-1 dhe h-2,
ather Th=<Th-1, x, Th-2> jan Pem-Fibonacci.
4. Nuk ka pem t tjera q jan Pem-Fibonacci.

407

Avni Rexhepi

Fig. 6.37 Pemt-Fibonacci me lartsi 2, 3 dhe 4.


Numri i nyjeve n Th sht i definuar prmes relacionit t thjesht vijues t
rekurrncs:
N0 = 0, N1 = 1
Nh = Nh-1 + 1 + Nh-2
Numrat Ni jan ata nymra t nyjeve pr t cilt mund t arrihet rasti m i keq
(kufiri i eprm i h) dhe jan t njohur si Numra t Leonardos.
Le t analizojm ka mund t ndodh kur t insertohet nj nyje e re n pmn e
balansuar. Me nyjen e dhn r, me nnpemn e majt L dhe t djatht R, mund
t dallohen restet vijuese. Supozojm se nyja e re insertohet n L, duke
shkaktuar rritjen e lartsis s saj pr 1:
1. hL=hR: L dhe R bhen me lartsi jo t barabarta, por kriteri i balansit
nuk prishet.
2. hL<hR: L dhe R fitojn lartsi t barabart, d.m.th., balansi
prmirsohet.
3. hL>hR: kriteri i balansit prishet dhe pema duhet t ristrukturohet.
Shqyrtoni pemn n figurn vijuese (Fig. 6.38). Nyjet me els 9 dhe 11 (q do
t shoheshin si fmij t nyjes 10) mund t insertohen pa ribalansim. Pema me
rrnj 10 do t bhej nj-anshe (rasti 1); Nyjes 8 do ti prmirsohet balansi
(rasti 2).

408

Algoritmet dhe strukturat e t dhnave

Fig. 6.38 Pema e balansuar.


Insertimi i nyjeve, 1, 3, 5, ose 7, sidoqoft, do t krkonte ribalansim pasues (1,3
si fmij t 2, kurse 5,7, si fmij t 6).
Nj vzhim i kujdesshm i situats do t zbulonte se jan vetm dy
konstelacione t ndryshme t cilat krkojn trajtim individual. T tjerat, mund t
nxirren prmes shqyrtimit simetrik t dy t parave. Rasti i par karakterizohet
me insertimin e nyjeve 1 ose 3. Rasti i dyt, me insertimin e nyjeve 5 ose 7.
T dy rastet jan prgjithsuar n figurn vijuese (Fig. 6.39), n t ciln format
drejtkndshe paraqesin nnpemt dhe lartsia e shtuar nga insertimi, sht
shnuar me x. Transformimet e thjeshta (si m hert) t t dy strukturave
restaurojn balansin. Rezultati i tyre sht paraqitur n figur. Vreni q lvizjet
e vetme t lejuara jan ato q ndodhin n drejtimin vertikal, ndrsa pozitat
relative horizontale t nyjeve t paraqitura dhe t nnpemve, mbesin t
pandryshuara.

Fig. 6.39a - ekuilibri (jobalansi) i shkaktuar prej insertimit - Rasti 1.

409

Avni Rexhepi

Fig. 6.39b - ekuilibri (jobalansi) i shkaktuar prej insertimit - Rasti 2.


Algoritmi pr insertim dhe ribalansim varet nga mnyra se si ruhet informacioni
pr balansin e pems. Nj opcion sht q edhe informacioni i balansit t jet
pjese e vet strukturs s pems. N kt rasit, faktori i balansit duhet t
rillogaritet sa her q t ket insertim ose fshirje n pem, gj q shkakton
mbingarkim. Nj zgjidhje tjetr poashtu sht q shtohet nj faktor eksplicit i
balansit pr seciln nyje.
Nse faktorin e balansit t nyjes e interpretojm si lartsia e nnpems s saj t
djatht minus lartsia e nnpems s saj t djatht, ather do t kemi algoritmin
vijues pr kt tip t nyjes. Procesi i insertimit t nyjes konsiston esencialisht n
tri pjest e njpasnjshme vijuese:
1. Prcille shtegun e krkimit deri sa t vrtetohet q nyja ve nuk sht n
pem (nuk ekziston paraprakisht).
2. Inserto nyjen e re dhe prcakto faktorin rezultues t balansit.
3. Kthehu prapa prgjat shtegut t krkimit dhe verifiko faktort e balansit
n seciln nyje. Nse sht e nevojshme, ribalanso.
Kjo procedur prshkruan operacionin e duhur t krkimit n seciln nyje dhe
pr shkak t formulimit rekurziv t saj mund t akomodoj me lehtsi
operacionet plotsuese n rrugn e kthimit prapa, prgjat shtegut t krkimit.
N secilin hap, duhet t prcillet nse informacioni nse sht rritur lartsia e
nnpems (n t ciln sht kryer insertimi). Pr kt arsye, shtohet nj
parametr bool-ean h, me kuptimin q lartsia e nnpems sht rritur. Qartazi,
h duhet t shnoj parametr variabil pasi q prdoret pr t transmetuar
rezultat.
Supozojm tani se procesi sht duke u kthyer n nyjen p^ nga dega e majt
(shiko figurn 6.39), me indikacionin se lartsia e saj sht rritur. Tani duhet t
dallojm ndrmjet tri kushteve t cilat prfshijn lartsit e nnpemve para
insertimit:
1. hL < hR, p.bal = +1, jobalansi paraprak sht ekuilibruar.
410

Algoritmet dhe strukturat e t dhnave


2. hL = hR, p.bal = 0, pesha tash sht anuar n t majt.
3. hL > hR, p.bal = -1, nevojitet ribalansimi.
N rastin e tret, inspektimi i faktorit t balansit t rrnjs s nnpems s majt
(le t themi p1.balansi) prcakton nse sht prezent rasti 1 ose rasti 2 i figurs
6.39. Nse ajo nyje ka edhe nnpemt t majt m t lart se e djathta, ather
kemi t bjm me rastin 1, prndryshe me rastin 2. (Merrni se n kt rast nuk
mund t ndodh nnpema e majt me faktor balansi t barabart me 0 n
rrnjn e saj). Operacionet e nevojshme t rebalansimit jan trsisht t
shprehura si sekuenca t ricaktimeve t pointerve. n fakt, pointert
ndryshohen n mnyr ciklike, duke rezultuar ose n rotacion t njfisht ose n
rotacion t dyfisht t dy ose tri nyjeve t prfshira. Si plotsim i rotacionit t
pointerve, duhet t bhet edhe azhurimi i faktorve t balansit.
Principi i puns, sht paraqitur n figurn vijuese. Shqyrtoni rastin e pems
binare (a) e cila prbhet prej dy nyjeve. Insertimi i nyjes me key (els) 7,
rezulton n pem t pabalansuar. Nevojitet rotacioni i njfisht RR, i cili
rezulton n pemn e balansuar n mnyr perfekte (b). Insertimi i mtejshm i
nyjeve 2 dhe 1 rezulton n jobalans t nndegs me rrnj 4. Kjo nndeg
balansohet me rotacion t njfisht LL (d). Iinsertimi vijues i nyjes 3 menjher
zhvendos kriterin e balansi n nyjen rrnj 5. Balansi ktu rikthehet me
rotacion t dyfisht LR dhe rezultati sht pem (e). Kandidati i vetm pr t
humbur balansin n insertimin vijues sht nyja 5. Vrtet, insertimi i nyjes 6
krkon rastin e ribalansimit t dyfisht RL. Pema finale sht paraqitur nn (f).

Fig. 6.40 Insertimet n pemn e balansuar


Parashtrohen dy pyetje lidhur me performansn e algoritmit t insertimit n
pemn e balansuar:

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

Algoritmet dhe strukturat e t dhnave


nnpema e rrnjs 7 ribalansohet me nj rotacion t njfisht RR. Fshirja e nyjes
2, edhe pse n vete e drejtprdrejt pasi q ka vetm n pasardhs, shkakton
rotacion t dyfisht RL. Rasti i katrt, rotacioni i dyfisht LR, thirret n fund pas
largimit t nyjes 7, e cila s pari sht zvendsuar me elementin e saj m t
djatht t nnpems s majt, d.m.th., nga nyja 3.

Fig. 6.41 Fshirjet n pemn e balansuar


413

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

Algoritmet dhe strukturat e t dhnave


Prcjellja e ngjyrs pr seciln nyje krkon 1 bit t informacioit pr nyje, sepse
jan vetm dy ngjyra. Pema kuq-e-zi nuk prmban ndonj t dhn tjetr
specifike pr t qenit pem kuq-e-zi, kshtu q gjurma e saj n memorie sht
pothuajse identike me pemn klasike (t pangjyrosur) binare. N shum raste,
ruajtja e nj biti shtes t informacionit mund t ruhet pa ndonj kosto shtes.

Fig. 6.42 Shembull i pems kuq e zi


Pemt kuq e zi i ka zbuluar Rudolf Bayer, n vitin 1972, i cili n fillim i kishte
quajtur B-Trees, kurse n vitin 1978, Leonidas J. Guibas dhe Robert
Sedgewick, n punimin e tyre A dichromatik frameork for balanced trees, i
prdorn ngjyrat kuq e zi (pasi q dukeshin m s miri n printerin e tyre, q e
prdornin gjat puns, Xerox Parc) dhe prej ather u quajtn me emrin e ri (...
pra nuk ka t bj me shqiptart!)
Pemt kuq e zi, jan tip special i pemve binare dhe prdoren pr t organizuar
t dhnat e krahasueshme, si fragmentet e teksteve ose numrat.
Nyjet gjethe t pems kuq-e-zi nuk prmbajn t dhna. Kto gjethe nuk duhet
t jen n memorie n mnyr eksplicite nj pointer fmij NULL (ose
prdoret edhe versioni NIL), mund t paraqes faktin se fmija sht gjethe
por kjo i thjeshton disa algoritme q operojn n pemt kuq-e-zi, nse gjethet
jan realisht nyje eksplicite. Pr t ruajtur memorien, ndonjher nj nyje e
vetme sentine (angl. sentine-roje, rojtar), kryen rolin pr t gjitha gjethet,
ashtu q t gjitha referencat prej nyjeve interne n ato gjethe, pointojn n nyjen
sentinel. (Nyja sentinel, shrben si prfundim dhe nuk prmban e as nuk
referon ndonj t dhn t menagjuar nga strutura e t dhnave. Prdoren si
alternativ ndaj prdorimit t NULL, pr t prfituar n shpejtsi t kryerjes s
operacioneve, redukim t kompleksitetit t algoritmit dhe madhsis s kodit
dhe pr t krijuar struktur m robuste (angl. robust-i fort, i fuqishm)).
415

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).

Tiparet e pemve kuq e zi


Prveq krkesave t imponuara nga pema binare e krkimit, pema kuq-e-zi
duhet ti plotsoj edhe kto kushte:
1. Nyja sht e kuqe ose e zez.
2. Rrnja sht e zez. (kjo nganjher evitohet, pasi rrnja mund t
ndryshohet kurdo nga e kuqe n t zez, por jo domosdo edhe
anasnjelltas, kshtu q kjo rregull nuk ka efekt n analiz).
3. T gjitha nyjet NIL jan t zeza (T gjitha gjethet kan ngjyr t njjt
me rrnjn).
4. Secila nyje e kuqe duhet t ket dy nyje fmij t zeza.
5. Secili shteg prej nyjes s dhn deri tek cilado nyje pasardhse prmban
numr t njjt t nyjeve t zeza.
Kto kufizime detyrojn (shtrngojn) tiparin kritik t pemve kuq-e-zi, q
shtegu prej rrnjs deri tek gjethja m e largt nuk sht m shum se dyfish i
gjat sa shtegu prej rrnjs deri tek gjethja m e afrt. Rezultat i ksaj sht q
pema sht prafrsisht e balansuar pr nga lartsia. Pasi q operacionet si
insertimi, fshirja dhe krkimi krkojn koh t rastit m t keq proporcionale me
lartsin e pems, ky kufi i eprm teorik n lartsi, u mundson pemve kuq-e-zi
q t jen efikase edhe n rastin m t keq, pr dallim prej pemve binare t
krkimit.
Pr t kuptuar prse kjo sht e garantuar, mjafton t shqyrtohen tiparet 4 dhe 5
s bashku. Pr nj pem binare T, le t jet B numri i nyjeve t zeza n tiparin 5.
Le t jet shtegu m i shkurtr prej rrnjs s T deri tek cilado nyje, me B nyje t
zeza. Shtigjet e mundshme m t gjata mund t konstruktohen duke insertuar
nyje t kuqe. Mirpo, tipari 4 e bn t pamundur q t insertohet m shum se
nj nyje e njpasnjshme e kuqe. Prandaj, shtegu m i gjat i mundshm
prbhet prej 2B nyjeve, nj e kuqe nj e zez.
Shtegu m i shkurtr i mundshm i ka t gjitha nyjet t zeza dhe shtegu m i
gjat i mudnshm kalon npr nyjet e kuqe dhe t zeza. Pasi q t gjitha shtigjet
maksimale kan numr t njjt t nyjeve t zeza, tipari 5, kjo tregon se asnj
shteg nuk sht m shum se dyfish m i gjat se ndonj shteg tjetr.

416

Algoritmet dhe strukturat e t dhnave

Aplikimet dhe strukturat e ndrlidhura t t dhnave


Pema kuq e zi garanton pr kohn e rastit m t keq pr insertim, fshirje dhe
krkim. Kjo jo vetm q i bn ato t vlefshme pr aplikacione sensitive n
aspektin kohor, si aplikacionet n koh reale (angl. real-time applicacions), por i
bn edhe si blloqe t dobishme ndrtimi n strukturat tjera t t dhnave t cilat
ofrojn garac pr rastin m t keq. Pr shembull shum struktura t t dhnave
t prdorura n gjeometrin llogaritse mund t bazohen n mept kuq-e-zi.
Edhe Completely Fair Scheduler i prdorur n kernel t Linux-it, prdor
pemt kuq-e-zi.
Pemt kuq-e-zi poashtu jan shum t rndsishme n programimin funksional,
ku ato jan struktura m e zakonshme persistente e t dhnave, e prdorur pr
ndrtimin e vargjeve asiciative dhe seteve, t cilat mund t rifitojn versionet e
mparshme, pas ndryshimeve.
N vitin 2008, Sedgewick prezentoi versionin e thjeshtsuar t pemve kuq-e-zi
t quajtur pema kuq-e-zi me tendenc majtas (angl. Left-leaning red-black
tree-LLRB), duke eliminuar shkalln e mhershme t paspecifikuar t liris n
implementim. Versioni LLRB ka edhe nj invariant plotsuese q t gjitha
lidhjet e kuqe duhet t anojn majtas, prve gjat insertimit dhe fshirjes. Edhe
pema tango (angl. tango tree), q sht nj tip i pems s optimizuar pr krkime
t shpejta, zakonisht prdor pemt kuq-e-zi si pjes t strukturs s saj t t
dhnave.

Operacionet n pemt kuq-e-zi


Sikur n rastin e pemve binare, insertimet dhe fshirjet nga pema kuq-e-zi i
prishin tiparet e pems kuq-e-zi, kshtu q duhet t bhet restaurimi, pr ka
prdoren operacionet prmatse.

Rotacionet
Rotacioni sht operacion lokal, i cili ruan renditjen e elsave pr prshkimin
in-order.

Fig. 6.43 Rotacionet


417

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

Algoritmet dhe strukturat e t dhnave


/* Tani, restauro tiparin kuq-e-zi */
x->colour = red;
while ( (x != T->root) && (x->parent->colour == red) ) {
if ( x->parent == x->parent->parent->left ) {
/* Nse prindi i x-it sht majtas,
y sht axh i djatht i x-it */
y = x->parent->parent->right;
if ( y->colour == red ) {
/* rasti 1 ndrysho ngjyrat */
x->parent->colour = black;
y->colour = black;
x->parent->parent->colour = red;
/* Lvize x-in prpjet pems */
x = x->parent->parent;
}
else {
/* y sht nyje e zez */
if ( x == x->parent->right ) {
/* dhe x sht n t djatht */
/* rasti 2 lvize x-in lart dhe rrotullo */
x = x->parent;
left_rotate( T, x );
}
/* rasti 3 */
x->parent->colour = black;
x->parent->parent->colour = red;
right_rotate( T, x->parent->parent );
}
}
else {
/* prsrtite pjesn me kushtin if me djatht-as
dhe majt-as t ndryshuara/shkmbyera*/
}
/* Ngjyrose rrnjn me t zez */
T->root->colour = black;
}

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.

sht thirrur funksioni pr


insertim, pr t insertuar n pem
nyjen 4.
Kjo m nuk sht pem kuq-e-zi,
sepse n shtegun 11 - 2 - 7 - 5 - 4
ka dy nyje t kuqe.
Shno nyjen e re me x, dhe
axhn e saj (vllaun e prindit)
me y. y sht i kuq (rasti 1) ...
Ndrysho ngjyrat e nyjeve 4, 7 dhe 8.
Lvize nyjen x-in te lart deri tek
gjyshi i saj, 7.
Prindi i x-it (2) sht akoma i kuq,
kshtu q prishet tipari kuq-e-zi.

Shno axhn me, y.


N kt rast, axha sht me
ngjyr t zez, rasti 2 ...

420

Algoritmet dhe strukturat e t dhnave


Lvize x-in te lart dh rrotullo
majtas.

Akoma nuk sht pem kuq-e-zi


... axha akoma me ngjyr t
zez, por prindi i x-it, sht n t
majt...

Ndrysho ngjyrat e 7 dhe 11 dhe


rrotullo djathtas...

Tani kemi pem kuq-e-zi, kshtu


q kemi prdunduar!
Koha O(logn)!

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

Algoritmet dhe strukturat e t dhnave

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:
-

implementimi i thjesht (m i thjesht sesa pemt AVL dhe ato kuq-e-zi),


performansa e krahasueshme (rasti mesatar, si edhe pr pemt tjera),
shfrytzimi i ult i memories (nuk kan nevoj pr t dhna plotsuese),
mundsia e krijimit t strukturave t persistente t t dhnave t pemve
splay (q pas azhurimit, lejojn qasje n t dy versionet: t vjetrin dhe t
riun),
puna e mir me nyjet q kan elsa identik (n t kundrt me tipet tjera t
pemve t vet-balansuara). Edhe pr elsat identik, performansa mbetet
e amortuzuar, O(log n). T gjitha operacionet n pem ruajn renditjen e
nyjeve identike prbrenda pems, q sht tipar i ngjashm me algoritmet
stabile t sortimit. Dizajni i kujdesshm i operacionit t krkimit mund t
kthej nyjen m t majt ose m t djatht pr elsin e dhn.

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

Algoritmet dhe strukturat e t dhnave


Reprezentimi i pemve splay mund t ndryshoj edhe kur ato qasen vetm pr
lexim (angl. read-only), p.sh, pr operacionin e krkimit. Kjo e komplikon
prdorimin e pemsve t tilla n ambient me shum rrjedha, fije (angl. multithreaded). N mnyr specifike, nevojitet menagjim shtes, nse lejohen rrjedhat
e shumfishta, pr t kryer operacionet paralele t krkimit.

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:
-

nse x sht fmij i majt apo i djatht i nyjes prind, p


nse prindi p sht rrnj, dhe nse jo
nse prindi p sht fmij i majt apo i djatht i prindit t tij, g (gjyshit t
x-it).

sht me rndsi t mbahet mend q pas do splay operacioni, nyja gg


(strgjyshi) i x-it, t pointoj tek nyja x. Nse gg sht null, ather tani sht e
dukshme se x sht rrnja dhe duhet t azhurohet si nyje e till.
Ekzistojn tri lloje t hapave splay, ku secili prej tyre sht rast i ans s majt
ose t djatht. Pr thjeshtsi, do t paraqitet vetm nga nj rast (tjetri sht
pasqyrim). Tipet e lvizjeve jan t ashtuquajturat: ZIG, ZIG-ZIG dhe ZIGZAG.
Hapi Zig (angl. Zig Step): ky hap kryhet kur p sht rrnj. Pema rrotullohet
(kryhet rotacioni) n degn ndrmjet x dhe p. Hapat Zig ekzistojn pr t trajtuar
qshtjen e paritetit dhe do t bhen vetm si hap i fundit i splay operacionit
dhe vetm kur x ka thellsi teke n fillim t operacionit.
p

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

Algoritmet dhe strukturat e t dhnave


majt t nnpems s tij t djatht (pasardhsin e tij in-order). Pastaj n vend t
saj e largojm at nyje. N kt mnyr, fshirja redukohet n problemin e
largimit t nyjes q ka 0 ose 1 fmij.
Pr dallim prej pems binare t krkimit, n pemn splay pas fshirjes, bhet
splay i prindit t nyjes s larguar deri n krye t pems. Ose, nyja q duhet t
fshihet, s pari splay-ohet, d.m.th., sillet n rrnj t pems dhe pastaj fshihet.
Kjo e l pemn me dy-nnpem (pa rrnj). Pastaj, elementi maksimal i
nnpems s majt (metoda e par) ose minimumi i nnpems s djatht
(metoda e dyt), splay-ohet deri n rrnj. Nnpema e djatht bhet fmij e
djatht i nnpems s majt rezultuese (pr metodn e par). Rrnja e nnpems
s majt sht rrnja e pems s bashkuar.
Prse funksionojn pemt splay: nse fillohet me nj pem t zbrazt dhe
kryhet nj sekuenc e m operacioneve t pems splay (insertimit, fshirja,
krkimi), ather koha totale e ekzekutimit do t jet O(m log n), ku n sht
numri maksimal i elementeve n pem n cilndo koh. Prandaj, koha mesatare
pr operacion sht O(log n). Vrtetimi i ksaj sht kompleks, por prmes
shembujve vijues do t mund t shihet si punojn pemt splay.

Fig. Insertimi i nyjes x

Fig.6.44 - Fshirja e nyjes x


Ndrsa, operacionet pr splay-imin e nyjes, do t dukeshin si n vijim.

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

Algoritmet dhe strukturat e t dhnave


Insertimi dhe krkimi:

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)

Nnhapat gjat fshirjes s nyjes 5:


5

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

Fig. 6.46 Operacionet n Splay-Tree

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

Algoritmet dhe strukturat e t dhnave


}
}
void replace( node *u, node *v ) {
if( !u->parent ) root = v;
else if( u == u->parent->left ) u->parent->left = v;
else u->parent->right = v;
if( v ) v->parent = u->parent;
}
node* subtree_minimum( node *u ) {
while( u->left ) u = u->left;
return u;
}
node* subtree_maximum( node *u ) {
while( u->right ) u = u->right;
return u;
}
public:
splay_tree( ) : root( 0 ), p_size( 0 ) { }
void insert( const T &key ) {
node *z = root;
node *p = 0;
while( z ) {
p = z;
if( comp( z->key, key ) ) z = z->right;
else z = z->left;
}
z = new node( key );
z->parent = p;
if( !p ) root = z;
else if( comp( p->key, z->key ) ) p->right = z;
else p->left = z;
splay( z );
p_size++;
}
node* find( const T &key ) {
node *z = root;
while( z ) {
if( comp( z->key, key ) ) z = z->right;
else if( comp( key, z->key ) ) z = z->left;
else return z;

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

Algoritmet dhe strukturat e t dhnave

Lista me kaprcime - Skip List


Pemt e zakonshme dhe pemt e balansuara, shrbejn pr ruajtjen e strukturs
s fjalorit (angl. Dictionary). Pemt e pabalansuara jan t thjeshta dhe punojn
mir n rastin mesatar, por n rastet e kundrta kan koh t keqe t
ekzekutimit. Pemt AVL garantonin performans t mir, por ishin t vshtira
pr tu implementuar. Pemt splay ofruan nj alternativ pr pemt AVL,
sepse ishin t thjeshta dhe vet-organizative. Nj struktur tjetr q prdoret pr
ruajtje t fjalorve, sht struktura e quajtur Lista me kaprcime (Skip List;
angl. skip-krcim, kaprcim, kalim, hedhje, etj).
Skip listat u zbuluan nga illiam Pugh n vitin 1989. N vitin 1990 u
propozuan si nj alternativ pr pemt e krkimit binar dhe pr pemt tjera t
balansuara.
Lista me kaprcime sht ndr strukturat m praktike pasi q sht mjaft e
thjesht dhe duket t jet m e shpejta. Lista me kaprcikme sht nj
prgjithsim i listave t lidhura. Si e till, ajo ka shum nga thjeshtsia e listave
t lidhura, por ofron performans optimale O(log n). Nj veti tjetr interesante
sht se ato jan struktur e t dhnave me rastsi (angl. randomized). Me fjal
t tjera, pr krijimin e tyre prdoret nj gjenerator i numrave t rastit. Skip listat
jan efikase n rastin e pritur, mirpo pr dallim prej pemve t pabalansuara
binare, pritja nuk ka t bj asgj me shprndarjen e elsave. Ajo varet vetm
prej gjeneratorit t numrave t rastit. S kndejmi, n t kundrtn, nuk mund t
ket sekuenc t operacioneve q do t jet gjithmon e keqe. N fakt,
probabiliteti q skip lista t performoj keq sht shum i vogl.
Skip lista perfekte
Skip lista ka filluar me iden si t prmirsohet lista e lidhur e sortuar? N
listn e lidhur sht e leht t bhet insertimi dhe fshirja, por sht sum e
veshtir t lokalizohen elementet n mnyr efikase, sepse duhet t kalojm nj
nga nj me radh n nj moment kohor, npr t gjith antart e lists. Nse do
t ishte e mundur t kaprcehen nga disa elemente, ather do t zgjidhej
problemi. N mnyr e t menduarit pr skip listat sht si nj hierarki e listave
t lidhura t sortuara, t vendosura mbi njra tjetrn.
Pr t konkretizuar, imagjinoni nj list t lidhur, t sortuar sipas vlerave t
elsave dhe supozojm se n fund kemi nj nyje speciale sentinel (angl.
sentinel-kujdestar, roje), t quajtur nil (angl. nil-zero, asgj, hiq), e cila
konsiderohet me vler . (madhsi infinit ). Pastaj le t marrim do t dytn
vler t lists s lidhur (ose p.sh vlerat teke) dhe i ngrisim nj nivel m lart n
nj list t re t lidhur me 1/2 (gjysmn) e elementeve. N vazhdim, marrim
433

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.

Fig. 6.47 Skip lista


Krkimi pr nj vler (els) x do t fillonte n nivelin m t lart. Mund t
skenohet n mnyr lineare prgjat lists n nivelin aktual i, duke krkuar pr
elementin e par q sht m i madh se x (duke rikujtuar q vlera e elsit pr
Nil sht ). Pointeri p le t pointoj n nyjen para ktij hapi. Nse vlera e ps sht e barabart me x, ather ndalemi. Prndryshe, zbrezim n nivelin e
ardhshm m t ult i-1 dhe e prsrisim krkimin. N nivelin 0 i kemi t
ruajtur t gjith elsat, kshtu q nse nuk e gjejm at n at nivel, e
ndrpresim krkimin (vlera nuk sht gjetur). Pr shembull, n figurn e
mparshme sht paraqitur me vija t ndrprer krkimi pr x=19.
N rastin m t keq koha e krkimit do t ket mundsi t kaloj npr t gjitha
[lg n] nivelet (nse elsi nuk sht n list). Themi se krkimi viziton m s
shumti dy nyje pr nivel, n listn e idealizuar (perfekte). Kjo sht e vrtet
pasi q dihet se n nivelin paraprak (m i lart) ndodhemi ndrmjet dy nyjeve t
njpasnjshme p dhe q, ku vlera e p sht m e vogl se x (vlera e krkuar
x), ndrsa vlera e q sht m e madhe se x. ndrmjet dy nyjeve t
njpasnjshme n nivelin e njjt ka saktsisht vetm nj nyje n nivelin e
ardhshm m t ult. Prandaj, gjersa zbresim pr nj nivel, krkimi do t
vizitoj nyjen aktual n m s shumti nj nyje shtes. Prandaj, vizitohen m s
shumti dy nyje pr nivel dhe O(log n) nivele, pr totalin e kohs O(log n).
Skip lista e randomizuar
Problemi me listn perfekte sht se ajo sht plotsisht e balansuar (sikur pema
binare e plot, e balansuar perfekt). Nse do t insistojm n struktur t till,
insertimi i nj nj nyjeje do t rezultonte me restrukturim t plot t lists. Skip
listat, sikur edhe t gjitha listat e balansuar mir, lejojn nj nivel t
jobalansimit. N fakt, skip lista e arrin kt faktor shtes t pjerrtsis
prmes rastsis (randomizimit).
Le t marrim parasysh strukturn probabilistike t skip lists n cilindo
moment kohor. (kjo nuk sht ajo q ndodh saktsisht me ndrtimin e
434

Algoritmet dhe strukturat e t dhnave


strukturs, por shrben si mnyr pr t sqaruar at q ndodhet n prapavi t
strukturs s till). N skip list nuk krkohet q saktsisht do e dyta nyje e
nivelit i t ngritet n nivelin i+1, por imagjinoni thua se hidhet monedha pr
t vendosur a do t promovohet nyja n nivelin m t lart apo jo. Nse bie
koka (d.m.th., me probabilitet 1/2) nyja promovohet n nivelin e ardhshm
m lart t lists s lidhur, prndryshe qndron n nivelin ku ndodhet. Duke
pasur parasysh rastsin (probalitetin 1/2), numri i pritur i nyjeve n nivelin 1
sht n/2, numri i pritur i nyjeve n nivelin 2 sht n/4, e kshtu me radh. Pr
m tepr, pasi q nyjet paraqiten me rastsi n secilin nivel, sht e pritshme q
nyjet n nivelin e dhn jan t shprndara mir (nuk jan t gjitha t
grumbulluara n nj skaj). Prandaj, skip listat e randomizuara sillen shum
ngjajshm me skip listat e idealizuara (perfekte), n rastin mesatar (n rastin e
pritur). Procedura e krkimit mbetet saktsisht e njjt me at t rastit ideal, si
n figurn n vijim.

Ajo q sht interesante me skip listat sht se sht e mundur q t insertohen


dhe t fshihen nyjet n lsit, ashtu q struktura probabilistike t ruhet n do
koh. Pr insertimin e elsit x s pari krkojm n list pr elsin x pr t
gjetur paraardhsit e drejtprdrejt n skip list (n secilin nivel t strukturs).
Nse x nuk ndodhet n list, e krijojm nyjen pr x dhe e insertojm at n
nivelin m t ult. Pastaj hedhim monedhn (gjenerohet nj numr i plot i
rastit, nga gjeneratori i numrave t rastit). Nse rezultati sht pil (numri i
rastit sht tek), ndalemi. Prndryshe e insertojm x-in n nivelin e ardhshm
m lart, t strukturs. Prsrisim procesin deri sa monedha t vazhdon t bie
pil ose t arrijm n nivelin maksimal t strukturs. Pasi q kjo n fakt sht
vetm insertim i prsritur n listn e lidhur, kodi sht i thjesht.
435

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

Fig. 6.48 Skip lista ideale (perfekte)


Operacionet n listn me kaprcime, n rastin m t keq jan t rendit O(n). Kjo
do t ndodhte kur t gjitha nyjet (ose pothuajse t gjitha nyjet) ndodhen n
nivelin 1 t insertimit. N kt rast, skip lista do t ishte list e lidhur e
zakonshme. Mirpo, moto e skip listave sht: Dont orry, be happy!, pasi q

436

Algoritmet dhe strukturat e t dhnave


ky rast ka pak gjasa q t ndodhe, me rritjen e n-it. Pr shembull, probabiliteti q
10 nyje me radh t ndodhen n nivelin 1 sht 1/1024!
Skip listat mund t shihen si pem, ku Header-i do t isht rrnja dhe nivelet e
nyjeve i prgjigjen niveleve t pems, ndrsa nyjet e nivelit 1 jan gjethet.

Algoritmet pr skip lista


Algoritmet pr skip lista jan shkruar nga zbuluesi i tyre, illiam Pugh dhe do t
jepen prmes pseudokodeve prkatse, ashtu si jan shkruar nga autori.
Vrejtjet lidhur me kto algoritme jan:
-

Pointert prpara t nyjes s nivelit i ruhen n nj varg, t emrtuar


forard (angl. forard-prpara) dhe jant t indeksuar prej 1 deri n i.
Niveli i nyjes nuk sht i ruajtur
Niveli i lists = maksimumi{niveli i nyjeve n list}

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

Insertimi ose fshirja e nyjes konsiston kryesisht n krkimin e pasuar me


azhurim t pointerve.
Pr t ruajtur nyjen e fundit t qasur n secilin nivel, prdoret nj varg i
quajtur update (angl. update-azhurimi, prditsimi). Ai prdoret pr
ndryshimin e pointerve pasi t jet insertuar apo fshir nj nyje.
Niveli i nyjes s re t insertuar prcaktohet me rastsi, nga funksioni
Random-Level (niveli i rasitit).

Algoritmet dhe strukturat e t dhnave


Funksioni pwr insertim: INSERT(list, searchKey, newValue)
1. x <- list.header
2. for i <- list.level downto 1
3.
do while x.forward[i].key < searchKey
4.
do x <- x.forward[i]
5.
update[i] <- x
6. x <- x.forward[1]
7. if x.key = searchKey
8.
then x.value <- newValue
9.
else newLevel <- RANDOM-LEVEL()
10.
if newLevel > list.level
11.
then for i <- list.level + 1 to newLevel
12.
do
update[i] <- list.header
13.
list.level <- newLevel
14.
x <- MAKE-Node(newLevel, searchKey, newValue)
15.
for i <- 1 to newLevel
16.
do x.forward[i] <- update[i].forward[i]
17.
update[i].forward[i] <- x

NIL

19
4
2

16
5

15

28

42
33

35

Insertimi i 16

Funksioni pr fshirje: DELETE(list, searchKey)


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

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)

Niveli i rastit - RandomLevel


Sqarimet pr kt algoritm:
-

Funksioni RANDOM() kthen nj numr ndrmjet 0 dhe 1.0.


p sht konstant ndrmjet 0 dhe 1.0 (supozojm p = 0.5).

Funksioni RandomLevel funksionon sikur hedhja e monedhs. Le t marrim q


koka sht rasti kur fitohet ndrsa pil rasti kur humbet. Monedha
elektronike hidhet deri sa t del pil. Seciln her q bie koka, niveli ngritet
pr nj dhe hidhet monedha prsri.
Vini re: nse p=1/4, ather do t ket mesatarisht 1.33 pointer pr nyje. Kjo
kursen hapsirn pa e zvogluar dukshm (n mas t rndsishme) kohn e
krkimit.

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

Algoritmet dhe strukturat e t dhnave


Qartazi, kjo sht koha q dominon insertimin dhe fshirjen. S pari vrejm se
numri i pritur i niveleve n skip list sht O(log n). Arsyeja sht se n nivelin
0 kemi n elsa, n nivelin 1 presim t ket n/2 elsa, n nivelin 2 presim n/4, e
kshtu me radh. Kjo sipas argumentit t njjt q u prdor pr rastin e lists
ideale, q pas O(log n) niveleve, nuk do t ket m elsa t mbetur.
Argumenti pr tu provuar kufirin e pritur t kohs s krkimit sht interesant.
Le t shikojm shtegun e anasjellt t krkimit. (Kjo sht teknik e zakonshme
n algoritmet probabilistike dhe ndonjher quhet anailza te prapa). Vreni se
shtegu i krkimti te para zbret n nivel sa her q lidhja e ardhshme do t na
drgonte prtej nyjes t ciln jemi duke e krkuar. Kur e kthejm mbrapsht
shtegun e krkimit, vreni se ai do t na gjithnj do t na drgoj n hapa te
lart, nse mundet (d.m.th., nse nyja q e viziton paraqitet n nivelin e
ardhshm m t lart), prndryshe do t bj nj hap n t majt.

Fig.6.49 Kthimi prapa n shtegun e krkimit pr 11.


Tani, kur t arrijm n nivelin i t cilsdo nyje n skip list, themi se
probabiliteti q do t ket nivel m lart sht vetm 1/2. Arsye pr kt sht se
kur sht insertuar nyja, ky ka qen probabiliteti q ajo sht promovuar n
nivelin e ardhshm m t lart. Prandaj, me probabilitetin 1/2 kalojm n
nivelin e ardhshm m t lart. Me probabilitetin e mbetur 1 (1/2) = 1/2
mbesim n nivelin e njjt. Numri i pritur i hapava t nevojshm pr t ecur
npr j nivele t skip lists sht i dhn sipas rekurrencs vijuese:
1
1
C ( j ) 1 C ( j 1) C ( j )
2
2

Vlera 1 sht pr hapin aktual. Me probabilitetin 1/2 kalojm n nivelin m t


lart t ardhshm dhe kshtu kemi nj nivel m pak npr t cilin duhet kaluar
dhe me probabilitetin 1/2 mbesim n nivelin e njjt. Kjo mund t rishkruhet si:
C ( j ) 2 C ( j 1)

Duke zgjeruar (zbrthyer), leht vrtetohet se C ( j ) 2 j . Pasi q j sht s


shumti (maksimalisht) numri i niveleve n pem (list), ather kemi se koha e
pritur e krkimit sht n t shumtn O(log n).

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

SkipList(int maxLev) //konstr. me nivelin maks. t dhn


{
maxLevel = maxLev;
// aloko nyjen header
header = new SkipListNode(null, maxLevel);
// bashkangjite nyjen "nil" n nyjen header

442

Algoritmet dhe strukturat e t dhnave


SkipListNode sentinel = new SkipListNode(INFINITY,
maxLevel);
for (int i = 0; i <= maxLevel; i++)
header.forward[i] = sentinel;
}
}

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;
}

sht me rndsi t theksohet se nuk ka nevoj t ruhet niveli i nyjes si pjes e


fushs s pointerit forard. Funksionet e skip lists ruajn njohurit e tyre t
veta pr nivelin gjersa lvizin npr strukturn e t dhnave. Gjithashtu vreni se
(nse implementohet korrekt) nuk do t asnjher nuk do t tentohet t
indeksohet prtej kufijve t nyjes.
Element me rndsi pr insertimin n skip list ishte funksioni pr gjenerimin e
nivelit t rastit:
//Gjenero nivelin e rasitit, newLevel=NiveliiRi
int generateRandomLevel()
{
int newLevel = 0;
while (newLevel < maxLevel && Math.random() < 0.5)
newLevel++;
return newLevel;

443

Avni Rexhepi
}

Kodi pr insertim dhe fshirje, mund t bazohet ngjashm n pseudo kodin e


dhne nga autori i skip listave.

Prfitimet prej Skip Listave


Pema binare e krkimit sht efikase, por shum leht mund t bhet e
pabalansuar pas vetm disa insertimeve dhe fshirjeve. Pemt e balansuara
garantojn q mbesin t balansuara dhe prandaj kryejn operacionet themelore
n rastin m t keq me O(log n). Teorikisht ato jan efikase, mirpo
implementimi i tyre sht i komplikuar. N ann tjetr, skip listat jan m t
lehta pr tu implementuar. Algoritmet pr insertim dhe fshirje jan t thjeshta
dhe t shpejta. Ato nuk garantojn performans O(log n), por n fakt ato kan
performans O(log n) n rastin mesatar (pr insertim, fshirje, krkim) dhe
probabiliteti i devijimit t madh prej mesatares sht shum i vogl. Prandaj,
performansa shum e keqe (O(n)) sht shum pak gjasa q t ndodh dhe
probabiliteti i saj zvoglohet eksponencialisht gjersa n rritet. Pr shumicn e
aplikacioneve, skip listat jan poaq efikase sa edhe strukturat e pemve t
balansuara. Ato poashtu jan efikase n aspektin hapsinor, pasi q nuk ka
nevoj t ruhet informacion pr balansim n nyje dhe ato mund t punojn mir
edhe me nj mesatare prej vetm 1.33 pointerve pr nyje. Pr dallim prej
pemve t krkimit binar, performansa e skip listave nuk varet nga rendi i
insertimit.

444

Algoritmet dhe strukturat e t dhnave

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

Fig. 6.50 Pema 3-are


Pr t ilustruar pemn M-rrugshe, do t jet e prshtatshme t prdoren vlerat e
vogla t M. Por, keni parasysh q n praktik, M sht zakonisht vler shum e
madhe. P.sh., secila nyje i korrespondon nj blloku t t dhnave n disk dhe M
reprezenton numrin maksimal t elementeve q mund t ruhen n nj bllok t
445

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

Algoritmi pr krkimin e nj vlere (elsi) n pemn M-are t krkimit sht


prgjithsim i algoritmit t krkimit t pems binare t krkimit. Nse jemi duke
krkuar vlern X dhe momentalisht jemi n nyjen e cila prmban vlerat
V1...Vk, ather jan katr mundsi:
1. Nse X < V1, krkoni rekurzivisht pr X n nnpemn e majt t V1shit.
2. Nse X > Vk, krkoni rekurzivisht pr X n nnpemn e djatht t Vks.
3. Nse X=Vi, pr ndonj i, athere kemi mbaruar (X sht gjetur).
4. Mundsia e vetme e mbetur sht q, nse pr nj i, Vi < X < V(i+1).
N kt rast, krkoni n mnyr rekurzive pr X n nnpemn q
ndodhet ndrmjet Vi dhe V(i+1).
Pr shembull, supozojm se jemi duke krkuar vlern 68 n pemn e paraqitur
m lart. N rrnj, do t aplikohej rasti 2, kshtu q do t vazhdonim krkimin
n nnpemn e djatht t V2-shit. N rrnj t ksaj nnpeme, apliohet rasti 4,
68 sht ndrmjet V1=55 dhe V2=70, kshtu q do t vazhdonim krkimin n
446

Algoritmet dhe strukturat e t dhnave


nnpemn ndrmjet tyre. Tani aplikohet rasi 3, 68=V2, kshtu q kemi mbaruar
(vlera sht gjetur). Nse do t kishim krkuar pr vlern 69, do t ndodhte
procedura e njjt si m par deri n kt pik, e pastaj do t aplikohej rasti 2,
por nnpema n t ciln do t dshironim t vazhdojm krkimin sht e
zbrazt. Prandaj, do t konkludohej se vlera 69 nuk ndodhet n pem.
Algoritmet tjera pr pemn binare t krkimit, insertimi dhe fshirja,
prgjithsohen n mnyr t ngjashme. Si n rastin e pemve binare, insertimi i
vlerave n renditje rritse do t rezultonte n pem t degjeneruar M-are;
d.m.th., pema lartsia e s cils do t isht O(N) n vend t O(log N). Ky sht
problem sepse t gjitha operacionet e rndsishme jan O(lartsia), kurse qllimi
sht q ato t bhen O(log N). Nj zgjidhje pr kt problem do t ishte
detyrimi i pems q t jet e balansuar pr kah lartsia. N kt rast do t fitohen
Pema M-are me balansim perfekt pr kah lartsia, e njohur si Pema-B (B-Tree).

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

Versioni i balansuar i pems s mparshme M-ay, q prmban vlerat e njjta


dhe q sht shndrruar n Pem-B sht:
50

10

66

22

44

55

68

70

Fig. 6.51 B-tee (Pema-B)


Ndrsa, pema-B 5-are (secila nyje, prveq rrnjs, duhet t prmbaj ndrmjet 2
dhe 4 vlera):

10

50

22

44

Fig. 6.52 Pema-B 5-are

448

55

66

68

70

Algoritmet dhe strukturat e t dhnave


Kufizimet e definuara e bjn pemn-B q t jet s paku gjysm e mbushur, t
ket disa nivele dhe t mbetet perfekt e balansuar.
Nyjet dhe pointert e B-pems paraqiten npr literatur n forma t ndryshme,
por me rndsi sht q t kihet parasysh struktura e nyjeve, me pointert dhe
vlerat

Fig. 6.53 Struktura e nyjeve tw B-pemws


Nyjet e pems-B zakonisht implementohen si klas q prmban nj varg me m1 qelula pr vlerat (elsat), nj varg me m pointer pr tek nyjet e nivelit t
ardhshm dhe informacionet plotsuese t nevojshme pr t mundsuar
mirmbajtjen e pems.
//template=shablloni
template <class T, int M>
class BTreeNode
{
public:
BTreeNode();
BTreeNode( const T & );
private:
T keys[M-1];
BTreeNode *pointers[M];
...
};

Pr shkak t natyrs s veant, algoritmet e ristrukturimit t pems-B dallojn


prej algoritmeve t restrukturimit t pms AVL dhe t tjerave. Gjersa
insertohen vlerat e reja, algoritmi zhvendos elementet npr nyje, ashtu q t
plotsoj trsisht nyjen aktuale dhe nyjet e nivelit t njjt (nyjet
vllezr/motra) para se t tentoj krijimin e nyjeve t reja. Kur nuk ka vende t
mjaftueshme, vjen deri te procesi i krijimit t nyjeve t reja, e pasi q n kto
raste paraqitet nevoja pr ndarje t nyjes, procesi i krijimit t nyjeve t reja
quhet ndarja e nyjes (angl. node splitting). Kur nuk ka vend pr elementin e ri,
449

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

Algoritmet dhe strukturat e t dhnave


12

13

15

13

15

Insertimi i numri 7 do t rezultoj n:


12

Fig. 6.54 Insertimi n rastin kur ka vend t lir


Rasti 2: Gjethja n t ciln duhet t insertohet elsi sht e mbushur
N kt rast, gjethja n t ciln duhet t insertohet vlera e re ndahet n dy pjes
(prgjysm), duke rezultuar n nj gjethe t re. Gjysma e elsave do t
zhvendosen prej nyjes s plot n nyjen e re. Nyja e re pastaj inkorporohet n BTree.

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

rezulton me ndarjen e nyjes s par gjethe:


12

13

15

13

15

12

Nyja e re duhet t inkorporohet n pem, gj q kryhet duke marr vlern e


mesit dhe duke e insertuar at n nyjen prind si dhe duke prshtatur pointert:

12

13

15

Fig. x Insertimi n rastin kur ka vend t lir


Rasti 3: Rrnja sht e mbushur
Lvizja te lart e vlerave nga rasti i dyt do t thot q sht e mundur q vlera
mund t lviz te lart deri n rrnjn e B-pems. Nse rrnja sht e mbushur,
do t aplikohet procedura e njjt si pr rastin e dyt, ku do t krijohet nj nyje e
re. Ky tip i ndarjes do t rezultoj me shtimin e dy nyjeve t reja n B-pem.

452

Algoritmet dhe strukturat e t dhnave


Insertimi i 13 n pemn vijuese:

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

Nyja 15 insertohet n nyjen prind, gj q do t thot se ajo bhet nyje e re rrnj:


15

12

10

11

13

14

18

19

20

30

21

23

25

28

31

33

34

35

Fshirja e nyjes nga pema-B


Si zakonisht, fshirja sht procesi m i vshtir pr tu aplikuar. Procesi i
fshirjes n esenc do t jet e kundrta e insertimit, prandaj n vend t ndarjes s
nyjeve sht e mundur q t ndodh bashkimi i nyjeve, ashtu q tiparet e pemsB, d.m.th., krkesa q nyja duhet t jet s paku prgjysm e mbushur, mund t
mirmbahet.
Jan dy raste t zakonshme t cilat duhet t mirren parasysh:
1. Fshrija nga nyja gjethe
2. Fshirja nga nyja q nuk sht gjethe.
453

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

b-1). Nse ka nyje vlla/motr t majt ose t djatht me numr t elsave q e


tejkalon krkesn pr minimum, t gjith elsat prej gjetheve dhe nyjeve t
nivelit t njjt (vllezr/motra) do t rishprndahen duke lvizur elst ndars
prej prindit n gjethe dhe duke lvizur elsin e mesit prej nyjes dhe
vllaut/motrs t kombinuar n prind.

454

Algoritmet dhe strukturat e t dhnave


16

13

14

18

15

22

25

23

24

22

25

23

24

20

27

37

27

37

Tani fshijm 8 nga pema:


16

13

14

18

15

20

b-2). Nse numri i elsave n vlla/motr nuk tejkalon krkesn pr minimum,


ather gjethja dhe nyja vlla/motr bashkohen duke vendosur elsat nga
gjethja, vllau/motra dhe vlera ndarse prej prindit, n gjethe. Nyja vlla/motr
asgjsohet dhe elsat n prind lvizen pr t plotsuar zbraztirn. sht e
mundur q kjo do t shkaktoj nn-rrjedhje t prindit. Nse ky sht rasti,
trajtoni prindin si gjethe dhe vazhdoni t prsritni hapin b-2 gjersa t plotsohet
krkesa pr minimum ose t arrihet rrnja e pems.
Rasti special pr b-2: Gjat bashkimit t nyjeve (angl. merge-bashkim,
shkrirje), nse prindi sht nyje me vetm nj els, elsat prej nyjes, nyjes s
nivelit t njjt (vlla/motr) dhe elsi i vetm i rrnjs vendosen n nj nyje
dhe kjo nyje do t bhet rrnja e re e B-pems. T dyja nyjet, rrnja e vjetr dhe
nyja e nivelit t njjt do t asgjsohen.
16

13

14

13

18

15

14

15

16

18

20

22

22

25

23

24

20

27

37

25

23

24

27

37

Fig. x Fshirja dhe bashkimi


455

Avni Rexhepi

Rasti 2: Fshirja prej nyjeve q nuk jan gjethe


Ky rast mund t drgoj n probleme me riorganizimin e pems, mirpo zgjidhet
n mnyr t ngjashme me rastin e fshirjes prej pems binare t krkimit.
elsi q duhet t fshihet do t zvendsohet me paraardhsin (ose pasardhsin)
e tij t drejtprdrejt dhe pastaj paraardhsi (ose pasardhsi) do t fshihet, pasi q
ai mund t gjindet vetm n nyje gjethe.
P.sh., fshirja e 16 nga pema paraprake do t rezultoj n:
3

13

14

15

18

22

25

20

23

24

27

37

Zbraztira e krijuar do t mbushet nga parardhsi i drejtprdrejt (n kt rast


15):

13

14

15

15

18

20

22

25

23

24

27

37

23

24

27

37

dhe pastaj parardhsi i drejtprdrejt fshihet.

13

14

15

18

20

22

25

Nse pr zvendsim do t ishte zgjedhur pasardhsi i drejtprdrejt (n kt rast


18), ather do t kishim:

13

14

15

18

18

20

Fshirja e pasardhsit do t rezultoj n:


456

22

25

23

24

27

37

Algoritmet dhe strukturat e t dhnave


3

13

14

15

18

22

25

20

23

24

27

37

N vazhdim, vlerat n vllaun/motrn e majt kombinohen me elsin ndars


(18) dhe me vlerat e mbetura. Ato ndahen ndrmjet dy nyjeve:
3

13

14

15

22

18

25

20

23

24

27

37

23

24

27

37

dhe pastaj vlera e mesit zhvendoset tek prindi:


3

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:

Metodat e krijimit t t dhnave


Qllimi i t dhnave
Koha dhe data e krijimit
Krijuesi ose autori i t dhnave
Lokacioni n rrjetin kompjuterik ku jan krijuar t dhnat
Standardet e prdorura, etj.

Pr shembull, nj imazh digjital mund t prmbaj metadata t cilat


prshkruajn se sa sht madhsia e fotografis, thellsia e ngjyrs,
rezolucioni, data e krijimit apo t dhna t tjera. Metadata e nj dokumenti
tekstual mund t prmbaj inforamcion lidhur me gjatsin e dokumentit,
autorin, kohn e shkruarjes ose nj prmbledhje t shkrutr t tij.

Pr shembull, n rastin e qyteteve t vendit, rrjeti rrugor i cili i lidh qytetet


mund t prezentohet si graf dhe pastaj t analizohet. Mund t analizojm nse
nj qytet sht i arritshm (i lidhur) prej nj tjetri ose t gjejm rrugn m t
shkurtr ndrmjet dy qyteteve.
S pari, do t prezentojm disa terme dhe definicione t grafeve. Pastaj do t
shohim se si reprezentohen grafet prbrenda kompjuterit. N fund do ti
kthehemi algoritmeve themelore t grafeve.
Formalisht, grafi konsiderohet si nj ift i renditur, G=(V,E), i dy seteve q
paraqesin nyjet ose kulmet/majet e grafit dhe degt/rrugt e grafit (angl. Vertex
(shumsi: Vertices) - Kulm, maje, nyje etj. ; angl. Edge an, buz, teh, kufi
etj),. Nj deg specifikon se cilat nyje kan lidhje ndrmjet tyre. Kur punohet
me grafe, shpesh her jemi t interesuar n at se si mund t bashkohen kto
458

Algoritmet dhe strukturat e t dhnave


deg q t mund t lvizet npr graf. Pr kt arsye, shpesh do t flasim pr
udhtimin npr nj deg/rrug, q do t thot se kemi ndryshuar nyjn ton t
interesit duke prcjellur njrn prej rrugve t lidhura me t. Me fjal t tjera,
nse grafi i jon ka nyjet A dhe B q jan t lidhura prmes nj dege/rrug, nd
do t flasim pr lvizjen prej A n B, udhtimin prej A n B ose
qarkullimin/prshkimin e rrugs prej A n B pr t paraqitur faktin se fokusi i
jon ka ndryshuar prej nyjs A n nyjn B. Pr t lehtsuar diskutimin, ne do t
shkruajm vetm emrat (etiketat) e nyjeve si stenografi (trajt e shkurtr, e
shkruar) pr rrugn/degn q lidh ato. Kshtu, AB do t paraqes rrugn
ndrmjet nyjes A dhe nyjes B dhe ne do t themi se B sht fqinje me A (e afrt,
n afrsi).
Grafi mund t jet i padrejtuar ose i drejtuar. Nj graf i padrejtuar, zakonisht i
quajtur vetm graf, ka degt/rrugt t cilat mund t prshkohen n cilindo
drejtim. N kt rast, nj deg/rrug sht set (bashksi), e cila prmban
etiketat (labelat) e nyjeve t cilat jan dy skajet e degs/rrugs.1
Grafi i drejtuar, gjithashtu i quajtur edhe digraf, ka degt t cila mund t
prshkohen (qarkullohen) vetm n nj drejtim. Pr digrafin, seti i
degve/rrugve do t ket iftet e renditura n t cilat elementi i par sht
fillimi dhe i dyti sht fundi i rrugs/degs.
Pra, jan dy sete (bashksi) t rndsishme t objekteve, t cilat specifikojn
grafin dhe strukturn e tij. Seti i par sht seti i nyjeve t grafit. N shembullin
e rrjetit t rrugve, qytetet jan nyjet e grafit. Secila nyje mund t vizatohet si
nj rreth me numrin e nyjes prbrenda.

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

N vijim do t jepen disa definicione themelore t grafeve:


Sekuenca e nyjeve (rendi i nyjeve), t tilla q ekziston nj deg prej secils nyje
tek tjetra n rend, quhet shteg (angl. path). Nyja e par e shtegut quhet nyja
fillestare (startuese, burimi, etj); nyja e fundit n shteg quhet nyja prfundimtare
(cak). Nse nyja fillestare sht edhe nyje prfundimtare, shtegu quhet cikl.
Shtegu quhet i thjesht, nse e prmban seciln nyje vetm nj her. Cikli
quhet i thjesht, nse e prmban seciln nyje, prveq asaj t fillimit(fundit),
vetm nj her. N vazhdim do t paraqiten disa shembuj t shtegut dhe ciklit.

shtegu (i thjesht)
460

cikli (i thjesht)

Algoritmet dhe strukturat e t dhnave


Grafi quhet Graf i lidhur nse ekzistojn lidhje t tilla q mund t vizitohen t
gjitha nyjet e grafit, gjegjsisht q nuk ka ndonj pjese t shkputur t nyjeve t
grafit. Nse ka ndonj nyje t shkputur dhe nuk ka rrug/deg pr t arritur deri
tek ajo ose ato nyje, athere grafi sht Graf i pa lidhur (Graf jo i lidhur).
Shtegu i Euler-it quhet shtegu (rruga) e cila kalon npr seciln deg saktsisht
nj her, pa prsritje. Nse shtegu prfundon n nyjen nga e cila ka filluar,
ather ai sht cikl i Euler-it (qark i Euler-it).
Shtegu i Hamilton-it kalon npr seciln nyje t grafit, saktsisht nj her. Nse
mbaron n nyjn e fillimit, ather quhet cikl (qark) i Hamilton-it.
Historia e teoris s grafeve filloi n vitin 1736, kur Leonard Euler s pari zgjidhi
problemin e shtat urave t Knigsberg-ut. Knigsberg (tash Kaliningrad) sht
qytet n brigjet e lumit Pregel. N kohn e Eulerit, ishin shtat ura q lidhnin
brigjet dhe dy ishuj, t cilt mund t modelohen si nj multigraf me shtat deg
dhe katr nyje. Euleri krkohi mnyrn e kalimit npr seciln ur saktsisht
vetm nj her dhe t kthehet n fillim, d.m.th, ciklin Eulerian. Pasi q t gjitha
nyjet kishin shkall teke, Euleri vrtetoi se nj tur i till sht i pamundur. Urat
ishin shkatrruar gjat lufts s dyt botrore.
Algoritmi i Fleury-it sht qasje direkte dhe elegante e konstruktimit t cikleve
Euleriane. Filloni t ecni nga cilado nyje dhe fshini degt q jan kaluar
(prshkuar). Kriteri i vetm n zgjedhjen e degs s ardhshme sht q t
evitohet prdorimi i degs q fshihet, prveq nse nuk ka alternativ tjetr.
Asnj graf Eulerian nuk prmban ur (deg), mirpo ajo ka mbetet n
ndonj pik t rrugtimit mbaron t jet graf i bikonektuar. Teknika e turit t
Eulerit sht nj paradigm e rndsishme n algoritmet paralele t grafeve.
Ekzistojn algoritme efikase pr numrimin e cikleve t Eulerit n graf.

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

Reprezentimi i grafit t padrejtuar


Ka disa mnyr t mundshme t reprezentimit (prfaqsimit) t grafit n
kompjuter. Dy prej tyre, q prdoren zakonisht, jan matrica e fqinjsis (angl.
adjacency matrix) dhe lista e fqinjsis (angl. adjacency list).

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

Algoritmet dhe strukturat e t dhnave


Secili lokacion [i,j] i ktij vargu do t ruaj vlern 0, prveq nse ka ndonj
deg/rrug prej nyjes vi tek nyja vj, lokacioni do t ruaj vlern 1. M formalisht:
pr do i dhe j n rangun 1 deri n
N
Pr grafet dhe digrafet me pesh, vlerat n matricat e fqinjsis do t ishin
nse nuk ka rrug/deg dhe pesha prkatse pr t gjitha rastet tjera. Elementet e
diagonales do t ishin 0, sepse nuk do t ket kostro t udhtimit prej nyjes n
vetveten.
Pra, secili element aij i nj matrice t fqinjsis prmban 0, nse nuk ekziston
lidhje (deg) ndrmjet nyjeve i dhe j dhe 1, nse ato jan t lidhura. Le t
shohim nj shembull.

Grafi

Matrica e fqinjsis

463

Avni Rexhepi

Dega (2, 5)

Elementet pr degn (2, 5)

Dega (1, 3)

Elementet pr degn (1, 3)

Grafi i paraqitur prmes shembullit paraprak sht i padrejtuar. Kjo do t thot


se matrica e tij e fqinjsis sht simetrike. N t vrtet, n grafin e padrejtuar,
nse ekziston dega (2, 5) ather ekziston edhe dega (5, 2). Kjo edhe sht
arsyeja, pse ke dy elemente t matrics pr seciln deg t grafit. Unazat
(rrotullat, laqet), nse lejohen n graf, ju prgjigjen elementeve t diagonals t
matrics s fqinjsis.
Prparsit. Matrica e fqinjsis sht shum e prshtatshme pr prdorim.
Shtimi (largimi) i nj dege mund t bhet n koh O(1), e njjt me at pr
verifikimin nse ekziston lidhje/deg ndrmjet dy nyjeve. Gjithashtu sht e
thjesht pr tu programuar.
464

Algoritmet dhe strukturat e t dhnave


T metat.

Martica e fqinjsis konsumon hapsir t madhe t memories pr


ruajtjen e grafeve t mdha. T gjitha grafet mund t ndahen n dy
kategori, grafe t rralla dhe grafe t dendura. Grafet e rralla nuk kan
shum deg (numri i degve sht shum m i vogl se katrori i numrit
t nyjeve (q sht numri i elementeve t matrics s fqinjsis),
|E|<<|V|2). N ann tjetr, grafet e dendura kan numr t degve t
krahasueshm me katrorin e numrit t nyjeve. Matrica e fqinjsis sht
optimale pr grafet e dendura, por sht e panevojshme (e teprt) pr
grafet e rralla.
Dobsi tjetr e matrics s fqinjsis sht se n shum algoritme duhet
t dihen nyjet, fqinj me nyjen aktuale. Pr t nxjerr nj informacion t
tille nga matrica e fqinjsis, duhet t shqyrtohet rreshti prkats, gj q
rezulton n kompleksitet O(|V|). Pr algoritmet si DFS (Depth First
Search krkimi thellsia s pari) ose ato t bazuara n t, prdorimi i
matrics s fqinjsis rezulton n kompleksitet t prgjithshm prej
O(|V|2), gjersa ai mund t zvoglohet n O(|V| + |E|), kur prdoret lista e
fqinjsis.
Problem tjetr i cili duhet theksuar, sht se matrica e fqinjsis krkon
shum prpjekje pr shtimin/largimin e nyjes. Nse grafi prdoret vetm
pr analiz, ather kjo gj nuk sht e nevojshme, mirpo nse
dshironi t konstruktoni nj struktur trsisht dinamike, prdorimi i
matrics s fqinjsis e bn at mjaft t ngadalshme pr grafe t mdha.

Si prfundim, matrica e fqinjsis sht zgjidhje e mir pr grafet e dendura gj


q kkron t pasurit e nj numri konstant t nyjeve.

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

Nyjet, fqinje t {2}

Rreshti n listn fqinjsis

Prparsit. Lista e fqinjsis mundson q grafi t ruhet n form m


kompakte sesa matrica e fqinjsis, por diferenca zvoglohet gjersa grafi
dendsohet. Prparsi tjetr sht se lista e fqinjsis mundson q t merret
lista e nyjeve fqinj n koh O(1), gj q sht prparsi e madhe pr disa
algoritme.
T metat.

466

Shtimi/largimi i nj nyje n/nga lista e fqinjsis nuk sht aq i leht si


n rastin e matrics s fqinjsis. Kjo krkon, mesatarisht, koh O(|E| /
|V|), q mund t rezultoj n kompleksitet kubik pr grafet e dendura, pr
t shtuar t gjitha degt.

Algoritmet dhe strukturat e t dhnave

Verifikimi, nse ekziston lidhje ndrmjet dy nyjeve mund t bhet n


O(|E| / |V|) kur lista e nyjeve fqinje sht e parenditur ose O(log2(|E| /
|V|)) kur ajo sht e sortuar. Ky operacion mbetet mjaft i lir.
Lista e fqinjsis nuk mundson implementim efikas, nse krkohet
ndryshim dinamik i numrit t nyjeve. Shtimi i nyjes s re mund t bhet
n O(V), por largimi rezulton n kompleksitet O(E).

Si prfundim, lista e fqinjsis sht zgjidhje e mir pr grafet e rralla dhe


mundson ndryshimin e nmrit t nyjeve n mnyr m efikase sesa prdorimi i
matrics s fqinjsis. Por megjithat, pr ruajtje t grafit trsisht dinamik, ka
zgjidhje m t mira.
Pjes kodi
Pr thjeshtsi, pjest e kodit kan t bjn me matricat e fqinjsis, pr grafet e
padrejtuara.
class Graph {
private:
bool** adjacencyMatrix;
int vertexCount;
public:
Graph(int vertexCount)
{
this->vertexCount = vertexCount;
adjacencyMatrix = new bool*[vertexCount];
for (int i = 0; i < vertexCount; i++)
{
adjacencyMatrix[i] = new bool[vertexCount];
for (int j = 0; j < vertexCount; j++)
adjacencyMatrix[i][j] = false;
}
}
void addEdge(int i, int j)
{
if (i >= 0 && i < vertexCount && j > 0 && j < vertexCount)
{
adjacencyMatrix[i][j] = true;
adjacencyMatrix[j][i] = true;
}
}
void removeEdge(int i, int j)
{
if (i >= 0 && i < vertexCount && j > 0 && j < vertexCount)
{
adjacencyMatrix[i][j] = false;
adjacencyMatrix[j][i] = false;

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;
}
};

Algoritmet pr grafet e padrejtuara


Kur punojm me grafet, nganjher mund t dshirojm q t bjm dika n
seciln nyje t grafit saktsisht vetm nj her. Pr shembull, mund t jet nj
informacion q duhet t shprndahet n t gjith kompjutert n rrjet. Ne
dshirojm q ky informacion t arrij n secilin kompjuter, por nuk duam q
at tia japim dy her ndonj kompjuteri. E njjta gj do t ishte e vrtet nse
jemi duke krkuar pr ndonj informacion n vend t shprndarjes.
Ekzistojn dy teknika t cilat do ti analizojm dhe t cilat e realizojn/kryejn
prshkimin e grafit. Dy algoritmet themelore pr prshkimin e nyjeve t grafit
(bredhjen, lvizjen npr graf) jan algoritmet:
-

Thellsia s pari (angl. Depth-first search, ose shkurt DFS), dhe


Gjersia s pari (angl. Breadth-first search, ose shkurt BFS).

N depth-first (thellsia-s-pari), rrugtimi i jon do t shkoj sa m larg q t


jet e mundur shtegut teposht, para se t konsideroj ndonj rrug tjetr dhe n
breadth-first (gjersia-s-pari) rrugtimi i jon do t shkoj barabart n
shum drejtime. N vazhdim do t shikojm kto dy metoda m detajisht. Pr
kto dy metoda t prshkimit/rrugtimit, ne zgjedhim nj nyje n graf si pik
tonn startuese/fillestare. N diskutimet tona, ne prdorim nj fraz (shprehje)
tjetr vizito nyjen pr t paraqitur veprimin q duhet t bhet n seciln nyje.
Pr shembull, nse jemi duke krkuar, vizitimi i nyjs do t nnkuptonte q ne
duhet ta verifikojm at pr informacionin q na duhet. Kto metoda

468

Algoritmet dhe strukturat e t dhnave


funksionojn pa ndonj ndryshim edhe n grafet e drejtuara edhe n ato t
padrejtuara. Do ti ilustrojm ato prmes grafeve t padrejtuara.
Secila prej ktyre metodave t rrugtimit/prshkimit gjithashtu do t prdoret
pr t prcaktuar nse grafi sht i lidhur. Nse ne krijojm nj list t nyjeve t
cilat i vizitojm gjat rrugtimit ton, kjo list mund t krahasohet me setin
(bashksin) e nyjeve n graf. Nse ato jan t njjta, grafi sht i lidhur. Nse
ato nuk jan t njjta, ather ka disa nyje q nuk mund t arrihen prej vendit ku
kemi startuar (filluar), q do t thot se grafi nuk sht i lidhur.

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.

Figura 7.1 - Grafi


Shqyrtoni grafin n Fig. 7.1. Nse fillojm me prshkimin thellsia-s-pari nga
nyja 1, n vazhdim vizitojm me radh nyjet 2,3,4,7,5 dhe 6 para se t arrijm
n rrug pa dalje. Pastaj, do t ktheheshim prapa n nyjen 7 pr t gjetur se nyja
8 nuk sht vizituar, por kjo menjeher do t oj n rrug pa dalje. Pastaj
469

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

Ky algoritm rekurziv bazohet n stekun e sistemit t kompjuterit pr t ndjekur


pozitn ku ka qen n graf ashtu q t mund t kthehet prapa kur t arrij n
rrug pa dalje. Ne mund t krijojm algoritmin e ngjashm jorekurziv duke
prdorur strukturn e stekut dhe vet duke futur/shtyer n stek dhe duke
nxjerr/trhequr nga steku kulmet (pikat, nyjet).
Shembull: Depth-first search (DFS)
Krkimi Thellsia s pari, sht nj mnyr pr prshkimin e grafit. Fillimisht
algoritmi u krijua pr grafet dhe mundson vizitimin e nyjeve t grafit, por
ekzistojn me qindra algoritme t bazuara n DFS. Kjo edhe sht arsyeja q t
kuptuarit e parimeve t krkimit thellsia s pari sht i rndsishm pr
studimin e mtejm n teorin e grafeve. Parimi i algoritmit sht mjaft i
thjesht: t shkohet prpara (n thellsi) gjersa t jet e mundur, prndryshe t
kthehet prapa.

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

Algoritmet dhe strukturat e t dhnave


Pr shumicn e algoritmeve klasifikimi bool-ean e pavizituar/e vizituar
sht i mjaftueshm, mirpo do t paraqitet rasti i prgjithshm.
Fillimisht t gjitha nyjet jan t bardha (e pavizituar). Algoritmi DFS fillon n
nj nyje arbitrare dhe punon si vijon:
1. Shno (marko) nyjen u si ngjyr hiri (e vizituar).
2. Pr seciln deg (u, v), ku u sht e bardh, ekzekuto DFS pr u n
mnyr rekurzive.
3. Shno nyjen u si t zez dhe ktheu prapa tek prindi.
Shembull. Prshko grafin e mposhtm duke prdorur algoritmin DFS
(Thellsia s pari). Fillo nga nyja me numr 1.

Grafi burimor (fillestar).

Shno (marko) nyjen 1


me ngjyr hiri.

471

Avni Rexhepi

Kemi nj deg (1, 4) dhe


nyja 4 sht e pavizituar.
Shko tek ajo.

Shno
nyjn
ngjyr hiri.

4 me

Kemi degn (4, 2) dhe


nyja 2 sht e pavizituar.
Shko tek ajo.

Shno
nyjn
ngjyr hiri.

472

2 me

Algoritmet dhe strukturat e t dhnave

Kemi degn (2, 5) dhe


nyja 5 sht e pavizituar.
Shko tek ajo.

Shno
nyjn
ngjyr hiri.

5 me

Kemi degn (5, 3) dhe


nyja 3 sht e pavizituar.
Shko tek ajo.

Shno
nyjn
ngjyr hiri.

3 me

473

Avni Rexhepi

Nuk ka rrug/deg tutje


prej nyjes 3. Shnoj
nyjen 3 me ngjyr t
zez dhe kthehu prapa
tek nyja 5.

Ka nj deg (5, 4), por


nyja 4 sht me ngjyr
hiri.

Nuk ka rrug/deg tjera


pr tek ndonj nyje e
pavizituar nga nyja 5.
Shnoje me t zez dhe
kthehu prapa tek nyja 2.

Nuk ka deg t tjera,


fqinje me nyjen 2.
Shnoje me t zez dhe
kthehu prapa tek nyja 4.

474

Algoritmet dhe strukturat e t dhnave

Ka nj deg (4, 5), por


nyja 5 sht e zez.

Nuk ka deg t tjera,


fqinje me nyjen 4.
Shnoje me t zez dhe
kthehu prapa tek nyja 1.

Nuk ka deg t tjera,


fqinje me nyjen 1.
Shnoje me t zez. DFS
prfundoi.

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

Algoritmet dhe strukturat e t dhnave

Fig. 7.1 - Grafi


Shqyrtoni prsri grafin nga Fig. 7.1. nse fillojm rrugtimin (prshkimin) ton
nga nyja 1, do t vizitojm nyjet 2 dhe 8 n kalimin e par. N kalimin e dyt,
do t vizitojm nyjet 3 dhe 7 (Edhe pse nyjet 2 dhe 8 jan gjithashtu n fund t
shtigjeve t gjatsis 2, ne nuk do t kthehemi n to pr arsye se ato jan vizituar
n kalimin e par). N kalimin e tret, vizitojm nyjet 4 dhe 5 dhe n kalimin e
fundit vizitojm nyjet 6 dhe 9.
Gjersa prshkimi thellsia-s-pari varej nga steku, prshkimi gjersia-s-pari
bazohet n radhn e pritjes (angl. queue radh, bisht, grshet, etj.). Algoritmi
pr prshkimin gjersia-s-pari sht:
BreadthFirstTraversal(G, v)
G is the graph
v is the current node
Visit( v )
Mark( v )
Enqueue( v )

PershGjeresiaSePari(G, v)
G - Grafi
v nyja aktuale
Vizito( v )
Shno( v )
Enqueue( v ) // n Queue

while queue is not empty do

while queue jo i zbrazt

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

Dequeue(x)//largo nga Queue


for secila deg xw n G do
if w nuk sht shnuar then

Vizito( w )
Shno( w )
Enqueue( w )
end if
end for
end while

Ky algoritm do t shtoj rrnjn e pems s prshkimit gjersia-s-pari n


queue (radh t pritjes) por pastaj menjher do ta largoj at. Pasi q shikon
(i sheh) n nyjet q jan fqinje t rrnjs, ato do t shtohen n fund t radhs.
Kur t gjitha nyjet fqinj t rrnjs t jen vizituar, ne do t kthehemi n radh
477

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).

Pema e shtrirjes minimale


Pema e shtrirjes (Spanning tree pema e prhapjes etj) sht nj nnbashksi e
lidhur e grafit e cila nuk ka cikle dhe prmban t gjitha nyjet e grafit dhe
nnbashksi e degve (rrugve). Pema minimale e shtrirjes sht pema e
prhapjes ku shuma e peshave (kostove) pr degt (rrugt) e prfshira ka totalin
m t vogl t mundshm. Nj shembull i prdorimit t pems minimale t
shtrirjes sht n konstruktimin e intranetit t kompanis me routert q duhet t
vendosen n pikat strategjike npr disa zona. Nse dshirojm t minimizojm
shpenzimet e lidhjes s routerve , ne do t mund t ndrtonim grafin me secilin
router si nyje dhe me peshat n bashksin/setin e degve si mim/kosto e
lidhjes s secilit ift t routerve. Pema minimale e prhapjes s ktij grafi do t
na tregoj se cilat ifte t routerve t lidhen me prques ashtu q intraneti i jon
t jet i lidhur n trsi me mimin m t lir t mundshm.
Aplikim i ngjashm sht gjetja e rrugs m t shkurtr ndrmjet dy nyjeve t
grafit. Kjo ka aplikim praktik gjat planifikimit t rrugs pr udhtimin e
veturave ose drgimin e mesazhve npr rrjetn kompjuterike.
Pr shembull, n figurn vijuese, jan paraqitur pemt e shtrirjes pr grafin e
dhn dhe shihet se rati i dyt dhe i tret prfaqsojn pemn minimale t
shtrirjes. Varsisth prej kostove t degve, mund t ndodh q grafi ka m
shum se nj pem minimale t shtrirjes.

478

Algoritmet dhe strukturat e t dhnave

Fig. 7.2 Pemt e shtrirjes Pema minimale e shtritjes me kosto 22


Pema me shtrirje minimale e grafit t lidhur me pesh, sht nngraf i cili
prmban t gjitha nyjet e grafit origjinal dhe nnbashksin e degve/rrugve t
till q (ashtu q) nngrafi sht i lidhur dhe totali i peshave t degve/rrugve
sht m i vogli i mundshm (angl. Span shtrirje e krahve, hapsir, hapje,
interval, etj). Nse grafi origjinal nuk sht i lidhur, procesi i mposhtm mund
t prdoret n seciln prej komponenteve t ndara pr t prodhuar pemn e
shtrirjes pr seciln.
Ekziston nj mnyr e forcs s thjesht (e drejtprdrejt) e cila mundson q t
gjindet pema e shtrirjes minimale (angl. MST shqip PSHM) pr grafin e
lidhur. Pasi q degt/rrugt n PSHM jan nnbashksi e degve/rrugve n tr
grafin, ne do t mund t shikonim n t gjitha nnbashksit e mundshme t
bashksis s degve/rrugve deri sa t gjejm PSHM. Do t duhej t shihni se
ky proces krkon shum koh. S pari, nse ka N deg/rrug, do t kishte 2 N
nnbashksi. Pr seciln prej ktyre nnbashksive, do t duhej q s pari t
vrtetohet se ai shtrihet n t gjitha degt dhe nuk ka cikle. Pastaj do t mund t
llogarisnim peshat totale t tyre. Ne do t mund t prshpejtonim procesin pasi
q t kemi gjetur s pari pemn e par t shtrirjes (prfshirjes). Cilado
nnbashksi e degve/rrugve me pesh totale q sht m e madhe sesa ajo
momentale e jona me pemn m t mir t shtrirjes me gjas nuk mund t
performoj m mir, kshtu q nuk ka nevoj t verifikohet pr t par nse ai
shtrihet n t gjitha nyjet dhe sht jociklik. Edhe me kt prmirsim, kjo
metod e forcs s thjesht (e drejtprdrejt) do t ishte e rendit O(2N).

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

Figura 7.3 jep nj shembull t ktij algoritmi n veprim (gjat puns). N


mnyr arbitrare kemi zgjedhur nyjen A pr t filluar kt proces. Si kemi thn,
zgjidhja e ndryshme pr nyjen fillestare nuk do t ndryshoj rezultatitn, prveq
nse ka m shum se nj PSHM.
Grafi origjinal sht treguar n Fig. 7.3(a) dhe si theksuam, kemi zgjedhur q t
fillojm konstruktimin e PSHM n nyjen A. T gjitha nyjet e lidhura direkt n
nyjen A bhen bashksia e fillestare e periferis (fqinjsis). Shohim se dega me
peshn m t vogl i lidh nyjet A dhe B, kshtu q B shtohet n PSHM s
bashku me rrugn AB.

480

Algoritmet dhe strukturat e t dhnave

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.

Shtimi i nyjes C dhe degs/rrugs AC n pemn e shtrirjes (Fig. 7.3(e)) nuk ka


br q ndonj deg/rrug t azhurohet. N vazhdim zgjodhm degn/rrugn
AF, kshtu q ajo dhe nyja F jan shtuar n pem. Ne gjithashtu azhurojm
lidhjet sepse dega/rruga FD ka pesh m t vogl sesa BD dhe dega/rruga FG ka
pesh m t vogl sesa EG. N fqinjsin/periferin rezultuese (Fig. 7.3(f)),
shohim se dega/rruga FD tani sht dega/rruga e mbetur me peshn m t vogl,
kshtu q kjo shohet n vazhdim.
Tani kemi vetm nj nyje q nuk i sht shtuar pems (Fig. 7.3(g)). Kur t
shtohet kjo, procesi kompletohet dhe ne kemi prcaktuar PSHM me rrnj n
nyjen A (Fig. 7.3(h)).

482

Algoritmet dhe strukturat e t dhnave

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

Algoritmet dhe strukturat e t dhnave

Figura 7.4G
Pema e shtrirjes minimale.

Figura 7.4H
Nj pem tjetr/alternative e shtrirjes
minimale.

Algoritmi i prgjithshm i cili do t prmbush/realizoj kt sht (ku E


paraqet numrin e degvge/rrugve dhe N numrin e kulmeve/nyjeve).
1.Sorto degt sipas peshs (prej t vogls)
Inicializo strukturn e ndarjes (particioneve)
numriDegeve = 1
numratoriePerfshire = 0
while numriDegeve E and numratoriePerfshire N-1 do
prindi1 = GjejeRrenjen ( dega [numriDegeve].start )
prindi2 = GjejeRRenjen ( edge[numriDegeve].end )
if prindi1 prindi2 then
shto dega[numriDegeve] n pemn e shtrirjes
numratoriePerfshire = numratoriePerfshire + 1
Union(prindi1, prindi2)
end if
numriDegeve = numriDegeve + 1
end while

Unaza e jon kryesore do t vazhdoj gjersa variabla numriDegeve t tregoj


se ne kemi shikuar n t gjitha degt/rrugt ose numratoriePerfshire t
tregoj se ne kemi shtuar mjaft deg/rrug pr t krijuar pemn e shtrirjes. Do
t duhej t shihnit se nse kemi N nyje n graf, pema e shtrirjes do t ket nj
deg/rrug m pak sesa nyje.
Brenda unazs, s pari gjejm prindrit e dy nyjeve q jan t lidhura nga
dega/rruga e ardhshme q jemi duke e marr n konsiderim. Nse ato nyje jan
n pjes (copa, particione) me rrnj t ndryshme, duke shtuar nj deg/rrug
ndrmjet tyre nuk do t krijoj cikl, kshtu q kjo deg/rrug aktuale mund t
shtohet n PSHM dhe kto dy pjes mund t bashkohen ashtu q ato tani kan
485

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).

Algoritmi i shtegut m t shkurtr


Algoritmi i shtegut/rrugs m t shkurtr (angl. Shortest-Path Algorithm) pr dy
nyje t caktuara do t gjej serit e degve/rrugve ndrmjet tyre t cilat do t
rezultojn n peshn totale m t vogl (minimale) t rrugs.
Mund t duket se do t mund t prdornim pemn e shtrirjes minimale pr t
krasitur (shkurtuar) disa nga degt/rrugt dhe pastaj vetm t shikohet pr
shtegun ndrmjet nyjeve n pemn e shtrirjes. Fatkeqsisht, kjo nuk do t
prodhoj gjithmon shtegun/rrugn m t shkurtr. Rikujtoni se algoritmi i
pems s shtrirjes minimale mundohet t gjej nj total t prgjithshm q sht
m i vogli, kshtu q ai do t shikoj pr peshat m t vogla t mundshme. Pr
shembull, mendoni pr grafin i cili sht rrethor pr nga forma. Me fjal t
tjera, nyja e par sht e lidhur me t dytn, e cila sht e lidhur me t tretn e
kshtu me radh deri tek nyja e fundit, e cila sht e lidhur me t parn. Ky graf
sht nj unaz ku secila nyje sht e lidhur saktsisht me dy nyje, nga nj n
seciln an t saj. Pr shembull, Fig. 7.5(a) paraqet grafin me gjasht nyje.
Vreni se t gjitha peshat n t gjitha degt/rrugt jan 1, prveq pr
degn/rrugn prej nyjes A tek nyja B, e cila ka peshn 2. Algoritmi i pems s
shtrirjes minimale (PSHM) do t zgjedh t gjitha degt/rrugt me pesh 1 dhe
do t hedh posht (prjashtoj) degn me pesh 2. Por kjo do t thot se shtegu
ndrmjet nyjes A dhe nyjes B n pemn e shtrirjes minimale (Fig. 7.5(b)) duhet
t shkoj/kaloj npr t gjitha nyjet tjera pr shtegun me gjatsi 5. Kjo qartazi
nuk sht shtegu me i shkurtr, sepse n Fig. 7.5(a) mund t shihni se ekziston
shteg direkt ndrmjet nyjes A dhe nyjes B i cili ka pesh 2.

486

Algoritmet dhe strukturat e t dhnave

Figura 7.5A

Figura 7.5B

Grafi i unazs.

Pem e shtrirjes minimale t tij.

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

Figura 7.6 paraqet nj shembull t ekzekutimit t ktij algoritmi. Ne fillojm me


grafin e njjt t cilin e prdorm pr algoritmin e pems s shtrirjes minimale
(t riprodhuar n Fig. 7.6(a)) dhe do t krkojm pr shtegun m t shkurtr q
fillon n nyjen A dhe mbaron n nyjen G.
487

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.

Fillimi i shtegut ton nga nyja A jep katr deg/rrug t mundshme pr tu


shqyrtuar. Prej ktyre katrve, dega/rruga AB sht m e shkurtra.
Nyja B shtohet n pemn ton t shtegut m t shkurtr (Fig. 7.6(c)) dhe tani
bjm azhurimin e shtigjeve. Tash mund t arrihen edhe nyjet E dhe G, kshtu
q edhe ato shtohen. Gjithashtu shikojm n nyjen D dhe krahasojm shtegun e
488

Algoritmet dhe strukturat e t dhnave


saj direkt prej nyjes A me gjatsi 7 me shtegun i cili shkon npr nyjen B, i cili
sht me gjatsi 8. Pasi q shtegu direkt sht m i shkurtr, nuk ka ndryshim n
rrugn pr tek nyja D. Duke shikuar opcionet, shohim se shtegu nga nyja A tek
nyja C sht me gjatsi 4 dhe sht m i shkurtri. Dega/rruga BE sht m e
shkurtr, por tani jemi duke marr n konsiderim rrugn e tr prej nyjes A dhe
kshtu gjatsia e shtegut deri tek nyja E aktualisht sht 5. Nyja C shtohet n
pemn e shtegut m t shkurtr (Fig. 7.6(d)). Duke analizuar grafin, shohim se
mund t arrijm tek nyja F npr nyjen C, por gjatsia totale e shtegut sht 10,
q sht m e gjat sesa shtegu momental pr tek nyja F, prandaj nuk ka ndonj
ndryshim.
Duke pasur situatn n Fig. 7.6(d), ne do t mund t zgjedhim oft shtegun prej
A tek F ose shtegun prej A tek E i cili kalon npr nyjen B, sepse ata t dy jan
me gjatsi 5. Ai q zgjedhet, gjat ekzekutimit t programit do t varet nga
mnyra se si jan ruajtur t dhnat. Pr qllimet tona, kur t na paraqitet
zgjedhja, do t zgjedhim nyjen e cila sht m e vogl/afrt alfabetikisht, si n
Fig. 7.6(e). Pr shkak se shtimi i nyjes E n graf nuk ka ndryshuar ndonj prej
lidhjeve ekzistuese, ne tani zgjedhim nyjen F pr t arritur n Fig. 7.6(f). Do t
duhej t shihni se edhe pse zgjedhja e nyjes F ndryshoi degn/rrugn pr tek
nyja D, sikur t kishim zgjedhur s pari nyjen F, ne do t kishim zgjedhur nyjen
E t dytn.

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.

N shembullin n Fig. 7.6, kemi pemn e plot t shtegut m t shkurtr pr


nyjen A sepse nyja e jon e cakut ishte e fundit q duhej t shtohet. Sikur t
kishim arritur nyjen G m hert, algoritmi do t ishte ndalur n at pik. Ka
apkiacone ku ne do t mund t ishim t interesuar pr shtegun m t shkurtr
nga nj nyje tek secila nyje tjetr. Pr shembull, nse kemi nj rrjet t vogl
kompjuterike e cila ka shpejtsi t transmetimit relativisht stabile ndrmjet
nyjeve, ne do t mund t llogarisnim shtegun m t shkurtr pr tek secila nyje
tjetr pr secilin kompjuter. Pastaj, kur t duhet t drgohet nj mesazh, ne nuk
ka nevoj t bjm asgj tjetr pos ti qasemi tabels son t paracaktuar t
shtegut-m-t-shkurtr pr t gjetur mnyrn m t shpejt pr t drguar
mesazhin.

490

Algoritmet dhe strukturat e t dhnave

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:

Njerzit pyesnin veten a sht e mundur q dikush t kaloj npr t gjitha


pjest e qytetit n at mnyr q t kaloj npr seciln ur vetm nj her.
Problemi
1
Provoni. Skiconi hartn e msiprme t qytetit n nj flet dhe vizatoni rrugn e
kaluar me nj laps ashtu q t kaloni npr seciln ur vetm nj her dhe t
kompletoni rrugn pa e ngritur lapsin nga fleta.
A po keni problem? N rregull sht, mos u brengosni, sepse kt problem e pati
edhe Euler-i. Nuk sht e mundur q t kalohet secila ur vetm nj her. Pr ta
kuptuar se prse, duhet shikuar zgjidhja e problemit nga Euler-i, e njohur si
Shkalla e Nyjs.
Tentime t dshtuara:

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:

Tani, zgjidhja e problemit sht e mundur. Ja nj prej mudnsive:

ka e bn kt t ndryshme nga problemi real i Konigsbergut? Udhzim: Sa


ura drgojn n seciln pjes t qytetit? Prse sht problematike kur ka numr
tek t urave q drgojn n nj pjes t qytetit?
Problemi
A ka rndsi se ciln ur e largoni? ka nse e shtoni nj ur? Provoni!

Zgjidhja e Euler-it: Shkalla e nyjes


Euler-i iu qas problemit duke bashkuar pjest e tokave t ndara nga lumi n
pika, t cilat i shnoi me shkronja t mdha. N teorin moderne t grafeve,
492

Algoritmet dhe strukturat e t dhnave


kto quhen nyje dhe kan vazhduar q t prfaqsojn ato dhe urat n mnyra
grafike.
Pr rastin e Konigsberg-ut, le t prfaqsojm pjest e qytetit (tokt) me pika t
kuqe dhe urat me lakore t zeza (harqe, deg):

Prandaj, n versionin e thjeshtuar, problemi i shtat urave t Konigsberg-ut


duket si n vijim:

Tani problemi shndrrohet n problemin e vizatimit t ksaj figure, pa e larguar


lapsin nga fleta dhe pa kaluar dy her npr ndonjrn pjes t rrugs.
Ju kujtohet sigurisht sfida e vizatimit t shtpis, n kt mnyr:

Merrni n shqyrtim sa vijon: t gjitha katr nyjet, n figurn e mparshme t


rastit t Konigsberg-ut kan nj numr tek t degve (harqeve) t cilat i lidhin
ato. Merrni njrn prej tyre dhe filloni rrugtimin me laps. Hern e par q vini
n nyje, ju mund t largoheni npr nj rrug tjetr, mirpo hern e ardhshme
q arrini n t njjtn nyje, nuk mund t dilni?! Prandaj, secila nyje me nj
numr tek t rrugve t lidhur n t duhet t jet ose fillimi ose fundi i shtegut t
493

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

Algoritmet dhe strukturat e t dhnave

Figura 3

Figura 5

Figura 4

Figura 6

Zgjidhjet:
Figura 1

Figura 2

Figura 3

Figura 4

Ktu ka katr nyje teke,


prandaj nuk ka zgjidhje

495

Avni Rexhepi
Figura 5

Figura 6

Nuk ka shteg t Euler-it.

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

Algoritmet dhe strukturat e t dhnave


hyrjeve dhe daljeve dhe kto dy nyjet kan dallim pr nj n shkall t
hyrjes dhe daljes.
Me karakterizimin e dhn t grafeve Euler-iane, sht e leht q n koh
lineare t testohet nse ekziston cikli i till: testoni a sht grafi i lidhur duke
prdorur DFS ose BFS dhe pastaj numroni numrin e nyjeve me shkall teke.
N fakt, konstruktimi i ciklit t till merr koh lineare. Prdorni DFS pr t
gjetur ciklin n graf.
Fshini kt cikl dhe prsritni deri sa i tr seti i degve t jet ndar n nj set
t cikleve t degve t ndara. Pasi q fshirja e ciklit redukon shkalln e secils
nyje pr nj numr ift, grafi i mbetur do t vazhdoj t knaq t njejtat kushte
Euleriane t shkalls s nyjeve. Pr cilindo graf t lidhur kto cikle do t ken
nyje t prbashkta dhe kshtu duke ndar kto cikle n figurn tet n nyjen
e bashkndar, mund t konstruktojm nj qark t vetm q i prmban t gjitha
degt.
Nj cikl Eulerian, nse ekziston nj, e zgjidh problemin e pastrimit t bors,
pasi q cilido rrugtim q do t vizitoj t gjitha degt nga nj her duhet t ket
gjatsin minimale. Sidoqoft, pak ka t ngjar q ndonj rrjet real i rrugve do
t knaq kushtet q ta bjn at qark t Eulerit. Na duhet t zgjidhim problemin
m t prgjithsuar t Postierit Kinez (angl. Chinese postman problem), i cili
minimizon gjatsin e rrugs q e kalon seciln deg s pakunj her.
N fakt, mund t tregohet se ky cikl minimal nuk do t vizitoj asnjher
ndonj deg m shum se dy her, kshtu q rruga (xhiroja, itinerari) ekziston
pr cilindo rrjet t rrugve.
Turi optimal i postierit mund t konstruktohet duke shtuar degt e duhura n
grafin G ashtu q ta bj at Eulerian. N mnyr specifike, ne gjejm shtegun
m t shkurtr ndrmjet secilit ift t nyjeve me shkall teke n grafin G.
Duke shtuar shtegun ndrmjet dy nyjeve me shkall teke n G i kthen ato n
nyje me shkall ifte, prandaj duke na drguar m afr nj grafi Eulerian. Gjetja
e setit m t mir t shtigjeve m t shkurtra pr tu shtuar n G redukohet n
identifikimin e prshtatjes perfekte me pesh minimale n graf n nyjet me
shkall teke, ku pesha e degs (i,j) sht gjatsia e shtegut m t shkurtr prej
nyjes i n nyjen j. Pr grafet e drejtuara, kjo mund t zgjidhet me prshtatjen
bipartite, ku nyjet ndahen varsisht nga fakti se a kan m shum deg hyrse
apo dalse. Kur grafi nj her sht Eulerian, cikli aktual mund t nxirret n
koh lineare duke prdorur procedurn e prshkruar m par.

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

Algoritmet dhe strukturat e t dhnave


//N

numri i elementeve n list

for i = 1 to N do
if (caku = lista[i])
return i //lokacioni-pozita ku u gjet caku
end if
end for
return 0;

Le t shqyrtojm se sa koh duhet pr t gjetur elementin q i prshtatet elsit


t krkimit (vlers q krkohet) n bashksin e elementeve. Ne jemi t
interesuar pr:
a. kohn mesatare,
b. kohn e rastit m t keq, dhe
c. kohn e rastit m t mir
Sidoqoft, n prgjithsi do t jemi m t brengosur pr kohn e rastit m t keq,
pasi q llogaritjet e bazuara n kohn e rastit m t keq mund t drgojn n
parashikimet e performanss s garantuar. Pr fat, koha e rastit m t keq n
prgjithsi llogaritet m leht sesa koha e rastit mesatar.
Nse kemi n elemente n bashksin e krkimit, oft ajo e ruajtur si varg ose si
list e lidhur, sht e qart se n rastin m t keq, kur asnjri element n
bashksi nuk sht me vlern e krkuar t elsit, ather duhet t bhen n
krahasime t elsit me elementet e bashksis.
Pr t thjeshtuar analizn e algoritmeve t krahasimit, krkojm operacionin
dominant dhe numrojm numrin e herave t kryerjes s operacionit dominant.
N rastin e krkimit, operacioni dominant sht operacioni i krahasimit, e pasi
q n rastin m t keq krkimi krkon n krahasime, themi se sht algoritm i
rendit O(n) (thuhet Big-O n).
Rasti m i mir, n t cilin krahasimi i par kthen prputhje, krkon nj
krahasim t vetm dhe sht O(1).
Koha mesatare varet nga gjasa (probabiliteti) i gjetjes s elsit n bashksin e
vlerave, gj q sht dika q nuk sht e prithshme ta dijm n shumicn e
rasteve. Prandaj, n kt rast, si n shumicn e t tjerave, vlersimi i kohs
mesatare sht me dobi t vogl. Nse performansa e sistemit sht vitale,
d.m.th., sht pjes e ndonj sistemi kritik pr jet, ather n llogarit pr
dizajn duhet t prdoret rasti m i keq, pasi q ai prfaqson performansn m t
mir t mundhsme t garantuar.

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

Pra, tiparet e krkimit binar jan:


a. krkimi binar sht rekurziv: ai prcakton nse vlera e krkuar ndodhet
n gjysmn e poshtme ose t eprme t vargut dhe pastaj e thrret
vetveten pr gjysmn prkatse t mbetur.
b. Kemi kushtin e ndrprerjes s krkimit (n fakt dy!)
i. Nse kufiri i poshtm > kufiri i eprm, ather particioni q
duhet krkuar nuk ka m elemente n t (vlera nuk sht gjetur),
dhe
ii. Nse ka prputhje me elementin e pozits s mesit t particionit
aktual, ather mund t kthejm rezutlatin menjher.
500

Algoritmet dhe strukturat e t dhnave


Analiza:
Do t marrim se vargu i elementeve sht vargu A me n elemente: A[n]. Le t
shnojm me kp-kufiri i poshtm, ke-kufiri i eprm, mesi-lokacioni n mes dhe
vk-vlera e krkuar (elsi). N fund, do t arrijm n hapin kur vlera e krkuar
sht e barabart me vlern n kufirin e eprm ose t poshtm (d.m.th., vlera
gjindet n varg) ose kur kufiri i poshtm bhet m i madhe se kufiri i eprm
(d.m.th., vlera nuk gjinder n varg fare). Shihet, se n secilin hap t krahasimit,
numri i vlerave pr krahasim prgjysmohet.
A[n]; n elemente

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

Fig. x Krkimi binar


Secili hap i algoritmit e ndan bllokun e elementeve q krkohen n gjysm.
Bashksin e n elementeve mund ta prgjysmojm m s shumti log2 n her.
Prandaj, koha e ekzekutimit t krkimit sht proporcionale me log n dhe themi
se ky algoritm sht i rendit O(log n). Kjo do t thot se numri maksimal i
krkimeve pr vargun me 1 milion elemente sht m pak se 20 (pasi q 220>1
milion).

501

Avni Rexhepi

Fig. x Shkalla e rritjes pr funksionet lineare dhe llogaritmike


Krkimi binar pr numr t vogl t vlerave t hyrjes, n, mund t ekzekutohet
m ngadal se algoritmi i thjesht linear, mirpo pr vlera t mdha t n-it,
log n
0
n
n

lim

Prandaj, pr n t madh, log n sht shum m i vogl se n, kshtu q algoritmi


O(log n) sht shum m i shpejt sesa ai O(n).
Sa i prket insertimit, n rastin m t keq, insertimit mund t krkoj n
operacione pr t insertuar nj vler n listn e sortuar.
1. Mund t gjejm vendin n list ku duhet t vendoset elementi i ri, duke
prdorur krkimin binar me O(log n) opearcione.
2. Mirpo, pr ti br vend nyjes s re duhet t zhvendosim t gjitha
elementet pr nga nj vend. N rastin m t keq, elementi i ri sht ai q
duhet t vendoset n pozitn e par n list, gj q krkon n operacione
t lvizjes/zhvendosjes.
Analiza e ngjashme do t tregoj q edhe fshirja/largimi sht operacion i rendit
O(n).
Nse koleksioni sht statik, d.m.th., nuk ndryshon shpesh madhsia e tij,
ather mund t mos jemi t brengosur me kohn e krkuar pr ndryshimin e
prmbajtjes s tij dhe mund t jemi t prgatitur pr ndrtimin fillestar t
koleksionit me ndonj fshirje ose insertim t rastit q do t merr koh. N ann
tjetr, do t jemi n gjendje t prdorim nj struktur t thjesht t t dhnave
(nj varg) q ka mbingarkes t vogl memorike.
502

Algoritmet dhe strukturat e t dhnave


Mirpo, nse koleksioni sht i madh dhe dinamik, d.m.th., elementet insertohen
dhe fshihen vazhdimisht, ather performans m e mir arrihet duke prdorur
strukturn e pems.

Analiza e algoritmit t krkimit binar


Algoritmi sht krejt i thjesht. Mund t bhet n mnyr rekurzive ose iterative:
1. Gjeje elementin e mesit;
2. Nes elementi i mesit sht i barabart me vlern e krkuar, algoritmi
ndalet;
3. Prndryshe, jan t mundshme dy raste:
o Vlera e krkuar sht m e vogl sesa elementi i mesit. N kt rast,
shko n hapin 1 pr pjesn e vargut para elementit t mesit (gjysmn e
prparme t vargut).
o Vlera e krkuar sht m e madhe, sesa elementi i mesit. N kt rast,
shko n hapin 1 pr pjesn e vargut pas elementit t mesit (gjysmn e
pasme t vargut).
Tani duhet t definojm, kur duhet t ndalen iteracionet (prsritjet). Rasti i par
sht kur elementi i krkuar gjendet. Rasti i dyt sht kur nnvargu nuk ka m
elemente. N kt rast, mund t konkludojm q vlera e krkuar nuk sht
prezente n varg.

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

Hapi 2 (elementi i mesit sht 5 < 6):

-1 5 6 18 19 25 46 78 102 114

Step 3 (elementi i mesit sht 6 == 6):

-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

Algoritmet dhe strukturat e t dhnave


Me iteracione:
/*
* krkon vlern n vargun e sortuar array
*
arr 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 arr[], int value, int left, int right)
{
while (left <= right) {
int middle = (left + right) / 2;
if (arr[middle] == value)
return middle;
else if (arr[middle] > value)
right = middle - 1;
else
left = middle + 1;
}
return -1;
}

505

Avni Rexhepi

Algoritmi pr bashkim t vargjeve t sortuara


N vijim do t shohim algoritmin pr bashkimin (angl. merge-bashkim, shkrirje)
e dy vargjeve t sortuara, i cili ndihmon n operimin me disa vargje dhe
kontrollimin e indekseve read/write (lexo/shkruaj). Gjithashtu, ky algoritm
prdoret n disa aplikacione praktike, si pr shembull merge sort-i (sorti i
bashkimit/shkrirjes).

Algoritmi i bashkimit - Merge algoritmi


Supozojm se t dy vargjet q duhet bashkuar jan t sortuara n renditje rritse
dhe dshirojm q vargu rezultues t ruaj t njjtn renditje. Algoritmi pr
bashkimin e dy vargjeve A[0..m-1] dhe B[0..n-1] n nj varg C[0..m+n-1] sht
si n vijim:
1. Prezento indekset e leximit (read-indices) i, j pr t prshkuar vargjet
A dhe B.
2. Prezento indeksin pr shkruarje (write-index) k pr t ruajtur pozitn e
celuls s par t lir n vargun rezultues. N fillim i = j = k = 0.
3. N secilin hap: nse t dy indekset jan n rangun (i < m dhe j < n),
zgjedhe minimumin prej (A[i], B[j]) dhe shkruaje at n C[k].
Prndryshe shko n hapin 4.
4. Rrite k-n dhe indeksin e vargut n t cilin u gjet minimumi, pr nj.
Prsrite hapin 2.
5. Kopjo pjesn e mbetur nga vargu, indeksi i t cilit akoma sht n rang,
n vargu rezultues (d.m.th, nse njri varg sht prfunduar, pjesa e
mbetur e tjetrit vetm vetm kopjohet)

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

Algoritmet dhe strukturat e t dhnave


Pjes kodi
// m madhesia e A
// n - madhesia e B
// madhesia e vargut C duhet te jete baraz ose me e madhe se m + n
void merge(int m, int n, int A[], int B[], int C[]) {
int i, j, k;
i = 0;
j = 0;
k = 0;
while (i < m && j < n) {
if (A[i] <= B[j]) {
C[k] = A[i];
i++;
} else {
C[k] = B[j];
j++;
}
k++;
}
if (i < m) {
for (int p = i; p < m; p++) {
C[k] = A[p];
k++;
}
} else {
for (int p = j; p < n; p++) {
C[k] = B[p];
k++;
}
}
}

507

Avni Rexhepi
Algoritmet teorike-numerike

Testimi i numrave primar (qasja naive)


Testimi i numrit se a sht primar, sht nj detyr e rndsishme n shkencat
kompjuterike. N t shumtn e rasteve, numrat primar prdoren n algoritmet e
kriptografis s elsave publik (angl. public key cryptography algorithms).
Gjithashtu ka edhe aplikacione pr hash tabelat dhe gjeneratort e numrave
pseudorandom (pseudo t rastit). Ka dy tipe t algoritmeve pr tstim t numrit
primar: deterministik dhe probabilistik. N t vrtet, ka shum algoritme t
verifikimit se a sht numri primar. N vazhdim do t paraqesim qasjen m
naive, t quajtur trial division (ndarja me prov) dhe modifikimet e saj.

ka jan numrat primar?


Numr primar sht numri i cili plotpjestohet me vetm dy numra natyral, 1
dhe vetveten. Qasja m naive q t vrtetohet se a sht numri primar, sht
duke prcjellur definicionin. Algoritmi pr t verifikuar numrin a sht a primar:

Nse numri sht 1, kthe false (return false);


Prndryshe, pr t gjith numrat e plot m, prej 2 deri n n-1, verifiko
nse n sht i plotpjestueshm me m. Nse sht i plotpjestueshm,
ather n sht numr i prbr (jo primar);
Nse nuk sht gjetur asnj pjestues, ather mund t prfundojm se n
sht primar.

Vrejtje. Numri 1 nuk sht numr primar, sepse nuk e plotson definicionin!

Prmirsimi i mundshm i algoritmit


Si mund t prmirsohet kjo qasje e thjesht? S pari le t vm re se mund t
verifikojm pjestuesit m t vegjl ose baraz se rrnja katrore e n-it. Pason
prova.
Formulim. Ns n ka plotpjestuesin d (1<d< n), ather ka plotpjestuesin d0
(1<d0<n).
Nse n plotpjestohet me rrnjn katrore, ather ai nuk sht numr primar.
Prndryshme, supozojm se plotpjestuesi i par i gjetur sht d1, n < d1 < n.
Mirpo n sht i plotpjestuar me d2 = n / d1, i cili sht m i vogl se n.
Prandaj, supozimi sht fals (jo i vrtet) dhe nse ka plotpjestues m t madh
se n, ather ka nj ift m t vogl se n. Me kt, vrtetohet formulimi.

508

Algoritmet dhe strukturat e t dhnave

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:

Nse numri sht 1, kthe false (return false);


Nse numri sht 2, kthe false;
Nse numri sht ift, kthe false;
Prndryshe, pr t gjith numrat e plot tek m prej 3 deri te n, verifiko nse
n sht i plotpjestueshm me m. Nse sht, ather n sht numr i prbr;
Nse nuk sht gjetur asnj pjestues, ather prfundojm se n sht primar.
Prgjithsim i ksaj ideje sht kur algoritmi verifikon vetm plotpjestuesit
primar.

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.

100 numrat e par primar


2
31
73
127
179
233
283
353
419
467

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;
}

Sita e Eratostenit (Sieve of Eratosthenes)


Sita e Eratostenit, prdoret pr t gjetur numrat primar deri tek nj numr i plot
i caktuar, n. Sigurisht q duke prdorur ndonj qasje, mund t testojm t gjith
numrat n rangun prej 2 deri n n se a jan primar, mirpo kjo sht shum
joefikase. Sita e Eratostenit sht nj algoritm i thjesht pr gjetjen e numrave
primar. Megjithse n kohn e sotme ekzistojn algoritme edhe m t mira, sita
e Eratostenit sht nj shembull shum i mir i qasjes me sit.
1. Eratosteni (Eratosthenes of Cyrene) ishte matematikan, gjeograf, poet, astronom
dhe teoricient i muziks, ishte grek i lindur n vitin 276 para ers son n
Cyrene, Shahhat, t Libis dhe i vdekur n vitin 194 p.e.s, n Aleksandri, Egjipt.
Ishte studiues i ditur dhe ishte br drejtor i Libraris s Aleksandris.
Algoritmi
S pari, algoritmi krkon vargun e bitave isComposite (shtIPrbr) pr t
ruajtur n - 1 numrat: isComposite[2 .. n]. Fillimisht, vargu prmban zero (0) n
t gjitha celulat (pozitat). Gjat puns s algoritmit, numrat, t cilt mund t
reprezentohen si k * p, ku k 2, dhe p sht primar, shnohen si numra t
prbr, duke shkruar 1 n celulat gjegjse (prkatse). Algoritmi prbhet
prej dy unazave t ndrthurrura: e jashtme, e cila krkon numrat e pa-shnuar
(primar) dhe e brendshme, e cila i shnon shumfishet e numrave primar, si t
prbr.

510

Pr t gjith numrat m: 2 .. n, nse m sht i pashnuar:


o shtoje m n listn e numrave primar;
o shno t gjith shumfishet e tij, m t vegjl ose baraz me n (k *
m n, k 2);

Algoritmet dhe strukturat e t dhnave

Prndryshe, nse m sht shnuar, ather ai sht numr i prbr.

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

Ve ka qen i shnuar n hapin m = 2.


Pr m = 5,
2 * 5

3 * 5

4 * 5 (= 10 * 2)

Ve jan shnuar n hapat m = 2 dhe m = 3.


Pa dhn provn e fort, mund ta provoni leht. Algoritmi i modifikuar sht si
vijon:

Pr t gjith numrat m: 2 ... n, nse m sht i pashnuar:


o shtoje m n listn e numrave primar;
o shno t gjith shumfishet e tij, duke filluar nga katrori, m t
vegjl
ose
baraz
me
n
(k * m n, k m);
Prndryshe, nse m sht shnuar, ather ai sht numr i prbr;
Verifiko t gjith numrat n rangun n .. n. T gjith numrat e mbetur
t pashnuar jan numra primar. Shtoji n listn e numrave primar.

Shembull
Apliko sitn e Eratostenit pr gjetjen e numrave primar nga 2 deri n 100.

511

Avni Rexhepi
Rrjeta integrale

2 shtr primar, shno t gjitha shumfishet e 2, duke filluar nga 4

512

Algoritmet dhe strukturat e t dhnave


3 shtr primar, shno t gjitha shumfishet e 3, duke filluar nga 9

5 shtr primar, shno t gjitha shumfishet e 5, duke filluar nga 25

513

Avni Rexhepi
7 shtr primar, shno t gjitha shumfishet e 7, duke filluar nga 49

112 sht m shum (m i madh) sesa 100, kshtu q t gjith numrat e


pashnuar, jan primar

514

Algoritmet dhe strukturat e t dhnave


Rezultati prfundimtar:

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

Algoritmet dhe strukturat e t dhnave

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

Algoritmet dhe strukturat e t dhnave

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

Breshkat dhe lepujt


Nj problem tjetr i sortit bubble sht se koha e tij e ekzekutimit sht shum e
varur nga renditja fillestare e elementeve t vargut. Elementet e mdha (lepujt)
shkojn prpjet shpejt, gjersa ato t voglat (breshkat) shkojn teposht shum
ngadale. Ky problem sht zgjidhur n sortin Cocktail.
Shembull i breshks. Edhe pse vargu {2, 3, 4, 5, 1} sht pothuajse i sortuar, ai
merr nevojiten O(n2) iteracione pr t sortuar vargun. Elementi {1} sht
breshk.

520

Algoritmet dhe strukturat e t dhnave

Shembull i lepurit. Vargu {6, 1, 2, 3, 4, 5} sht poashtu, pothuajse i sortuar,


por ky merr O(n) iteracione pr tu srotuar. Elementi {6} sht lepuri. Ky
shembull demonstron vetin adaptive t sortit bubble.

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

Algoritmet dhe strukturat e t dhnave

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

Algoritmet dhe strukturat e t dhnave


Pjes kodi
void selectionSort(int arr[], int n) {
int i, j, minIndex, tmp;
for (i = 0; i < n - 1; i++) {
minIndex = i;
for (j = i + 1; j < n; j++)
if (arr[j] < arr[minIndex])
minIndex = j;
if (minIndex != i) {
tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
}
}

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

Le t shohim nj shembull t sortit t insertimit, pr t pasur m t qart iden e


algoritmit.
Shembull. Sortoni {7, -5, 2, 16, 4} duke prdorur sortin insertion.

526

Algoritmet dhe strukturat e t dhnave

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

Kjo qasje, i shkruan shum her elementet e shoshitura n pozitat e


prkoshme. Implementimi vijues, i eliminon kto shkruarje t panevojshme.

Shiftimi n vend t shkmbimit


Algoritmi paraprak mund t modifikohet, ashtu q t shkruaj elementet e
shoshitura, vetm n pozitn e tyre t duhur prfundimtare, pozitn finale, si n
vijim:

Kjo sht mnyra e prdorur m s shpeshti e sortit t insertimit.

Duke prdorur krkimin binar


sht e arsyeshme q t prdoret algoritmi i krkimit binar, pr t gjetur vendin
e duhur pr insertim. Ky variant i sortit t insertimit, quhet sorti binar i
insertimit. Pasi t jet gjetur pozita pr insertim, algoritmi e zhvendos (shifton)
pjesn e vargut dhe e inserton elementin. Kjo mnyr ka numr m t vogl t
krahasimeve, por kompleksiteti mesatar i prgjithshm mbetet O(n2). Nga
528

Algoritmet dhe strukturat e t dhnave


kndveshtrimi praktik, ky prmirsim nuk sht i rndsishm, sepse sorti i
insertimit prdoret n bashksi mjaft t vogla t t dhnave.

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.

Tiparet e sortit Insertion

adaptiv (performansa adaptohet me renditjen fillestare t elementeve);


stabil (ruan renditjen relative t elementeve t njjta);
in-place (angl. in place n vend) krkon madhsi konstante t hapsirs
shtes);
online (elementet e reja mund t shtohen gjat sortimit).

(Sqarim: sortet t cilat mund t sortohen prbrenda hapsirs memorike q e


kan para sortimit, do t thot q sortohen n vendin ku jan vlerat, njihen si
in-place n vend, pr dallim prej atyre q krkojn hapsir shtes
memorike, pr t vendosur prkohsisht vlerat q duhet sortuar, pr t br
krahasimin e tyre, rirenditjen, etj, e q thuhet s nuk jan in-place).
Pjes kodi
Do t paraqitet idea e sortimit me shiftime dhe ajo me shkmbime:
//me shiftim
void insertionSort(int arr[], int length) {
int i, j, newValue;
for (i = 1; i < arr.length; i++) {
newValue = arr[i];
j = i;
while (j > 0 && arr[j - 1] > newValue) {
arr[j] = arr[j - 1];

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

Algoritmet dhe strukturat e t dhnave

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.

Prse funksionon kjo?


N hapin e particionit algoritmi e ndan vargun n dy pjese dhe secili element a
nga pjesa e majt sht m i vogl ose baraz se secili element b nga ana e
djatht. Gjithashtu, a dhe b e plotsojn (e knaqin) jobarazin a pivot b.
pas kompletimit t thirrjeve rekurzive t dy pjest bhen t sortuara dhe duke
marr parasysh argumentet e dhna m sipr, sortohet i tr vargu.

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

Algoritmet dhe strukturat e t dhnave


i++;
while (arr[j] > pivot)
j--;
if (i <= j)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
/* recursion */
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}

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);
}

Shembull me elementin e parw si pivot:

Fig. xx Ekzekutimi i QuickSort-it nw vargun (3,6,8,1,0,7,2,4,5,9) duke


pwrdorur elementin e parw si pivot. Thirrja e parw e pwrdorw 3-shin si pivot
dhe gjeneron nwnproblemet (1,0,2), (3) dhe (6,8,7,4,5,9). Thirrja rekurzive pwr
nwnproblemin e tretw pwrdorw 6-shin si pivot dhe gjeneron nwnproblemet
(4,5), (6) dhe (8,7,9)

534

Algoritmet dhe strukturat e t dhnave

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.

Fig. 3.1 Katr kalimet e Shell Sortit


Nnlista e par tani ka elementet e lokacionit t par, t pest, t nnt dhe t
trembdhjet. Nnlista e dyt, ka elementet e lokacionit t dyt, t gjasht, t
535

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

Variabla increment jep distancn ndrmjet elementeve n nnlist (N Fig. 3.1,


inkermentet e prdorura jan 8,4,2,1). N kt algoritm, fillojm me nj
inkrement q sht pr 1 m i vogl se fuqia m e madhe 2-shit q sht m e
vogl sesa madhsia e lists. Kshtu, nse lista e jon ka 1000 elemente,
inkrementi i jon i par do t jet 511. (Vrejtje: Vlera=1000, Fuqia e 2-shit:
210=1024, kshtu q 29=512, => 29-1=511). Inkrementi gjithashtu tregon numrin
e nnlistave q i kemi. Nse nnlista e jon e par ka elementet n lokacionet 1
dhe 1+increment, nnlista e fundit duhet t filloj n lokacionin increment.
Hern e fundit q ekzekutohet unaza while, kalimet do t ken vlern 1, gj q
do t bj vlern increment 1 pr InsertionSort-in e fundit.
Analiza e ktij algoritmi varet nga analiza q e bm pr InsertionSort-in. Para
se t fillojm analizn e Shellsort, rikujtoni q n seksionin 3.1, kemi par se
pr listn me N elemente rasti m i keq pr sortin e insertimit isht (N2-N)/2 dhe
rasti mesatar pr sortin e insertimit isht: N2/4.

536

Algoritmet dhe strukturat e t dhnave

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

Do t fillojm me shqyrtimin e ktij algoritmi. Llogaritja e bucketNumber do t


trheq nj shifr njshe nga elsi (vlera kye). Pjestimi me shift do t bj q
vlera kye t lvizet n t djatht pr nj numr t shifrave dhe pastaj mod do t
eliminoj t gjitha tjerat prveq shifrave t njsheve t numrit rezultues. N
kalimin e par me vlern e shift-it 1, pjestimi nuk do t bj asgj dhe rezultati
i mod do t kthej vetm shifrat njshe t vlers kye. N kalimin e dyt, shift
do t jeta 10, kshtu q pjestimi i plot dhe pastaj mod do t kthej vetm shifrat
e dhjetsheve. N secilin kalim t ardhshm, do t prdoret shifra e ardhshme
e vlers kye (elsit).
Funksioni CombineBuckets do t bashkangjes (shtoj) kovat prapa n nj list
q fillon me bucket[0] e deri n bucket[9]. Kjo list e rikombinuar sht pika
fillestare pr kalimin e ardhshm. Pasi q kovat rikombinohen n renditje
(radh) dhe pasi q numrat shtohen n fund t secils list t kovave, vlerat kye
537

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

310 130 330 120

301 201 111 231

222 032 002 102

213 023 013 323


(a) Kalimi 1, Shifra e njsheve

Lista e kalimit 1
310 130 330 120 301 201 111 231 222 032 002 102 213 023 013 323
Numri i kovs

Prmbajtja

301 201 002 102

310 111 213 013

120 222 023 323

130 330 231 032

(b) Kalimi 2, Shifra e dhjetsheve


Lista e kalimit 2
301 201 002 102 310 111 213 013 120 222 023 323 130 330 231 032
Numri i kovs

538

Prmbajtja

Algoritmet dhe strukturat e t dhnave


0

002 013 023 032

102 111 120 130

201 213 222 231

301 310 323 330

(c) Kalimi 3, Shifra e qindwsheve


FIGURA 3.2 Tri kalimet e sortit radix

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

Ka nj numr t detajeve q mbesin pr t qen ky algoritm i kompletuar. S


pari duhet t prcaktojm se ka prfshihet n procesin e konstruktimit dhe
fiksimit (riparimit) t pirgut, sepse kjo do t luaj rol n efikasitetin e ktij
algoritmi. Duhet t brengosemi pr at se si do t implementohet (zbatohet) ky
algoritm. Tejngarkesa (overhead ngarkesa e kreut, fillestare) e krijimit t
krijimit t pems binare do t ishte problem me rritjen e madhsis s lists.
Sidoqoft, ne mundemi t prdorim hapsirn e vet lists dhe kshtu t bjm
kt sortim pa hapsir shtes t veant. Ne mund t ndrtojm listn n pirg
nse e vrejm se n pirg secila nyje e jashtme ka dy fmij, prveq ndoshta
njrs nyje kah fundi. Nse shqyrtojm planifikimin (pasqyrimin) vijues, pr
mbajtjen e ktyre vlerave mund t prdorim listn. Pr nyjn n lokacionin i, do
t ruajm dy fmijt e saj n lokacionet 2*i dhe 2*i+1. Vreni se ky proces
prodhon lokacione t ndryshme pr secilin fmij t nyjs. E dijm se nyja i
sht gjethe nse 2*i sht m e madhe sesa N, dhe e dijm se nyja i ka vetm
nj fmij nse 2*i sht i barabart me N. Figura 3.3 paraqet pirgun dhe
verzionin e tij n form liste.
540

Algoritmet dhe strukturat e t dhnave

FIGURE 3.3 - Pirgu dhe implementimi i tij si list

Fiksimi i pirgut (FixHeap)


Kur e marrim elementin m t madh nga rrnja dhe e vendosim n list, kjo e l
rrnjn t zbrazt. E dijm se elementi m i madh i dy fmijve t saj duhet t
lvizt lart, por ather nyja e ktij fmije bhet e zbrazt dhe kshtu shikojm
n dy fmijt e saj, e kshtu me radh. N kt proces, duhet t mirmbajm
pirgun sa m afr pems s plot q t jet e mundur. Kur e fiksojm (riparojm)
pirgun, gjithashtu do t kalojm (prcjellim) nyjn m t djatht prej nivelit t
poshtm, q t insertohet prapa n pirg. Kjo do t largoj nyjet n mnyr t
barabart prej fundit. Nse nuk e bjm kt dhe t gjitha vlerat e mdha jan n
nj an t pirgut, pirgu do t jet i pabalansuar dhe efikasiteti i algoritmit do t
ulet. Kjo na jep algoritmin vijues:
FixHeap( list, root, key, bound )
list lista/pirgu qe sortohet
root indeksi i rrenjes se pirgut
key vlera qe duhet te reinsertohet ne pirg
bound kufir i eperm (index) ne pirg
//vacant=i/e lir
vacant = root
while 2*vacant bound do
largerChild = 2*vacant
// gjeje me te madhin prej dy femijeve
if (largerChild < bound) and (list[largerChild+1]
list[largerChild]) then

>

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

Kur shikoni n parametrat e ktij algoritmi, mund t pyesni veten (t uditeni, t


habiteni) se prse kemi zgjedhur q t prcjellim (bartim, lvizim) lokacionin
rrnj. Pasi q kjo rutin (ky nnprogram, funksion) nuk sht rekurzive, rrnja
e pirgut do t duhej gjithmon t jet n lokacionin 1. Sidoqoft, do t shihni se
ky parametr shtes do t bj t mundur pr ne q t prdorim kt funksion
pr t konstruktuar pirgun prej posht lart. Ne e prcjellim madhsin e pirgut,
sepse me lvizjen e elementeve nga pirgu n list, pirgu tkurret (ngushtohet,
zvoglohet).

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

Algoritmet dhe strukturat e t dhnave


FixHeap( list, i, list[ i ], N )
end for
for i = N down to 2 do
max = list[ 1 ]
FixHeap( list, 1, list[ i ], i-1 )
list[ i ] = max
end for

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

Do t duhej t jet e qart se e tr puna sht duke u br n funksionin


MergeLists .
N vazhdim do t krijojm funksionin MergeLists.
Merrni n shqyrtim listat A dhe B, t dyjat t sortuara n renditje rritse. Kjo
renditje do t thot se elementi m i vogl n seciln list ndodhet n lokacionin
e par dhe elementi m i madh i secils list ndodhet n lokacionin e fundit. Pr
t bashkuar kto s bashku n nj list, ne e dijm se elementi m i vogl i
prgjithshm duhet t jet ose elementi i par i lists A ose elementi i par i B
dhe se elementi m i madh i prgjithshm duhet t jet ose elementi i fundit i A
ose elementi i fundit i B. Nse dshirojm t krijojm nj list t re C e cila
sht kombinimi i sortuar i A dhe B, do t fillojm me vendosjen e m m t
voglit mes A[1] dhe B[1] n C[1]. Por, ka vendoset n C[2]? Nse A[1] ka
qen m i vogl se B[1], A[1] sht vendosur n C[1] dhe elementi i ardhshm
mund t jet B[1] prveq nse A[2] gjithashtu sht m i vogl se B[1]. Kjo
sht e mundur sepse krejt ka ne me t vrtet e dijm sht se A[2] sht m i
madh se A[1] dhe m i vogl se A[3], por ne nuk e dijm se si qndrojn pr nga
madhsia elementet e A n krahasim me elementet e B. Duket se mnyra m e
mir pr t realizuar bashkimin, do t ishte q t kemi dy indeksa pr A dhe B
dhe t inkrementohet (rritet pr 1) indeksi pr listn q ka elementin m t
vogl. Procesi i prgjithshm vazhdon krahasimin e elementeve m t vogla prej
atyre q kan mbetur n listat A dhe B dhe e vendos m t voglin prej tyre n
C. N nj pik, sidoqoft, do t mbesim pa elemente ose t lists A ose t
lists B. Elementet e mbetura tepric do t jen ato nga njra list t cilat jan
m t mdha sesa elementi i fundit i lists tjetr. Duhet t sigurohemi se kto
544

Algoritmet dhe strukturat e t dhnave


elemente jan vendosur n fund t lists rezultuese. Bashkimi i ktyre ideve n
nj algoritm do t na jep:
MergeLists( list, start1, end1, start2, end2 )
List elementet qe duhet te sortohen
start1 fillimi i listes A
end1 fundi i listes A
start2 - fillimi i listes B
end2 - fundi i listes B
//suposon qe elem. e A dhe B jane ne liste te vazhduar (ngjitur)
finalStart = start1
finalEnd = end2
indexC = 1
while (start1 end1) and (start2 end2) do
if list[start1] < list[start2] then
result[indexC] = list[start1]
start1 = start1 + 1
else
result[indexC] = list[start2]
start2 = start2 + 1
end if
indexC = indexC + 1
end while
// zhvendose pjesen e listes qe ka mbetur
if (start1 end1) then
for i = start1 to end1 do
result[indexC] = list[i]
indexC = indexC + 1
end for
else
for i = start2 to end2 do
result[indexC] = list[i]
indexC = indexC + 1
end for
end if
// tash vendose rezultatin prapa ne liste
indexC = 1
for i = finalStart to finalEnd do
list[i] = result[indexC]
indexC = indexC + 1
end for

Pr nj rast konkret, do t kishim shembullin si n figurn vijuese:


545

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

Algoritmet dhe strukturat e t dhnave


jan t ndrthurrura (t shtresuara) bazuar n vlerat e tyre. Me fjal t tjera,
ka ndodh nse vlera e A[1] ndodhet ndrmjet B[1] dhe B[2], vlera e A[2]
ndodhet ndrmjet B[2] dhe B[3], vlera e A[3] ndodhet ndrmjet B[3] dhe B[4], e
kshtu me radh. Vreni se secili krahasim e lviz nj element ose nga A ose
nga B, n listn C. Bazuar n shembullin e renditjes s msiprme, ne e lvizim
nj element t B, pastaj nj t A, pastaj nj t B, pastaj nj t A deri sa t kemi
lvizur t gjitha prveq elementit t fundit nga A. Pasi q krahasimet rezultuan
me lvizjen e t gjitha elementeve prveq atij t fundit t A, do t kemi br
NA+NB-1 krahasime, n rastin m t keq.
N total (bashk me pjesn e bashkimit, pas ndarjes s listave) MergeSort
sht sort shum efikas, i rendit O(N lgN), edhe n rastin m t keq, por
problemi sht se funksioni MergeList ka nevoj pr hapsir shtes t veant
pr t realizuar bashkimin (shkrirjen).

547

Avni Rexhepi

Koncepte t programimit

10. Algoritmet e prshtatjes s stringjeve


Algoritmet e prshtatjes (prputhjes) s stringjeve (angl. string matching
algorithms ose pattern matching algorithms) jan nj kategori shum e
prdorur e algoritmeve. Problemi kryesor i adresuar sht ai q trajton nj
mostr t stringut (angl. pattern) q krkohet brenda nj stringu tjetr (n
mnyr tipike ndonj string m i gjat ose ndonj tekst i tr). Thuhet se po
krkohet nn-stringu brenda stringut ose mostra brenda stringut. Rezultati i
krkimit ose do t jet gjetja e mostrs (nn-stringut) brenda tekstit (stringut), q
zakonisht tregohet me nj pointer n pozitn e prshtatjes ose do t jet
informacioni q teksti nuk e prmban nn-stringun e krkuar. Kjo zakonisht
sht e aplikuar si opcioni i krkimit n editort e tekstit.
Ekzistojn disa algoritme t prshtatjes, gjegjsisht krkimit t tekstit. Mnyra e
zakonshme, e krkimit t tekstit duke krahasuar shkronj pr shkronj nnstringun me stringun njihet si algoritmi naiv i prshtatjes ose algoritmi bruteforce i prshtatjes. Fillohet me krahasimin e shkronjs s par t nn-stringut
me shkronjn e par t stringut dhe nse ato prputhen, ather vazhdohet me
krahasimin e shkronjave n pozitn e dyt, e kshtu me radh. Nse nuk ka
prshtatje n ndonj pozit, ather zhvendoset (shiftohet) nn-stringut pr nj
pozit m djathtas dhe fillohet prej fillimit krahasimi i pozits s par t nnstringut me t dytn e stringut, e tutje. Sa her q paraqitet ndonj mosprshtatje n ndonj pozit, nn-stringu zhvendoset (shiftohet) pr nj pozit m
djathtas. Nse prshtaten t gjitha pozitat, ather lajmrohet prshtatja,
gjegjsisht gjindet prputhja e plot e nn-stringut brenda stringut.
Algoritmet tjera tentojn t gjejn mnyr m t shpejt t krahasimit, duke
prfituar nga informacioni i mbledhur paraprak pr natyrn e mostrs (nnstringut), ashtu q t anashkalohen krahasimet e panevojshme n rastet kur
parashihet q nuk do t ket prshtatje. Dy nga algoritmet m t njohura t ksaj
kategorie jan:
-

Algoritmi Knuth-Morris-Pratt (KPM), dhe


Algoritmi Boyer-Moore (BM)

Kto algoritme jan emrtuar sipas emrave t zbuluesve t tyre.


Pr do algoritm t prshtatjes, prdoren termat standarde.
Stringu sht sekuenc e karaktereve. Shembuj t stringut jan: teksti i
zakonshem, nj program n C++, nj HTML dokument, nj sekuenc e ADN-s
(AGCTTCGA...), nj imazh i digjitalizuar, etj.
548

Algoritmet dhe strukturat e t dhnave


Alfabeti, q zakonisht shnohet me sht bashksia e t gjitha karaktereve t
mundshme pr nj familje t stringjeve. P.sh, alfabeti i gjuhs shqipe, si
bashksi e t gjitha shkronjave t gjuhs shqipe ose {0.1} si alfabeti binar.
Shembuj t alfabeteve jan edhe ASCII, Unicode, {A,G,C,T}, etj.
Le t jet P (nga Pattern mostra) string me madhsi (gjatsi) m. Pozitat
indeksohen prej 0 deri n m-1.
-

Nn-stringu P[i..j] i stringut P sht nn-sekuenc e P q prmban


karakteret ndrmjet i dhe j.
Prefiksi (parashtesa, pjesa e fillimit) i P sht nn-stringu i tipit P[0..i].
Sufiksi (prapashtesa, pjesa e fundit) i P sht nn-stringu i tipit P[i..m-1].

Pr stingun e dhn T (Text-teksti) dhe mostrn e dhn P (Pattern), problemi i


prshtatjes/prputhjes s mostrs (angl. Pattern Matching) konsiston n gjetjen
e nnstringut t T q sht i barabart me P.
Aplikacionet n t cilat zbatohet ky problem jan: editort e teksteve, makinat
krkuese, krkimi bilogjik (i ADN), etj.

Algoritmi naiv i prshtatjes


Algoritmi brute-force ose algoritmi naiv i prshtatjes, krahason mostrn P me
tekstin T, pr seciln zhvendosje (secilin shiftim) t P relativ ndaj T, deri sa t
gjindet prshtatja ose deri sa t jen provuar t gjitha pozitat dhe rrjedh se
teksti T nuk e prmban mostrn P.
Algoritmi naiv ekzekutohet n kohn O(nm), n sht gjatsia e tekstit T, ndrsa
m sht gjatsia e mostrs P.
Rasti m i keq do t paraqitej n rast se T=aaa...ah, kurse P=aaah. Kjo mund t
ndodh n rastet e imazheve ose sekuencave t ADN-ve, por jo n tekstet e
zakonshme.
Pseudokodi:
BruteForceMatch(T, P)
Input (Hyrja)
Output (Dalja)

for

teksti T me gjatsi n dhe


mostra P me gjatsi m
indeksi fillestar i nn-stringut
t T t barabart me P, ose -1
nse nuk gjindet nn-stringu

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

break while loop {mosprputhje}


-1 {ska asnj prshtatje/prputhje}

Algoritmi Knuth-Morris-Pratt (KMP)


Algoritmi Knuth-Morris-Pratt e krahason mostrn P me tekstin T nga e majta n
t djatht (njsoj si algoritmi naiv), mirpo e zhvendos (shifton) mostrn n
mnyr m intelegjente, duke anashkaluar krahasimet e panevojshme, kur t jet
e qart se nuk do t ket prputhje n tentimin e ardhshm, nse zhvendosja
bhet vazhdimisht vetm pr nga nj pozit, si n rastin e algoritmit naiv.
Pr t gjetur madhsin maksimale t zhvendosjes s mundshme, n rast t
mosprputhjes n ndonj krahasim, algoritmi KMP e bn prpunimin paraprak
(preprocesimin) e mostrs, pr t nxjerr informatat e nevojshme.
Kur t ndodh mosprputhja, sa sht zhvendosja maksimale e mundshme e
mostrs, ashtu q t evitohen krahasimet e teprta (redundante)?
Prgjigja sht: sa prefiksi m i gjat i mostrs P q sht njherit edhe sufiks i
mostrs P, pra prefiksi P[0..j] q sht njkohsisht edhe sufiks i P[1..j].

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

Algoritmet dhe strukturat e t dhnave


Algoritmi Knuth-Morris-Pratt paraproceson mostrn pr t gjetur prputhjet e
prefikseve t mostrs me vet mostrn. Funksioni Failure (angl. Failuredshtim) F(j) definohet si gjatsia e prefiksit m t gjat t P[0..j] q sht
poashtu edhe sufiks i P[1..j].
Algoritmi Knuth-Morris-Pratt modifikon algoritmin naiv (brute force) ashtu q
nse ndodh mosprputhje n P[j] T[i], caktojm jF(j-1).

Funksioni i dshtimeve F(j) mund t reprezentohet prmes nj vargu dhe mund


t llogaritet n koh t rendit O(m).
N seciln prsritje t unazs while ose:
-

i rritet pr nj, ose


madhsia e zhvendosjes i-j rritet pr s paku nj (vreni q F(j-1)<j )

Rrjedhimitsh, nuk ka m shum se 2n prsritje t unazs while.


Prandaj, algoritmi KPM ekzekutohet n koh optimale O(m+n).
Algoritmi KMP:
KMPMatch(T, P)
F FunksioniDeshtimit(P)
i 0
j 0
while i < n
if T[i] = P[j]
if j = m - 1
return i - j { prputhje/match }

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}

Llogaritja e funksionit t dshtimeve


Funksioni i dshtimeve F(j) mund t reprezentohet prmes nj vargu dhe mund
t llogaritet n koh t rendit O(m).
Algoritmi i tij sht i ngajshm me vet KMP algoritmin.
N seciln prsritje (secilin iteracion) t unazs while, ose:
-

i rritet pr nj, ose


madhsia e zhvendosjes i-j rritet pr s paku nj (vreni q F(j-1)<j )

Rrjedhimisht, nuk ka m shum se 2m iteracione t unazs while.


Algoritmi i funksionit t dshtimit:
FunksioniDeshtimit (P)
F[0] 0
i 1
j 0
while i < m
if P[i] = P[j]
{jan prputhur j + 1 karaktere}
F[i] j + 1
i i + 1
j j + 1
else if j > 0 then
{prdore funksionin e dshtimit pr t shiftuar P}
j F[j - 1]
else
F[i] 0 { ska prputhje/no match }
i i + 1

552

Algoritmet dhe strukturat e t dhnave


Shembull i KMP:

Nga tabela e llogaritjes s funksionit t dshtimeve F(j), vreni se prefiksi m i


gjat q sht sht njherit sufiks, sht me gjatsi 2 (ab), pr karakteret a dhe b
n lokacionet 4 dhe 5. Kjo, pasi q n fillim, deri n lokacionin 2, kemi
P[2]=aba, prandaj prefiksi/sufiksi sht vetm a, me gjatsi 1, dmth F(2)=1.
N lokacionin 3 kemi: P[3]=abac, keshtu q kemi F(3)=0, pasi ska prefiks q
prputhet me sufiks. N lokacionin 4, P[4]=abaca, prseri kemi vetm nj
prputhje prefiks/sufiks, pr a, me gjatsi 1. N fund, pr gjatsin deri n
lokacionin 5, kemi P[5]=abacab dhe prefiks/sufiks ab, me gjatsi 2,
d.m.th.,F(5)=2.
Nga krahasimi vrehet, se n fillim kemi prputhje t 5 karaktereve t para,
mirpo n krahasimin e 6 (T=a, P=b) nuk ka prputhje, kshtu q bjm
zhvendosjen deri n pozitn e prefiksit/sufiksit q veq prputhet: a (F(4)=1).
Vrehet, se n kt rast, n krahasim me algoritmin naiv prfitohet duke mos
br zhvendosjen e mostrs pr vetm nj pozit, por duke prfituar me
zhvendosjen e menjhershme deri n pozitn ku kemi prefiks q sht njherit
edhe sufiks i pjess ku ka pasur prputhje n krahasimin paraprak.
Vazhdojm krahasimin e 7, (T=a, P=b), por pasi q menjher kemi
mosprputhje (F(1)=0), zhvendosemi pr vetm nj pozit m tutje.
N krahasimet 8, 9, 10,dhe 11 kemi prputhje (prshtatje-match), por n
krahasimin 12 (T=c, P=a) kemi mosprputhje dhe prsri zhvendosemi deri n
pozitn ku kemi prefiks/sufiks t njjt.

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:
-

sht shum i shpejt


algoritmi kurr nuk ka nevoj t kthehet prapa n tekstin q analizohet, T.
kjo e bn algoritmin t prshtatshm pr procesimin e fajllave t mdhenj
t cilt lexohen nga pajisjet e jashtme ose prmes rrjetit.

Algoritmi KMP nuk punon aq mir me rritjen e madhsis s alfabetit. Me


rritjen e alfabetit ka:
-

m shum gjasa pr mosprputhje


mosprputhjet kan tendenc t paraqitjes hert n mostr, por KMP sht
i shpejt kur mosprputhjet paraqiten m von

Algoritmi Boyer-Moore
Algoritmi Boyer-Moore sht i bazuar n dy llogaritje heuristike:
-

Looking-glass heuristic (heuristika e dritares krkuese) me parimin


krahasimi nga ana e djatht n t majt (angl. right-to-left matching):
Krahaso P me nnsekuencn e T, duke lvizura prej fundit kah fillimi, dhe
Character-jump heuristic (heuristika e krcimit t karaktereve) me
rregulln e shiftimit t karaktereve t kqija (angl. bad character shift
rule): kur paraqitet mosprputhje n pozitn T[i]=c
Nse P prman c, shifto P pr tu barazuar (drejtuar) me paraqitjen e
fundit t c n P me T[i];
Prndryshe, shifto P pr tu barazuar P[0] me T[i+1].

Shembull:
N tekstin T=a pattern matching algorithm (algoritmi i prputhjes s mostrs),
krkohet mostra (pattern-i) P=rithm (ritmi).

554

Algoritmet dhe strukturat e t dhnave

ose, n form m kompakte:

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:
-

indeksi m i madh i, i till q P[i]=c (lokacioni i n mostr, ku paraqitet


karakteri i analizuar c), ose
-1, nse nuk ekziston indeks i till (d.m.th., karakteri c nuk ndodhet fare
n mostr).

Pr shembull, nse kemi alfabetin = {a, b, c, d} dhe mostrn (Pattern)


P=abacab:

Funksioni i rastisjes s fundit, mund t reprezentohet prmes nj vargu t


indeksuar me kodet numerike t karaktereve
Funksioni i rastitjses s fundit mund t llogaritet n koh O(m+s), ku m
sht gjatsia (madhsia) e mostres P, ndrsa s sht madhsia e alfabetit .

Nga tabela shihet se karakteri a ekziston n pozitat 0, 2 dhe 4 t mostrs


abcacab (indeksimi i pozitave/lokacioneve fillon prej 0), kshtu q rastisja e
fundit (e skajshme, m e djatht) sht n lokacionin 4. Pr shkronjn d e cila
nuk ekziston fare n mostr, merret -1.
Pseudokodi i algoritmit BoyerMoore:
BoyerMooreMatch(T, P, )
LLOF(P, ) //LOF ose L(c),funksioni i rastisjes s fundit
i m - 1
j m - 1
repeat
if T[i] = P[j]
if j = 0
return i { match at i (prputhje n i) }
else

556

Algoritmet dhe strukturat e t dhnave


i i - 1
j j - 1
else
{ character-jump (krcimi i karaktereve) }
l L[T[i]]
i i + m min(j, 1 + l)
j m - 1
until i > n - 1
return -1 { no match (ska prputhje}

Mund t paraqiten dy raste kur karakteri i analizuar nuk prputhet:


Rasti 1 Nse karakteri q nuk prputhet ndodhet n mostr, mirpo n pjesn
q veq ka kaluar krahasimin e suksesshm, dhe
Rasti 2 Karakteri q nuk prputhet ndodhet n mostr, n pjesn e majt,
akoma t pakrahasuar, me rast zhvendosja bhet deri sa t vijn n vij t drejt
karakteret e njjta.

557

Avni Rexhepi
Shembull:

Krahasimi 1: T[5]=a, P[5]=b, nuk prputhen, kurse a e tekstit ndodhet n


mostr, prandaj zhvendosim mostrn pr t vn n vij t drejt shkronjat a t
T dhe P.
Krahasimi 2 dhe 3 jan t suksesshm (ka prputhje), mirpo n krahasimin 4,
prsri kemi mosprputhje (a me c). Prsri a ndodhet n pjesn e majt t
mostrs, prandaj zhvendosemi deri sa t vihen n vij t drejt a e T dhe a e
mostrs P.
Krahasimi 5: Mosprputhje e a me b, zhvendosemi nj pozit m tutje (pr t
vn n vij t drejt karakteret a t T dhe P).
Krahasimi 6: Mosprputhje e d (nga teksti T) me b (nga mostra P) dhe pasi
q karakteri d nuk ndodhet n mostr, bjm zhvendosjen e plot t mostrs.
Krahasimi 7: Mosprputhje n karakteret a nga teksti T me b nga mostra P.
Zhvendosim mostrn, pr t vn n vij t drejt karakteret a.
Krahasimet 8 deri n 13 jan t suksesshme (ka prputhje) dhe rezulton se sht
gjetur prputhje e mostrs me tekstin.
Analiza:
Algoritmi Boyer-Moore ka kohn e ekzekutimit t rendit O(nm+A), ku A- sht
alfabeti.
Algoritmi Boyer-Moore sht i shpejt kur alfabeti (A) sht i madh, por i
ngadalshm nse alfabeti sht i vogl, kshtu q del se sht i shpejt pr tekste
t zakonshme por i ngadalshm pr rastin e alfabetit binar ose ADN-s.
558

Algoritmet dhe strukturat e t dhnave


Algoritmi Boyer-Moore sht dukshm m i shpejt sesa algoritmi naiv dhe pr
tekste natyrale sht algoritmi m i shpejt i krkimit t stringjeve.
Rasti m i keq do t ndodh nse kemi:
-

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

Hash tabela sht struktur e t dhnave q prdoret pr t implementuar vargjet


asociative, nj struktur q mund t pasqyroj elsat n vlera. Hash tabela e
prdor hash funksionin pr t llogaritur indeksin n vargun e sloteve (vendeve),
prej t cilave mund t gjindet vlera korrekte.
M mnyr ideale, hash funksioni do t caktoj secilin els n nj vend unik,
por kjo situat sht rrall her e arritshme n praktik (prveq nse hash elsat
jan fiks, d.m.th., nuk shtohen vlera t reja pas krijimit t tabels). N realitet,
shumica e dizajneve t hash tabelave supozojn se do t ndodhin kolizionet
(angl. collision-ndeshje, prplasje, konflikt), rastet kur elsat e ndryshm t
cilt caktohen prej hash funksionit u ndahen sloteve t njjta dhe disi duhet t
akomodohen.
N hash tabeln e dimensionuar mir, kostoja mesatare (numri i instruksioneve)
pr seclin krkim sht i pavarur prej numrit t elementeve t ruajtura n tabel.
Shum dizajne t hash tabelave poashtu lejojn insertimet dhe fshirjet arbitrare
t ifteve els-vler, m kosto mesatare pr operacion.
N shum raste, hash tabela del t jet m efikase sesa pema binare e krkimit
ose ndonj struktur tjetr e krkimit t tabelave. Pr kt arsye, hash tableat
prdoren n shum softvere kompjuterike, posaqrisht n vargjet asociative,
indeksim t bazave t t dhnave, cash dhe sete.

Fig. 9.1 Hash tabela dhe hash funksioni

Hash tabela sht struktur q mundson vetm nj pjes t operacioneve t


pems binare t krkimit dhe kryen operacionet e insertimit, fshirjes dhe
560

Algoritmet dhe strukturat e t dhnave


krkimit n koh mesatare konstante. Pr dallim prej pems binare t
krkimit, koha e rastit mesatar t hash tabelave sht e bazuar n tiparet
statistikore m shum sesa n pritjen e hyrjeve n dukje t rastit. Ky
prmirsim prfitohet n kurriz t humbjes s renditjes s informacioneve
prbrenda elementeve. Operacionet si gjetja e minimumit ose maksimumit
dhe shtypja e tr tabels n renditje t sortuar n koh lineare, nuk
prkrahen. Rrjedhimisht, hash tabelat kan disa karakteristika t ndryshme t
performanss ndaj pems binare t krkimit.
Hash tabela (angl. Hash table, hash map) sht nj prej implementimeve t
mundshme t fjalorit (dictionary ADT). N parim, Hash tabela i mapon
(pasqyron) elsat unik me vlerat e shoqruara (angl. map-plan, skem, hart;
mapping-vendosje n hart, planifikim; ka t bj me pasqyrimin sipas
rregullave t bashksive, lidhjen e elementeve t nj bashksie me tjetrn, etj).
N aspektin e implementimit (zbatimit), hash tabela sht nj struktur e t
dhnave e bazuar n varg, e cila e prdor hash funksionin, pr t konvertuar
elsin n indeks t elementit t vargut, ku duhet t krkohet vlera e
shoqruar.
Hash tabela prkrah nxjerrjen ose fshirjen e fardo elementi t emrtuar.
Interesi sht q t jemi n gjendje q t kryejm operacionet themelore n
koh konstante, si pr rastin e stekut dhe rreshtit (queue-s). pasi q qasja sht
shum m pak e kufizuar, kjo prkrahje duket e paarritshme. Kjo sht
sigurisht kur bashksia e vlerave rritet dhe krkimi n bashksi do t krkoj
m shum koh. Mirpo, kjo nuk sht domosdoshmrisht ajo ka ndodh.
Hash tabela prdoret pr t implementuar bashksin (set-in) n koh
konstante pr operacion.
Le t supozojm se kemi t bjm me disa vlera t vogla t numrave t plot
jonegativ, n rangun prej 0 deri n 65535. Nj opcion sht q t prdorim
vargu e thjesht pr t implementuar secilin operacion si n vijim. S pari
inicializojm vargun a me indekset prej 0 deri n 655635, me t gjitha vlerat
zero (0). Pr t performuar insert(i), ekzekutojm a[i]++. Vreni se a[i]
reprezenton numrin e herave t insertimit t i-s. Pr t performuar find(i)
(gjeje(i)), verifikojm q a[i] nuk sht zero. Pr t performuar remove(i)
(largo(i)), sigurohemi q a[i] sht pozitiv dhe pastaj ekzekutojm a[i]--.
Koha pr secilin operacion sht qartsisht konstante. Edhe vet mbingarkesa
e inicializimit t vargut sht pun konstante (pr 65535 prcaktime t
vlerave).
Mirpo, me kt zgjidhje kemi dy probleme. S pari, supozojm se kemi vlera
t mdha t integjerve 32-bitsh, n vend t atyre 16-bitsh. N kt rast,
vargu do t duhet t prmbaj 4 miliard antar, gj q sht jopraktike. S
561

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.

Prdorimi i hash funksioneve paraqet nj komplikim: dy ose m shum


elemente t ndryshme mund t hash-ohen n pozit t njjt, duke shkaktuar
ndeshje (angl. colision ndeshje, konflikt, prplasje). Kjo situat nuk mund t
evitohet asnjher sepse ka shum m tepr elemente sesa pozita n
dispozicion. Mirpo, ka shum metoda t cilat jan n dispozicion pr
zgjidhje t shpejt t kolizioneve.
Pasi q hash funksioni nuk sht pasqyrim nj n nj, disa elemente mund t
ndeshen n indeksin e njjt, duke shkaktuar kolizione.

562

Algoritmet dhe strukturat e t dhnave

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.

Hash tabela dhe faktori i ngarkess


Struktur themelore e t dhnave q prdoret pr ruajtjen e hash tabels sht
vargu. Faktori i ngarkess sht hersi (raporti) ndrmjet numrit t
elementeve t ruajtura dhe madhsis s vargut. Hash tabela mund t jet ose
me madhsi konstante ose n proces dinamik t ndryshueshimit t madhsis,
kur faktori i ngarkess t tejkaloj nj prag t caktuar. Ndryshimi i madhsis
bhet para se tabela t mbushet plotsisht pr t mbajtur numrin e kolizioneve
nn nj vler t caktuar dhe pr t parandaluar degradimin e performanss.

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:

Closed addressing (open hashing) Adresimi i mbyllur (hashingu i


hapur). Secili slot (angl. slot-ndarje, vend i caktuar, etj.) i hash tabels
prmban lidhjen pr n nj struktur tjetr t t dhnave (p.sh. lista e
lidhur), e cila i ruan iftet els-vler me hash t njjt. Kur ndodh
kolizioni, kjo struktur e t dhnave krkohet pr iftin els-vler, q i
prgjigjet elsit.
Open addressing (closed hashing) Adresimi i hapur (hashingu i
mbyllur). Secili slot n fakt prmban iftin els-vler. Kur t ndodh
kolizioni, algoritmi i adresimit t hapur e llogarit nj lokacion tjetr
(d.m.th. tjetri/i ardhshmi) pr t lokalizuar nj slot t lir. Hash tabelat e
bazuar n strategji t adresimit psojn rnie drastike t performanss, kur
tabela sht e mbushur fort (faktori i ngarkess sht 0.7 e m shum).

563

Avni Rexhepi

Shembull i thjesht i hash tabels


Do t paraqesim n shembull t hash tabels s thjesht, e cila prdor nj hash
funksion t thjesht, ku kolizionet zgjidhen duke prdorur kontrollimin linear
(strategjia e adresimit t hapur) dhe hash tabela ka madhsi konstante.
Shembulli paraqet qartazi bazat e tekniks s hashingut.

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).

Strategjia e zgjidhjes s kolizioneve


Pr zgjidhjen (zbrthimin) e kolizioneve do t prdoret kontrollimi linear. Nse
sloti i treguar nga hash funksioni veq sht i zn, algoritmi provon t gjej nj
t zbrazt duke kontrlluar slotet n vazhdim t vargut.
(Vrejtje. Kontrollimi linear nuk sht teknika m e mir n rastin e tabels me
madhsi konstante. Kur faktori i ngarkess tejkalon nj vler t caktuar
(afrsisht 0.7), performansa e hash tabels do t zvoglohet jolinearisht.
Gjithashtu numri i ifteve t ruajtura els-vler sht i kufizuar me madhsin e
tabels (128)).

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

Algoritmet dhe strukturat e t dhnave


int key;
int value;
public:
HashEntry(int key, int value) {
this->key = key;
this->value = value;
}
int getKey() {
return key;
}
int getValue() {
return value;
}
};
const int TABLE_SIZE = 128;
class HashMap {
private:
HashEntry **table;
public:
HashMap() {
table = new HashEntry*[TABLE_SIZE];
for (int i = 0; i < TABLE_SIZE; i++)
table[i] = NULL;
}
int get(int key) {
int hash = (key % TABLE_SIZE);
while (table[hash] != NULL && table[hash]->getKey()
!= key)
hash = (hash + 1) % TABLE_SIZE;
if (table[hash] == NULL)
return -1;
else
return table[hash]->getValue();
}
void put(int key, int value) {
int hash = (key % TABLE_SIZE);
while (table[hash] != NULL && table[hash]->getKey()
!= key)
hash = (hash + 1) % TABLE_SIZE;

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;
}
};

Hash tabela. Zgjidhja e kolizionit prmes vargzimit (adresimi i


mbyllur)
Vargzimi (angl. chaining - si shtimi i hallkave t zingjirit) sht nj mnyr e
mundshme e zgjidhjes/zbrthimit t kolizioneve. Secili slot i vargut prmban
nj lidhje/link pr n listn e lidhur njfish (angl. singly-linked list) e cila
prmban iftet els-vler me hash t njjt. iftet e reja els-vler shtohen n
fund t lists. Algoritmi i krkimit, krkon npr list pr t gjetur elsin q
prshtatet. N fillim slotet e tabels jan t zbrazta (prmbajn null). Lista
krijohet kur vlera me nj hash t caktuar shtohet pr her t par.

Analiza e kompleksitetit
566

Algoritmet dhe strukturat e t dhnave


Duke supozuar se hash funksioni i shprndan hash kodet n mnyr uniforme
dhe tabela mundson ndryshim dinamik t madhsis, kompleksiteti i
amortizuar i operacioneve t insertimit, fshirjes dh krkimit sht konstant.
Koha aktuale e marrur nga kto operacione varet linearisht nga faktori i
ngarkess s tabels.
Vrejtje. Edhe hash tabela dukshm e ngarkuar, e bazuar n vargzim, shfaq
performans t mir. Supozoni nj hash tabel me 1000 slote q ruan 100000
elemente (faktori i ngarkess sht 100). Kjo krkon pak m shum memorie
(madhsi tabele) sesa lista e lidhur njfish, por t gjitha operacionet themelore
do t kryehen afro 1000 her m shpejt, n mesatare. Keni parasysh q
komleksiteti llogarits i t dyjave, lists s lidhur njfish dhe hash tabels me
madhsi konstante sht O(n).
Pjes kodi
Kodi i mposhtm implementon vargzimin me krer t lists. Kjo do t thot,
se hyrjet e hash tabels prmbajn elementin e par t lists s lidhur njfish, n
vend se t ruajn pointerin pr t.
class LinkedHashEntry {
private:
int key;
int value;
LinkedHashEntry *next;
public:
LinkedHashEntry(int key, int value)
{
this->key = key;
this->value = value;
this->next = NULL;
}
int getKey()
{
return key;
}
int getValue()
{
return value;
}
void setValue(int value)
{
this->value = value;

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

Algoritmet dhe strukturat e t dhnave


if (table[hash] == NULL)
table[hash] = new LinkedHashEntry(key, value);
else
{
LinkedHashEntry *entry = table[hash];
while (entry->getNext() != NULL)
entry = entry->getNext();
if (entry->getKey() == key)
entry->setValue(value);
else
entry->setNext(new LinkedHashEntry(key, value));
}
}
void remove(int key)
{
int hash = (key % TABLE_SIZE);
if (table[hash] != NULL) {
LinkedHashEntry *prevEntry = NULL;
LinkedHashEntry *entry = table[hash];
while (entry->getNext() != NULL && entry->getKey() != key)
{
prevEntry = entry;
entry = entry->getNext();
}
if (entry->getKey() == key)
{
if (prevEntry == NULL)
{
LinkedHashEntry *nextEntry = entry->getNext();
delete entry;
table[hash] = nextEntry;
}
else
{
LinkedHashEntry *next = entry->getNext();
delete entry;
prevEntry->setNext(next);
}
}
}
}
~HashMap()
{
for (int i = 0; i < TABLE_SIZE; i++)
if (table[i] != NULL)
{
LinkedHashEntry *prevEntry = NULL;

569

Avni Rexhepi
LinkedHashEntry *entry = table[i];
while (entry != NULL)
{
prevEntry = entry;
entry = entry->getNext();
delete prevEntry;
}
}
delete[] table;
}
};

Hash tabela. Strategjia e adresimit t hapur


Vargzimi sht mnyr e mir pr zgjidhje t kolizioneve, mirpo ka
shpenzime shtes t memories pr t ruajtur strukturn e listave t lidhura. Nse
hyrjet jan t vogla (p.sh. integer) ose nuk ka vlera fare (p.sh. seti si ADT),
ather shprdorimi i memories (memoria e zn kot) sht e krahasueshme me
vet madhsin e t dhnave. Kur hash tabela sht e bazuar n strategjin e
adresimit t hapur, t gjitha iftet els-vler jan t ruajtura n vet hash
tabeln dhe nuk ka nevoj pr struktur t jashtme t t dhnave.

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 (linear probing): distanca ndrmjet pikave t


kontrollimit sht konstante (d.m.th., 1 kur kontrollohen slotet
konsekuente (t vazhdueshme, t njpasnjshme);
Kontrollimi kuadratik (quadratic probing): distanca ndrmjet pikave
kontrollueserritet sipas nj konstante t caktuar n secilin hap (n kt
rast distanca deri tek sloti i par varet mnyr kuadratike nga numri i
hapit);
Hashingu i dyfisht (double hashing): distanca ndrmjet pikave t
kontrollimit llogaritet duke prdorur nj hash funksion tjetr.

Strategjia e adresimit t hapur krkon q hash funksioni t ket karakteristika


shtes. Prveq performimit t shprndarjes uniforme, ai duhet t evitoj
grumbullimin (angl. clustering) e hash vlerave, t cila jan konsekuente n
renditjen e kontrollimit.
570

Algoritmet dhe strukturat e t dhnave

Kontrollimi linear
Po t kemi nj hash funksionin si vijon:
unsigned int hash (const string &key, int madhesiaTabeles)
{
...
return hashVlera % madhesiaTabeles)
}

i cili e kthen vlern n baz t modulit m madhsin e tabels, ather pr


vargun e vlerave vijuese: 89, 18, 49, 58, dhe 9, n tabeln me kontrollim linear t
pozitave t insertimit, do t kishim:

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

Pasi hash funksioni kthen: elsiX modul madhesiaTabeles, kolizioni i


par do t ndodh kur t insertohet 49. N kt rast, 49 do t insertohet n
pozitn e ardhshme n dispozicion, q sht sloti/pozita 0, e cila sht e lir.
Pastaj 58 ndeshet m 18, 89, dhe 49, para se t gjej vendin e lir n pozitn 1.
Ngjashm zgjidhet edhe kolizioni pr 9. Prderisa tabela sht mjaft e madhe,
571

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.

Mesatarisht do t pritej q t kemi nevoj t krkojm npr gjysmn e tabels


pr ta gjetur at, gj q sht shum larg (shum m tepr) sesa koha konstante
pr qasje, pr t ciln do t shpresonim. Por, nse tabela mbahet relativisht e
zbrazt, insertimet nuk do t jen shum t kushtueshme.
Algoritmi i krkimit thjesht do t ndjek t njejtn rrug sikurse algoritmi i
insertimit. Nse arrin n slot t zbrazt, elementi q krkohej nuk sht gjetur,
prndryshe, n fund do t gjindet elementi i krkuar. Pr shembull, pr t gjetur
vlern 58, fillojm nga sloti 8 (si tregohet nga hash funksioni). Aty gjejm nj
vler, por nuk sht ajo e krkuara, kshtu q vazhdojm n slotin 9. Prsri,
kemi element, por jo at t duhurin, kshtu q vazhdojm tutje dhe provojm
slotin 0 (lvizja me rrotullim, qarkore), pastaj slotin 1 deri sa t arrijm
prshtatje (vlera e krkuar t gjindet). Krkimi pr vlern 19 do t prfshinte
tentimet n slotet: 9, 0, 1 dhe 2, para se t arrinte n vendin (slot-in) e zbrazt 3.
Prandaj, do t thot q vlera e krkuar (19) nuk sht gjetur.
Sa i prket fshirjes, nuk mund t performohet fshirja e zakonshme, sikur n
rastin e pems binare t krkimit, sepse nj element n hash tabel, nuk e
reprezenton vetm vetveten, por ai poashtu i lidh edhe elementet tjera, duke
shrbyer si placeholder (angl. place-vend, holder-mbajts, pra mbajts i
pozits, vendit) gjat zgjidhjes s kolizioneve. Prandaj, nse do t kishim larguar
vlern 89 nga hash tabela, virtualisht t gjitha operacionet vijuese t krkimit do
t dshtonin. Rrjedhimisht, implementohet lazy deletion (fshirja prtace, e
vonuar) ose elementet vetm shnohen si t fshira n vend se t largohen
fizikisht nga tabela. Kjo informat regjistrohet n nj element shtes (extra) t
tabels. Secili element ose sht aktiv ose i fshir (angl. deleted).
Nse t dhnat n hash tabel jan stringje ose kombinime (jo vetem numra t
plot), do t kemi rastin si n vijim:

572

Algoritmet dhe strukturat e t dhnave

Nse pr zgjidhjen e kolizioneve prdoret metoda e kontrollimit kuadratik, do t


eliminohet problemi kryesor i grupimeve, i kontrollimit linear, ku n pozita t
njpasnjshme vendosen elementet q kan kolizion. Kontrollimi kuadratik
analizon qelulat (vendet, slotet) larg prej piks fillestare t kontrollimit. Emri i
tij rrjedh nga prdorimi i formuls F(i)=i2, e cila prdoret pr zgjidhje t
kolizioneve. N mnyr specifike, nse hash funksioni rezulton n H dhe qelula
H del se sht joprfundimtare (vendi sht i zn), nuk provohet qelula vijuese
menjher n vazhdim, por provohen qelulat H+12, H+22, H+32...H+i2 (duke
prdorur rrotullimin, kalimin prej fundit n fillim). Kjo strategji dallon nga
strategjia e krkimit linear: H+1, H+2, ...H+i.
Kontrollimi linear i analizon pozitat sekuenciale 1, 2, 3...;
Kontrollimi kuadratik analizon pozitat: 1, 4, 9, .... larg nga pozita fillestare e krkimit.

Figura vijuese paraqet tabeln n rastin e prdorimit t kontrollimit kuadratik,


pr insertimin e sekuencs s njjt q e prdorm m par: 89, 18, 49, 58, dhe 9.

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

Kur ndodh kolizioni i par, gjat insertimit t 49 (kolizion me 89), altertantiva


e par sht nj qelul m tutje (0). Pasi sht e lir, 49 vendoset aty. Pastaj, 58
ndeshet me 18 (n pozitn 8). Pozita 9 (nj vend m tutje) sht e zn, kshtu
q qelula e zbrazt gjindet n tentimin e ardhshm, q sht 22=4 pozita m
tutje prej hash pozits fillstare. Kshtu, 58 vendoset n qeluln (vendin, slotin)
2. Ngjashm vazhdohet pr vlerat tjera n vijim. Vreni q lokacionet alternative
pr elementet t cilat hash-ohen n pozitn 8 dhe lokacionet alternative pr
elementet q hash-ohen n pozitn 9, nuk jan t njjta. Sekuenca e provave
(kontrollimeve) pr insertim t 58 nuk ndikon n insertimin pasues t 9, gj q
sht ndryshe nga ajo q ndodhte tek kontrollimi linear.
N kt rast, madhesia e tabels nuk sht e prshtatshme, sepse zakonisht
zgjedhet vler q sht numr primar, pr t pasur implementim m efikas.
Duhet sqaruar disa detaje. N kontrollimin linear, secila prov tenton qelula t
ndryshme. A garanton kontrollimi kuadratik kt gj, q kur tentohet nj qelul,
at nuk e kemi provuar gjat procesit aktual t insertimit. A garanton kontrollimi
kuadratik q kur sht duke u insertuar X dhe tabela nuk sht e mbushur,
ather X do t insertohet?
Kontrollimi linear implementohet leht. Kontrollimi kuadratik duket t krkoj
operacionet e shumzimit dhe modulit. Ky kompleksitet i shtuar, a e bn
kontrollimin kuadratik jopraktik pr tu implementuar? ka ndodh (n t dy
574

Algoritmet dhe strukturat e t dhnave


rastet e kontrollimit) kur faktori i ngarkess bhet shum i madh? A mund t
zgjerohet tabela n mnyr dinamike, si veprohet zakonisht me strukturat e
bazuara n vargje?
Pr fat, prgjigjet jan relativisht t mira n t gjitha rastet. Nse madhsia e
tabels sht numr primar dhe faktori i ngarkess nuk e tejkalon vlern 0.5,
gjithnj do t mund t insertohet elementi i ri X dhe asnj qelul nuk do t
provohet dy her gjat qasjes. Sidoqoft, pr t vlejtur kto garancione, duhet t
sigurohet q madhsia e tabels sht numr primar.
Nse madhsia e tabels sht numr primar dhe faktori i ngarkess nuk sht m i
madh se 0.5, t gjitha provat do t jen n lokacione t ndryshme dhe gjithmon do t
ket mundsi pr t insertuar elementet.

Operacioni i largimit
Ka disa nuanca, kur bhet largimi i elsit nga hash tabela bhet me adresim t
hapur. Merrni n konsiderim rastin vijues:

Nse algoritmi thjesht e liron pozitn Sandra Miller, struktura e tabels do t


prishet. Algoritmi nuk do t ket sukses n tentimin pr t gjetur elsin
Andrew Wilson. N fakt, elsi Andrew Wilson sht i hashuar n slotin e
kuq. Sloti prmban els tjetr dhe algoritmi i kontrollimit linear do t tentoj
t gjej Andrew Wilson n pozitn e ardhshme (n vazhdim, konsekuente),
por ajo sht e zbrazt:

575

Avni Rexhepi

Zgjidhja sht si vijon. N vend se vetm t fshij elsin, algoritmi e shkruan


vlern speciale DELETED (angl. e fshir) n at slot.

Tani algoritmi i krkimit do t punoj si duhet. Algoritmi i insertimit do t duhej


t prdor slotet e fshira, kur t jet e mundur.
Vrejtje. Ky algoritm e zgjidhe problemin, por me koh hash tabela do t bhet
e mbushur me hyrjet "DELETED", gj q ndikon keq n performans. Nse
hash tabela duhet t lejoj largimin (fshirjen) e elementeve, athre vargzimi
sht mnyr m e preferuar pr zgjidhjen e kolizioneve.
576

Algoritmet dhe strukturat e t dhnave

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 kundrejt vargzimit


Vargzimi (Chaining)

Adresimi i hapur
Open addressing

Zgjidhja e kolizioneve Duke prdorur struktur t Duke prdorur vet hash


jashtme t t dhnave
tabeln
Shpenzimi i kot i
memories

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

Jo. Pr m tepr, rekomandohet


mbajtja e faktorit t ngarkess
nn 0.7

Krkesat pr Hash
funksionin

Shprndarje Uniforme

Shprndarje Uniforme, duhet


evituar grumbullimin (clustering)

Trajtimi i fshirjeve

Fshirjet jan OK

Fshirjet e mbushin hash tabeln


m vlerat "DELETED"

Implementimi

I thjesht

Implementimi korrekt i hash


tabels s bazuar n adresim t
hapur sht mjaft i ndrlikuar

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

Algoritmet dhe strukturat e t dhnave


{
}
public:
static DeletedEntry *getUniqueDeletedEntry()
{
if (entry == NULL)
entry = new DeletedEntry();
return entry;
}
};
DeletedEntry *DeletedEntry::entry = NULL;
const int TABLE_SIZE = 128;
class HashMap
{
private:
HashEntry **table;
public:
HashMap()
{
table = new HashEntry*[TABLE_SIZE];
for (int i = 0; i < TABLE_SIZE; i++)
table[i] = NULL;
}
int get(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;
}
if (table[hash] == NULL || hash == initialHash)
return -1;
else
return table[hash]->getValue();

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

Algoritmet dhe strukturat e t dhnave


}
if (hash != initialHash && table[hash] != NULL)
{
delete table[hash];
table[hash] =
DeletedEntry::getUniqueDeletedEntry();
}
}
~HashMap()
{
for (int i = 0; i < TABLE_SIZE; i++)
if (table[i] != NULL && table[i]
!=
DeletedEntry::getUniqueDeletedEntry())
delete table[i];
delete[] table;
}
};

Hash tabela. Ndryshimi dinamik i madhsis


Me rritjen e faktorit t ngarkess s hash tabels, numri i kolizioneve rritet, gj
q ond n zvoglimin e performanss s prgjithshme t tablels. Kjo sht e
durueshme pr hash tabelat me vargzim, por e papranueshme pr tabelat e
bazuar n adresim t hapur, pr shkak t rnies esenciale t performanss.
Zgjidhje pr kt sht ndryshimi i madhsis s tabels, kur faktori i ngarkess
t tejkalon pragun e dhn.
Gjithashtu, kur tabela bhet shum e rrall, sht e arsyeshme q t paketohet
vargu pr t kursyer hapsirn.

Algoritmi i ndryshimit t madhsis


Mbani mend q vlerat e hash tabels varen nga madhsia e tabels. Prandaj,
hashet e hyrjeve ndryshojn kur t ndryshoj madhsia e tabels dhe algoritmi
nuk mundet vetm t kopjoj t dhnat nga vendi i vjetr i ruajtjes n t riun. Pr
hash funksionin e prgjithshm e vetmja gj q duhet br sht t kalohet npr
tr hash tabeln e vjetr dhe t shtohet (insertohet) secila vler n tabeln e re.

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.

Hash tabela ndaj pems binare t krkimit


Pr t implementuar insertimin dhe krkimin mund t prdoren edhe pemt
binare t krkimit. Megjithse koht mesatare rezultuese jan n kufijt O(log
N), pemt binare t krkimit gjithashtu prkrah funksionet t cilat krkojn
renditje dhe prandaj jan m t fuqishme.
Duke prdorur hash tabela, nuk mund t gjejm elementin minimal n mnyr
efikase ose t zgjedrojm tabeln pr t lejuar llogaritjen e statistiks s rendit.
Nuk mund t krkojm n mnyr efikase pr ndonj string, prve nse stringu
i sakt sht i njohur. Pema binare e krkimit do t mund t gjente shum shpejt
t gjitha elementet n nj rang (brez) t caktuar, por kjo aftsi nuk prkrahet nga
hash tabelat. Pr m tepr, kufiri O(log N) nuk sht domosdoshmrisht aq
shum n krahasim me O(1), posaqrisht pasi q nga pemt e krkimit nuk
krkohen shumzime ose pjestime.
Prdorni hash tabel n vend t pems binare t krkimit nse nuk ju duhen statistika
t renditjes dhe nuk brengoseni pr hyrjet jo t rastit.

Rasti m i keq pr hash-ing n prgjithsi rezulton prej nj gabimi t


implementimit, ndrsa hyrja e sortuar mund t bj q pema binare e krkimit t
performoj dobt. Pemt e balansuara jan implementim mjaft i kushtueshm.
S kndejmi, nse nuk krkohet informacion pr renditje dhe nuk ka dyshime se
hyrja mund t jet e sortuar, hash-i sht struktura e duhur e t dhnave.
Aplikimet e hash tabelave
Aplikacionet me hash jan t shumta. Kompajlert i prdorin hash tabelat pr t
prcjellur variablat e deklaruar n kodin burimor. Struktura e t dhnave n kt
rast quhet tabel e simboleve. Hash tabelat jan aplikacion ideal pr kt
problem sepse kryhen vetm operacinet e insertimit dhe krkimit. Identifikatort
zakonisht jan t shkurtr, kshtu q hash funksioni mund t llogaritet shpejt.
N kt aplikacion, shumica e krkimeve jan t suksesshme.
Nj prdorim i zakonshm i hash tabelave jan programet e lojrave. Gjersa
programi krkon npr rreshtat e ndryshm t lojs, ai prcjell pozitat q i ka
hasur duke llogaritur hash funksionin e bazuar n pozit (dhe duke ruajtur
lvizjen e tij pr at pozit). Nse ndodh prsri pozita e njjt, zakonisht
prmes nj ndrrim vendesh (angl. transposition) t lvizjeve, programi mund t
582

Algoritmet dhe strukturat e t dhnave


evitoj rillogaritjet e kushtueshme. Kjo karakteristik e prgjithshme e
programeve t lojrave quhet tabel e transpozicioneve.
Prdorim tjetr i zakonshm i hash tabelave sht n spell checker-t online
(angl. spell check verifikim i gabimeve t shtypit). Nse detektimi i gabimeve
sht i rndsishm (n krahasim me korrektimin), mund t prehash-ohet nj
fjalor i plot dhe fjalt mund t verifikohen n koh konstante. Hash tabelat jan
t prshtatshme pr kt qllim pasi q fjalt nuk duhet t jen t renditura
alfabetikisht. Shtypja e gabimeve n renditjen n t ciln kan ndodhur n
dokument sht e pranueshme.
Si prmbledhje, hash tabelat mund t prdoren pr t implementuar operacionet
e insertimit dhe krkimit n koh konstante. Kujdesi pr detajet si faktori i
ngarkess sht posaqrisht i rndsishm n prdorimin e hash tabelave,
prndryshe kufijt e kohs konstante nuk kan domethnie. Zgjidhja e
kujdesshme e hash funksionit poashtu sht e rndsishme kur elsi nuk sht
string i shkurtr ose integer. Duhet zgjedhur nj funksion q llogaritet leht dhe
q bn shprndarje t mir.
Pr hash me vargzim t veant, faktori i ngarkess n mnyr tipike sht afr
1-shit, edhe pse performansa nuk degradon dukshm prveq nse faktori i
ngarkess sht shum i lart. Pr kontrollimin kuadratik, madhsia e tables
duhet t jet numr primar dhe faktori i ngarkess nuk duhet t tejkaloj 0.5shin. Rihash-imi duhet t prdoret pr kontrollimin kuadratik pr t lejuar rritjen
e tabels dhe pr t mirmbajtur faktorin korrekt t ndarkess. Kjo qasje sht e
rndsishem nse hapsira sht e ngjeshur dhe nuk sht e mundshme thjesht
vetm t deklarohet hash tabela shum e madhe.

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

Algoritmet dhe strukturat e t dhnave

Sequence containers (kontejnert e sekuencave/vargjeve/fushave):


array
vector
deque
forward_list
list

Klasa Array (e vargut) (class template)


Vector/Vektor (class template)
Double ended queue (rresht me dy skaje) (class template)
Lista prpara (e lidhur vetwm nw njw kahje) (class template)
Lista (class template)

Container adaptors (adaptort e kontejnerve):


LIFO steku (class template)
stack
FIFO queue (class template)
queue
priority_queue Rreshti me prioritet (class template)

Associative containers (kontejnerwt asociativ):


set
multiset
map
multimap

set (class template)


set me elsa t shumwfisht/duplikat (class template)
map (class template)
map me elsa t shumwfisht/duplikat (class template)

Unordered associative
parenditur):
unordered_set
unordered_multiset
unordered_map
unordered_multimap

containers

(kontejnerwt

asociativ

tw

set i parednitur (class template)


multiset i parenditur (class template)
map i parennditur (class template)
multimap e parenditur (class template)

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

Vargjet jan kontejner t sekuencave me madhsi fikse. Ata mbajn nj numr


specifik t elementeve t renditur n sekuenc lineare strikte.
S brendi (angl. internally), vargu nuk mban t dhna t tjera prveq
elementeve t tij (as edhe madhsin e tij, e cila sht parametr i templejtit, i
fiksuar n kohn e kompajlimit). Sa i prket madhsis n memorie sht po aq
efikas sa edhe nj varg i zakonshm, i deklaruar me sintaksn e C++, prmes
kllapave t mesme ([ ]). Kjo klas vetm shton nj shtres t antarve dhe
funksioneve globale, ashtu q vargu t mund t prdoret si kontejnert standard.
Pr dallim prej kontejnerve t tjer standard, vargjet (arrays) kan madhsi
fikse dhe nuk e menagjojn alokimin e elementeve t tyre prmes alokatorit: ata
jan nj tip prmbledhs q enkapsulon elementet e vargut me madhsi fikse. S
kndejmi, ata nuk mund t rriten ose zvoglohen n mnyr dinamike (gjat
kohs s ekzekutimit), gj q sht e mundur me vargun e krijuar prmes klass
<vector>.
Vargjet me madhsi zero jan valid, por nuk duhet t dereferencohen (antart:
front, back, dhe data).
Pr dallim prej kontejnerve t tjer t STL-it (libraris standarde), shkmbimi i
dy vargjeve sht operacion linear q prfshin shkmbimin e t gjitha
elementeve n rangjet individuale, gj q n prgjithsi sht operacion
dukshm m jo-efikas. N ann tjetr, kjo lejon q iteratort n t dy kontejnert
t ruajn ndrlidhjen e tyre me kontejnerin origjinal.
Tiparet e kontejnerit <array>:
Sekuenca: elementet n kontejnerin e vargut (sekuencs) jan t renditur n
renditje lineare strikte. Elementet individuale qasen sipas pozits s tyre n varg.
Ruajtja n lokacione t njpasnjshme: elementet ruhen n lokacione t
njpasnjshme n memorie, gj q mundson koh konstante t qasjes me
586

Algoritmet dhe strukturat e t dhnave


rastsi t elementeve. Pointert n nj element mund t zhvendosen (angl.
offset) pr t ju qasur elementeve t tjera.
Madhsi fikse: Kontejneri prdor konstruktort dhe destruktort e brendshm
(implicit) pr t alokuar hapsirn e krkuar n mnyr statike. Madhsia e
vargut sht konstante e kohs s kompajlimit. Nuk ka mbingarkim memorik
ose kohor.

Parametrat e templejtit
template < class T, size_t N > class array;

T - Tipi i elementeve t vargut. Aliased (angl. alias nofk, pseudonim) si tipi


i antarit: array::value_type.
N - Madhsia e vargut, n terma t numrit t elementeve.
N referencat pr funksionet (member functions), t array, kta emra t njjt
supozohen pr paramterat te templejtit.
Tipet e antarve (Member types)
Aliaset vijuese jan tipe t antarve t vargut (array). Kto prdoren gjersisht
si tipe t parametrave dhe vlerave t kthyera (me return) nga ana e funksioneve
antare (member functions): value_type, reference, const_reference,
pointer, const_pointer, iterator, const_iterator,
reverse_iterator, const_reverse_iterator, size_type,
difference_type.

Funksionet (funksionet e klass, Member functions)


Iteratort
begin
end
rbegin
rend
cbegin
cend
crbegin
crend

Kthe (me return) iteratorin n fillimin e vargut (funksion publik)


Kthe iteratorin n fund t vargut (funksion publik)
Kthe iteratorin revers (vargu i rrotulluar anasjelltas, me renditje t
kundrt) n fillimin revers (funksion publik)
Kthe iteratorin revers pr fundin revers(funksion publik)
Kthe iteratorin konstant pr fillimin (funksion publik)
Kthe iteratorin konstant pr fundin (funksion publik)
Kthe iteratorin revers konstant pr fillimin revers (funksion publik)
Kthe iteratorin revers konstant pr fundin revers (funksion publik)

Kapaciteti
size

Kthe madhsin e vargut (numri i antarve) (funksion publik)


587

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

Qasju elementit (funksion publik)


Qasju elementit (funksion publik)
Qasju elementit t par (funksion publik)
Qasju elementit t fundit (funksion publik)
Merr pointerin n vler (funksion publik)

Modifikatort
fill
swap

Mbushe vargun me vler (funksion publik)


Shkmbe prmbajtjen (funksion publik)

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:

elementet e vargut tim: 2 16 77 34 50

Nse nuk deklarohet prdorimi i emrtimeve standarde nga libraria std,


(rreshti 4 sht koment n kt rast), ather pr secilin urdhr duhet shnuar s
pari prej cils librari vjen, n formn: std::cout (prmes operatorit :: - scope
resolution operator).
588

Algoritmet dhe strukturat e t dhnave


N rreshtin 8, sht deklaruar vargu vargu-im dhe jan inicializuar vlerat e
elementeve (antarve).
N rreshtin 11 deklarohet unaza for (n versionin 11, me opcionin auto dhe
me iteratorin it). Iteratori lviz prej vargu-im.begin( ) (funksioni begin, q
kthen fillimin e vargut/antarin e par) deri tek vargu-im.end( ) (funksioni end(
), q kthen fundin e vargut/elementin e fundit). Pra, prmes funksioneve
prkatse, kthehen fillimi dhe fundi i vargut.
N rreshtin 12, dereferncohet vlera e antarit (prmes pointerit *it).
Shembulli 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// 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

N rreshtin 9, i qasemi elementit t par (fillimt t vargut), prmes funksionit


front, emriiVargut.front( ), ndrsa n rreshtin 10, elementit t fundit (fundit t
vargut), prmes funksionit back, emriiVargut.back( ).
Shembulli 4:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 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

Algoritmet dhe strukturat e t dhnave


varguIm permbane: 1 2 3 4 5 6 7 8 9 10

Funksioni at( ), (angl. at n, te, tek), q nnkupton elementi n pozitn me


indeksin prkats, p.sh. varguIm.at(0), i bie elementi n pozitn 0, gjegjsisht
elementi i par i vargut. N kt shembull, prmes unazs for, lvizim nga
indeksi 0 deri tek indeksi 9, kshtu q urdhri 15 q ndodhet brenda unazs,
shtyp me radh t gjith antart e vargut, n pozitat prej 0 deri n 9, prmes
funksionit varguIm.at(i). Zvendsim pr funksionin at( ), sht operatori
i qasjes [ ] (si n shembullin vijues).
Shembulli 5:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

// 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

Prmes unazs n rreshtin 12, secilit antar n pozitn i, i japim vlern


prmes operatorit [ ], q nnkupton antarin n pozitn e shnuar brenda
kllapave t mesme, gjegjsisht operatorit t pozits.
Shembulli 6:
1 // array::fill
2 #include <iostream>

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

N rreshtin 7, bhet deklarimi i vargut prmes forms:


array<tipi, numrianetareve> emri;

me rast, rezervohet hapsira pr numrin e anetareve t deklaruar, n ket rast,


6 antar t tipit int.
N rreshtin 9, prmes funksionit fill(x), (angl. fill mbush, mbushje), mbushet
vargu me vlern e zgjedhur t x-it (n kt rast, 5).
Shembull 7:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// array::rbegin/rend
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,5> varguIm = {1, 2, 3, 4, 5} ;

592

cout<<"\nvarguIm "<<(varguIm.empty() ? "eshte i zbrazet" :


"nuk eshte i zbrazet")<<'\n';
cout << "varguIm permbane:";
for (auto it=varguIm.begin();it<varguIm.end(); ++it)
cout << ' ' << *it;
cout<<endl;
cout << "varguIm mbrapsht:";

Algoritmet dhe strukturat e t dhnave


18
for (auto rit=varguIm.rbegin();rit<varguIm.rend(); ++rit)
19
cout << ' ' << *rit;
20
cout<<endl;
21
return 0;
22 }
Rezultati/dalja:

varguIm nuk eshte i zbrazet


varguIm permbane: 1 2 3 4 5
varguIm mbrapsht: 5 4 3 2 1

Opcioni auto mundson


bashksis/vargut.

prshkimin

automatik

antarve

N rreshtin 10, prmes funksionit empty (angl. empty i zbrazt), testohet a


sht vargu i zbrazt?
N unazn e for, (rreshti 13) deklarohet iteratori (it-shkurtesa pr iterator) i cili
prmes funksioneve begin( ) (angl. begin fillimi) dhe end( ) (angl. end fundi)
bn prshkimin e vargut.
Pastaj, n unazn e dyt for (rreshti18) deklarohet iteratori revers (rit-shkurtesa
pr revers iterator) i cili prmes funksioneve rbegin( ) (angl. reverse begin
fillimi i kundrt) dhe rend( ) (angl. reverse end fundi i kundrt) bn
prshkimin e vargut n ann e kundrt.

593

Avni Rexhepi

<stack> - Steku
Stack header-i

Header-i q definon kontejner klasn stack.


Klasa: stack (LIFO stack)
Steku sht tip i kontejnerit adaptor, i dizajnuar n mnyr specifike pr t
operuar n kontekstin LIFO (angl. Last-in First-out - I fundit brenda, i pari
jasht), ku elementet insertohen dhe nxirren vetm nga njri skaj i kontejnerit.
Steku implementohet si adaptor kontejneri, q sht klas q prdor nj objekt
t enkapsuluar t klass specifike t kontejnerit si nn-kontejner, duke ofruar nj
set specifik t funksioneve antar pr t ju qasur elementeve t tij. Elementet
shtyhen/nxirren nga skaji i kontejnerit, i njohur is top (angl. Top Krye, Maje,
Kulm).
Nn-kontejneri mund t jet cilido templejt i klass s kontejnerit standard ose
ndonj klas e dizajnuar posaqrisht. Kontejneri duhet t prkrah operacionet
vijuese:

empty (i/e zbrazt, zbraze)

size (madhsia)

back (fundi, pjesa e pasme)

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

//mbushe stekun me vlerat: 1,2,...,10

while (!stekuIm.empty()) //gjersa steku nuk eshte i zbrazet


{
cout<<"Madhesia aktuale e stekut: "
<<setw(2)<<stekuIm.size()<<"; ";

Algoritmet dhe strukturat e t dhnave


17
18
19
20
21
22
23
24
25 }

cout<<"Ne krye: "<<setw(2)<<stekuIm.top()<<"\n";


shuma += stekuIm.top();
stekuIm.pop();
}
cout<<"Shuma e anetareve te stekut, gjithsej:
system("Pause");
return 0;

"<<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

N rreshtin tre, prfshihet libraria <stack>. N rreshtin 8, deklarohet steku me


emrin stekuIm, antart e t cilit jan t tipit int. N rreshtin 11, brenda unazs
for (nga rreshti 10), pr do iteracion (prsritje t unazs), shtyhet n stek vlera
e re, i. N rreshtin 16, prdoret funksioni size( ), q kthen madhsin e stekut.
N rreshtin 17, prdoret funksioni top( ), q kthen antarin n krye t stekut. N
rreshtin 19, thirret funksioni pop( ), q trheq (largon) nga steku antarin n
krye t stekut.

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

Algoritmet dhe strukturat e t dhnave


Pr operacionet t cilat prfshijn insertime ose largime t shpeshta t
elementeve n pozita t tjera e jo n skajet (fillim ose fund), dequeu-t
performojn me keq dhe kan m iterator m pak konsistent sesa <list> dhe
<forard_list>.
Funksionet e definuara pr dequeue, jan:

deque::assign (caktimi i vlerave t antarve)

deque::at (vlera n pozitn e caktuar)

deque::back (fundi)

deque::begin (fillimi)

deque::cbegin (fillimi konstant)

deque::cend (fundi konstant)

deque::clear (fshije, pastro)

deque::crbegin (fillimi konstant revers)

deque::crend (fundi konstant revers)

deque::emplace (vendose)

deque::emplace_back (vendose n fund)

deque::emplace_front (vendose n fillim)

deque::empty (zbraze)

deque::end (fundi)

deque::erase (fshije)

deque::front (fillimi, fronti)

deque::get_allocator (merr alokatorin)

deque::insert (inserto)

deque::max_size (madhsia maksimale)

deque::operator= (operatori =)

deque::operator[] (operatori i pozits, [ ] )

deque::pop_back (trhiqe fundin)

deque::pop_front (trhiqe fillimin)

deque::push_back (vendose n fund)

deque::push_front (vendose n fillim)


597

Avni Rexhepi

deque::rbegin (fillimi revers)

deque::rend (fundi revers)

deque::resize (ndrysho madhsin)

deque::shrink_to_fit (tkurrje pr prshtatje)

deque::size (madhsia)

deque::swap (shkmbe)

N ndarje sipas kategorive t veprimeve, kemi listn si n vijim.


Funksionet antare (Member functions):
(constructor) Kontejneri pr konstruktim t deque (funksion publik)
(destructor) Destruktori i deque (funksion publik)
Operatori pr ndarje (caktim) t prmbajtjes (funksion publik)
operator=
Iteratort (Iterators):
begin
end
rbegin
rend
cbegin
cend
crbegin
crend

Kthe iteratorin n fillim (funksion publik)


Kthe iteratorin n fund (funksion publik)
Kthe iteratorin revers n fillimin revers (funksion publik)
Kthe iteratorin revers n fundin revers (funksion publik)
Kthe iteratorin const n fillim (funksion publik)
Kthe iteratorin const n fund (funksion publik)
Kthe const_reverse_iterator fillimin revers (funksion publik)
Kthe const_reverse_iterator n fundin revers (funksion publik)

Kapaciteti (Capacity):
size
max_size
resize
empty
shrink_to_fit

Kthe madhsin (size) (funksion publik )


Kthe madhsin maksimale (funksion publik )
Ndrysho madhsin (funksion publik )
Testo nse kontejneri sht i zbrazt (funksion publik )
Tkurre pr prshtatje (funksion publik )

Qasja n elemente (Element access):


operator[] Operatori [ ] - Qasja n elemente (funksion publik )
Qasja n element (funksion publik )
at
Qaja n elementin e par (funksion publik )
front
598

Algoritmet dhe strukturat e t dhnave


Qasja n elementin e fundit (funksion publik )

back

Modifikatort (Modifiers):
assign
push_back
push_front
pop_back
pop_front
insert
erase
swap
clear
emplace
emplace_front
emplace_back

Cakto prmbajtjen e kontejneri (funksion publik )


Shto element n fund (funksion publik )
Inserto element n fillim (funksion publik )
Fshije elementin e fundit (funksion publik )
Fshije elementin e par (funksion publik )
Inserto elemente (funksion publik )
Fshij elementet (funksion publik )
Shkmbe prmbajtjen (funksion publik )
Pastro prmbajtjen (funksion publik )
Konstukto dhe inerto elementin (funksion publik )
Konstukto dhe inerto elementin n fillim (funksion publik )
Konstukto dhe inerto elementin n fund (funksion publik )

Alokator (Allocator):
get_allocator Alokatori get (funksion publik )
Funksionet jo-antare, mbingarkime:
relational operators
sap

Operatort relacional pr deque (funksion )


Shkmbe elementet (templejt funksioni)

T gjitha kto funksione mund t thirren pr secilin rast t dequeue-s t


deklaruar n program.
Shembull:
1
2
3
4
5
6
7
8
9
10
11
12
13

// Insertimi n deque dhe funksionet tjera


#include <iostream>
#include <deque>
#include <vector>
using namespace std;
int main ()
{
deque<int> deque1;
// inserto disa vlera nga fundi:
for (int i=1;i<6;i++) deque1.push_back(i); // 1 2 3 4 5
// inserto disa vlera nga fillimi:

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

for(int i=1;i<6;i++) deque1.push_front(i);// 5 4 3 2 1 1 2 3 4 5


//deklaro iteratorin it, pozicionoje ne fillim
deque<int>::iterator it = deque1.begin();
it = deque1.begin()+0;
//shtypi vlerat, prmes iteratorit
cout << "deque1 permbane:";
for (it=deque1.begin(); it!=deque1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
// fshije elementin e 6-te
deque1.erase (deque1.begin()+5);
// fshiji 3 elementet e para
deque1.erase (deque1.begin(),deque1.begin()+3);
//pastro deque-n teresisht (fshiji te gjitha elementet)
deque1.clear();
// inserto disa vlera nga fundi:
for (int i=1; i<=3; i++) deque1.push_back(i); // 1 2 3
//pozicionohu n fillim
it = deque1.begin();
++it;
it = deque1.insert (it,10);
// 1 10 2
// "it" tani pointon ne elementin e sapo insertuar, 10
deque1.insert (it,2,20);
// "it" nuk eshte me valid!

// 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;

Algoritmet dhe strukturat e t dhnave


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

N rreshtin 3, prfshijm pr prdorim librarin <deque>. N rreshtin 9


deklarojm deque1. Urdhrat tjer t programit jan sqaruar prmes komenteve
prkatse.

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)

Klasat standarde deque dhe list i prmbushin kto krkesa. N mnyr t


parazgjedhur (default), nse nuk specifikohet ndonj klas e caktuar, prdoret
deque.
Parametrat e template-it
T Tipi i elementeve. Alias si member type queue::value_type.
Container Tipi i objektit t nn-kontejnerit t brendshm ku ruhen elementet.
Tipi i tij duhet t jet T. Alias si member type queue::container_type.
Member functions:
602

Algoritmet dhe strukturat e t dhnave


(constructor)
empty
size
front
back
push
emplace
pop
sap

Kontejneri pr queue (funksion publik)


Teston a sht kontejneri i zbrazt (funksion publik)
Kthen madhsin (funksion publik)
Qasja n elementin e ardhshm (funksion publik)
Qasja n elementin e fundit (funksion publik)
Insertimi i elementit (funksion publik)
Konstruktimi dhe insertimi i elemenit (funksion publik)
Largimi i elementit t ardshm (funksion publik)
Shkmbimi i prmbajtjes (funksion publik)

Mbingarkimet e funksioneve jo-antare


operatort Operatort relacional pr queue (funksion)
relacional
Shkmbimi i prmbajtjeve t queue-ve (funksioni publik)
swap
(queue)
Specializimet jo-antare t klass
uses_allocator<queue. Prdor alokatorin pr queue (class template)
Shembull1: push, pop, empty, front, back
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//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 }

cout << ' ' << queue1.front();//elementi i pare


queue1.pop();
//terhiqe nga queue1
}
cout << '\n';
queue1.push(77);
//shtyje ne queue1
queue1.push(16);
//shtyje ne queue1
queue1.front() -= queue1.back();
// 77-16=61
cout << "queue1.front() tani eshte " << queue1.front() << '\n';
system("Pause");
return 0;

Rezultati/dalja:

Ju lutemi jepni disa numra te plote (jepni 0 per fund):


1 2 3 4 5 0
queue1 permban: 1 2 3 4 5 6 0
queue1.front() tani eshte 61

Shembull 2: size (madhsia)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// 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

Shembulli 3: queue:: emplace


604

Algoritmet dhe strukturat e t dhnave


template <class... Args> void emplace (Args&&... args);
Konstrukton dhe inserton elementin
E shton nj element t ri n fund t rreshtit (queue), pas elementi t fundit
aktual. Ky element i ri konstruktohet n vend (in place) duke ia prcjellur
argumentet (args) pr konstruktorin e tij.
Ky funksion antar n mnyr efektive e thrret funksionin emplace_back t
nn-kontejnerit, duke ia prcjellur argumentet.
Parametrat
args Argumentet e prcjellura pr t konstruktuar elementin e ri
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// 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

Algoritmet dhe strukturat e t dhnave


4 using namespace std;
5
6 int main ()
7{
8
priority_queue<int> rrp1;
9
10
rrp1.push(30);
11
rrp1.push(100);
12
rrp1.push(25);
13
rrp1.push(40);
14
15
cout << "Terheqja e elementeve...";
16
while (!rrp1.empty())
17
{
18
std::cout << ' ' << rrp1.top();
19
rrp1.pop();
20
}
21
cout << '\n';
22
system("Pause");
23
return 0;
}
Rezultati/dalja:
Terheqja e elemeneteve... 100 40 30 25

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

Algoritmet dhe strukturat e t dhnave


Shembulli 1: Krijimi dhe shtypja e listave
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

// 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

N rreshtin 3 prfshijm pr prdorim librarin <list>. N rreshtin 9, deklarojm


listn me antar int, t krijuar nga 5 antart e vargut vlerateMia. (Po t mirrej
n argumentin e dyt vlerateMia+2, do t mirreshin vetm 2 antart e par). N
rreshtin 12 prdorim unazn for, me iteratorin e deklaruar it, pr t kaluar npr
antart e lists, prej fillimit t lists (funksioni begin( )), deri n fund t lists
(funksioni end( ) ).
N rreshtin 17 deklarojm listn lista2, me antar t tipit int. N rreshtin 19,
mbushim listn nga fundi, me vlerat i t unazs for nga rreshti 18.

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)

Ky funksion i sorton elementet e listws, duke ua ndwrruar vendet pwrbrenda


kontejnerit.
Sortimi bwhet duke aplikuar njw algoritwm i cili e pwrdorw ose operatorin <
(nw versionin (1)) ose krahasuesin comp (nw versionin (2)), pwr ti krahasuar
elementet. Ky krahasim duhet tw prodhojw strict weak ordering tw
elementeve (krahasim konsistent transitiv, pa shqyrtuar refleksivitetin).
Renditja rezultuese e elementeve ekuivalente wshtw stabile, d.m.th., elementet e
barabarta i ruajnw pozitat relative (nw krahasim me njwra tjetrwn) qw i kanw
pasur para sortimit.
Operacioni nuk pwrfshinw krijimin, asgjwsimin ose kompjimin e ndonjw
elementi, por elementet lwvizen pwrbrenda kontejnerit.
Parameterat
comp atributi binar, i cili kur i merr dy vlera tw tipit tw njwjtw prej atyre tw
pwrmbajtura nw listw, kthen true nwse argumenti i parw shkon para tw dytit
nw strict weak ordering qw e definon, pwrndryse kthen false. Ky duhet tw
jetw pointer funksioni ose objekt funksioni.
Vlera kthyese
Asnjw (none)
1
2
3
4
5
6
7
8
9
10

// 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

Algoritmet dhe strukturat e t dhnave


11
12
cout << "lista1 para sortimit
:";
13
for (list<double>::iterator it=lista1.begin(); it!=lista1.end();
14 ++it)
15
cout << ' ' << *it;
16
cout << '\n';
17
18
lista1.sort();
19
// 1 2 3 4 5 6 7 8 9 10
20
cout << "lista1 e sortuar
:";
21
for (list<double>::iterator it=lista1.begin(); it!=lista1.end();
22 ++it)
23
cout << ' ' << *it;
24
cout << '\n';
25
26
cout << '\n';
27
system("Pause");
28
return 0;
29 }
30
Rezultati/dalja:

lista1 para sortimit


lista1 pas sortimit

: 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:

lista1 para sortimit


lista1 pas sortimit
lista1 pas sortimit me atribut

: Zero nje dy Tre


: Tre Zero dy nje
: dy nje Tre Zero

Shembulli 3: Largimi i vlerave duplikate - list::unique


Nj funksion shum i dobishm, q nevojitet shpeshher n praktik, sht
funksioni unique (angl. unique unkike) pr eleminimin e vlerave duplikate,
gjegjsisht pr ruajtjen e vlerave unike. Kemi dy versione t tij:
1. void unique();
2. template <class BinaryPredicate>
void unique (BinaryPredicate binary_pred);

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

Algoritmet dhe strukturat e t dhnave


Prandaj, ky funksion sht posaqrisht i rndsishm pr listat e sortuara. Pra,
nse nga lista dshironi ti largoni duplikatet, s pari e sortoni listn, e pastaj e
thirrni funksionin unique.
Versioni i dyt (2), merr si argument nj funksion t caktuar q prcakton
unicitetin e elementit. N fakt, mund t implementohet cilado veti (jo vetm
krahasimi pr barazi), por vreni se funksioni do t thrret t
ashtuquajturin binary_pred(*i,*(i-1)) pr do ift t elementeve (ku i sht
iteratori n nj element, duke filluar nga i dyti) dhe largohet nga lista elementi i
i-t, nse parakushti (angl. predicate) kthen vlern logjike true.
Elementet q duhet larguar nga lista, asgjsohen.
Paramatrat
binary_pred
angl. Binary predicate parakushti binar, q duke marr dy vlera t tipit t
njjt, prveq atyre q kan mbetur n list, kthen true, pr t larguar
elementin e prcjellur si argument i par prej kontejnerit dhe false prndryshe.
Ky duhet t jet pointer funksioni ose objekt funksioni.
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

// 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

listaIme.unique (pjesaIntBaraz); //largon elementet sipas funksionit


// 2.72, 3.14, 12.15, 15.3, 72.25, 73.0
listaIme.unique (eAfert()); // largon elementet sipas klases
// 2.72, 12.15, 72.25
cout << "listaIme :";
for (list<double>::iterator it=listaIme.begin(); it!=listaIme.end();
++it)
cout << ' ' << *it;
cout << '\n';
system("Pause");
return 0;

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

Shembull 5: Largimi i elementeve sipas vlers


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// remove - largo nga lista


#include <iostream>
#include <list>
int main ()
{
int vleratInt[]= {10,20,30,40, 50};
std::list<int> lista1 (vleratInt,vleratInt+5);

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");

Algoritmet dhe strukturat e t dhnave


18 return 0;
19 }
Rezultati/dalja:
lista1 permbane: 10 20 40 50

Shembull 6: Ndrthurrja e listave - list::splice


Funksioni splice (angl. ndrthurrje, ngjitje, bashkim, etj), mundson
bashkimin e dy listave n disa forma:
(1)
tr lista

(2)
nj element

(3) rangu i
elementeve

void splice (const_iterator position,


list& x);
void splice (const_iterator position,
list&& x);
void splice (const_iterator position,
list& x, const_iterator i);
void splice (const_iterator position,
list&& x, const_iterator i);
void splice (const_iterator position,
list& x,const_iterator first,
const_iterator last);
void splice (const_iterator position,
list&& x, const_iterator first,
const_iterator last);

Pra, mund ta bashkojm nj list ekzistuese, me nj tjetr, prej pozits s treguar


ose mund ta bartim vetm nj element t lists, n listn tjetr. Poashtu, me
mnyrn e tret, mund ta bartim nj brez t vlerave t nj list, n nj list tjetr,
prej pozits s prcaktuar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

//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.splice (it, lista2);

//
//
//
//

lista1: 1 10 20 30 2 3 4
lista2 (e zbrazt)
"it" akoma pointon ne 2
(elementi i 5-t)

lista2.splice (lista2.begin(),lista1, it);


// lista1: 1 10 20 30 3 4
// lista2: 2
// "it" tash eshte jo-valid
it = lista1.begin();
std::advance(it,3);
// "it" pointon ne 30
lista1.splice ( lista1.begin(), lista1, it, lista1.end());
// lista1: 30 3 4 1 10 20
std::cout << "lista1 permban:";
for (it=lista1.begin(); it!=lista1.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::cout << "lista2 permban:";
for (it=lista2.begin(); it!=lista2.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
system("Pause");
return 0;

Rezultati/dalja:

lista1 permban: 30 3 4 1 10 20
lista2 permban: 2

Shembull 7: Bashkimi i listave t sortuara - list::merge


(1)

(2)

void merge (list& x);


void merge (list&& x);
template <class Compare>
void merge (list& x, Compare comp);
template <class Compare>
void merge (list&& x, Compare comp);

E bashkon x-in (listn e dyt) n list (n listn e par), duke transferuar t


gjitha elementet e tij n pozitat respektive, sipas renditjes s pozitave n
kontejner (t dy kontejnert duhet t jen t sortuar paraprakisht).
Kshtu, efektivisht largohen t gjitha elementet e x-it (i cili bhet i zbrazt) dhe i
inserton ata n pozitat e tyre adekuate sipas renditjes prbrenda kontejnerit (i cili
e rrit madhsin (size) pr numrin e elementeve t transferuara). Veprimi
616

Algoritmet dhe strukturat e t dhnave


kryhet pa krijuar ose asgjsuar ndonj element: ata transferohen, pa marr
parasysh far vlere sht x dhe nse tipi prkrah lvizjen-krijimin apo jo.
Versioni (2) i templejtit me dy parametra, ka t njjtt karakteristika, por e merr
edhe atributin e krahasimit (comp), pr t br krahasimin ndrmjet elementeve.
Funksioni krkon q kontejnert e list-ave t ken elementet e sortuara
praprapakisht, sipas vlers ose sipas krahasimit. Pr bashkimin e listaev t
parenditura, shikoni funksionin list::splice.
Elementet e barabarta ruajn renditjen para bashkimit, ndrsa elementet
ekzistuese ekuivalente i paraprijn ato t insertuara nga x.
Funksioni nuk bn asgj nse (&x = = this).
Parametrat:
x objekti list i tipit t njjt (me templejt t njt). Gjithmon modifikohet xi, pa marr parasysh referencn tipin e referencs (lvalue apo rvalue).
comp atributi binar, i cili i merr dy vlera t tipit t njjt dhe kthen true nse
argumenti i par duhet t shkoj para t dytit. Duhet t jet pointer funksioni ose
objekt funksioni.
Vlera kthyese (none) asgj
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

#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:

lista1 permban: 1.4 2.2 2.9 3.1 3.7 7.1


Tani lista1 permban: 1.4 2.2 2.9 2.1 3.1 3.7 7.1

618

Algoritmet dhe strukturat e t dhnave

<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;

N map, vlerat els (key values) n prgjithsi prdoren pr t sortuar ose


identifikuar elementet n mnyr unike, ndrsa vlerat e pasqyruara, shoqruara
me to (mapped values) e ruajn prmbajtjen e shoqruar me elsin. Tipet e
elsit dhe vlers s mapuar mund t ndryshojn dhe grupohen s bashku n
tipin e antarit value_type, i cili sht tip i iftit (pair), i cili i kombinon t dy:
typedef pair<const Key, T> value_type;
S brendshmi (internally), elementet n map gjithmon sortohen sipas vlers s
elsit, sipas kriterit strict weak ordering t prcaktuar nga objekti i
brendshm krahasues. Ekziston edhe versioni i pasortuar: unordered_map.
Kontejnert map n prgjithsi jan m t ngadalshm sesa kontejnert e
parenditur, pr t ju qasur elementeve individuale sipas vlers s elsit, mirpo
mundsojn iteracion direkt n nnbashksi bazuar n renditjen e tyre.
Vlerat e mapuara n map mund t qasen drejtprdrejt prmes elsit t tyre
prkats, duke prdorur operatorin kllapa t mesme ([ ]).
Map zakonisht implementohet si binary search tree(pem binare e krkimit).
619

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

Algoritmet dhe strukturat e t dhnave


(constructor) Kontejneri pr konstruktim t map (funksion publik)
(destructor) Destruktori i map (funksion publik)
Kopjon prmbajten e kontejnerit (funksion publik)
operatori =
Iteratort:
begin
end
rbegin
rend
cbegin
cend
crbegin
crend

Kthen iteratorin n fillim (funksion publik)


Kthen iteratorin n fund (funksion publik)
Kthen iteratorin revers n fillimin revers (funksion publik)
Kthen iteratorin revers n fundin revers (funksion publik)
Kthen iteratorin konstatn n fillim (funksion publik)
Kthen iteratorin konstant n fund (funksion publik)
Kthen iteratorin konstant revers n fillimin revers (funks. publik)
Kthen iteratorin konstant revers n fundin revers (funksion publik)

Capacity:
empty
size
max_size

Teston a sht kontejneri i zbrazat (funksion publik)


Kthen madhsin (funksion publik)
Kthen madhsin maksimale (funksion publik)

Qasja n elemente:
I qaset elementit (funksion publik)
I qaset elementit (funksion publik)

operatori [ ]
At
Modifikatort:
insert
erase
swap
clear
emplace
emplace_hint

Inserton elementet (funksion publik)


Fshin elementet (funksion publik)
Shkmben prmbajtjen (funksion publik)
Pastron prmbajtjen (funksion publik)
Konstrukton dhe inserton elementin (funksion publik)
Konstrukton dhe inserton elementin sipas paralajmrimit (f.p.)

Observert:
key_comp

Kthen objektin e krahasimit t elsave (funksion publik)


621

Avni Rexhepi
value_comp Kthen objektin e krahasimit t vlerave (f.p.)
Operacionet:
find
count
lower_bound
upper_bound
equal_range

Merr iteratorin n element (funksion publik)


Konstrukton dhe inserton elementin sipas paralajmrimit (f.p.)
Kthen iteratorin n kufirin e poshtm (funksion publik)
Kthen iteratorin n kufirin e eprm (funksion publik)
Merr rangun e elementeve t barabarta (funksion publik)

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

N rreshtin 6 sht deklaruar dhe inicializuar map1.


N rreshtin 7 sht deklaruar iteratori pr map1.
N rreshtat 9-11, jan ndar vlerat tek elementet prkatse.
Shembull 2: map::at
1
2

// map::at
#include <iostream>

622

Algoritmet dhe strukturat e t dhnave


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#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

// versioni i trete i funksionit insert (insertimi ne rang):


map<char,int> map2;
map2.insert(map1.begin(),map1.find('c'));

Algoritmet dhe strukturat e t dhnave


31
// paraqitja e permbajtjes:
32
cout << "map1 permban:\n";
33
for (it=map1.begin(); it!=map1.end(); ++it)
34
cout << it->first << " => " << it->second << "\n";
35
cout << "map1 ka " << map1.size() << "elemente\n\n";
36
37
cout << "map2 permban:\n";
38
for (it=map2.begin(); it!=map2.end(); ++it)
39
cout << it->first << " => " << it->second << '\n';
40
cout << "map2 ka " << map2.size() << "elemente\n\n";
41 system("Pause");
42
return 0;
43 }
Rezultati/dalja:

elementi 'z' veq ekziston me vleren 200


map1 permban:
a => 100
b => 300
c => 400
z => 200
map1 ka 4 elemente
map2
a =>
b =>
map2

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;

Set-et jan kontejner q ruajn elementet specifike sipas nj renditjeje t


caktuar. Ekziston edhe versioni i parenditur: unordered_set.
N set, vlera e nj elementi poashtu edhe e itentifikon at (vet vlera sht els
(key) i tipit T) dhe secila vler duhet t jet unike. Vlera e elementit n set nuk
mund t modifikohet kur nj her t jet vendosur n kontejner (elementet jan
gjithmon konstante), por ato mund t insertohen ose t largohen nga kontejneri.
S bernshmi (internally), elementet n set jan gjithmon t sortuara sipas nj
kriteri strikt eak ordering t treguar me an t objektit t brendshm t
krahasimit (t tipit Compare).
Kontejnert e set-eve n prgjithsi jan m t ngadalshm sesa kontejnert e
parenditur t set-eve (unorderd set containers), pr t ju qasur elementeve
individuale sipas elsit, mirp ato lejojn iteracion t drejtprdrejt n nn-sete
bazuar n renditjen e tyre.
Set-et zakonisht implementohen si pem binare t krkimit.

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

Algoritmet dhe strukturat e t dhnave


Set
Vlera e nj elementi sht poashtu edhe elsi (key) q prdoret pr ta
identifikuar at.
elsat unik
Nuk mund t ket dy elemente me elsa t njjt (ekuivalent) n kontejner.
Allocator-aare
Kontejneri prdor nj objekt alokator pr t manipuluar n mnyr dinamike
nevojat e veta pr memorie.

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

Kthen iteratorin n fillim (funksion publik)


Kthen iteratorin n fund (funksion publik)
Kthen iteratorin revers n fillimin revers (funksion publik)
Kthen iteratorin revers n fundin revers (funksion publik)
Kthen iteratorin konstatn n fillim (funksion publik)
Kthen iteratorin konstant n fund (funksion publik)
Kthen iteratorin konstant revers n fillimin revers (funks. publik)
Kthen iteratorin konstant revers n fundin revers (funksion publik)

Capacity:
Empty
Size
max_size

Teston a sht kontejneri i zbrazat (funksion publik)


Kthen madhsin (funksion publik)
Kthen madhsin maksimale (funksion publik)

Modifikatort:
insert
erase
swap
clear
emplace
emplace_hint

Inserton elementet (funksion publik)


Fshin elementet (funksion publik)
Shkmben prmbajtjen (funksion publik)
Pastron prmbajtjen (funksion publik)
Konstrukton dhe inserton elementin (funksion publik)
Konstrukton dhe inserton elementin sipas paralajmrimit (f.p.)

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

Merr iteratorin n element (funksion publik)


Konstrukton dhe inserton elementin sipas paralajmrimit (f.p.)
Kthen iteratorin n kufirin e poshtm (funksion publik)
Kthen iteratorin n kufirin e eprm (funksion publik)
Merr rangun e elementeve t barabarta (funksion publik)

Allocator:
get_allocator Merr alokatorin (funksion publik)

628

Algoritmet dhe strukturat e t dhnave


Shembulli 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// 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

//fshirja sipas pozites


//fshirja siapas vleres

std::cout << "set1 permban:";


for (it=set1.begin(); it!=set1.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
system("Pause");
return 0;
}

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:

Vargu i alokuar permban:

10 20 30 40 50

Shembulli 4: set::emplace
set::emplace
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);

Konstruktimi dhe insertimi i elementeve


Inserton nj element t ri n set, nse sht unik. Ky element i ri konstruktohet
n vend duke prdorur args si argument pr konstruktimin e tij. Ky insertim
ndodh vetm nse nuk ekziston element i till n kontejner, q sht ekuivalent
me elementin q bhet emplaced (elementet e setit an unike).
630

Algoritmet dhe strukturat e t dhnave


Nse insertohet, ky efektivisht e rrit madhsin (size) e kontejnerit pr nj.
Prbrenda, set-i i mban t gjitha elementet e tij t sortuara sipas kriterit t
specifikuar n objektin e krahasimit (comparison). Elementi gjithmon
insertohet n pozitn e tij respektive, sipas ksaj renditjeje.
Elementi konstruktohet in-place duke thirrur
allocator_traits::construct me args t prcjellur.
Ekziston edhe funksioni antar i ngjashm, insert, i cili ose i kopjon ose i
lviz objektet ekzistuese n kontejner.
Parameterat
args argumentet e prcjellura pr konstruktim t elementi t ri.
Vlera kthyese (Return value)
Nse funksioni e inserton elementin me sukses (pasi q nuk ekziston ndonj
element ekuivalent n set), funksioni kthen iftin (pair)e nj iteratori pr
elementin e ri t insertuar dhe vlern true.
Prndryshe, e kthen iteratorin e elementit ekuivalent prbrenda kontejnerit dhe
vlern false.
Member type iteratori sht iterator i tipit bidireksional q pointon n nj
element. pair sht templlejt i klass i deklaruar n <utility>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// 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:

Jepe qytetin: Prishtina

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

Algoritmet dhe strukturat e t dhnave


Shembulli 6: set::count
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// 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

nuk sht element i set1.


nuk sht element i set1.
nuk sht element i set1.
sht element i set1.
nuk sht element i set1.
nuk sht element i set1.
sht element i set1.
nuk sht element i set1.
nuk sht element i set1.
sht element i set1.

Shembulli 7: set::rbegin, set::rend


1
2
3
4
5
6
7
8
9
10
11

// 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

Algoritmet dhe strukturat e t dhnave

<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

Kthen iteratorin n fillim (funksion publik)


Kthen iteratorin n fund (funksion publik)
Kthen iteratorin revers n fillimin revers (funksion publik)
Kthen iteratorin revers n fundin revers (funksion publik)
Kthen iteratorin konstatn n fillim (funksion publik)
Kthen iteratorin konstant n fund (funksion publik)
Kthen iteratorin konstant revers n fillimin revers (funks. publik)
Kthen iteratorin konstant revers n fundin revers (funksion publik)

Kapaciteti
size
max_size
resize
capacity
empty
reserve
shrink_to_fit

Kthe madhsin (numri i antarve) (funksion publik)


Kthe vlern maksimale ((funksion publik)
Ndrysho madhsin (funksion publik)
Kthe madhsin e hapsirs s alokuar (funksion publik)
Testo nse vektori sht i zbrazt (funksion publik)
Krko ndryshim t kapacitetit (funksion publik)
Ngushto pr t ju prshtatur (funksion publik)

Qasja n elemente
operator[ ]
636

Qasju elementit (funksion publik)

Algoritmet dhe strukturat e t dhnave


at
front
back
data

Qasju elementit (funksion publik)


Qasju elementit t par (funksion publik)
Qasju elementit t fundit (funksion publik)
Merr pointerin n vler (funksion publik)

Modifikatort
assign
push_back
pop_back
insert
erase
swap
clear
emplace
emplace_back

Cakto prmbajtjen e vektorit (funksion publik)


Shto elementet n fund (funksion publik)
Fshije elementin e fundit (funksion publik)
Inserto elementet (funksion publik)
Fshiji elementet (funksion publik)
Shkmbe prmbajtjet (funksion publik)
Pastro prmbajtjen (funksion publik)
Konstrukto dhe inserto elementin (funksion publik)
Konstrukto dhe inserto elementin n fund (funksion publik)

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)

void assign (initializer_list<value_type> il);

Definon prmbajtjen e re t vektorit, duke zvendsuar prmbajtjen e tij


ekzistuese dhe duke ia modifikuar madhsin e tij n mnyr gjegjse.
N versionin (1) t rangut, prmbajtja e re e elementeve konstruktohet prej t
gjitha elementeve ndrmjet kufijve first dhe last, n renditje t njjt.
N versionin e mbushjes (fill (2)), prmbajtja e re jan n-elementet secili i
inicializuar n kopjen e val (vlers).
N versionit (3), t lists inicializuese, prmbajtja e re kopjohet prej vlerave t
prcjellura si list inicializuese, n renditje t njjt.
Alokatori intern prdoret (prmes tipareve t tij) pr t alokuar/dealokuar
memorien nse ndodh realokimi. Ai poashtu prdoret pr t asgjsuar (angl.
destroy) elementet ekzistuese dhe pr t kontstruktuar ato t rejat.
T gjitha elementet q ndodhen n kontejner para thirrjes, asgjsohen dhe
zvendsohen me elementet e reja t konstruktuara (nuk ndodh ndarja e vlerave
(angl. assignement) t elementeve).
Kjo shkakton nj realokim automatik t hapsirs s alokuar t memories nse
dhe vetm nse madhsia e re e vektorit e tejkalon kapacitetin aktual t vektorit.
Parameterat
first (i pari), last (i fundit) itaratort e hyrjes pr n pozitn fillestare dhe
prfundimtare n sekuenc. Rangu i prdorur sht [fist,last], i cili i prfshin t
gjitha elementet ndrmjet first dhe last, duke prfshir edhe elementet elementet
e pointurara me first dhe last (kufijt). Argumenti InputIterator i templlejtit t
funksionit duhet t jet nj tip i iteratorit pr hyrje i cili i pointon elementet e
tipit prej t cilit mund t konstruktohen objektet e tipit t vlerave t definuara
(value_type).
n madhsia e re pr kontejnerin. Tipi i antarit size_type sht tip integral
unasigned.
val Vlera pr mbushje t kontejnerit. Secili prej n elementeve n kontejner do
t inicializohet m nj kopje t ksaj vlere. Tipi i antarit value_type sht tipi
i elementeve n kontejner, i definuar n vektor si nj alias i parametrit t tij t
par t templlejtit (T).
638

Algoritmet dhe strukturat e t dhnave


il objekti initializer_list (lista inicializuese). Kompajleri do t konstruktoj
automatikisht objektet e tilla prej lists s inicializuar sipas deklarimit.
Tipi i vlerave t antarve (member type) sht tipi i elementeve n kontejner, i
definuar n vektor si nj alias i parametrit t tij t par t templlejtit (T).
Shembulli 1: vector::assign
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
33
34
35
36
37
38
39
40
41
42
43
44

// 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);

// vlerat prej vargut.

//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";

Algoritmet dhe strukturat e t dhnave


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 }

cout << "\n";


vector<int> vektori3;
vektori3.push_back(10);
while (vektori3.back() != 0)
{
vektori3.push_back ( vektori3.back()-1 );
}
cout << "vektori3 permban:";
for (unsigned i=0; i<vektori3.size() ; i++)
cout << " " << vektori3[i];
cout << "\n";
cout << "vektori3.front()=" << vektori3.front() <<"\n";
cout << "vektori1.back() =" << vektori3.back() <<"\n\n";
cout<<"\n\n";
system("Pause");
return 0;

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 }

for (int i=1; i<=10; i++)


vektori1.push_back(i);
while (!vektori1.empty())
{
shuma+=vektori1.back();
vektori1.pop_back();
}
cout << "Shuma e anetareve te vektorit, S=" << shuma << "\n";
system("Pause");
return 0;

Rezultati/dalja:

Shuma e anetareve te vektorit, S=55

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';

Algoritmet dhe strukturat e t dhnave


30
31
32
33
34
35
36
37 }

vektori2.shrink_to_fit(); //ngushtoje deri sa t'i pershtatet madhesia


cout << "3. Kapaciteti i vektori2: " << vektori2.capacity() << '\n';
cout << '\n';
system("Pause");
return 0;

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:

vektori1 permban:100 100 100


vektori2 permban:200 200 200 200 200
Pas shkembimit,
vektori1 permban:200 200 200 200 200
vektori2 permban:100 100 100

644

Algoritmet dhe strukturat e t dhnave

Fjalori STL - Dictionary ADT


Fjalori (angl. Dictionary) (map, association list harta, pasqyrimi, lista e
shoqrimit, etj) sht nj struktur e t dhnave, e cila n prgjithsi sht
asociacion (shoqrim) i elsave unik me disa vlera. Prmes tyre mund t lidhet
vlera me elsin, t fshihet elsi (dhe natyrisht nj vler shoqruar) dhe t
krkohet pr ndonj vler prmes elsit. Vlerat nuk krkohet t jen unike. Nj
shembull i thjesht i prdorimit sht nj fjalor me shpjegime. N kt rast,
fjalt jan elsat dhe shpjegimet prkatse jan vlerat.
Operacionet e zakonshme pr Fjalorin (Dictionary):

Dictionary create()
Krijon fjalor t zbrat

boolean isEmpty(Dictionary d)
tregon a sht i zbrazt (angl. empty) fjalori d

put(Dictionary d, Key k, Value v)


asocion (shoqron) elsin k me vlern v. Nnse elsi k veq sht
prezent n fjalor vlera e vjetr zvendsohet nga v

Value get(Dictionary d, Key k)


kthen vlern e asociouar me elsin k, ose null, nse fjalori nuk ka els
t till

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)

Fjalori implementohet n rastin e pems s krkimit binar (BST) dhe hash


map.
Fjalori sht struktura q sht: bashksia e n rekordeve, secili i identifikuar
prmes nj ose m shum elsave. Prdoret pr t mirmbajtur strukturn e t
dhnave n n mnyr efikase t lokalizohet, insertohet ose fshihet rekordi i
shoqruar me cilindo els q t krkimit (query).

645

Avni Rexhepi

Tipi abstrakt i t dhnave Dictionary sht nj prej strukturave m t


rndsishme n shkencat kompjuterike. Pr implementimin e fjalorve (angl.
dictionary) jan propozuar shum struktura t t dhnave, si:
- vargjet e renditura ose pa-renditura,
- hash tabelat,
- listat me kaprcime (skip list-at),
- pemt binare t krkimit (t balansuara dhe t pabalansuara), etj.
kshtu q zgjedhja e t duhurs sht e komplikuar.
Varsisht prej aplikacionit, sht poashtu nj vendim q mund t ket ndikim t
rndsishm n performans. N praktik, sht m e rndsishme q t evitohet
prdorimi i strukturs s gabuar t t dhnave sesa t identifikohet opcioni i
vetm m i mir n dispozicion.
Nj kshill esenciale sht q t izolohet me kujdes implementimi i strukturs
s fjalorit nga interfejsi i saj. Prdorni thirrjet eksplicite t funksioneve t cilat
inicializojn, krkojn dhe modifikojn strukturn e t dhnave, m mir se ti
insertoni ato prbrenda kodit. Kjo drgon n program m t pastr, por
gjithashtu e bn m t leht t provohen implementime t ndryshme t fjalorit,
pr t par se si ndikojn n performans. Mos u obsesiononi me koston e
mbingarkimit (angl. overhead) t thirrjes s funksionit q sht i pandar n nj
abstraksion t till. Nse aplikacioni sht aq kritik n aspektin kohor sa q nj
mbingares e till mund t ndikoj n performans, ather sht edhe m
esenciale q t jeni t aft t eksperimentoni me lehtsi me implementime t
ndryshme t fjalorit tuaj.
646

Algoritmet dhe strukturat e t dhnave


N zgjedhjen e strukturs s duhur t t dhnave pr fjalorin tuaj, parashtroni
pyetjet vijuese:

Sa elemente do t keni n mnyr tipike n strukturn tuaj t t dhnave?


A do ta dini kt numr paraprakisht? A jeni duke shikuar nj problem
mjaft t vogl sa q nj struktur e thjesht e t dhnave do t jet m e
mira apo do t jet aq e madhe sa q duhet t brengoseni pr prdorimin
e teprt t memories ose shkmbimeve?

A e dini numrin relativ t insertimeve, fshirjeve dhe krkimeve? A do


t ket modifikime n strukturn e t dhnave pasi t jet konstruktuar s
pari, ose do t jet statike prej asaj pike e tutje?

A keni njohuri pr frekuencn relative me t ciln do t qasen elsat e


ndryshm? - A mund t supozojm se modeli i qasjes do t jet unform
dhe i rastit apo do t shfaq nj shprndarje t shtrembruar (t pjerrt) t
qasjes (d.m.th., disa elemente jan shum m popullore se t tjerat) ose
me sens lokaliteti (d.m.th., elementet jan t prirur q t jen t qasur n
grupe, n vend se n intervale mjaft t rastit). Zakonisht, bota sht edhe
e shtrembruar edhe e grupuar.

A sht kritike q operacionet indifiduale t jen t shpejta ose vetm ajo


q sasia totale e puns s br prgjat tr programit t minimizohet?
kur koha e prgjigjes sht kritike, si sht n programin e kontrollimit
t nj pajisjeje mjeksore, nuk mund t pritni shum gjat ndrmjet
hapave. Kur keni nj program q sht duke br shum pyetje (query)
n bazn e t dhnave, por pr tema jo shum t rnsishme, nuk sht
kritike zgjedhja e nj shnimi t veant, sepse do ti keni t gjith me
prpjekje minimale.

Kur njher t kuptoni se cilat jan nevojat e juaja, provoni t identifikoni


strukturn m t mir t t dhnave, nga lista vijuese:

Vargjet ose lista e lidhura t pasortuara pr bashksi t vogla t t


dhnave, le t themi me 10 deri n 20 elemente, nj varg i pasortuar me
gjas do t jet struktura m e leht dhe m efikase pr tu mirmbajtur.
Vargjet jan m t lehta pr pun sesa listat e lidhura dhe nse fjalori do
t mbahet n kt madhsi, me gjas nuk do t keni mundsi t kurseni
sasi t rndsishme t memories ndaj alokimit t vargut t plot. Nse
fjalori do t jet shum i madh, ather koha e krkimit do t jet
problematike n cilindo rast.
Nj variant interesant sht edhe lista vet-organizuar. Sa her q nj
els qaset ose insertohet, ai vendoset n fillim t lists. Kshtu, nse
elsi qaset prsri n t ardhmen e afrt, ai do t jet afr fillimit dhe
647

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.

Vargjet ose listat e lidhura t sortuara mirmbajtja e lists s lidhur t


sortuara zakonisht nuk shpaguhet (prveq nse jeni duke tentuar t
eliminoni duplikatet), pasi q n strukturn e till nuk mund t bhet
krkimi binar. Vargu i sortuar do t jet i prshtatshm nse dhe vetm
nse nuk ka shum insertime ose fshirje. Kur vargu bhet shum i madh
sa q nuk e z memoria, mendoni pr B-pemt (angl. B-trees) n vend t
ksaj.

Hash tabelat pr aplikacioniet q prfshijn numrt t elsave


mesatar deri n shum t madh (le t themi ndrmjet 100 dhe 1,000,000),
hash tabela me ndarje (angl. hash table bucketing) me gjas sht
mnyra e duhur pr t prcjellur. N hash tabela ne prdorim nj
funksion i cili pasqyron elsat (ofshin ata string, numr ose kado
tjetr) n numra t plot (integers) ndrmjet 0 dhe m-1. Ne mbajm nj
varg t m kovave (angl. buckets), secila n mnyr tipike e implementuar
prmes prdorimit t lists s lidhur t pasortuar. Pr nj els t dhn,
hash funksioni menjher e identifikon se n ciln kov ndodhet ai. Nse
prdorim hash funksion i cili shprndan mir elsat dhe hash tabel
mjaftueshm t madhe, secila kov do t duhej t prmbaj shum pak
elemente, duke br kshtu t pranueshm krkimin linear. Insertimi dhe
fshirja nga hash tabela redukon insertimin dhe fshirjen nga lista/kovat.

Hash tabela e sinkronizuar mir me gjas do t tejkaloj performansn e


vargut t sortuar n shumicn e aplikacioneve. Mirpo, n krijimin e
hash tabels s sinkronizuar mir, prfshihen disa vendime lidhur me
dizajnin:
o Sa duhet t jet e madhe tabela? Zakonisht, m duhet t jet
afrsisht sa numri maksimal i elementeve q pritet t vendosen n
tabel. Sigurohuni q m t jet numr primar, ashtu q t
minimizohen rreziqet e hash funksionit t keq.
o Cilin hash funksion duhet prdorur? Pr stringje, dika si
S

F(S)=

char (S ) a
i 1

648

i 1

mod m

Algoritmet dhe strukturat e t dhnave


duhet t funksionoj (ku a sht madhsia e alfabetit dhe char(x)
sht funksioni i cili pasqyron secilin karakter x n ASCII kodin e
tij). Pr stringjet e gjata, duhet t mjaftojn 8 deri n 10 karaktere
duhet t jen t mjaftueshme pr hash, duke supozuar se ka pak gjasa
q t jen t mbushur me zbraztira ose ndonj tjetr invariant.
Prdorni rregulln e Hornerit pr t implementuar llogaritjet efikase
t ktij hash funksioni.

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;

Dallimi i vetm ndrmjet ktyre dy prototipeve sht prdorimi i fjals s


rezervuar class ose typename. Prdorimi i tyre nuk bn dallim, pasi q t dy
shprehjet kan kuptim t njjt dhe sillen n mnyr t njjt.
Pr shembull, pr t krijuar funksionin template (shabllon), q kthen vlern m
t madhe prej dy vlerave (objekteve) t prcjellura n t, mund t veprohet si n
vijim:
template <class TipiIm>

650

Algoritmet dhe strukturat e t dhnave


TipiIm VleraMax (TipiIm a, TipiIm b)
{
return (a>b?a:b);
}

Ktu kemi krijuar funksionin template me TipiIm si parametr templejt i tij.


Ky paremetr templejt reprezenton tipin i cili nuk sht specifikuar akoma, por
mund t prdoret n funksionin templejt sikur t ishte tip i zakonshm. Si mund
t shihet, funksioni VleraMax kthen vlern m t madhe prej dy parametrave t
ktij tipit akoma t padefinuar.
Pr t prdorur templejt funksionin, pr thirrjen e funksionit prdoret formati
vijues:
Emri_funksionit <tipi> (parametrat);

Pr shembull, pr t thirrur funksionin pr krahasim t dy numrave t plot


(integer), n kod mund t shkruhet:
int x,y;
VleraMax <int> (x,y);

Kur kompajleri t has n kt thirrje t funksionit templejt, ai e prdor


templejtin pr t gjeneruar automatikisht funksionin duke zvendsuar seciln
paraqitje t TipiIm me tipin e prcjellur si templejt parametr aktual (n kt
rast int) dhe e thrret at. Ky proces kryhet automatikisht nga kompajleri dhe
sht i padukshm pr programerin.
Shembulli i kompletuar, do t dukej si n vijim:
// Templejti i funksionit
#include <iostream>
using namespace std;
template <class T>
T VleraMax (T a, T b)
{
T rezultati;
rezultati = (a>b)? a : b;
return (rezultati);
}
int main ()
{
int i=5, j=6, k;
long l=10, m=5, n;

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

N kt rast, kemi prdorur T si emr t parametrit t templejtit (n vend t


TipiIm, pasi q n fakt T sht m i leht dhe m shkurtr) dhe sht emr i
zakonshm pr parametrat e templejtave. Por, natyrisht, mund t prdorni fardo
indentifikatori tjetr, sipas dshirs.
N shembullin e msiprm, funksioni VleraMax sht prdorur tri her, por
seciln her me versionin e duhur t funksionit, t prshtatur pr tipin prkats
t vlerave.
Si mund t shihet, tipi T sht prdorur n funksionin templejt VleraMax( )
edhe pr t deklaruar objektet e reja t atij tipi: T rezultati;
Prandaj, rezultati do t jet nj objekt i tipit t njjt si edhe parametrat a dhe b,
kur funksioni templejt t thirret pr rastin prkats, me tipin e specifikuar.
N kt rast specifik, ku tipi gjenerik T sht prdorur si parametr pr
funksionin VleraMax, kompajleri mund t gjej automatikisht se cilin tip t t
dhnave duhet ta prdor pa pasur nevoj t specifikoj at n mnyr eksplicite
n kllapa t drejta < >, ashtu si kemi vepruar para specifikimit <int>, <long>
dhe <double >. Pra, kemi mundur t shkruajm vetm:
int i,j;
VleraMaxh(i,j);
Pasi q t dy vlerat i dhe j, jan t tipit int, kompajleri mund t gjej
automatikisht q parametri i templejtit mund t jet vetm i tipit int. Kjo metod
implicite jep rezultat t njjt.
// Templejti II
#include <iostream>
using namespace std;

652

Algoritmet dhe strukturat e t dhnave

template <class T>


T VleraMax(T a, T b)
{
return (a>b?a:b);
}
int main ()
{
int i=5, j=6, k;
long l=10, m=5, n;
double o=1.5, p=2.5, q;
k = VleraMax(i,j);
n = VleraMax(l,m);
q = VleraMax(o,p);
cout << k << endl;
cout << n << endl;
cout << q << endl;
return 0;
}

Rezultati:
6
10
2.5

Vreni se si n kt rast, templejti i funksionit sht thirrur pa specifikuar n


mnyr eksplicite tipin, brenda kllapave < >. Kompajleri prcakton n mnyr
automatike se cili tip nevojitet n seciln thirrje.
Pasi q templejti n kt rast prmban vetem nj parametr (class T) dhe vet
funksioni tempeljt pranon dy parametra, t dy t ktij tipit T, nuk mund ta
thrrasim templejtin e funksionit me dy objekte t tipeve t ndryshme, si
argumente.
int i
double j;
VleraMax(i,j);

Kjo nuk do t ishte korrekte, pasi q templejti VleraMax pret dy argumente t


tipit t njjt dhe n kt rast t thirrjes jan prdorur objektet e dy tipeve t
ndryshme.
653

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);
}

N kt rast, templejt funksioni VleraMin( ) i pranon dy parametra t tipeve t


ndryshme dhe kthen nj objekt t tipit t njjt sikur parametri i par (T), q i
prcillet. Pr shembull, pas deklarimit, mund t thirret funksioni VleraMin( ),
me:
int i,j;
long l;
i = VleraMin<int,long> (j,l);

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;
}
};

Klasa q sapo u definuar shrben pr t ruajtur dy elemente t fardo tipi. Pr


shembull, nse do t dshironim t deklarojm nj objekt t ksaj klas pr t
ruajtur dy vlera t tipit int, 115 dhe 36, do t shkruanim:
dyshja <int> objektiIm (115, 36);

654

Algoritmet dhe strukturat e t dhnave


E njjta klas do t mund t prdorej pr t krijuar nj objekt q do t ruante
fardo tipi tjetr:
dyshja <double> vleratFloat (3.0, 2.18);

Funksioni i vetm antar n templejtin e klass sht definuar inline prbrenda


vet deklarimit t klass.
N rast se definojm funksionin antar jasht deklarimit t templejtit t klass,
ather at definicion gjithmon duhet ta paraprijm me prefiksin: template
<...>.
//class templates
#include <iostream>
using namespace std;
template <class T>
class dyshja
{
T a, b;
public:
dyshja (T ePara, T eDyta)
{a = ePara; b = eDyta;}
T vlMax();
};
template <class T> //prefiksi templejt per funksionin
T dyshja <T>::vlMax()
{
T rezultati;
rezultati = a>b? a : b;
return rezultati;
}
int main()
{
dyshja <int> objektiIm (100, 75);
cout << objektiIm.vlMax();
return 0;
}

Rezultati do t ishte:
100

Vreni sintaksn e definicionit t funksionit vlMax.


template <class T>
T dyshja <T>::vlMax()

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

Algoritmet dhe strukturat e t dhnave

int main ()
{
kontejneriIm<int> itegeriIm (7);
kontejneriIm<char> karakteriIm ('j');
cout << itegeriIm.rrite() << endl;
cout << karakteriIm.uppercase() << endl;
return 0;
}

Rezultati:
8
J

Kjo sintaks prdoret n specializimin e templejtave t klasave:


template <> class mycontainer <char> { ... };

S pari, vreni se deklarimi i klass paraprihet me emrin e templejtit t klass


me nj template< > me list t zbrazt t parametrave. Kjo pr ta deklaruar
at n mnyr eksplicite si specializim t templejtit.
Mirpo, m i rndsishm se ky prefiks, sht specializimi <char> i parametrit
pas emrit t templejtit t klass. Ky parametr i specializimit vet identifikon
tipin pr t cilin do t deklarojm specializimin e klass s templejtit (char).
Vreni dallimimet ndrmjet templejtit t prgjithshm (t gjeneralizuar) t
klass dhe specializimit:
template <class T> class kontejneriIm { ... };
template <> class kontejneriIm <char> { ... };

Rreshti i par sht templejti gjenerik, ndrsa i dyti specializimi.


Kur deklarohen specializimet pr klasn templejt, duhet t definohen edhe t
gjith antart e saj, edhe ata saktsisht t njjt (t barabart) me klasn
gjenerike t templejtit, sepse nuk ka trashgimi t eleementeve prej templejtit
gjenerik tek specializimi.

Parametrat pa tip pr templejta


Prveq argumenteve t templejtit t cilt jan t paraprir nga fjalt e rezervuara
class ose typename, t cilat prfaqsojn tipet, templejtat poashtu mund t
ken edhe tipe t rregullta t parametrave, t ngjashm me ata tek funksionet. Si
shembull, shikoni kt templejt t klass q prdoret pr t ruajtur sekuencat e
elementeve:
// Templejti i sekuencs
#include <iostream>
using namespace std;

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

Mund t plotsojm dhe shtypim t gjitha pozitat (me unaz), si n vijim:


for (int i=0;i<5;i++)
{
vlerat_int.caktoAnetarin (i,100);
vlerat_float.caktoAnetarin (i,3.1416);

658

Algoritmet dhe strukturat e t dhnave


}
for (int i=0;i<5;i++)
{
cout << vlerat_int.merrAnetarin(0) << '\n';
cout << vlerat_float.merrAnetarin(i) << '\n';
}

Poashtu, sht e mundur q t caktohen vlerat ose tipet e nnkuptuara (angl.


default) pr parametrat e templejtit t klass. Pr shembull, nse n klasn
paraprake definicioni i templejtit do t ishte:
template <class T=char, int N=10> class sekuencaIme {..};

kemi mundur t krijojm objekte duke prdorur parametrat default t templejtit,


duke deklaruar:
sekuencaIme<> sekIme;

q sht ekuivalent me:


sekuencaIme<char,10> sekIme;

Templejtat dhe projektet me shum fajlla


Prej kndvshtrimit t kompajlerit, templejtat nuk jan klasa ose funksione
normale. Ata kompajlohen vetm me krkes, q do t thot se kodi i funksionit
templejt nuk kompajlohet deri n momentin kur t krkohet krijimi i nj
instance (nj rasti) me argumente specifike t templejtit. N at moment, kur t
krkohet nj rast konkret, kompajleri prej templejtit gjeneron funksionin n
mnyr specifike pr ato argumente.
Kur projektet t rriten, sht e zakonshme q kodi i programit t ndahet n fajlla
t ndryshm t korit burimor. N kto raste, interfejsi dhe implementimi jan
zakonisht t ndar. Nse marrim si shembull librarit e funksioneve, interfejsi n
mnyr t prgjithshme prbhet prej deklarimeve t prototipeve t t gjitha
funksioneve q mund t thirren. Ato zakonisht deklarohen n header file
(fajlla t kreut, header fajlla), me tipin .h dhe implementimi (definicioni i
ktyre funksioneve) sht n nj fajll t pavarur me kodin n c++.
Pasi q templejtat kompajlohen kur t krkohen, kjo vendos kufizime pr fajllat
me shum projekte: implementimi (definicioni) i klass ose funksionit templejt
duhet t jet n t njjtin fajll si edhe deklarimi. Kjo do t thot q nuk mund t
ndajm interfejsin n header fajll t veant dhe se duhet t prfshijm t dyja,
edhe interfejsin edhe implementimin, n cilindo fajll q i prdor tempeljtat.
Pasi q deri n krkimin e instancs konkrete t templejtit nuk gjenerohet kod,
kompajlert jan t prgatitur pr t lejuar prfshirjen m shum se nj her t
659

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

Algoritmet dhe strukturat e t dhnave

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

Llogaritja e shums 1 + 2 + ... + (N-2) + (N-1) + N sht elementare: rezultati i


dhn pason kur e mbledhim shumn me vetvetn, por n kahjen e kundrt, term
pas termi. Ky rezultat dy her vlera e krkuar prbhet prej N termave, ku
secili prej tyre rezulton n N+1.
Formula 2.2 - rekurrenc q paraqitet kur programi rekurziv e prgjysmon
hyrjen n nj hap, sht:
CN = CN/2 + 1, pr N2, me C1 = 1,
Zgjidhja
CN sht afrsisht lg N. Kshtu si sht shkruar, ekuacioni nuk ka kuptim prveq
nse N sht numr ift ose supozojm se N/2 sht pjestim i plot. Pr
momentin, supozojm se N = 2n, ashtu q rekurrenca sht gjithmon mir e
definuar. (Vni re se n = lg N). Ather, rekurrenca teleskopohet edhe m leht
se ajo e mparshmja:
C2n = C2n-1 + 1
C2n = C2n-2 + 1 + 1
C2n = C2n-3 + 3
...
C2n = C20 + n
C2n = n + 1.
Zgjidhja precize pr N t prgjithshm varet nga interpretimi i N/2. N rastin
kur N/2 reprezenton N/2, kemi zgjidhjen e thjesht: CN sht numri i bitave n
reprezentimin binar t N, dhe numri sht lg N + 1, sipas definicionit (Kllapat
speciale , nnkuptojn kufirin e poshtm dhe ato kufirin e eprm). Ky
konkluzion pason drejtprdrejt prej faktit se operacioni i eliminimit t bitit m t
djatht (angl. rightmost) n reprezentimin binar t cilitdo numr t plot N > 0,
konvertohet n N/2 (Shih figurn 2.6).

662

Algoritmet dhe strukturat e t dhnave

Figura 2.6. Funksioniet integer dhe reprezentimi binar


Pr reprezentimin e dhn binar t numrit N (kolona e mesit), duke larguar bitin
m t djatht fitojm N/2 . Kjo do t thot se, numri i bitave n reprezentimin
binar t N sht 1 m i madh se numri i bitave n reprezentimin binar t N/2 .
Prandaj, lg N + 1, numri i bitave n reprezentimin binar t N, sht zgjidhja e
formuls 2.2, pr rastin kur N/2 interpretohet si N/2 .
Formula 2.3 rekurrenca q paraqitet pr programin rekurziv q e prgjysmon
hyrjen, por ndoshta duhet t ekzaminoj seclin element t hyrjes, sht:
CN = CN/2 + N,

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.

Zgjidhja zhvillohet shum ngjashm me at n formuln 2.2, por me trikun


plotsues t pjestimit t t dy anve me 2n n hapin e dyt, pr t br q
rekurrenca t teleskopohet.
Formula 2.5 rekurrenca q paraqitet pr programin rekurziv i cili e ndan
hyrjen n dy gjysma dhe pastaj bn sasi konstante t puns tjetr, sht:
CN = 2CN/2 + N,

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

Algoritmet dhe strukturat e t dhnave

Shtojca C - Pemt binare


Kodi i shembullit n libr:
// BinarySearchTree.h"
#include <string>
#define SIZE_KEY 32
#define SIZE_VALUE 256
typedef struct Metadata
{
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;
class BinarySearchTree
{
private:
int size;
METADATA* root;
bool addNode(METADATA** current_node, METADATA* new_node);
bool getNode(METADATA* current_node, char* key, char* value);
void removeAllNodes(METADATA* node);
void processNodesInOrder(METADATA* node);
int getTreeDepth(METADATA* node);
bool containsNode(METADATA* node, char* key);
bool removeNode(METADATA** node, char* key);
void removeRootNode(METADATA** node);
void moveLeftMostNode(METADATA** node, METADATA* root);
public:
BinarySearchTree();
virtual ~BinarySearchTree();
bool add(char* key, char* value);
bool remove(char* key);
void removeAll();
bool get(char* key, char* value);
bool contains(char* key);
void displayInOrder();
int getSize();
int getDepth();
};
// BinarySearchTree.cpp
#include <iostream>
#include "BinarySearchTree.h"

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

Algoritmet dhe strukturat e t dhnave


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;
}
}
void BinarySearchTree::removeRootNode(METADATA** root)
{
METADATA* temp;
if((*root)->left == NULL && (*root)->right == NULL)
{
delete(*root);
*root = NULL;
}
else if((*root)->right == NULL)
{
temp = *root;
*root = (*root)->left;
delete(temp);
}
else if((*root)->left == NULL)
{
temp = *root;
*root = (*root)->right;
delete(temp);
}
else
{
moveLeftMostNode(&((*root)->right), *root);
}
}
void BinarySearchTree::moveLeftMostNode(METADATA** node, METADATA* root)
{
if(*node != NULL && (*node)->left == NULL)
{
METADATA* temp = *node;

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

Algoritmet dhe strukturat e t dhnave


}
}
}
bool BinarySearchTree::contains(char* key)
{
return containsNode(root, key);
}
bool BinarySearchTree::containsNode(METADATA* node, char* key)
{
if(node == NULL)
{
return false;
}
else
{
if(strcmp(key, node->key) == 0)
{
return true;
}
else if(strcmp(key, node->key) < 0)
{
return containsNode(node->left, key);
}
else
{
return containsNode(node->right, key);
}
}
}
void BinarySearchTree::displayInOrder()
{
processNodesInOrder(root);
}
void BinarySearchTree::processNodesInOrder(METADATA* node)
{
if(node != NULL)
{
processNodesInOrder(node->left);
cout << "elesi: "<<node->key<<"\tvlera: " <<node->value << endl;
processNodesInOrder(node->right);
}
}
int BinarySearchTree::getSize()
{
return size;
}
int BinarySearchTree::getDepth()
{
return getTreeDepth(root);
}
int BinarySearchTree::getTreeDepth(METADATA* node)
{

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

Algoritmet dhe strukturat e t dhnave


bool
void
void
void
void
void
void
void
void

isEmpty() const { return root==NULL; }


print_inorder();
inorder(tree_node*);
print_preorder();
preorder(tree_node*);
print_postorder();
postorder(tree_node*);
insert(int);
remove(int);

};
// 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

Algoritmet dhe strukturat e t dhnave


{
parent->left = curr->left;
delete curr;
}
else
{
parent->right = curr->left;
delete curr;
}
}
return;
}
//Kerkohet nyja gjethe
if( curr->left == NULL && curr->right == NULL)
{
if(parent->left == curr) parent->left = NULL;
else parent->right = NULL;
delete curr;
return;
}

//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

Algoritmet dhe strukturat e t dhnave


if(p != NULL)
{
cout<<" "<<p->data<<" ";
if(p->left) preorder(p->left);
if(p->right) preorder(p->right);
}
else return;
}
void BinarySearchTree::print_postorder()
{
if(isEmpty())
{
cout<<"\n Pema eshte e zbrazet! "<<endl;
return;
}
postorder(root);
}
void BinarySearchTree::postorder(tree_node* p)
{
if(p != NULL)
{
if(p->left) postorder(p->left);
if(p->right) postorder(p->right);
cout<<" "<<p->data<<" ";
}
else return;
}
int main()
{
BinarySearchTree b;
int ch,tmp,tmp1;
while(1)
{
cout<<endl<<endl;
cout<<" Operacionet ne \"Binary Search Tree\" "<<endl;
cout<<" -------------------------------------- "<<endl;
cout<<" 1. Insertim/Krijim"<<endl;
cout<<" 2. Pershkimi: In-Order "<<endl;
cout<<" 3. Pershkimi: Pre-Order "<<endl;
cout<<" 4. Pershkimi: Post-Order "<<endl;
cout<<" 5. Largimi/Fshirja"<<endl;
cout<<" 6. Dalja "<<endl;
cout<<" \n Zgjedheni opsionin perkates: ";
cin>>ch;
switch(ch)
{
case 1 : cout<<" Jepni vleren(numrin) qe duhet te insertohet:
";
cin>>tmp;

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

Algoritmet dhe strukturat e t dhnave


// Tree class
class Tree {
Node* root;
public:
Tree();
~Tree();
Node* Root() { return root; };
void addNode(int key);
void inOrder(Node* n);
void preOrder(Node* n);
void postOrder(Node* n);
private:
void addNode(int key, Node* leaf);
void freeNode(Node* leaf);
};
// Constructor
Tree::Tree() {
root = NULL;
}
// Destructor
Tree::~Tree() {
freeNode(root);
}
//Liroje nyjen (Fshirja)
void Tree::freeNode(Node* leaf)
{
if ( leaf != NULL )
{
freeNode(leaf->Left());
freeNode(leaf->Right());
delete leaf;
}
}
// Shto nyje
void Tree::addNode(int key) {
// Nuk 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);
}
}

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

Algoritmet dhe strukturat e t dhnave


// Programi kryesor (main)
int main() {
Tree* tree = new Tree();
tree->addNode(30);
tree->addNode(10);
tree->addNode(20);
tree->addNode(40);
tree->addNode(50);
cout << "Pershkimi In-order " << endl;
tree->inOrder(tree->Root());
cout << endl;
cout << "Pershkimi Pre-order " << endl;
tree->preOrder(tree->Root());
cout << endl;
cout << "Pershkimi Post-order " << endl;
tree->postOrder(tree->Root());
cout << endl;
delete tree;//Fshije pemen
system("Pause");
return 0;
}

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

Algoritmet dhe strukturat e t dhnave


// Shto nyje tjeter(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);
n->setParent(leaf);
leaf->setLeft(n);
}
}
else
{
if ( leaf->Right() != NULL )
addNode(key, leaf->Right());
else {
Node* n = new Node();
n->setKey(key);
n->setParent(leaf);
leaf->setRight(n);
}
}
}
// Gjeje nyjen [O(lartesia e pemes) mesatarisht]
Node* Tree::findNode(int key, Node* node)
{
if ( node == NULL )
return NULL;
else if ( node->Key() == key )
return node;
else if ( key <= node->Key() )
findNode(key, node->Left());
else if ( key > node->Key() )
findNode(key, node->Right());
else
return NULL;
}
// Shtype pemen
void Tree::walk(Node* node)
{
if ( node )
{
cout << node->Key() << " ";
walk(node->Left());
walk(node->Right());
}
}

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

Algoritmet dhe strukturat e t dhnave


// (3) Nese ka 2 femije. Gjeje paraardhesen(ose pasardhesen).
// Fshije paraardhesen(ose pasardhesen). Zevendesoje nyjen
// qe duhet te fshihet me paraardhesen (ose pasardhesen).
void Tree::deleteNode(int key)
{
// Gjeje nyjen.
Node* thisKey = findNode(key, root);
// (1)
if ( thisKey->Left() == NULL && thisKey->Right() == NULL )
{
if ( thisKey->Key() > thisKey->Parent()->Key() )
thisKey->Parent()->setRight(NULL);
else
thisKey->Parent()->setLeft(NULL);
delete thisKey;
}
// (2)
if ( thisKey->Left() == NULL && thisKey->Right() != NULL )
{
if ( thisKey->Key() > thisKey->Parent()->Key() )
thisKey->Parent()->setRight(thisKey->Right());
else
thisKey->Parent()->setLeft(thisKey->Right());
delete thisKey;
}
if ( thisKey->Left() != NULL && thisKey->Right() == NULL )
{
if ( thisKey->Key() > thisKey->Parent()->Key() )
thisKey->Parent()->setRight(thisKey->Left());
else
thisKey->Parent()->setLeft(thisKey->Left());
delete thisKey;
}
// (3)
if ( thisKey->Left() != NULL && thisKey->Right() != NULL )
{
Node* sub = predecessor(thisKey->Key(), thisKey);
if ( sub == NULL )
sub = successor(thisKey->Key(), thisKey);
if ( sub->Parent()->Key() <= sub->Key() )
sub->Parent()->setRight(sub->Right());
else
sub->Parent()->setLeft(sub->Left());
thisKey->setKey(sub->Key());
delete sub;
}

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

Algoritmet dhe strukturat e t dhnave

Shtojca D - Hash Tabela implementim n C++


N vazhdim do t jepet nj implementim komplet i hash tabels me kontrollim
kuadratik. Prdoret templejti i klass dhe supozohet se sht ofruar nj hash
funksion i duhur n formn:
unsigned int hash( const Object & x ) ;

pr secilin tip t ilustruar me shembull konkret (pr seciln instanc).


Vreni se nuk ka parametr t madhsis s tabels (tableSize); algoritmi i
kontrollimit kuadratik performon operacionin mod final n mnyr interne pas
prdorimit t hash funksionit t ofruar nga shfrytzuesi. Versioni pr string
ofrohet n klas, me deklarimin n rreshtin 48. (Nj default (i nnkuptuar)
poashtu ofrohet m von n form t templejtit t funksionit, por ka pak gjasa q
t ket kuptim pr objektet e komplikuara). S fundi, supozojm se operatori
!= sht i definuar pr objektin. Interfejsi i klass sht paraqitur n kodin n
vazhdim. Q algoritmi t punoj n mnyr korrekte, operatori != dhe hash-i
duhet t jen konsistent. Kjo do t thot, nse dy objekte jan t barabarta, edhe
hash vlerat e tyre duhet t jen t barabarta.
Hash tabela prbhet prej nj vargu t strukturave. Secila struktura ruan nj
element dhe nj antar t t dhnave q na tregon se hyrja/pozita sht e
zbrazt, aktive ose e fshir (deleted). Pr kt qllim prdoret variabla e grupit
(enumeration) EntryType, e deklaruar n rreshtin 27. Ajo sht e vendosur n
pjesn publike (sepse ndonj kompajler mund t ket vrejtje pr rreshtin 36,
nse sht private). Vargu sht deklaruar n rreshtin 40. Duhet t prcjellim
numrin e elementeve n hash tabel (duke prfshir edhe elementet e shnuara si
Deleted); kjo vler sht ruajtur n occupied q sht deklaruar n
rreshtin 41.
Pjesa tjetr e interfejsit t klass prmban deklarimet pr funksionet e hash
tabels. Pasi q antart jan t gjitha objekte first-class, jan t pranueshme
default-et. Metoda (funksioni) i vetm interesant sht find, i cili kthen
elementin e gjetur n krkimin pr x, t mbshtjellur n objektin Cref (q
shrben pr t evituar referencn n objektin q nuk ekziston dhe mund t ruaj
referenc NULL).
1
2
3
4
5
6
7
8

// Quadraticprobing Hash table class.


//
// Object must have operator!= and global function
// unsigned int hash( const Object & key );
//CONSTRUCTION: with no paramet. or another hash table.
//
// ******************PUBLIC DECLAERATIONS************
// void insert ( x ) --> Insert x

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 );

Jan deklaruar tri funksionet (metodat) private, t cilat do t prshkruhen kur t


prdoren n implementimin e klass. Tani mund t diskutohet imlementimi i
klass HashTable.
686

Algoritmet dhe strukturat e t dhnave


Konstruktori i hash tabels dhe makeEmpty (bje t zbrazt) jan paraqitur n
kodin vijues (ku nuk ka ndonj gj t jashzakonshme).
1 // Construct the hash table.
2 template <class Object>
3 HashTable<Object>::HashTable( )
4 : array( nextprime( 101 ) )
5 (
6 makeEmpty( ) ;
7 }
8
9 // Make the hash table logically empty.
10 template <class Object>
11 void HashTable<Object>::makeEmpty( )
12 (
13 occupied = 0;
14 for( int i = 0; i < array.size( ); i++ )
15 array[ i ].info = EMPTY;
16 }

Funksioni pr krkim sht paraqitur n kodin vijues. Ky e prdor funksionin


privat isActive, t paraqitur n kodin e ardhshm. Poashtu e thrret edhe
funksionin findPos, t paraqitur m von, pr t implementuar kontrollimin
kuadradik. Pastaj, sht leht t implementohet find: nj element sht gjetur
nse rezultati i findPos sht nj qelul aktive (nse findPos ndalet n nj
qelul aktive, duhet t ket prshtatje).
Shumica e funksioneve jan me vetm disa rreshta kodi sepse ato e thrrasin
funksionin findPos pr t performuar kontrollimi kuadradik.
1 // Find item x in the hash table.
2 // Return the matching item, wrapped in a Cref object.
3 template <class Object>
4 Cref<Object> HashTable<Object>: :find( const Object & x )
const
5 (
6 int currentpos = findPos( x );
7
8 if( isActive( currentpos ) )
9 return Cref<Object>( array[ currentpos ].element );
10 else
11 return Cref<Object> ( ) ;
12 }

Funksioni find pr hash tabeln me kontrollim kuadratik


1 // Return true if currentpos exists and is active.

687

Avni Rexhepi
2
3
4
5
6

template <class Object>


boo1 HashTable<Object>::isActive( int currentpos ) const
{
return array[ currentpos ].info == ACTIVE;
}

Funksioni isActive pr hash tabeln me kontrollim kuadratik


Ngjashm, n vijim sht treguar funksioni remove. Verifikojm nse funksini
findPos nga drgon n qelul aktive; nse po, ajo markohet me Deleted.
Prndryshe thro exception (angl. thro hedh, hedhje).
1 // Remove item x from the hash table.
2 // Throw ItemNotFoundException if x is not present.
3 template <class Object>
4 void HashTable<Object>::remove( const Object & x )
5 {
6 int currentpos = findPos( x 1;
7 if( isActive( currentpos ) )
8 array[ currentpos ].info = DELETED;
9 else
10 throw ItemNotFoundException( );
11}

Funksioni remove pr hash tabeln me kontrollim kuadratik


N vijim sht dhn funksioni insert. Funksioni insert e bn rehash-imin
nse tabela sht (gjysm) e mbushur. N rreshtin 7 thirret findPos. Nse x
sht gjetur, hidhet gjetja e duplikatit n rreshtin 9, prndryshe, findPos jep
vendin pr t insertuar x-in. Insertimi kryhet n rreshtin 10. N rreshtin 12
prshtatet occupied dhe kthehet, prveq nse rehash sht n rregull;
prndryshe thirret metoda private rehash.
1 // Insert item x into the hash table. If the item is
2 // already present, then throw DuplicateItemException.
3 template <class Object>
4 void HashTable<Object>::insert( const Object & x )
5 {
6 // Insert x as active
7 int currentpos = findPos( x );
8 if( isActive( currentpos ) )
9 throw DuplicateItemException( ) ;
10 array[ currentpos ] = HashEntry( x, ACTIVE );
11
12 if( ++occupied > array.size( ) / 2 )
13 rehash( ) ;
14 }

688

Algoritmet dhe strukturat e t dhnave


Funksioni insert pr hash tabeln me kontrollim kuadratik
Kodi q implementon rehash-imin sht treguar n vijim. Rreshti 5 bn kopjen e
tabels origjinale. N rreshtin 13 krijohet hash tabela e zbrazt me madhsi t
dyfishuar. Pastaj skenohet vargu origjinal dhe elementet aktive insertohen n
tabeln e re. Funksioni insert prdor hash funksionin e ri (pasi q sht i
bazuar n madhsin e vargut, q tash sht m i madh) dhe automatikisht i
zgjidh kolizionet. Mund t jemi t sigurt q thirrja rekurzive e insert (n
rreshtin 16) nuk shkakton nj rehash tjetr (prndryshe mund ta zvendsojm
rreshtin 16 me dy rreshta t kodit n kllapa).
1 // Expand the hash table.
2 template <class Object>
3 void HashTable<Object>::rehash( )
4 {
5 vector<HashEntry> oldArray = array;
6
7 // Create new double-sized, empty table
8 array.resize( nextprime( 2 * oldArray.size( ) ) );
9 for( int j = 0; j < array.size-1; j++ )
10 array[ j ].info = EMPTY;
11
12 // Copy table over
13 makeEmpty( ) ;
14 for( int i = 0; i < oldArray.size( ); i++ )
15 if ( oldArray [i].info == ACTIVE )
16 insert ( oldArray[i].element ) ;
17 }

Funksioni rehash pr hash tabeln me kontrollim kuadratik


T gjitha deri m tani jan t pavarura nga kontrollimi kuadratik. Kodi n
vazhdim implementon findPos, q prfunsimisht mirret me algoritmin pr
kontrollimin kuadratik. Vazhdojm t krkojm tabeln deri sa t gjendet nj
qelul e zbrazt ose prshtatja. Rreshtat 12-14 drejtpdrejt implementojn
metodologjin prkatse t evitimit t shumzimeve dhe moduleve.
1// Method that performs quadratic probing resolution.
2// Return the position where the search for x terminates.
3 template <class Object>
4 int HashTable<Object>::findPos(const Object &x) const t
5 {
6 int i = 0;
7 int currentpos = hash( x ) % array.size( );
8
9 while( array[ currentpos ].info ! = EMPTY &&
10 array [ currentpos ].element ! = x )

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;
}

Funksioni q prfundimisht mirret me kontrollim kuadratik


N fund, kodi n vijim jep hash funksionin e prgjithshm. Duke kryer
konvertimi e tipeve n rreshtin 7, ai punon duke trajtuar mostrn e bajtave t
key si string primitiv (por q nuk mund t ket null terminator) dhe pastaj
duke prdorur hash funksionin e njjt si pr stringjet. Ky hash funksion punon
pr tipet primitive por nuk sht i prshtatshm pr objektet e komplikuara sepse
mund t mos knaq krkesn q dy objekte q jan deklaruar t barabarta do t
ken gjithmon hash vlera t barabarta. M shum sht hash funksion
prshkrues sepse garancioni i vetm sht q dy objekte me bit mostra t njjta
do t ken hash vlera t njjta.
1 // Generic hash function -- used if no other matches.
2 template <class Object>
3 unsigned int hash( const Object & key )
4 {
5 unsigned int hashVal = 0;
6
7 const char *keyp = reinterpret-cast<const char *>(&key);
8 for( size_t i = 0; i < sizeof( Object ); i++ )
9 hashVal = 37 * hashVal + keyp[ i ];
10
11 return hashVal;
12 }

Hash funksioni i prgjithshm.

690

Algoritmet dhe strukturat e t dhnave

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.

Pat Morin Open Data Structures (in C++)

11.

Jeffrey J. McConnell Analysis of Algorithms An Active Learning

Approach
12.

692

Algoritmet dhe strukturat e t dhnave

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.

Memoria, Tipet Abstrakte t t dhnave dhe Adresat ........................... 27


Vshtrim mbi memorien................................................................................. 27
T dhnat dhe memoria .............................................................................. 29
Sistemi numerik binar ................................................................................ 30
Rezervimi i memories ................................................................................ 30
Grupet e tipeve abstrakte t t dhnave ...................................................... 32
Adresat e memories .................................................................................... 37
Adresat reale t memories .......................................................................... 38
ADT dhe adresat e memories ..................................................................... 38
Variablat dhe Pointert ................................................................................... 38
Deklarimi i variablave dhe objekteve......................................................... 39
Tipet primitive t t dhnave dhe tipet e definuara prej shftyrzuesit ....... 39
Definimi i tipeve t definuara nga shfrytzuesi ......................................... 40
Tipet e t dhnave t definuara prej shftytzuesit dhe klasat ..................... 43
Pointert ..................................................................................................... 45
693

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

Algoritmet dhe strukturat e t dhnave


Alokimi dinamik i vargjeve........................................................................ 92
Vargjet dinamike ........................................................................................ 95
Menaxhimi i kapaciteti: Sigurimi i kapacitetit, Paketimi(ngjeshja,
kompresimi) ............................................................................................... 96
Funksionet pr qasje n t dhna: Set, Get, InsertAt, RemoveAt .............. 99
Verifikimi i kufijve ....................................................................................... 99
Funksionet Get dhe Set .................................................................................. 99
Funksioni InsertAt .......................................................................................... 99
Funksioni RemoveAt.................................................................................... 100
2.

Analiza e algoritmeve .............................................................................. 103


RAM modeli i llogaritjes.......................................................................... 104
ka sht analiza e algoritmit ...................................................................... 107
Klasifikimi i rritjes ....................................................................................... 111
O-Notation (Kufiri i eprm/lart) ................................................................. 111
-Notation (Rendi i njjt) .......................................................................... 112
-Notation (Kufiri i poshtm/ult) .............................................................. 113
Reduktimi ................................................................................................. 114
Rishikim i asimptots ............................................................................... 115
Efikasiteti i algoritmit rastet e ndryshme dhe shembujt ........................ 117
Rekurrenca ................................................................................................... 121
2.3. Rritja e funksioneve .......................................................................... 124
Algoritmet pr nga teknika dhe qasja ........................................................... 132
Coptimi i problemeve ............................................................................. 132
Programimi linear zgjidhja pr kutin e zez ....................................... 137
Algoritmet lakmitare kurr mos shiko prapa........................................ 137
Prapaveprimi ............................................................................................ 137
Krkimi lokal Mendo globalisht, vepro lokalisht.................................. 138
Hill Climbing............................................................................................ 138
Kalitja e simuluar t msuarit nga natyra .............................................. 138

3.

Implementimi i strukturave themelore Stack dhe Queue ................ 141


695

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.

Listat e lidhura ........................................................................................ 174


Struktura e lists s lidhur ......................................................................... 177
Lista e lidhur njfish ndaj lists s lidhur dyfish ...................................... 178
Listat e lidhura n C++ ................................................................................. 190
Listat e lidhura nj-fish (Singly-linked list) .................................................. 194
Shembull: ..................................................................................................... 195
Opearcionet (Veprimet) n listn e lidhur njfish.................................... 195
Lista e lidhur njfish, reprezentimi i brendshm....................................... 195
Prshkimi i lists s lidhur njfish ............................................................ 197
Algoritmi i prshkimit ............................................................................... 197
Lista e lidhur njfish Operacioni i largimit (fshirjes) ............................ 204
Lista e lidhur n STL .................................................................................... 215
Steku prmes lists s lidhur ......................................................................... 217
Steku.......................................................................................................... 217
Konstruktori dhe destruktori StackLinkedList .......................................... 220
Steku si list e lidhur n C++ .................................................................... 224
Aplikacioni StackLinkedList .................................................................... 228

696

Algoritmet dhe strukturat e t dhnave


Queue prmes lists s lidhur ....................................................................... 230
Queue-List e lidhur ................................................................................. 230
Queue-list e lidhur n C++ ..................................................................... 238
Klasa e zgjeruar LinkedList ..................................................................... 258
Radha me prioritet - Priority queue ADT..................................................... 266
Operacionet .............................................................................................. 267
5.

Rekursioni ................................................................................................ 268


Llogaritja e faktorielit................................................................................... 268
Llogaritja e 3! n detaje ............................................................................ 268
Prparsit dhe t metat e rekursionit .......................................................... 269
Algoritmet rekurzive ................................................................................ 270
5.2. Algoritmet praj-e-sundo................................................................. 278

6.

Pemt ........................................................................................................ 304


Tiparet matematike t pemve binare .......................................................... 312
Prshkimi i pems ........................................................................................ 315
Algoritmet rekurzive t pems binare .......................................................... 325
Prshkimi i grafit .......................................................................................... 330
Krkimi thellsia s pari ....................................................................... 333
Krkimi gjersia s pari ........................................................................ 336
Pema binare .................................................................................................. 339
Elementet e pems binare ......................................................................... 339
Thellsia dhe madhsia ............................................................................ 341
Prse prdoret pema binare? .................................................................... 342
Vlera els ................................................................................................ 344
Krijimi i pems binare .............................................................................. 345
Pema binare n C++ ..................................................................................... 363
Krkimi tek pema binare .............................................................................. 368
Shembull i pems binare ............................................................................ 368
Operacionet ................................................................................................. 368
697

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.

Grafet ....................................................................................................... 458


Reprezentimi i grafit t padrejtuar ............................................................ 462

698

Algoritmet dhe strukturat e t dhnave


Matrica e fqinjsis .................................................................................. 462
Lista e fqinjsis ....................................................................................... 465
Algoritmet pr grafet e padrejtuara .............................................................. 468
Prshkimi thellsia-s-pari ....................................................................... 469
Prshkimi gjersia-s-pari ........................................................................ 476
Pema e shtrirjes minimale ............................................................................ 478
Algoritmi Dijkstra-Prim ........................................................................... 479
Algoritmi i Kruskal-it ............................................................................... 483
Algoritmi i shtegut m t shkurtr................................................................ 486
Algoritmi i Dijkstras ................................................................................... 487
Topologjia ................................................................................................ 491
8.

Algoritmet e krkimit.............................................................................. 498


Krkimi sekuencial ....................................................................................... 498
Krkimi binar ............................................................................................... 500
Analiza e algoritmit t krkimit binar .......................................................... 503
Algoritmi pr bashkim t vargjeve t sortuara ............................................. 506
Algoritmi i bashkimit - Merge algoritmi ...................................................... 506
Testimi i numrave primar (qasja naive) ....................................................... 508
ka jan numrat primar? .......................................................................... 508
Prmirsimi i mundshm i algoritmit ..................................................... 508
Sita e Eratostenit (Sieve of Eratosthenes) ................................................ 510
Shembull....................................................................................................... 511
Analiza e kompleksitetit ............................................................................... 515

9.

Algoritmet e sortimit ............................................................................... 517


Bubble Sort ................................................................................................... 518
Selection Sort ............................................................................................... 522
Insertion Sort ................................................................................................ 525
Shiftimi n vend t shkmbimit ............................................................... 528
Tiparet e sortit Insertion........................................................................ 529
699

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.

Algoritmet e prshtatjes s stringjeve ................................................ 548


Hash tabelat .......................................................................................... 560

Hash funksioni .............................................................................................. 563


Hash tabela dhe faktori i ngarkess........................................................... 563
Kolizionet .................................................................................................. 563
Shembull i thjesht i hash tabels ............................................................. 564
Hash tabela ................................................................................................ 564
Hash funksioni .......................................................................................... 564
Strategjia e zgjidhjes s kolizioneve ......................................................... 564
Pjes kodi ...................................................................................................... 564
Hash tabela. Zgjidhja e kolizionit prmes vargzimit (adresimi i mbyllur)
................................................................................................................... 566
Analiza e kompleksitetit............................................................................ 566
Hash tabela. Strategjia e adresimit t hapur .................................................. 570
Zgjidhja e kolizioneve ............................................................................... 570
Kontrollimi linear ...................................................................................... 571
Operacioni i largimit ................................................................................. 575
Analiza e kompleksitetit............................................................................ 577
Adresimi i hapur kundrejt vargzimit ....................................................... 577
Hash tabela. Ndryshimi dinamik i madhsis ........................................... 581
12.

STL ........................................................................................................ 584

STL Kontejnert ............................................................................................ 584


Kontejnert standard ................................................................................. 584
700

Algoritmet dhe strukturat e t dhnave


Kontejnert: .................................................................................................. 586
<Array> (prej versionit C++ 11 e tutje) ................................................... 586
<Stack> - Steku ........................................................................................ 594
<Deque> ................................................................................................... 596
<Queue> ................................................................................................... 602
<Priority queue> ....................................................................................... 605
<List> ....................................................................................................... 608
<Map> ...................................................................................................... 619
<Set> ........................................................................................................ 626
<vector> ................................................................................................... 635
Fjalori STL - Dictionary ADT ..................................................................... 645
Shtojcat ............................................................................................................. 650
Shtojca A - Templates - Shabllonet.............................................................. 650
Shtojca B - Rekurrenca ................................................................................ 661
Rekurrencat themelore ............................................................................. 661
Shtojca C - Pemt binare .............................................................................. 665
Shtojca D - Hash Tabela implementim n C++ ........................................ 685
Literatura: ..................................................................................................... 692

701

Vous aimerez peut-être aussi