algoritme dhe struktura e te dhenave libri

701
Algoritmet dhe strukturat e të dhënave 1 Avni Rexhepi Prishtinë   2014

Upload: iliriana-s-kukaj

Post on 13-Feb-2018

816 views

Category:

Documents


59 download

TRANSCRIPT

Page 1: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 1/699

Algoritmet dhe strukturat e të dhënave

1

Avni Rexhepi

Prishtinë –  2014

Page 2: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 2/699

Avni Rexhepi

2

Page 3: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 3/699

Algoritmet dhe strukturat e të dhënave

3

Parathënie

 Ky libër u dedikohet studentëve të “Fakultetit t ë Inxhinierisë Elektrike dhe

 Kompjuterike”, t ë Universitetit të Prishtinës, mirëpo natyrisht se mund të përdoret edhenga të gjithë të interesuarit për këtë lëmi. Ky është botimi i parë dhe vërejtjet e

 sygjerimet e lexuesve janë të mirëseardhura. Të gjithë shembujt në libër, janë marrë më

 shumë për qëllime shkollore, për të shërbyer si udhëzime në realizimin e detyrave të

caktuara, e jo si projekt i gatshëm për përdorim apo pjesë të ndonjë projekti. Emrat e

 përdorur si shembuj janë të rastit dhe përjashtohet mundësia e keqpërdorimit të

qëllimshëm.

 Për vërejtjet dhe sygjerimet mund të na kontaktoni përmes postës elektronike, në

adresën: [email protected]

Page 4: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 4/699

Avni Rexhepi

4

Page 5: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 5/699

Algoritmet dhe strukturat e të dhënave

5

Hyrje

Algoritmet dhe strukturat e të dhënave janë “veglat/pajisjet” e programerëve përkryerjen e punëve. Ato definohen dhe përdoren (në programe) për të realizuarllogaritjet e nevoshme për zgjidhjen e problemeve nga jeta reale, përmes përdorimit të programeve dhe kompjuterëve. Algoritmet na mundësojnëkryerjen e operacioneve/veprimeve llogaritëse në një mënyrë të caktuar. Këtollogaritje i bëjnë me të dhënat e thjeshta ose me “strukturat e të dhënave” të cilatna shërbejnë që të krijojmë “objektet abstrakte” në programe, të cilat pasqyrojnë

në mënyrën më të mirë të mundshme “objektet konkrete” (reale, fizike) nga botareale dhe jeta e përditshme. 

Algoritmet

Çka është algoritmi? Algoritmi  është procedurë hap pas hapi, për zgjidhjen e  problemit. Algoritmi është proceudra e kryerjes së ndonjë detyre të caktuar.Algoritmi është idea prapa cilitdo program kompjuterik. Këto do të ishin disa prej definicioneve më të thjeshta lidhur me atë se çka është algoritmi.Përndryshe ekzistojnë edhe shumë definicione të tjera, të cilat në mënyra tëndryshme e japin shpjegimin ose mundohen ta sqarojnë se çka është algoritmi.

Algoritmi definohet edhe si: Algoritmi është bashkësi e rregullave për kryerjen ellogaritjeve me dorë ose me ndonjë pajisje. Algoritmi është një procedurë e përcaktuar hap pas hapi për arritjen e një rezultati të caktuar. Algoritmi është njëvarg i hapave llogaritës që e transformojnë hyrjen në dalje. Algoritmi është njëvarg i operacioneve të kryera në të dhënat që duhet të jenë të organizuara nëstruktura të të dhënave. Algoritmi është një abstraksion i programit që duhet të

ekzekutohet në një makinë fizike (modeli i llogaritjes), etj. Algoritmi më injohur në histori daton që nga koha e Greqisë antike: ky është “Algoritmi iEuklidit” për llogaritjen e pjestuesit më të madh të përbashkët të dy numrave të plotë.

Page 6: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 6/699

Avni Rexhepi

6

Termi algoritëm konsiderohet të ketë ardhur nga emri i dijetarit islam, matematikanitarab  Abū Ja’far ʿAbdallāh Muḥammad ibn Mūsā al -Khwārizmī , i cili jetoi në vitet780-850 në Bagdad. Ai ishte një matematikan që shkroi për numrat indo-arab dheishte ndër të parët që e përdori zeron si “pozicion” në notacionin bazë të

pozicioneve për numrat. Nga punimi i tij “Hisab al-jabr wa’ l-muqabala”, qëkonsiderohet si libri i parë i shkruar për algjebrën, e ka prejardhjen termi algjebër.

 Al-Khwarizmi, i përkthyer në latinisht si “Algoritmi” ose “Algaurizin”, ishtematematikan, astronom dhe gjeograf gjatë perandorise Abaside (Kalifati Abasid,ishte kalifati i tretë islam që pasoi Profetin Muhamed) dhe ishte dijetar, studiues dheshkencëtar në “Shtëpinë e  diturisë/urtësisë”  (Dār al -Ḥikma), në Bagdad. Nëshekullin e dymbëdhjetë, përkthimet e punës së tij në latinishte për numrat indianprezentuan sistemin numerik pozicional decimal në botën përëndimore. Libri i tij“Përmbledhje e llogaritjeve me kompletim dhe balansim” prezentoi zgjidhjen e parësistematike të ekuacioneve lineare dhe kuadratike. Në kohën e renesansësevropiane, ai konsiderohej si zbuluesi origjinal i algjebrës, edhe pse tash dihet sepuna e tij bazohej në burime më të vjetra indiane dhe të greqisë antike.

Edhe fjalët e mbetura prej punimeve të tij flasin për kontributin e tij në matematikë.Fjala algjebër, që rrjedhë prej fjalës “al- jabr”, që ishte njëri prej dy operacioneve qëai përdori për të zgjidhur ekuacionet kuadratike. Poashtu, termi “Algorism” dhe“Algorithm”, buron prej formës latine të emrit të tij. Punimi i tij në latinisht ishtequajtur “Algoritmi de numero indorum”. 

Në hyrje të librit të tij, ai kishte shkruar: “ Dashuria për shkencë…

dashamirësia dhe përfillja të cilën

 Zoti e tregon për të diturit, ajo përpikmëri me të cilën ai i mbronë

dhe përkrahë ata në sqarimin e

 paqartësive dhe eleminimi e

vështirësive, më ka inkurajuar që të

 përpiloj një punim të shkurtër për

llogaritjen me “al - jabr” dhe “al -

muqabala”, 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ëpjesës së zbritur në anën tjetër tëekuacionit; al-muqabala  është"krahasimi" dhe i referohetzbritjes/thjeshtimit të vlerave të njëjtanë të dy anët e ekuacionit). 

Abu Ja'far Abdallah Muhammad ibn

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

Kur në ndonjë gjuhë programuese shkruajmë programe për kompjuter, ne në

 përgjithësi implementojmë metodën që është zbuluar (shpikur, krijuar) më parë,

Page 7: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 7/699

Algoritmet dhe strukturat e të dhënave

7

 për zgjidhjen e ndonjë problemi, gjegjësisht algoritmin për zgjidhjen e problemit. Çdo program është ilustrim i ndonjë algoritmi.

Kjo metodë është zakonisht e pavarur prej gjuhës programuese dhe prej

kompjuterit të veçantë që do të përdoret dhe zakonisht është njësoj e përshtatshme për shumë kompjuterë dhe për shumë gjuhë programuese. Në fakt,më shumë është metoda sesa vetë programi kompjuterik që duhet të studiohet për të mësuar se si është duke u “atakuar” problemi. Termi algoritëm përdoretnë shkencat kompjuterike për të përshkruar metodën e përshtatshme përzgjidhjen e problemit dhe për ta implementuar si program kompjuteri.Algoritmet janë “material” (lëndë e parë, lëndë pune) për shkencatkompjuterike. Ato janë objekti qëndror i studimit në të gjitha pjesët e kësajfushe.

Shumica e algoritmeve të rëndësishme përfshijnë metodat për organizimin e tëdhënave të përfshira në llogaritje. Objektet e krijuara në këtë mënyrë quhenstruktura të të dhënave dhe këto janë gjithashtu objekte qëndrore të studimitnë shkencat kompjuterike. Prandaj, algoritmet dhe strukturat e të dhënave,shkojnë “dorë për dore” (së bashku). Pra, për të kuptuar algoritmet duhetstudiuar edhe strukturat e të dhënave. Ka raste kur algoritmet e thjeshta “nxjerrinnë pah” struktura të kom plikuara dhe anasjelltas, algoritmet e komplikuaramund të përdorin struktura të thjeshta të të dhënave. Parimisht, do të studiohendhe prezentohen tiparet (vetitë, karakteristikat) e shumë strukturave të tëdhënave.

Kur përdorim kompjuterin për të zgjidhur një problem, zakonisht ballafaqohemime një numër të qasjeve të ndryshme të mundshme për zgjidhjen e problemit.Për problemet e vogla, rrallë herë është me rëndësi se cila qasje përdoret, përderisa e kemi atë që e zgjidhë problemin si duhet. Mirëpo, për problemet emëdha (ose për aplikacionet ku duhet njënumër i madh i problemeve të vogla),shpejt motivohemi që të krijojmë metoda të cilat përdorin kohën dhe hapësirën(memorike) në mënyrë sa më efikase të mundshme.

Arsyeja kryesore për studimin e dizajnit të algoritmeve është se kjo disciplinë na

 jep potencialin për të bërë kursime të shumta edhe deri në pikën e mundësimit 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ë bëhet një program miliona herë më i shpejtë, duke përdorur një algoritëm të dizajnuarmirë. Kurse, investimi në blerjen e kompjuterit të ri me performansa më të mira, për të njëjtin problem, ka potencial të përshpejtimit me faktor prej vetëm 10 ose100 herë. Dizajni i kujdesshëm i algoritmit është pjesë jashtëzakonisht efektive e procesit të zgjidhjes së problemeve të mëdha, në çdo sferë të aplikimit.

Kur duhet zhvilluar një program jashtëzakonisht i madh ose i komplikuar, duhet

“investuar” shumë përpjekje në të kuptuarit dhe definimin e problemit që duhet

Page 8: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 8/699

Avni Rexhepi

8

zgjidhur, menaxhimin e kompleksitetit dhe dekompozimin (zbërthimin) nënënprobleme të vogla të cilat mund të implementohen me lehtësi. Shpeshherë,shumë prej algoritmeve, janë të lehta për t’u implementuar  pas dekompozimit.Mirëpo, në shumicën e rasteve, janë disa algoritme, zgjedhja e të cilave ështëkritike, sepse shumica e resurseve të sistemit do të shpenzohet në ekzekutimin etyre. Pra, është me rëndësi të studiohen algoritmet themelore të cilat janë tëdobishme për zgjidhjen e problemeve në një spektër 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,mirëpo ne do të mirremi me implementimin e versioneve të thjeshta tëalgoritmeve themelore, përmes së cilave ato kuptohen më mirë dhe pastaj mëlehtë përdoren për të “akorduar” (përmirësar në detaje) versionet e gatshme ngalibraritë. Ç’është më e rëndësishme, mundësia e reimplementimit të algoritmeve bazike paraqitesh shumë shpesh. Arsyeja primarë për të vepruar kështu qështë seshumë shpesh ballafaqohemi me ambient tërësisht të ri hardverik dhe softverik,me veti të cilat implementimet e vjetra nuk mund t’i përdorin për të përfituar samë shumë. Me fjalë tjera, shpeshherë implementojmë algoritmet bazike të“qepura” për problemin tonë, sesa të varemi nga një rutinë (nënprogram)sistemor, për t’i bërë zgjidhjet më portabile dhe më afatgjata. Një arsye tjetër eshpeshtë për të reimplementuar algoritmet bazike është se përkundëravantazheve të inkorporuara në C++, mekanizmat që përdoren për

 bashkëpërdorim (sharing) të softverit nuk janë gjithmonë mjaft të fuqishme përtë na lejuar që të përshtasim programet e librarive që të performojnë efektivishtnë detyra specifike.

Programet kompjuterike janë shpeshherë të tejoptimizuara (angl.overoptimized). Mund të mos ia vlenë që të mirret mundimi për t’u siguruar qënjë implementim i një algoritmi të caktuar është më efikasi i mundshëm, përveqnëse ai algoritëm do të përdoret për detyra jashtëzakonisht të mëdha ose do të përdoret shumë herë. Përndrsyhe, një implementim relativisht i thjeshtë, izgjedhur 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 ngadalshëm sesa versioni më i mirë imundshëm, por kjo do të thotë se do të marrë disa sekonda kohë shtesë përekzekutim. Për kontrast, zgjidhja e duhur e algoritmit në vend të parë, mund të bëjë ndryshimin për faktorë 100 ose 1000 apo më shumë herë, gjë që mund të përkthehet në minuta, orë ose edhe më shumë kohë për ekzekutim. Kryesisht dotë koncentrohemi në implementimet më të thjeshta të arsyeshme të algoritmevemë të mira.

Zgjedhja e algoritmit më të mirë për ndonjë detyrë të caktuar mund të jetë proces i komplikuar, ndoshta duke kërkuar analizë matematikore të sofistikuar.

Page 9: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 9/699

Algoritmet dhe strukturat e të dhënave

9

Dega e shkencave kompjuterike e cila përfshinë studimin e pyetjeve të tilla,quhet “analiza e algoritmeve”. Shumë prej algoritmeve përmes analizës së tillë janë treguar që kanë performansë të shkëlqyeshme, ndërsa të tjerat thjeshtë dihetse punojnë mirë, nga përvoja. Qëllimi kryesor është që të mësohen algoritmet earsyeshme për detyrat e rëndësishme, mirëpo duke u kujdesur për krahasimin e performansave të metodave. Nuk duhet të përdoret një algoritëm pa pasur ide sesa resurse mund të konsumojë dhe duhet përpjekur që të jemi të vetëdijshëm përatë se si mund të pritet të performojë algoritmi.

Algoritmet manipulojnë me të dhënat, të cilat mund të jenë vlera të veçanta tëtipeve të thjeshta të të dhënave ose të quajtura ndryshe primitive (primitivedata), si bitat, karakteret, numrat natyral, numrat real, etj dhe mund të jenë tëdhëna të strukturuara në forma më të avansuara, për të ju përshtatur nevojavenga realiteti, të ashtuquajtura struktura të të dhënave (angl. Data Structures).

Strukturat e të dhënave

Organizimi i të dhënave për përpunim (angl. processing- përpunim, procesim,shqyrtim), është detyrë thelbësore në zhvillimin e programeve kompjuterike.

Shumë algoritme kërkojnë përdorimin e reprezentimit të duhur të të dhënave përtë qenë efektive. Ky reprezentim i të dhënave dhe operacionet përcjellëse për to,njihen si struktura të të dhënave. Secila strukturë e të dhënave mundësoninsertimin arbitrar por dallojnë në atë se si mundësojnë qasjen në anëtarët e

grupit. Disa struktura të të dhënave lejojnë qasjen dhe fshirjen arbitrare, gjersa tëtjerat imponojnë kufizime, si lejimi i qasjes vetëm në elementin e fundit tëinsertuar ose vetëm në elementin e parë të insertuar në grup.

Struktura e të dhënave mundëson arritjen e një prej qëllimeve të programimit tëorientuar në objekte: ripërdorimi i komponenteve. Secila strkuturë e të dhënavee implementuar një herë, mund të ripërdoret përsëri në aplikacione të ndryshme.

Struktura e të dhënave pra është reprezentimi i të dhënave dhe operacioneve nëato të dhëna. Shumë struktura të të dhënave ruajnë një koleksion të objektevedhe pastaj ofrojnë metodat për të shtuar objekte, për të larguar objektet

ekzistuese ose për të ju qasur objekteve të koleksionit.

Standardi i C++-it kërkon që të gjitha implementimet të ofrojnë libraritë përkrahëse të njohura si Standard Template Library (Libraria Standarde eShablloneve, shkurt STL). STL ofron koleksionin e strukturave të të dhënavedhe ofron disa algoritme themelore, si p.sh., sortimi. Si tregon edhe vet emri,STL përdorë me të madhe shabllonet.

Për shumë aplikacione, zgjedhja e strukturës së duhur të të dhënave ështëvendimi i vetëm i rëndësishëm i përfshirë në implementim: kur të jetë bërë

zgjedhja, algoritmet e nevojshme janë të thjeshta. Për të njëjtat të dhëna, ndonjë

Page 10: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 10/699

Avni Rexhepi

10

strukturë e të dhënave mund të kërkojë më shumë ose më pak hapësirë sesa tëtjerat; për ndonjë operacion (veprim) me të dhënat, disa struktura mund të çojnënë algoritme më efikase ose më pak efikase, se të tjerat. Zgjedhja e algoritmitdhe e strukturës së të dhënave janë të ndërlidhura ngushtë dhe vazhdimishtkërkojmë mënyra për të kursyer kohën ose hapësirën, duke bërë zgjedhjen eduhur.

Struktura e të dhënave nuk është objekt pasiv. Ne duhet të marrim nëkonsiderim edhe operacionet të cilat do të kryhen në të (dhe algoritmin e përdorur për këto operacione). Ky koncept është i formalizuar me nocionin: tipii të dhënave (angl. data type). Interesimi primar është në implementimin konkrettë qasjeve themelorë të cilat përdoren për strukturimin e të dhënave. Shqyrtojmëmetodat themelore të organizimit dhe metodat për manipulimin e të dhënave, përmes shembujve specifik të cilët ilustrojnë përfitimet për secilin dhe çështjet endërlidhura, si mengaxhimi i memories. Gjithashtu, do të diskutohen tipetabstrakte të të dhënave (ADT-Abstract Data Types), ku ndahen definicionet etipeve të të dhënave prej implementimeve.

Do të diskutohen tiparet e vargjeve, listave të lidhura dhe stringjeve. Këtostruktura klasike të të dhënave kanë përdorim të gjerë. P.sh., tek pemët(struktura e të dhënave, në formë peme), ato praktikisht formojnë bazën për pothuajse të gjitha algoritmet. Do të shqyrtohen edhe operacionet e ndryshme primitive për manipulimin e këtyre strukturave të të dhënave, për të zhvilluar një bashkësi themelore (angl. basic set) të veglave të cilat mund të përdoren përzhvillimin e algoritmeve të sofistikuara për problemet e vështira.

Studimi i ruajtjes së të dhënave si objekte me madhësi të ndryshueshme (angl.variable-size objects) dhe në strukturat e lidhura të të dhënave kërkon njohuri për mënyrën se si sistemi e menaxhon hapësirën e ruajtjes (hapësirën memorike- angl. storage) të cilën ua alokon (ndanë) programeve për të dhënat e tyre. Nëfakt, diskutohet qasja e menaxhimit të hapësirës dhe disa mekanizmavethemelorë të përgjithshëm, sepse shumë elemente janë të varura nga vet sistemetdhe pajisjet që përdoren. Do të shohim mënyrat specifike për të cilat përdoren

mekanizmat e C++-it për alokim të hapësirës.Poashtu, do të shqyrtohen disa shembuj të strukturave të përbëra, si vargjet elistave të lidhura dhe vargjet e vargjeve. Nocioni i ndërtimit të mekanizmaveabstrakt të rritjes së kompleksitetit nga nivelet e ulëta është temë që përsëritet.Shembujt, pastaj mund të shërbejnë si bazë për algoritme më të avansuara.

Këto struktura të të dhënave janë të blloqe ndërtimi  të rëndësishme (angl. building blocks) të cilat mund të përdoren në mënyrë natyrale në C++ dhe nëshumë gjuhë të tjera programuese. Vargjet, stringjet, listat e lidhura dhe pemët, janë elementet themelore të ndërtimit të shumë algoritmeve. Reprezentimi

Page 11: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 11/699

Algoritmet dhe strukturat e të dhënave

11

konkret i zhvilluar në ndërtimin e tipeve abstrakte të të dhënave plotëson nevojate shumë apliacioneve.

Strukturat themelore të të dhënave

Të dhënat ruhen në memorie. Kur kemi për të ruajtur vlera të veçanta,interpretimi logjik përputhet shumë lehtë me realitetin fizik, sepse p.sh., kurdeklarojmë ‘int x=10;’ themi që kemi deklaruar një numër të plotë, me emrinx dhe i kemi dhënë vlerën 10. Është lehtë të imagjinohet, se diku në memorie,do të ruhet vlera 10.

Kur kemi një bashkësi të të dhënave, që dëshirojmë ta ruajmë si një tërësi, p.sh.,notat e studentit, pagat e punëtorëve, etj., atëherë e krijojmë një varg. Anëtarët evargut janë të njëjtë për nga tipi dhe lokalizohen në memorie në lokacione tënjëpasnjëshme. Deklarimi i vargut, bën që të rezervohet hapësira e duhur nëmemorie dhe pastaj aty vendosen vlerat e anëtarëve të vargut. Më vonë, përmesqasjes direkte ose pointerëve, mund të bëhet qasja në anëtarët e vargut. Edhe nëkëtë rast, interpertimi logjik është i thjeshtë, sepse e imagjinojmë vargun elokacioneve të njëpasnjëshme në memorie, ku i kemi të vendosura disa vlera.

Mirëpo, për arsye të ndryshme, ndonjëherë nuk ka mundësi ose nuk është e përshtatshme që të gjitha vlerat e bashkësisë të ruhen në lokacione tënjëpasnjëshme në memorie. Atëherë kemi “mospërputhje” ndërmjet realitetitfizik dhe interpretimit logjik nga ana e jonë.

Pra, të dhënat e një bashkësie, për nga pozicionimi fizik në memorie, mund të jenë:

-   Në lokacione të njëpasnjëshme (sekuenciale) në memorie-   Në lokacione të shpërndara (jo-sekuenciale).

Vargu është strukturë me anëtarë të vendosur në lokacione sekuenciale. Listat janë me anëtarë në lokacione josekuenciale. Nëse të dhënat nuk janë tëvendosura fizikisht në lokacione të njëpasnjëshme në adresat e memories, por

ato logjikisht duhet të përcillen si anëtarë të njëpasnjëshëm të bashkësisë,atëherë krijojmë strukturën e të dhënave, e cila më nuk përmbanë vetëm vlerat(të dhënat) e tipit të caktuar, por secili anëtarë është i përcjellur edhe meinformacione plotësuese, të cilat mundësojnë ndërlidhjen logjike me anëtërët etjerë të bashkësisë. Këto “tërësi” të reja, tani përveq vlerës, kanë edhe“elementin për ndërlidhje”, pra elementin e ri plotësues (pointerin), ashtu që tëna mundësojnë që të “lëvizim” prej një anëtari në tjetrin, ngjashëm sikur lëvizimnëpër anëtarët e vargut të zakonshëm, prej një lokacioni të memories në tjetrin(në fakt duke kaluar prej një anëtari në tjetrin). Kjo tërësi e re, e krijuar prej vetëvlerës dhe prej pointerëve të cilët e lidhin me anëtarin e përparshëm dhe/ose atë

Page 12: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 12/699

Avni Rexhepi

12

të ardhshëm, zakonisht quhet “Nyje” (angl. Node). Pra, nyja përmbanë vlerën(angl. value) ose të dhënat (angl. data) dhe pointerin ose pointerët, që endërlidhin atë me nyjen e ardhshme dhe atë të përparshme, ashtu që edhe psefizikisht të vendosur në lokacione të ndryshme të memories, logjikisht anëtarët përsëri krijojnë një listë me anëtarë të njëpasnjëshëm.

Ky organizim i të dhënave të renditura, ku anëtarët e njëpasnjëshëm janë nëlokacione të shpërndara të memories, por janë të “lidhur” mes vete përmes pointerëve quhet listë e lidhur. Pra, lidhjen prej një anëtari (lokacioni tëmemories) deri tek anëtari tjetër (lokacioni tjetër në memorie), e realizojmë përmes pointerëve, të cilët tregojnë pozitën e anëtarit të ardhshëm ose atij të përparshëm (Rikujtojmë se pointeri ruan adresa, kështu që pra tregon adresën seku ndodhet anëtari përkatës). Nëse struktura e të dhënave, për secilin anëtarë(nyje) definon vetëm vlerën dhe pointerin për në pozitën e ardhshme, themi sekemi të bëjmë më listën e lidhur njëfish, pasi që lëdhja është vetëm nënjërinkah (drejtim). Nëse struktura për secilin anëtarë të vetin, ka vlerën dhe dy pointerë, njëri për anëtarin e përparshëm dhe tjetrin për anëtarin e ardhshëm nëlistë, atëherë kemi të bëjmë me listën e lidhur dyfish.

 Figura 1 –  Krahasimi i vargut dhe listës së lidhur

Për nga aspekti i renditjes logjike të anëtarëve, strukturat mund të jenë:-  Lineare (vargu, listat e lidhura, steku, rreshti i pritjes, etj), -  Jo-lineare (pemët, grafet).

Strukturat lineare janë lineare në atë që ndërmjet objekteve në strukturë ruhetrenditja lineare. Relacioni linear është logjik, në atë që për dallim prej vargjeve,nuk mund të bëhet ndonjë presupozim për lidhjen ndërmjet renditjes lineare tëobjekteve dhe lokacioneve të tyre aktuale në memorie. Strukturat linearedallojnë prej njëra tjetrës për nga kufizimet në mënyrat te qasjes në anëtarët etyre.

Page 13: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 13/699

Algoritmet dhe strukturat e të dhënave

13

 Në varësi të zgjedhjeve të opcioneve për numrin e lidhjeve (pointerëve përlidhje) dhe për lidhjen e pointerit të fundit në strukturë, janë katër lloje tëreprezentimit të listave.

Për nga mënyra e lidhjes së pointerit të fundit, janë dy mundësi: ose pointeri ifundit bëhet “Null” (angl. Null-asgjë, nuk ekziston) ose kthehet në anëtarin e parë në strukturë. Nëse anëtari i fundit tregon në “Null”, thuhet se strukturaështë e “tok ëzuar” dhe paraqitet zakonisht me simbolin elektronik të tokëzimit. Nëse pointeri i fundit ktheht në anëtarin e parë në strukturë, atëherë thuhet sestruktura është qarkore (cirkulare).

Për nga numri i lidhjeve, mund të ketë vetëm një pointer për në elementin eardhshëm në strukturë ose dy pointerë, që pointojnë njëri në elementin e përparshëm dhe tjetri në elementin e ardhshëm. Struktura lineare e lidhur me

vetëm një element për lidhje (pointer) quhet listë e lidhur në një kahje ose listë elidhur një-fish. Struktura me dy lidhje formon listën e lidhur në dy kahje oselistën e lidhur dy-fish.

 Nga kjo del se listat e lidhura mund të jenë: një-fishe të tokëzuara, një-fishecirkulare, dy-fishe të tokëzuara dhe dy-fishe cirkulare.

...

...

.

.

.

...

.

.

.

...

  Figura 2 –  Llojet e listave të lidhura

Për nga aspekti i krijimit/rezervimit të hapësirës në memorie,vargjet/listat/strukturat ndahen në:

-  Statike, dhe-  Dinamike.

Kur bëhet deklarimi i zakonshmëm i vargut, si p.sh., ‘inta A[10];’, në fakt bëhet përcaktimi i tipit dhe numrit të anëtarëve dhe rezervohet hapësira enevojshme në memorie (në lokacione të njëpasnjëshme). Gjatë ekzekutimit të programit, madhësia e vargut dhe lokacioni në memorie nuk ndryshojnë, kështëqë themi se kemi të bëjmë me varg/strukturë statike.

Page 14: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 14/699

Avni Rexhepi

14

Kur deklarimi i vargut/listës bëhet në kohën e ekzekutimit, përmes përmesoperatorit ‘new’(i cili përcakton lokacionin në memorie dhe pointerin për atëlokacion) dhe gjatë ekzekutimit shtohen ose largohen anëtarët e listës, atëherëthemi se kemi të bëjmë më strukturë dinamike.

Krijimi i tipit abstrakt të të dhënave (angl. Abstract Data Type  –   ADT) namundëson që të krijojmë struktura logjike, të cilat i përshtaten nevojave të programit dhe realitetit nga jeta e përditshme, kurse realizimi fizik i tyre (“ në prapavi”) përsëri mbetet i bazuar në atë që është e realizueshme fizikisht, si bashkësi e lokacioneve të njëpasnjëshme ose të atyre të shpërndara në memorie. Nëse lokacionet janë të krijuara dinamikisht (gjatë ekezekutimit) dhe rezervohennë pozita të ndryshme në memorie, atëherë përmes pointerëve të tyre, i përcjellim lokacionit e të dhënave, si në figurën vijuese.

 Figura 3a - Vendosja e katër elementeve në memorie

 Figura 3b - Një mënyrë e ruajtjes së pointerëve për  përcjellje të lokacioneve

Kur krijojmë ADT dhe deklarojmë strukturën përkatëse, më nuk kemi të bëjmë

vetëm më vlerën (të dhënën) që ruhet në memorie, por edhe me të gjitha

Page 15: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 15/699

Algoritmet dhe strukturat e të dhënave

15

elementet përcjellëse, të cilat mundësojnë trajtimin logjik të të dhënave, siç janë pointerët të cilët mundësojnë lëvizjen nëpër dhe përcjelljen e anëtarëve si dhefunksioneve përkatëse, të cilat shërbejnë për ‘t’i dhënë jetë’ tëdhënave/elementeve të strukturës.

Funksionet krijohen për operacionet e zakonshme të cilat ndodhin me të dhënat:insertimi, leximi, shtypja, editimi, fshirja (largimi), etj. Kështu strukturat ekompletuara, të realzuara në C++, si strukturë ose klasë ose edhe ato të gatshmetnga STL-i, i kanë të gjitha këto 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, trashëgimisë,abstraksionit, ripërdorimit, polimorfizmit, etj.

Klasat 

Klasa është një strukturë (struct) e zgjeruar, që ofron tiparet e orientuara nëobjekte të C++-it. Klasa definohet nga shfrytëzuesi duke përshkruar një bashkësi

të të dhënave (vlerave) që mund t’i përfaqësojë dhe një bashkësi të funksionevetë cilat mund të veprojnë (operojnë) në ato të dhëna. Këto të dhëna dhefunksione të klasës quhen anëtarë të klasës (angl. class members). Klasat janëtipe të të dhënave nga të cilat krijohen objektet. Klasat enkapsulojnë (angl.encapsulate-futë në kapsulë) të dhënat përmes përdorimit të anëtarëve të dhënadhe anëtarëve funksione.

Objektet 

Bashkimi i të dhënave dhe funksioneve është koncepti në prapavi të gjuhëve

 programuese të orientuara në objekte. Njësia e tillë (e bashkuar) quhet objekt.Objekti është një instancë e klasës (një rast konkret, një konkretizim i klasës).Klasa ka relacion të njëjtë me objektet sikur tipet themelore të të dhënave mevariablat e tipit të tyre. Një objekt mund të definohet në mënyrë unike përmesnjë emri specifik (identifikatori). Objekteve u ndahet memoria dhe një objektmund të përmbajë disa atribute.

Trashëgimia 

Trashëgimia (angl. inheritance) është një prej vetive më të fuqishme të programimit të orientuar në objekte. Trashëgimia është procesi përmes të cilit

Page 16: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 16/699

Avni Rexhepi

16

një klasë mund të trashëgojë vetitë e një klase tjetër. Klasa ekzistuese quhetklasë bazë, ndërsa klasa e re quhet klasë trashëguese. Trashëgimia përdoret përtë redukuar kodin burimor në programimin e orientuar në objekte. Pa përdorimtë trashëgimisë, secila klasë do të duhet të definojë të gjitha karakteristikat eveta në mënyrë eksplicite. (angl. explicit  –   i caktuar, i hollësishëm, i qartë, isaktë; implicit-i nënkuptuar, i padyshimtë).

Klasa bazë i përmbledhë elementet e përbashkëta për një grup të klasavetrashëguese. Klasat trashëguese përveq që i ekzekuton elementet e përbashkëtaqë i trashëgon, i ekzekuton gjithashtu edhe ato që i ka karakteristike të vetat.Grupimi i karakteristikave të përbashkëta dhe vendosja e tyre në një vend, nëvend të përsëritjes së tyre në të gjitha vendet ku ato ndodhin, në një mënyrë eredukon madhësinë e programeve.

Ripërdorimi Kur klasa të jetë shkruar, krijuar dhe debug-uar (debaguar), ajo mund tëshpërndahet edhe tek programerët e tjerë për përdorim në programet e tyre. Kjoveti referohet si ripërdorshmëri (angl. reusability; nga use-përdorim dhe ability-mundësi, aftësi, pra aftësi e të qenit e ripërdorshme). Programerët mund tëmarrin një klasë ekzistuese dhe pa e modifikuar atë, t’i shtojnë karakteristikadhe aftësi plotësuese. Kjo veti referohet si ‘extensibility’ –  zgjerueshmëri (angl.extensibility-zgjerueshmëri, zgjatshmëri). Kjo bëhet duke derivuar (trashëguar)një klasë të re nga një klasë ekzistuese. Klasa e re do të trashëgojë tiparet e

vjetrës dhe poashtu do të shtojë tipare të veta të reja.Enkapsulimi 

Enkapsulimi ose enkapsulimi i të dhënave (angl. Data encapsulation) është një prej vetive më të rëndësishme të klasave. Në programimin e orientuar nëobjekte, një objekt krijohet duke përfshirë të dhënat dhe funksionet për lexim(hyrje) dhe shtypje (dalje) të të dhënave. Objekti përkrahë enkapsulimin.Enkapsulimi është procesi i kombinimit të funksioneve anëtare të klasës dhe tëdhënave (vlerave) anëtare të klasës, si dhe mbajtjes së tyre të sigurta ngainterferencat (ndërhyrjet) nga jashtë. Të dhënat nuk janë të qasshme nga jashtëdhe vetëm funksionet të cilat janë brenda klasës mund të ju qasen atyre. Izolimi itë dhënave nga qasja direkte, nga programerët quhet “fshehje e të dhënave”(angl. data hiding).

Abstraksioni 

Abstraksioni ose abstraksioni i të dhënave (angl. Data abstraction) ështëmundësia e krijimit të tipeve të të dhënave të shfrytëzuesit për të modeluarobjektet e botës reale, duke përdorur tipet e brenshme të të dhënave.Abstraksioni i të dhënave ndihmon për të ju qasjur të dhënave dhe funksioneve

së bashku, gjë që definon tip të ri të të dhënave të quajtur tip abstrakt i të

Page 17: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 17/699

Algoritmet dhe strukturat e të dhënave

17

dhënave (angl. Abstract data type-ADT) me setin e vet të operacioneve.Abstraksioni i të dhënave i referohet veprimit të reprezentimit të vetivethemelore, pa i përfshirë detajet ose shpjegimet. Klasat ndihmojnë në krijimin etipeve abstrakte të të dhënave. Klasat përdoren për abstraksionin e të dhënaveduke fshehur implementimin e tipit në pjesën private të definicionit të klasës dheduke ofruar interfejsin përmes pjesës publike të funksioneve.

Polimorfizmi 

Polimorfizmi është vetija që lejon që një emër të përdoret për dy ose më shumëqëllime të ndërlidhura, por teknikisht të ndryshme. (Fjala polimorfizëm rrjedhnga greqishtja e vjetër: poli-shumë, morphe-formë). Polimorfizmi lejon që njëemër të specifikohet për veprimet e përgjithshme të klasës. Polimorfizmi do tëthotë që një pjesë e kodit (zakonisht funksion) ose operacionie apo objekte, kanë

sjellje të ndryshme në kontekste të ndryshme. Në klasën e përgjithshme, ndonjëveprim specifik që duhet të aplikohet, përcaktohet (varet) nga tipi i të dhënave.Përparësia e polimorfizmit është se ndihmon në zvogëlimin e kompleksitetit të programit duke lejuar që një interfejs të specifikoj një klasë të përgjithshme tëveprimeve. Në C++, polimorfizmi kryesisht i referohet përdorimit tëfunksioneve ‘virtuele’. Mund të ketë dy objekte të krijuara nga nje klasë, por tëcilat funksionin virtuel (me emër të njëjtë për të dyjat), e përdorin për llogaritjetë ndryshme.

Mbingarkimi është veti shumë e dobishme në C++. Wshtë e mundur që të

 përdoret emri i njëjtë i funksionit për qëllime të ndryshme. Funksioni i duhur dotë thirret bazuar në numrin, radhën ose tipin e parametrave (argumenteve) tëfunksionit. Ky proces referohet si mbingarkim i funksioneve ose polimorfizëm ifunksioneve. Polimorfizmi mund të aplikohet poashtu edhe në operatorët endryshëm dhe ky proces njihet si mbingarkimi i operatorëve ose polimorfizmi ioperatorëve.

Përparësitë e programimit të orientuar në objekte

Programimi i orientuar në objekte ofron shumë përparësi për programerët dheshfrytëzuesit. POO zgjidhë shumë probleme të ndërlidhura me zhvillimin esoftverit, ofron kualitetit të përmirësuar dhe softver me çmime më të ulëta.Përparësitë e POO janë:

1.  Programet e orientuara në objekte mund të promovohen (angl. upgrade)në çdo kohë dhe me lehtësi.

2.  Duke përdorur trashëgiminë, mund të eliminohen kodet redundante (tëtepërta) dhe mund të vazhdohet përdorimi klasave të definuara më parë.

Page 18: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 18/699

Avni Rexhepi

18

3.  Vetia e fshehjes së të dhënave i mundëson programerit që të dizajnojëdhe të zhvillojë programe të sigurta të cilat nuk e çrregullojnë kodin në pjesët tjera të programeve.

4.  Vetia e enkapsulimit u lejon programerëve që të definojnë klasën meshumë funksione dhe karakteristika, ndërsa vetëm disa funksione iekspozohen shfrytëzuesit.

5.  Të gjitha gjuhët programuese të orientuara në objekte mund të krijojnë pjesë të zgjeruara dhe të ripërdorshme të programeve.

6.  POO zgjeron procesin e të menduarit të programerëve duke dërguar nëzhvillimin e shpejtë të softverit të ri në kohë më të shkurtë.

Strukturat e të dhënave dhe reprezentimi i tyre

Strukturat e të dhënave klasifikohen si lineare dhe jo-lineare. Struktura e tëdhënave thuhet se është lineare nëse elementet e saj të të dhënave formojnësekuencë. Kjo do të thotë që nëse e dijmë adresën e elementit të parë, ne mundtë marrim (përfitojmë) të dytin, të tretin, e kështu me radhë deri në elementin efundit të të dhënave. Shembuj të strukturave lineare janë vargjet, steku, queue,listat e lidhura, etj. Ndërsa në strukturat jo-lineare, elementet e të dhënave ruhennë hierarki ose në nivele. Shembuj të tyre janë pemët dhe grafet.

Mënyrat e ruajtjes

Synimi kryesor i strukturave të të dhënave ësthë ruajtja e disa të dhënave(vlerave) ose organizimi i të dhënave në ndonjë formë të veçantë. Për ngamënyra se si ruhen ose mirëmbahen në memorie të kompjuterit, janë dy mënyratë reprezentimit (përfaqësimit) të strukturave të të dhënave (lineare ose jo-lineare) në memorie. Njëra është metoda sekuenciale e ruajtjes ndërsa tjetraështë metoda e alokimit të lidhur. Ato poashtu quhen edhe si alokimi statik dhealokimi dinamik, repsektivisht.

Metoda e ruajtjes sekuenciale: nëse elementet janë të ruajtura në lokacione tënjëpasnjëshme në memorie, ato njihen si metoda e ruajtjes sekuenciale. Kjo do

të thotë që së brendshmi është e ruajtur në një varg një-dimensional. Poashtuquhet edhe si alokimi statik, pasi që memoria në esencë është një varg i adresaveose lokacioneve të memories.

Reprezentimi i lidhur: nëse elementet do të ruhen në lokacione të ndryshme (tëshpërndara) në memorie dhe pointerët do të japin (krijojnë) renditjen lineare, kjoquhet reprezentimi i lidhur.

Page 19: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 19/699

Algoritmet dhe strukturat e të dhënave

19

Operacionet në strukturat e të dhënave

Për çfarëdo strukture të të dhënave, mund të kryhen operacionet themelorevijuese:

1.  Krijimi ose insertimi2.  Fshirja/largimi3.  Paraqitja/përshkimi4.  Sortimi5.  Kërkimi6.  Bashkimi

 Në rastin e vargjeve, këto operacione kryhen si vijon.

 Krijimi ose insertimi. Kur struktura është fillimisht e zbrazët dhe në të insertohet

elementi i parë, atëherë bëhet krijimi. Në vazhdim, kur ajo më nuk është ezbrazët, për çdo element të ri kemi operacionin e insertimit.

Le të supozojmë se dëshirojmë të insertojmë një element të ri në varg, në pozitën e kërkuar ndërmjet 0 dhe (n-1). Logjika është që të zhvendoset (lëvizet)elementi i (n-1)-të në pozitën e n-të, ai i (n-2)-të në atë të (n-1)-të e kështu meradhë, deri sa të arrihet në pozitën e insertimit. Pastaj vendoset/insertohetelementi i ri në atë pozitë. Kështu, numri total i elementeve rritet për një.

 Fshirja. Fshirja bën largimin e një elementi nga pozita e kërkuar. Pas fshirjes sëelementit, duhet të zhvendosim/lëvizim të gjitha elementet pasuese për nga një pozitë majtas dhe numri total i elementeve zvogëlohet për një.

 Përshkimi i vargut . Përshkimi (angl. Traversal) nënkupton shtypjen ose procesimin e secilit element të vargut. Nëse vargu është i zbrazët atëherëeshtypet porosia se vargu është i zbrazët, përndryshe shtyen elementet prej të parit deri tek i fundit.

Sortimi. Përmes një procedure (funksioni) të sortimit renditen elementet e vargutnë renditje prej të voglit kah i madhi ose anasjelltas.

Lista e lidhur

Lista e lidhur është koleksion linear i elementeve të të dhënave të quajtura nyje,ku renditja lineare realizohet përmes pointerëve. Secila nyje është e ndarë në dyose më shumë pjesë, të quajtura fushat e informacionit dhe fushat e adresave.Fusha e informacionit ose fusha e të dhënave (vlerave) përdoret për të ruajturinformacionin ose elementin e të dhënës, ndërsa fusha e adresave përdoret për tëruajtur adresën e ndonjë nyjes tjetër në listë. Secila nyje do të ketë adresë unike. Nëse nyja përmbanë një fushë të të adresës dhe një ose më shumë fusha të tëdhënave thuhet se kemi listë të lidhur njëfish (angl. single linked list) ose

zingjirë në një kahje. Nëse nyja përmbanë dy fusha adresash dhe një ose më

Page 20: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 20/699

Avni Rexhepi

20

shumë fusha të dhënash, thuhet se kemi të bëjmë me listë të lidhur dyfish (angl.double linked list) ose zingjirë dy-kahësh.

Info Lidhja  a.  Nyja me një lidhje

Info Lidhja_dLidhja_m  b.  Nyja me dy lidhje

 Figura 4 –  Nyjet

 Në figurën ‘a’ është paraqitur nyja e listës së lidhur njëfish, e cila ka dy fusha:‘Info’ dhe ‘Lidhja’. Fusha ‘Info’ përdoret për të ruajtur informacionin (vlerën, tëdhënën), ndërsa fusha ‘Lidhja’ përdoret për të ruajtur adresën e nyjes sëardhshme. Nëse nyja është e fundit, atëherë në fushën e lidhjes adresa është NULL, që do të thotë se nuk tregon në ndonjë nyje.

 Në figurën nën ‘b’ është paraqitur nyja e listës së lidhur dyfish dhe ajo përmbanë dy fusha të adresave, të emërtuara ‘Lidhja_m’ (lidhja majtas) dhe‘Lidhja_d’ (lidhja djathtas). Zakonisht emërtohen edhe ‘ePërparshme’ dhe‘eArdhshme’, për të treguar në nyjen e përparshme dhe nyjen e ardhshme nëlistë. Fusha ‘Info’ përdoret për të ruajtur informacionin aktual.

Supozojmë se ‘p’ është adresa e nyjes së listës së lidhur njëfish. Atëherë, qasjanë të dhënën (vlerën) e nyjes bëhet përmes ‘p->Info’, ndërsa qasja në fushën elidhjes, përmes ‘p->Lidhja’.

 Në mënyrë të njëjtë, për nyjen q të listës së lidhur dyfish, do të kemi: ‘q->Infor’,

‘q->Lidhja_d’ dhe ‘q->Lidhja_m’. 

Lista e lidhur njëfish

Lista e lidhur një-fish (ose lista një-kahëshe) përbëhet prej nëj bashkësie tërenditur të elementeve, të cilat mund të ndryshojnë në numër. Një mënyrë ethjeshtë për të reprezentuar listën lineare është që të paraqiten nyjet të cilat përmbajnë të dhënat dhe lidhjet gjegjësisht pointerët për tek nyja e ardhshme.Për të përcjellur adresën e fillimit të listës, zakonisht përdoret edhe një variabël

ndihmëse (një pointer) me emrin ‘Fillimi’, i cili jep lokacionin e nyjes së parë nëlistë. Nyja e fundit në listë nuk ka ndonjë nyje pasardhëse, kështu që në fushën esaj nuk duhet ndonjë adresë. Në raste të tilla në fushën e adresës zakonisht“ruhet” NULL. 

10 12 18 22...

Fillimi 

 Figura 5 –  Lista e lidhur njëfish

Page 21: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 21/699

Algoritmet dhe strukturat e të dhënave

21

Le të supozojmë se të dhënat në listë mirëmbahen në renditje rritëse dhe sipasfushës me informacion dhe për thjeshtësi le të marrim vlerat të plota (integer).Secila nyje mund të ruaj një numër të plotë. Në këtë strukturë të të dhënavemund të kryhen operacione të ndryshme, si insertimi, fshirja paraqitja ose përshkimi i listës dhe kërkimi.

 Në nënprogramin (funksionin) e insertimit ose krijimit, së pari krijojmë nyjen ere dhe e vendosim në pozitën e duhur në listë. Në rast se nyja insertohet në listëtë zbrazët, fusha e saj e lidhjes (pointeri për në nyjen e ardhëshme) do të ketëvlerën ‘NULL’ ndërsa adresa e kësaj nyje ruhet (vendoset) në variablën(pointerin) ‘Fillimi’, që tregon nyjen e parë të listës. Rasti i dytë është insertimi inyjes në fillim të listës. Në këtë rast, adresa e variablës ‘Fillimi’ ruhet (vendoset)në fushën e lidhjes (Pointerit) për në nyjen e ardhshme, ndërsa adresa e adresa enyjes së parë (të re) vendoset ën variablën ‘Fillimi’. Rasti tjetër është insertimindërmjet dy nyjeve në listë ose në fund të listës. Në këtë rast, përshkojmë listënduke krahasuar elementin e insertuar me secilën nyje me radhë në listë deri sa tëvije në pozitën e duhur (vlera më e madhe ose baraz me nyjen aktuale). Kur të jetë gjetur vendi i duhur, insertohet nyja e re ndërmjet nyjes paraardhëse të nyjesaktuale dhe nyjes aktuale. Sipozojmë se adresa e nyjes paraardhëse është eruajtur dhe adresa e nyjes aktuale është ‘ptr’. Atëherë kjo adresë ruhet në fushëne adresës së nyjes së re dhe adresa e nyjes së re në fushën e lidhjes së nyjes paraprake.

Fshirja: për të fshirë nyjen e kërkuar (që ka vlerën e kërkuar), së pari ekërkojmë në listë. Edhe këtu rast paraqiten raste të ndryshme. Një është fshrija prej listës së zbrazët dhe ajo bëhen “nën-rrjedhë”. Nëse elementi i fshirë ështënë fillim të listës, atëherë pjesa e saj e lidhjes (adresa) ruhet në variablën‘Fillimi’. Përndryshe, kërkohet nyja që duhet të fshihet dhe poashtu nyja paraardhëse e saj. Pastaj, adresa e nyjes së ardhshme të nyjes që fshihet,vendoset në adresën e nyjes së ardhshme të nyjes para-ardhëse, e kështu meradhë.

Lista e lidhur dyfish

Lista e lidhur dyfish (ose lista dy-kahëshe) është e ngjashme me listën një-kahëshe, përveq se ajo e lidhë nyjen në të dy kahjet, edhe me nyjen paraardhëseedhe me atë pasardhëse. Lista është koleksion linear i nyjeve me lidhje tëdyfishta dhe prandaj quhet lista e lidhur dyfish, lista dy-kahëshe ose zingjiri dy-kahësh. Në figurën xxx është paraqitur lista e lidhur dyfish.

Page 22: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 22/699

Avni Rexhepi

22

Fillimi

10 20 30 40

Fundi

  Figura 6 - Lista e lidhur dy-fish

Lista e lidhur dy-fish përmbanë 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 vlerën ‘NULL, ashtu si edhe pointeri i djathtë i nyjes së fundit ‘Lidhja_d’. Përparësi e kësaj lise është semund të qarkullohet në të dy kahjet. Edhe në këtë strukturë të të dhënave kryhenoperacionet si: insertimi, fshirja, kërkimi, përshkimi dhe bashkimi. Për të krijuarose insertuar, së pari do të krijojmë nyjen e parë të re, me operatorin ‘Neë’ dhe

 pastaj e ruajmë vlerën e elementit në fushën e vlerës (informacionit) dhe evendosim nyjen në pozitën e duhur në listë. Kjo nyje vendoset në listën e zbrazëtose si nyje e skajit të majtë (fillimit) apo skajit të djathtë (fundit) ose diku nëlistë, ndërmjet nyjeve ekzistuese dhe i azhurohen vlerat e lidhjeve (pointerëve).Për të fshirë nyjen, së pari verifikohet mos është lista e zbrazët dhe pastajkërkohet vlera që duhet të fshihet. Nëse gjindet, ajo nyje fshihet përmes urdhërit‘Delete’ dhe pastaj azhurohen pointerët e nyjeve fqinje. Ngjashëm kryhen edheoperacionet tjera.

Për nga mënyra mënyra e organizimit dhe për nga mënyra se si e lejojnë ose si e

mundësojnë qasjen në të dhëna (në elemente, në anëtarë), dallojmë struktura tëndryshme.

Steku

Për vargjet/listat, bazuar në idetë nga jeta reale, kemi listat ku anëtarët janë sikurnjë grumbull i librave, njëri mbi tjetrin ose sikur fishekët në karikator. I fundit ivendosur, është i pari në rend për qasje. Këto struktura njihen si strukturat LIFO(Last In, First Out  –   I fundit brenda, i pari jashtë) (ose në renditjen e kundërtFILO (First In, Last Out  –   I pari brenda, i fundit jashtë). Lista LIFO është enjohur 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 anëtarët futen një nga një me radhë dhe mund tështyhen deri në ‘fund’ të stekut, por qasje kemi vetëm në elementin në krye (tëfundit të futur në stek), që quhet ‘top’ (kreu) i stekut. Insertimi dhe tërheqja eanëtarëve bëhet vetëm në një pozitë (në një skaj të gypit).

Page 23: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 23/699

Algoritmet dhe strukturat e të dhënave

23

Operacioni i insertimit quhet “Push”(angl. Push-shtyje, godite, etj), kurseai i nxjerrjes “Pop” (angl. Pop –  nxjerrje, tërheqje). Këto janë dyoperacionet e stekut, të cilat programohen përmes funksioneve përkatëse.

 Figura 7 - Steku

Rreshti, radha e pritjes

 Nëse imagjinojmë gypin e hapur në të dy anët, por ku lëvizet vetëm në njëdrejtim (kahje), atëherë anëtarët futen në “listë” në njërën anë, kursetërhiqen/nxirren nga skaji tjetër, thuhet se kemi të bëjmë me strukturën e quajtur“Queue” (lexohet si “Kju”) (angl. Queue –  radhë e pritjes, rresht, etj). Ky ështëorganizimi i njëjtë sikur vargu i pritjes për bileta të teatrit, për pagesa në arkë,etj., ku i pari që vije i pari shërbehet. Kjo njihet si strukturë FIFO (First In, FirstOut –  I pari brenda, i pari jashtë) (ose në të kundërtën, LILO (Last In, Last Out –  

i fundit brenda, i fundit jashtë). Queue lejon qasjen në dy skaje të gypit, nënjërin për insertim dhe në tjetrin për 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”. Nënkuptohet që insertimi bëhet vetëm në fund të radhës, kurse nxjerrja vetëm nëfillim të radhës.

 Nëse “gypi” do të jetë lejohet qasja nga të dy anët, edhe për insertim edhe përnxjerrje, atëherë kemi të bëjmë me ‘deque’ (Double Ended Queue –  rreshti me

dy skaje). Operacionet përkatëse, për insertim dhe nxjerrje tani do të quhen:“push back”, “pop back”, “push front”, dhe “pop front”. 

Page 24: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 24/699

Avni Rexhepi

24

...

...

...

Stack - Steku

FIFO queue - rreshti

Deque  – Rreshti dy-kahorë

push pop

pushBack popBackpopFront pushFront

push   pop

  Figura 8 - Operacionet në stek dhe në queue.

 Nëse nuk është përcaktuar ndryshe, radha e insertimit, përcakton edhe radhën enxjerrjes nga queue. P.sh., nëse printeri është në rrjetë dhe shumë kompjuterëkanë qasje në të, atëherë me queue ruhet lista e punëve për shtypje në printer. I pari që jep urdhërin për shtypje, i pari e merre rezultatin.

Mirëpo, në raste të ndryshme, ka anëtarë më të rëndësishëm, të cilëve ju jipet prioritet dhe pavarësisht renditjes në hyrje në listën e pritjes, ata shërbehen jashtë radhës, me prioritet. P.sh., ngashëm me atë që kemi në realitet tek lëvizjanë trafik e makinave me përparësi kalimi (ndihma e shpejtë, policia, zjarrfikësitdhe ata “të madhërishmit” që pa nevojë e shfrytëzojnë këtë përparësi(presidenca, qeveria), etj).

 Në këtë rast, kemi të bëjmë me “Priority Queue” (radha me prioritet). Në këtërast, anëtareve ju bashkangjitet edhe “informacioni” për prioritetin, i cili përcakton radhën e “ekzekutimit”. 

Fizikishit, steku dhe queue mund të realizohen edhe si struktura statike, përmesvargut edhe si struktura dinamike, përmes listave të lidhura (njëfishe ose

dyfishe).Për nga aspekti i renditjes logjike të anëtarëve, u tha se strukturat mund të jenëLineare ose Jo-lineare.

Tek strukturat lineare, dallohet anëtari i fillimit ose fundit dhe kalimi prej anëtarinë anëtarë, mund të bëhet vetëm në mënyrë të renditur (lineare) dhe zakonishtvetëm nëpër një rrugë të mundshme. Kryesisht, lidhja prej nyjes në nyje është pointeri përkatës, që mundëson kalimin tek nyja e përparshme ose ajo eardhshme.

Steku dhe Queue janë struktura lineare.

Page 25: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 25/699

Algoritmet dhe strukturat e të dhënave

25

Përveq strukturave lineare, shfrytëzohen edhe strukturat jolineare, si: Pemët(angl. Trees) dhe Grafet (angl. Graphs). Në esencë, pema është një rast special igrafit.

Tek strukturat jolineare, ekzistojnë rrugë të ndryshme të lidhjeve të anëtarëvemes veti dhe lëvizja prej njërës nyje tek tjetra mund të realizohet nëpër rrugë tëndryshme. Pastaj, një nyje mund të jetë e lidhur me disa nyje të tjera, e jo vetëmme atë paraprake dhe atë të ardhshme (si në rastin e listave të lidhura).

A

C

F

B

D

G   H   I

A

CB

D

G

H   F

  Figura 9 - Pema dhe Grafi 

Pema është strukturë e organizuar e nyjeve dhe degëve (rrugëve) që lidhin nyjetmes veti. Pema zakonisht është strukturë tek e cila dallohet nyja fillestare, equajtur rrënjëa e pemës dhe pastaj prej saj shpërndahen nyjet tjera, të organizuarnë formë të pemës. Në aspektin hierarkik, nëse i shikojmë si trung familjar,dallojmë nyjet prind dhe nyjet fëmijë. Pema mund të jetë me organizimespeciale, si pemë binare (çdo nyje, është prind për dy fëmijë), pemë n-are (çdonyje është prind për n-fëmijë), pemë e balansuar (çdo anë e pemës, është menumër të balansuar të nyjeve), etj.

Grafi, është strukturë e organizuar, e nyjeve dhe degëve që lidhin nyjet mes veti. Nuk ka nyje fillestare të përcaktuar sikur te pema. Nyjet mund të jenë të lidhurames veti me rrugë të ndryshme (sikur në realitet, rrugët e qytetit, pikat ecaktuara në qytet), etj. Lidhjet mes degëve mund të jenë të orientuara (melëvizje vetëm në kahun e caktuar, që në graf paraqitet me shenjë të shigjetës) osetë paorientuara (lidhjet janë dykahëshe). Degët e grafit, mund të plotësohen meinformacione plotësuese, që quhen pesha ose kostoja e rrugës, si p.sh., koha ekalimit prej nyjes në nyje, kualiteti i rrugës, shpejtësia e lëvizjes, etj.

Të gjitha këto struktura do të përdoren nëpër algoritme të ndryshme përllogaritje të ndryshme varësisht prej nevojës. Me rëndësi është që për rastinkonkret të problemit që duhet zgjidhur, të zgjidhet edhe struktura adekuate, ecila në mënyrën më të mirë do t’a pasqyrojë dhe do ta interpretojë logjikishtstrukturën reale të problemit.

Këto janë strukturat themelore, të cilat pastaj përdoren në mënyra të ndryshme,

 për realizimin e strukturave të tjera. Në praktikë, gjithmonë synohet që natyra e

Page 26: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 26/699

Avni Rexhepi

26

 problemit të interpretohet dhe të zbërthehet ashtu që të mund të paraqitet përmeskëtyre strukturave themelore, për të cilat janë krijuar dhe standardizuaralgoritmet dhe funksionet për kryerjen e operacioneve të ndryshme dhe përpunimin e të të dhënave.

 Nëse bëhet nja paraqitje e kategorizuar e strukturave të të dhënave, mund të bëhet një ndarje si në figurën 10.

Strukturat e të dhënave

Të brendshme

(Built-in)

Të definuara prej shfrytëzuesit

(User-Defined)

Numrat e plotë

(Integer)

Numrat jo të

plotë (Float)

Karakteret

(Char)  Pointer Vargjet Listat Fajllat

Listat l ineare Listat jo-lineare

Steku Queue Pemët Grafet

 

 Figura 10 –  Strukturat e të dhënave

Page 27: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 27/699

Algoritmet dhe strukturat e të dhënave

27

1. Memoria, Tipet Abstrakte të të dhënave dhe

Adresat

Shtrohet pyetja: Sa është numri maksimal i tentimeve për të gjetur një emër nëlistën prej një milion emrash? Idea e parë do të ishte një milion! Mirëpo, kjo nukështë aspak afër, sepse përgjigja e saktë është 20, nëse lista ka një strukturë që e bën kërkimin të lehtë dhe nëse kërkimi bëhet me një strukturë efikase. Kërkimi ilistës është një prej mënyrave se si strukturat e të dhënave na ndihmojnë për tëmanipuluar të dhënat e ruajtura në memorie të kompjuterit. Mirëpo, për të pasurtë qartë funksionimin, duhet pasur të qartë se si funksionon memoria ekompjuterit apo si dhe përse vetëm zerot dhe njëshet ruhen në memorie.

 Në fillim, do ta bëjmë një vështrim mbi gjërat themelere të cilat duhet të dihen,

 për të vijuar punën. Për të punuar me algoritmet dhe strukturat e të dhënave,duhet të jenë tërësishtë të qarta konceptet e memories, lokacioneve të memories,adresave të tyre, ndarja e lokacionieve në memorie (alokimi i memories),mënyrës së ruajtjes së të dhënave në memorie, qasja në to (për insertim, lexim,editim, fshirje), pastaj alokimi statik dhe dinamik, etj. Poashtu do të shohimedhe një herë gjërat e nevojshme për pointerët, përdorimin dhe funksionimin etyre.

Vështrim mbi memorienMemoria 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 përhershme (disku).

Memoria kryesore, e njohur edhe si RAM (Random Access Memory –  memoriame qasje të rastit) është vendi ku ruhen instruksionet (programet) dhe të dhënat

(angl. data). Memoria kryesore është volatile (e paqëndryeshme) sepseinstruksionet dhe të dhënat në të fshihen (humben) porsa të ndalet furnizimi(fiket kompjuteri).

 Kesh memoria në CPU përdoret për të ruajtur instruksionet e shpeshta dhe tëdhënat që janë, do të jenë, ose kanë qenë duke u përdorur nga CPU-ja. Njësegment i kesh memories në CPU quhet “regjistër” (angl. register). Regjistriështë një pjesë e vogël e memories përbrenda CPU-së dhe përdoret për ruajtjen e përkohshme të instruksioneve dhe të dhënave.

Page 28: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 28/699

Avni Rexhepi

28

“Bus”-i (zbrarra, lidhja) e lidhë procesorin dhe memorien kryesore. Bus-i ështënjë bashkësi (set) e fijeve përquese të gravuara në pllakën kryesore tëkompjuterit (angl. motherboard  –  pllaka amë) e ngjashme me autostradën dhetransporton instruksionet dhe të dhënat ndërmjet procesorit dhe memories dhe pajisjeve të tjera të lidhura në kompjuter.

 Figura 1.1: Bus-i e lidhë procesorin,

memorien kryesore dhe diskun, si dhe

 pajisjet tjera.

CPU  –  Central Processing Unit (Njësiaqëndrore procesuese)Main memory –  memoria kryesoreData bus –  bus-i i të dhënaveAddress bus –  bus-i i adresaveControl bus –  bus-i i kontrollësInput/Output devices  –   pajisjethyrëse/dalëse(Ekrani, tastiera, memoria eqëndrueshme/disku, lidhjet e rrjetës, etj)

 Disku  –   (angl. Persistent storage(memoria e qëndrueshme, persistente), ose Hard Disk   (disku i fortë)) ) është pajisje e jashtme (eksterne) për ruajtje tëinstruksioneve dhe të dhënave. Memoria e qëndrueshme është jovolatile, që dotë thotë se instruksionet dhe të dhënat mbesin të ruajtura edhe kur kompjuteriështë i fikur.

Disku shpeshherë përdoret nga sistemi operativ edhe si memorie virtuele.Memoria virtuele (angl. Virtual Memory) është teknikë e sistemit operativ për tërritur kapacitetin e memories kryesore përtej kufijve të RAM-it përbrendakompjuterit. Kur kapaciteti i memories tejkalohet, sistemi operativ përkohësisht

kopjon përmbajtjen e bllokut të memories në disk. Nëse programi ka nevojë përtë ju qasur instruksionev ose të dhënave në atë bllok, sisktemi operativ eshkëmben bllokun e vendosur në disk me ndonjë nga memoria, e që nuk ështëduke u përdorur për 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 shpejtësi të përafërt, edyta, është memoria kryesore. Disku është i treti, por shumë larg për ngashpejtësia, pasi që përfshinë procese mekanike, gjë që kufizon/pengon transferine shpejtë të të dhënave.

Page 29: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 29/699

Algoritmet dhe strukturat e të dhënave

29

Memoria kryesore (RAM-i) është lloji i memories që përdoret nga strukturat e tëdhënave, edhe pse strukturat e të dhënaev dhe teknikat e manipulimit të tyremund të aplikohen edhe në “file systems” (sistemet e fajllave) në disk. 

Të dhënat dhe memoria

Të dhënat që përdoren nga programi janë të ruajtura në memorie dhemanipulohen nga teknika të ndryshme të stukturave të të dhënave, varësisht prejnatyrës së programit. Të shohim se si ruhen të dhënat në memorie para se tëhulumtojmë manipulimin e tyre.

Memoria është një grumbulli ndërprerësave elektronik, të quajtur transistorë, tëcilët mund të vendosen në njërën prej dy gjendjeve të mundshme: kyçur ose

çkyçur (ndezur/fikur). Gjendja e ndërprerësit fiton kuptim, kur secilës gjendje indahet një vlerë, gjë që bëhet përmes përdorimit të sistemit numerik binar.

Sistemi binar  përbëhet prej dy shifrave, zero dhe një, të quajtura shifra binare(angl. binary digit) ose shkurt bit. Pra, ndërprerësi në gjendjen e fikurreprezenton zeron dhe kur është i kyçur/ndezur, paraqet “një”-shin. Pra, kjo dotë thotë se transistori reprezenton njërën prej shifrave.

Sidoqoftë, dy shifra nuk ofrojnë të dhëna të mjaftueshme për të bërë gjë tjetër përveq ruajtjes së zerove dhe njësheve nëmemorie. Mirëpo grupimi logjik i“ndërprerësave” të memories, mudnëson ruajtjen e të dhënave me kuptim logjik.Për shembull, dy ndërprerësa mudnësojnë ruajtjen e dy shifrave binare, që qëmudnëson katër kombinime të ndryshme, si në tabelën vijuese dhe këtokombinime 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. byte- bajt). Kjo mundëson 256 kombinime të zerove dhe njësheve të cilat mund tëruajnë numrat prej 0 deri në 255, pasi që kombinimet me gjatësi tetë prej 2elementve janë 28=256.

Tabela 1-1: Kombinimet e dy bitave dhe ekuivalenti i tyre decimal

Switch 1 Switch 2 Decimal Value

0 0 0

0 1 1

1 0 2

1 1 3

Page 30: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 30/699

Avni Rexhepi

30

Sistemi numerik binar

Sistemi numerik është një mënyrë e numërimit të gjërave dhe kryerjes sëveprimeve aritmetike. Për shembull, njerëzit e përdorin si më të përshtatshëm

sistemin decimal, ndërsa kompjuterët atë binar. Të dy sistemet numerike bëjnëtë njëjtën gjë: mundësojnë numërmin e gjërave dhe kryerjen e aritmetikës. Mundtë mbledhet, zbritet, shumëzohet, pjesëtohet dhe do të arrihet tek përgjigjet enjëjta sikur të jetë përdorur sistemi numerik decimal.

Mirëpo, ekziston një diference e dukshme ndërmjet sistemit numerik decimaldhe atij binar: sistemi decimal përbëhet prej 10 shifrave (0 deri në 9) ndërsasistemi binar përbehet prej vetëm dy shifrave (0 dhe 1).

Për të rikujtuar, sigurisht e mbani mend të mësuarit e mbledhjes dhe bartjes nënivelin më të lartë (mbajtjes në mend, p.sh. 9+1=0 e 1 në mend, gjegjësisht 1 nënivelin më të lartë)! Pra, kur arrihet velra maksimale ose kufiri, kalojmë në nivelmë të lartë. P.sh., nëse në kolonën e djathtë kishim 9 dhe shohej edhe 1, atëherëndryshohet 9 në 0 dhe vendoset 1 në të majtë të zeros, për të fituar 10:

Teknika e njëjtë e bartjes në nivel më të lartë përdoret edhe gjatë mbledhjes në

sistemin binar, me dallimin që tash në vend të kufirit maksimal 9, kemi 1. Nësekemi 1 në kolonën e djathtë dhe shtojmë 1, atëherë e ndërromë 1 në 0 dhevendosim 1 në të majtë të 0, për të fituar 10 binar.

Tani fillon ngatërrimi. Të dy numrat duket se kanë shifrat e njëjta: 10. Mirëpo, për numrin decimal ky është reprezentimi i vlerës 10, kurse për sistemin binar,shifrat 10 nuk paraqesin vlerën decimale 10, por vlrën binare 2.

Shifrat në sistemin numerik binar reprezentojnë gjendjen e ndërprerësit.Kompjuteri kryen aritmetikën duke përdorur sistemin numerik binar për tëndryshuar gjendjen e bashkësive të ndërprerësave (transistorëve).

Rezervimi i memories

 Njësia e memories mund të mbajë një bajt, ndërsa të dhënat në program mund të jenë më të mëdha sesa bajti dhe kërkojnë 2, 4 ose 8 bajta për t’u ruajturnëmemorie. Para se ndonjë e dhënë të ruhet në memorie, duhet treguar

Page 31: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 31/699

Algoritmet dhe strukturat e të dhënave

31

kompjutrit se sa hapësirë të rezervojë për të dhënat, duke përdorur një tipabstrakt të të dhënave (angl. Abstract Data Type-ADT).

ADT është një fjalë e rezervuar e gjuhëve programuese e cila specifikon sasinë

nevojshme të memories për të ruajtur të dhënat dhe tipin e të dhënave që do tëruhet në atë lokacion të memories. Sidoqoftë, një ADT nuk i tregon kompjuteritse sa bajta të rezevojë për të dhënat. Numri i bajtave të rezervuar për një ADTndryshon, varësisht prej gjuhës programuese të përdorur për shkruarjen e programit përkatës dhe nga tipi i kompjuterit që përdoret për të kompajluar programin (angl. compile-hartoj, përpiloj).

 Në C dhe C++, madhësia e një ADT-je është e bazuar në madhësinë eregjistrave të kompjuterit të përdorur për kompajlim të programit. Në Java,ADT-të kanë madhësi fikse, për t’u ekzekutuar në të gjitha ambientet

ekzekutuese të Java-s (Java runtime environment).Imagjinojeni një ADT si termin “pako mollash”. Nëse i thoni menagjerit tëshitjes se duhet rezevuar hapësirë në rafta për pesë pako mollash, ai e di se sarafta duhet t’i rezervojë sepse e di madhësinë e pakove të mollave. 

E njëjta vlenë edhe për tipet abstrakte të të dhënave. Ju i tregoni kompjuterit qëtë rezervojë hapësirën për një numër të plotë, duke përdorur ADT-në (tipin)“int” (shkurtesa për integer -numër i plotë). Kompjuteri veç e di se sa memorieduhet të rezervojë për të ruajtur një numër të plotë (integer).

ADT-ja poashtu i tregon kompjuterit tipin e të dhënave që do të ruhet në atëlokacion të memories. Kjo është e rëndësishme sepse kompjuterët i manipulojnëtë dhënat e një tipi të caktuar ndryshe nga të dhënat e tipit tjetër abstrakt. Kjoështë ngjashëm me atë se si menagjeri i shitjes i trajton pakot e letrave dhelëngjeve ndryshe nga ato të mollëve.

Tabela 1-2 përmbanë listën e tipeve abstrakte të të dhënave. Kolona e parë përmbanë fjalët e rezervuara (angl. keyword) për seclilin tip. Kolona e dytëtregon numrin gjegjës të bitave të cilët 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 përkatës.

Tabela 1-2: Tipet e thjeshta të të dhënave

Tipi Madhësia në

bita

Rangu i vlerave Grupi

byte 8  – 128 to 127 Integers

short 16 16  – 32,768 to 32,767 Integers

Page 32: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 32/699

Avni Rexhepi

32

Tabela 1-2: Tipet e thjeshta të të dhënave

Tipi Madhësia 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)

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 1 0 or 1 Boolean

Programeri e zgjedhë tipin abstrakt të të dhënave i cili më së miri i përshtatet tëdhënave që dëshiron t’i ruaj në memorie dhe e përdorë tipin përkatës në urdhërate deklarimit, për të deklaruar variablat dhe tipet e tyre. Variabla është një

referencë për në lokacionin e memories i cili rezervohet duke përdorur urdhërine deklarimit.

Gjithmonë duhet rezervuar sasinë e duhur të memories së nevojshme për tëruajtur të dhënat, sepse mund të humben të dhënat nëse rezervohet hapësirëshumë e vogël. Kjo është njësoj si të dërgohen 10 pako mollësh në vendin kuështë rezervaur hapësira për 5 të tilla. Sigurisht që 5 pako do të “hedhen” dikuanash.

Grupet e tipeve abstrakte të të dhënave

Ju përcaktoni sasinë e memories që duhet rezervuar duke përcaktuar grupin eduhur për tipin e të dhënave abstrakte dhe duke vendosur se cili tip përbrendagrupit është i duhuri për të dhënat.

Janë katër grupe të tipeve të të dhënave:

  Integer  ruan numrat e plotë dhe numrat me shenjë. Shumë i mirë përruajtjen vlerave të plota të eurove, kur nuk nevojiten vlerat decimale.

  Floating-point ruan numrat real (vlerat e thyesave, pjesët e të plotave).Perfekt për ruajtjen e depozitave bankare, kur centët (pjesët e euros)

mund të mblidhen së bashku.

Page 33: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 33/699

Algoritmet dhe strukturat e të dhënave

33

  Character ruan karakteret (shkronjat, numrat, shenjat e pikësimit). Ideal për ruajrne e emrave.

  Boolean ruan velrën “true” ose “false” (saktë ose pasaktë, e vërtetë ose jo e vërtetë). value. Është zgjidhja e duhur për të ruajtur përgjigjet po ose jo, apo e saktë ose jo e saktë, për pyetjen e bërë.

Integer

Grupi ADT integer përbëhet nga katër tipe abstrakte të të dhënave të përdorura për të rezevuar memorie për ruajtje të numrave të plotë: byte , short , int , dhelong , si është përshkruar në tabelën Tabelën 1-2.

Varësisht nga natyra e të dhënave, ndonjëherë velra e plotë duhet të ruhet duke përdorur edhe shenjën pozitive ose negative, si +10 ose -5. Ndonjë herë tjetër

një numër i plotë supozohet të jetë pozitiv, ashtu që nuk është i nevojshëm përdorimi i shenjës. Numri i ruajtur me shenjë, quhet “ signed number ” (numërme shenjë) ndërsa ai që nuk ruhet me shenjë quhet “unsigned number ” (numër pa shenjë).

Problemi është që shenja e zë 1 bit të memories, e cila përndryshe do të mund të përdorej për të reprezentuar vlerën. Për shembull, bajti i ka 8 bita dhe të gjithëata mund të përdoren për të ruajtur numrat pa shenjë prej 0 deri në 255. Nurmime shenjë mund të ruhet në rangun -128 deri në +127.

C dhe C++ përkrahin numrat me shenjë, ndërjsa Java jo. Një numër “unsigned

integer” është vlerë që është e nënkuptuar se është pozitive. Shenja plus nukruhet në memorie. Ndërsa në Java, të gjithë numrat paraqiten me shenjë. Zeroruhet si numër pozitiv.

Bajti - byte

Tipi abstrakt i të dhënave “byte” (bajt) është më i vogli në grupin integer dhedeklarohet përmes fjalës së rezervuar byte, Fig. 1.2. Programerët zakonisht e përdorin tipin byte për dërgimin ose pranimin e të dhënave nga fajllat ose nëpërrrjetë. Tipi byte poashtu zakonisht përdoret kur punohet me të dhëna binare tëcilat mund të mos jenë kompatibile (të pajtueshme) me tip tjetër abstrakt të tëdhëanve. Zgjedhni tipin byte sa herë që keni nevojë për të lëvizur të dhënat nëdhe nga fajlli ose nëpër rrjetë.

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

Page 34: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 34/699

Avni Rexhepi

34

short

Tipi “short” (angl. short-i shkurtër) është ideal për përdorim në programet qëekzekutohen në kompjuterët 16-bitësh, Fig. 1.3. Mirëpo, shumica e

kompjuterëve të tillë sot janë në mbeturina dhe janë zëvendësuar me 32 ose 64- bitësh. Prandaj, short është tipi më së paku i përdorur. Zgjedheni këtë tip nëse programi do të ekzeutohet në kompjuter të vjetër.

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

int

Tipi “int” (Fig. 1.4) është tipi i përdorur më së shpeshti për grupin integer, përnjë numër arsyesh:

  Për 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) përdoret sa herë që përdoren numrate plotë të cilët janë përtej rangut të vlerave të tipit int. Zgjedheni si tip, për tëruajtur vlerën e pagës së Bill Gate-it p.sh.

Floating-Point

Grupi “ floating- point” përdoret për të ruajtur numrat real, në memorie. Numrireal përmbanë vlerën decimale. Janë dy lloje të tipeve të të dhanve floating point: float dhe double (shiko tabelën 1-2). Tipi “float” është numër me precizitet të njëfishtë dhe “double” është me precizitet të dyfishtë. Preciziteti inumrit është numri i vendeve pas presjes decimale, që përmbanë vlerën e saktë.

Page 35: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 35/699

Algoritmet dhe strukturat e të dhënave

35

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

Termi floating-point i referohet mënyrës se si referohen decimalet në memorie.

Janë dy pjesë të numrit floating-point: pjesa reale, e cila ruhet si numër i plotëdhe pozita e presjes/pikës decimale brenda numrit të plotë. Kjo është arsyeja psethuhet se pika decimale “floats” (angl. float-noton, lëvizë, pluskon) përbrendanumrit.

Për shembull, vlera 43.23 ruhet si 4323 (pa pikë decimale). Krahas me të shkonreferenca në numër e cila tregon se pika decimale është e vendosur pas shifrës sëdytë.

float

ADT-ja float (Figura 1.6) përdoret për numrat real të cilët kërkojnë precizitet tënjëfishtë, si është rasti me vlerat monetare. Preciziteti i njëfishtë (angl. single precision) do të thotë se vlera është precize deri në 7 shifra djathtas prejdecimales. Për shembull, supozojmë se vlera €53.50 ndahet në pjesë të barabarta për 17 persona. Secili person do të marrë nga €3.147058823529. shifrat përtej(djathtas) vlerës €3.1470588 nuk garantohet të jenë precize për shkak tëmënyrës se si ruhet në memorie vlera float. Tipi float zgjedhet sa herë që tënevojitet të ruhet vlera decimale ku vetëm 7 shifra djathtas pikës decimale duhettë jenë të sakta.

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

Page 36: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 36/699

Avni Rexhepi

36

double

Tipi double (Figura 1.7) përdoret për të ruajtur numrat real të cilët janë shumë tëmëdhenj ose shumë të vegjël dhe kërkojnë sasi të dyfishtë të memories e cila

rezervohet për tipin float. Zgjedhni tipin double, sa herë që duhet ruajtur vlerëdecimale me saktësi më shumë se 7 shifra pas pikës 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 cilai përgjigjet bashkësisë (setit) të karaktereve. Seti karakter ia ndanë një vlerë

integer secilit karakter, simbol dhe shenjë pikësimi të gjuhës.

 Figura 1.8: char rezervon 16 bita të memories kryesore.

Për shembull, shkronja A ruhet në memorie si vlera 65, e cila i përgjigjetshkronjës A në setin e karaktereve. Kompjuteri di ta trajtojë vlerën 65 sishkronja A e jo si numri 65 për shkak se memoria është rezervuar duke përdorurtipin char . Fjala e rezervuar char  i tregon kompjuterit se numri integer i ruajturnë atë lokacion trajtohet si karakter e jo si numër.

Janë dy sete të karaktereve që përdoren në programim: American Standard Codefor Information Interchange (ASCII) dhe Unicode. ASCII është “gjyshi” i setevetë karaktereve dhe përdorë një bajt për të reprezentuar maksimalisht 256karaktere. Mirëpo, pas disa viteve të përdorimit, ishte evident problemi i paraqitjes së karaktereve të gjuhëve të ndryshme, si arabe, japoneze, kineze, etj,të cilat kanë më shumë se 256 karaktere në gjuhën e tyre. Për të zgjidhur këtë problem, u zhvillua një set i ri i karaktereve, i quajtur Unicode. Unicode përdorë

Page 37: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 37/699

Algoritmet dhe strukturat e të dhënave

37

2 bajta për të reprezentuar secilin karakter. Zgjedhni char   sa herë që duhetruajtur një karakter të vetëm në memorie.

Boolean

ADT “bool”-ean (Figura 1.9) rezervon memorie për të ruajtur një vlerë bool-eane, e cila është e saktë ose e pasaktë (e vërtetë ose fals) dhe reprezentohet sizero ose një. Zgjedhni tipin bool-ean sa herë që duhet ruajtur një prej dymundësive 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(katrorëve) të organizuar në grupe me nga tetë. Secilit grup prej tetë fushave (1 bajti) i ndahet një numër unik i quajtur “adresë e memories” (angl. memoryaddress, si në Figurën 1.10). Kjo është shumë me rëndësi gjatë mësimit tëstrukturave të të dhënave, përndryshe mund të shkaktohet konfuzion.

 Figura 1.10: Adresa e memories e bajtit të parë përdoret si referencë për të

 gjithë bajtat e rezervuar për një tip abstrakt të të dhënave.

Adresa e memories përdoret direkt ose indirekt përbrenda programit për të juqasur të gjithë tetë katrorëve. Për shembull, nëse programi i tregon/urdhëronkompjuterit që dëshiron të kopjojë të dhënat e ruajtura në lokacionin 423 tëmemories, d.t.th, katrorit me adresë 423. Kompjuteri shkon tek ai lokacion imemories dhe i kopjon të dhënat (zerot dhe njëshet) nga katrori 423 dhe shtatëkatrorët vijues. Këta shtatë katrorët vijues nuk kanë adresë të memories. Ndryshe thuhet se këta shtatë katrorët tjerë e ndajnë bashkarisht adresën e

katrorit 423.

Page 38: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 38/699

Avni Rexhepi

38

Adresat reale të memories

Edhe pse adresat e memories u prezentuan si vlera decimale, si më parë “katrori423”, në realitet adresat e memories janë numra 32-bitësh ose 64-bitësh,

varësisth nga sistemi operativ i kompjuterit dhe atë reprezentohen si vleraheksadecimale.

Sistemi Hexadecimal është sistem numerik i ngajsëhëm me sistemin decimal dheatë binar. Kjo do të thotë se vlerat heksadecimale përdoren për të numëruar dhekryer veprimet aritmetike. Sistemi numerik heksadecimal ka 16 shifra, prej 0deri në 9 dhe prej A deri në F, të cilat përfaqësojnë numrat prej 10 deri në 15.Për shembull, adresa e memories 258,425,506 reprezentohet në foramtinheksadecimal si 0x0F6742A2.

ADT dhe adresat e memoriesMë parë u tha se rezervimi i memories për të dhënat bëhet duke përdorur tipinabstrakt të të dhënave. Disa ADT rezervojnë memorie në madhësi më të madhese 1 bajt.

Pasi që secili bajt i memories e ka adresën e vet memorike, mund të supozohetse tipi “short” i ka dy adresa të memories sepse i përdorë 2 bajta të memories.Mirëpo kjo nuk ndodhë. Kompjuteri e përdorë adresën e memories së bajtit të parë për të ju referuar cilit do tipi abstrakt të të dhënaveqë rezervon bajta tëshumëfishtë në memorie.

Le të themi se në memorie është rezervuar hapësira për tipin “short” (shih Fig.1-10). Me këtë rast rezervohen dy lokacione të memories, me adresat 400 dhe401. Mirëpo, vetëm adresa e memories 400 përdoret për të ju referuar vlerës“short”. Kompjuteri automatikisht e di se vlera e ruajtur në adresën 401 është pjesë e vlerës së ruajtur në adresën 400, sepse hapësira është rezervuar duke përdorur tipin abstrakt “short”. Prandaj, kompjuteri i kopjon të gjithë bitat ngaadresa e memories 400 dhe të gjithë bitat nga adresa 401, sa herë që nga programi i bëhet kërkesë që të kopjojë numrin/vlerën e ruajtur në adresën ememories 400.

Variablat dhe Pointerët

Edhe pse me të përmendur pointerët, disa programerë kanë idenë se kjo çështëshumë komplekse, në esencë pointerët janë thjeshtë tregues që pointojnë(tregojnë, shenjojnë) në adresat e memories, si një fëmijë i vogël që tregon megisht (pointon) në gjërat që i dëshiron. Pra, pointeri është variabël që përdoret për të pointuar në adresat e memories, përmbajtjen e të cilave dëshirojmë ta

 përdorim në program.

Page 39: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 39/699

Algoritmet dhe strukturat e të dhënave

39

Deklarimi i variablave dhe objekteve

Memoria rezervohet përmes përdorimit të urdhërit për deklarim të variablave,duke përdorur tipin e të dhënave. Forma e deklarimit varet nga gjuha

 programuese që përdoret. Në C++ dhe në Java, urdhëri i deklarimit të njëvariable është p.sh., si në vijim:

int VariablIme;

Janë tri pjesë (elemente) në këtë urdhër të deklarimit:

  Tipi i të dhënave (Data type)  –  që tregon se sa memorie rezervohet përkëtë llojë të të dhënave që do të ruhet në atë lokaicon të memories,

  Emri i variablës (Variable name)  –  emri që përdoret në program, për të ju referuar përmbajtjes (vlerës) në atë lokacion të memories,

  Pikëpresja (Semicolon)  –   i tregon kompjuterit se ky është një urdhër(instruksion) dhe është shenja e fundit të urdhërit përkatës.

Tipet primitive të të dhënave dhe tipet e definuara prej

shftyrëzuesit

Është sqaruar koncepti i tipeve abstrakte të të dhënave, të cilat përdoren për tërezervuar memorien kompjuterike. Tipet abstrakte të të dhëanave ndahen në dykategori: tipet primitive të të dhënave dhe tipet e të dhënave të definuara prej

shfrytëzuesit. Tipet primitive janë të definuara prej gjuhës programuese dhe janëato që i përmendëm më parë: char, int, short, float, double, etj., të cilat njihenedhe si “built-in data types” (angl. built-in  –   të ndërtuara së brendshmi, të brendshme, etj).

Tipet e definuara prej shfrytëzuesit, janë grup i të dhënave primitive të definuaranga programeri. Për shembull, nëse duhet të ruhen të dhënat e studentit nëmemorie, atëherë do të duhen disa elemente të të dhënave, si : ID e studentit,Emri, Mbiemri, Nota, etj. Për secilën veç e veç mund të përdoren tipet primitivetë të dhënave, mirëpo tipet primitive nuk janë të grupuara së bashku. Secila prej

tyre ekziston në elemente të ndara të të dhënave.Qasje më e mirë është që të dhënat primitive të grupohen në të dhëna tëdefinuara prej shfrytezuesit për të formuar një “record” (angl. record –  regjistrim, shënim, dosje, dokument, koleksion të dhënash, etj., por do të përdorim termin rekord, si në origjinal). Termi rekord është i zakonshëm në bazat e të dhënave. Baza e të dhënave përbëhet nga një ose më shumë tabela.Tabela, përbëhet nga rreshtat dhe kolonat. Një rresht i tabelës njihet si “rekord”,kurse kolona si “fushë”. Ngjashëm si në tabelë, kolonat/fushat e të cilës do tëishin: ID, Emri, Mbiemri, Nota, etj., një rresht (të gjitha fushat e rreshtit) do të

 përmbante të dhënat e një studenti. Pra, tipi i të dhënave i definuar prej

Page 40: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 40/699

Avni Rexhepi

40

shfrytëzuesit, definon kolonat/fushat (tipet primitive të të dhënave) të cilat përbëjnë rreshtin/rekordin (tipin e të dhënave të definuar prej shfrytëzuesit).

Mënyra apo forma e përdorur për të definuar të dhënat e definuara prej

shfrytëzuesit ndryshon varësisht prej gjuhës programuese që përdoret për tëshkruar programin. Disa gjuhë programuese. Disa gjuhë programuese, si Java,nuk i përkrahin fare tipet e definuara prej shftytëzuesit. Në vend të kësaj, përdoren atributet e klasave, për të grupuar tipet primitive të të dhënave.

 Në gjuhët programuese C dhe C++, definimi i tipit të definuar prej shftytëzusit bëhet përmes definimit të “structure” (strukturës). Paramendojeni strukturën sinjë shabllon, p.sh., shablloni për shkronjën A. Shablloni nuk është shkronja A, por ai e definon si duket shkronja A. Nëse ju duhet shkronja A, e vendosnishabllonin mbi një letër dhe e “vizatoni” shkronjën A. Nëse duhet edhe një

shkronjë A, e përdorni shabllonin e njëjtë dhe e përsëritni procesin e njëjtë. Pra,duke e përdorur shabllonin mund të bëni sa të doni shkronja A.

E njëjta vlenë edhe për strukturën. Kur ju duhet një grup i të dhënave primtive,që përfaqësohen nga një strukturë, ju e krijoni një “instance” (një instancë, njërast, një shembull) të strukturës. Instanca është njësoj si shkronja A që paraqitetnë letër, pasi të largoni shabllonin. Secila instancë përmbanë të njëjtat të dhëna primitive të cilat janë definuar në strukturë, edhe pse secila instancë ka kopjen evetë të këtyrë të dhënave primitive.

Definimi i tipeve të definuara nga shfrytëzuesi

Definicioni i strukturës përbëhet nga katër elemente:

  struct  (fjala e rezervuar “struct”) I tregon kompjuterit se jeni dukedefinuar një strukturë

  Emri i strukturës (Structure name)   –   Emri që përdoret për tëidentifikuar në mënyrë unike strukturën dhe që përdoret për të deklaruarinstancat e strukturës,

  Trupi i strukturës (Structure body)  –   kllapa e madhe e hapur dhe embyllur, brenda të cilave ndodhen tipet primitive të të dhënave të cilatdeklarohen kur deklarohet një instancë e klasës,

  Pikëpresja (Semicolon)  –   I tregon kompjuterit se ky është një urdhër(instruksion).

Trupi i strukturës mund të mbajë çfarëdo kombinimi të tipeve të të dhënave primitive dhe tipeve të definuara më parë nga shfrytëzuesi, varësisht prej natyrëssë të dhënave që kërkohen në programin konkret. P.sh, definojmë strukturën e

Page 41: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 41/699

Algoritmet dhe strukturat e të dhënave

41

cila definon rekordin e studentit, i cili përbëhet nga numri i studentit dhe nota.Emri i kësaj strukture të definuar prej shfrytëzuesit është StudentRecord:

struct StudentRecord{int numriStudentit;char nota;

};

Deklarimi i tipit të definuar prej shftyëzuesit

Deklarimi i një instance të tipit të definuar prej shfrytëzuesit bëhet në mënyrë tënjëjtë si deklarimi i varibalave. Mirëpo, në këtë rast, me rastin e deklarimit, pranë urdhërin e deklarimit, në vend të tipit primitiv të të dhënave, përdoret emri istrukturës.

Le të marrim se dëshirojmë të krijojmë një instance të strukturës StudentRecord,të definuar më parë. Ja, si duhet të bëhet deklarimi dhe përdorimi 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;}

Urdhëri i deklarimit i tregon kompjuterit që të rezervojë memorie me madhësinëe kërkuar për të ruajtur tipin e definuar prej shfrytëzuesit, StudentRecord dhe tashoqërojë studenti1 me atë lokacion të memories. Madhësia e tipit të definuar

 prej shfrytëzuesit, është e barabartë me shumën e madhësive të tipeve primitivetë definuara në trupin e sturkturës.

 Nëse krahasojmë deklarimet:

int X;StudentRecord studenti1;

atëherë shohim se, njësoj sikur që tipi i variablës X është tipi primitiv “int”,atëherë tipi i variablës “studenti1” është “StudentRecord”, tipi i definuar prej

Page 42: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 42/699

Avni Rexhepi

42

shfrytëzuesit. Ose thënë ndryshe, X është variabël e tipit “int”, kurse studenti1është i tipit StudentRecord.

Madhësia e X-it është sa madhësia e tipit primitiv “int”, kurse madhësia e

studenti1, është sa shuma e tipeve primitive “int” dhe “char”, të definuara nëstrukturën StudentRecord.

Madhësia e tipit të definuar prej shfytëzuesit StudentRecord është sa shuma emadhësive të një numri të plotë (integer) dhe të një karakteri (char). Rikujtojmëse madhësia e tipit primitiv të të dhënave matet në bita. Numri i bitave të tipit primitiv të të dhënave varej nga gjuha programuese. Prandaj, programerët duhett’i referohen emrit të tipit primitiv, në vend se numrit të bitave. Kompjuteri e dise sa bita t’i rezervojë për secilin tip primitiv të të dhënave.

Tipet e definuara prej shfrytëzuesit dhe memoria

Elementet e të dhënave të definuara përbrenda trupit të strukturës vendosennjëra pranë tjetrës në memorie, në mënyrë sekuenciale (të njëpasnjëshme). Nëfigurën 1.11 ilustrohet memoria e rezervuar memoria e rezervuar me rastin edeklarimit të instancës së tipit StudentRecord.

 Figura 1.11: Elementet e strukturës vendosen në lokacione sekuanciale të

memories, kur deklarohet një instancë e strukturës.

Emri i instancës studenti1 është një “alias” (angl. alias-pseudonim, nofkë) përadresën e memories së rezervuar për tipin e parë primitiv të definuar përberndastrukturës StudentRecord, që është lokacioni me adresën e memories 1, nëfigurë. Për thjeshtësi, le të themi se secili bllok në figurën 2-1 reprezenton 1 bajttë memories dhe se madhësia e tipit “int” për këtë rast është 2 bajta. 

Secili tip primitiv i të dhënave të strukturës, ka adresën e vetë të memories. Tipii parë primitiv në këtë shembull është numriStudentit dhe emri i tij i referohetlokacionit 1 të memories. Tipi i dytë primitiv është “nota” dhe emri i saj i

referohet lokacionit 3 të memories.

Page 43: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 43/699

Algoritmet dhe strukturat e të dhënave

43

Çka ndodhi me lokacionin 2? Mbani mend, se secili bajt i memories i ndahet njëadresë unike memorike. Disa tipe primitive të të dhënave janë më të mëdha sesanjë bajt dhe prandaj duhet të zëne më shumë se një adresë të memories, si nëkëtë shembull që është rasti me tipin “int”. Pra, në këtë rast, tipi i parë primitiv ika zënë 2 bajta të memories, prandaj, tipi i dytë primitiv i definuar në strukturë,vendoset në bajtin e ardhshëm në dispozicion (të lirë) në memorie, që ështëlokacioni 3 i memories.

Qasja në elementet e tipit të definuar prej shfrytëzuesit

Elementet e strukturës së të dhënave qasen përmes emrit të instancës sëstrukturës dhe emrit të elementit, të ndarë me operatorin “.” (angl. dot operator -operatori pikë). Le të themi se dëshironi të jepni vlerësimin A për notën, nëelementin nota të instancës studenti1 të strukturës StudentRecord. Ja, si duhet

shkruar urdhëri përkatës:studenti1.nota = 'A';

Elementet e strukturës përdoren në program në mënyrë të njëjtë si edhe variablattjera, përveq se duhet referuar edhe emri i instancës edhe emri i elementit, nëmënyrë që t’i qaseni elementit. Kombinimi i emrit të instancës dhe emrit tëelementit është një alias për lokacionin e memories së elementit.

Tipet e të dhënave të definuara prej shftytëzuesit dhe klasat

Strukturat përdoren më shumë në gjuhët procedurale si C. Gjuhët e orientuara nëobjekte, si C++ dhe Java i përdorin të dyja, strukturat dhe klasat, për t’i grupuarsë bashku të tipet e ndryshme të të dhënave primitive në një njësi kohezive (tëlidhur, të bashkuar).

Definicioni i klasës është shabllon i ngjashëm me konceptin e definicionit tëstrukturës, për faktin se të dyja përdorin definicionin për të krijuar instancat.Definicioni i strukturës krijon instancë të strukturës, gjersa definicioni klasëskrijon instancë të klasës.

Definicionin i klasës i përkthen atributet dhe sjelljet (angl. behaviours –  sjelljet,

mënyrat e veprimit) të objekteve nga jeta reale në simulim të atij objekti përbrenda programit. Atributet janë elemente të të dhënave ngjashëm meelementet e strukturës. Sjelljet janë instruksione të cilat kryejnë detyra specifike,të njohura ose si “methods” (metoda) ose si “functions” (funksione), varësisht prej gjuhës programuese. C++ ju referohet si funksione, gjersa Java si metoda.

Definimi i klasës (Class)

Definicioni i klasës i përngjanë definicionit të sturkturës. Edhe definicioni iklasës, përbehet nga katër elemente:

Page 44: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 44/699

Avni Rexhepi

44

  class  –  fjala e rezervuar class, i tregon kompjuterit se jeni duke definuarnjë klasë,

  Emri i klasës (Class name)  –  emri që përdoret për të identifikuar klasënnë mënyrë unike dhe për të deklaruar instanca të klasës,

  Trupi i klasës (Class body)  –  kllapa e madhe e hapur dhe e mbyllur, përbrenda të cilave janë tipet primitive të të dhënave të cilat deklarohenkur deklarohet një instancë e klasës dhe definicionet e metodave dhefunksioneve të cilat janë anëtarë të klasës

  Pikëpresja (Semicolon)  –   i tregon kompjuterit se ky është një urdhëri(instruksion)

Definicioni vijues i klasës, i shkruar në C++ definon rekordin e njëjtë tëstudentit, që ishte definuar në strukturën e mëparshme. Mirëpo, definicioni i

klaësë do të definojë gjithashtu edhe funksionin i cili i paraqet (i shtypë) tëdhënat e studentit, emrin dhe notën, në ekran.

class StudentRecord{

int numriStudentit;char nota;void shtypeNoten() {

cout<<"Student: " << numriStudentit << " Nota: "<< nota << endl;

}

};

Deklarimi i një instance të klasës dhe memoria

Deklarimi i një instance të klasës bëhet njësoj si deklarimi në rastin e strukturës.Pra, në urdhërin e deklarimit, përdoret emri i klasës dhe emri i instancës sëklasës. Ja si deklarohet një instancë e klasës StudentRecord:

StudentRecord studenti1;

Kur deklarohet një instancë e klasës, memoria për atributet e definicionit tëklasës rezervohet në mënyrë sekuenciale, njësoj siç rezervohej memoria përelementet e strukturës. Fig. 1.12 paraqet alokimin e memories për intancënstudenti1 të klasës StudentRecord. Vëreni se në esencë është e njëjta mënyrë ealokimit të memories, si në rastin e strukturës.

Page 45: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 45/699

Algoritmet dhe strukturat e të dhënave

45

 Figura 1.12: Memoria për atributet e klasës vendoset në lokacione sekuencialetë memories kur të deklarohet instanca e klasës.

Metodat dhe funksionet ruhen në memorie veçmas prej atributeve, kur tëdeklarohet një instancë, sepse metodat dhe funksionet ndahen bashkarisht nga tëgjitha instancat e së njëjtës klasë.

Qasje në anëtarët e klasës

Atributet, metodat dhe funksionet ndryshe referohen edhe si anëtarët e klasës(class members). Ju mund t’i qaseni anëtarëve të një instance të klasës, duke

 përdorur emrin e instancës, dot operatorin (operatorin pikë) dhe emrin e anëtarit,njësoj si i qaseni elementit të strukturës.

Ja si i qaseni atributit nota të instancës studenti1, të klasës StudentRecord dhe sithirret metoda shtypeNoten():

studenti1.nota = 'A';

studenti1.shtypeNoten();

Pointerët

Pointeri (treguesi) është një objekt i cili mund të përdoret për të ju qasur njëobjekti tjetër. Pointeri ofron qasje indirekte në një objekt. Njerëzit i përdorin pointerët në jetën e përdithshme gjatë tërë kohës. Për shembull:

-  Kur profesori thotë: “Zgjidheni problemin 1.1 në libër”, detyra aktualeështë duke u dhënë në mënyrë indirekte;

-  Kërkimi i një titulli/teme në indeksin e librit, është një shembull i qasjesindirekte. Indeksi tregon se ku mund të gjeni mësimin përkatës.

Page 46: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 46/699

Avni Rexhepi

46

-  Adresa e banimit është pointer. Ajo tregon se ku banon dikush. Adresa përcjellëse është pointer në pointer.

-  Adresa në internet, URL-ja, si www.uni-pr.edu  është pointer. URL-jatergon se ku ndodhet ëeb faqja cak.. Nëse ëeb faqja cak ështëzhvendosur, URL- ja “bëhet bajat” (angl. Stale  - bajat, e ndenjur) dhe pointon në një faqe që nuk ekziston më.

 Në të gjitha këto raste, informacioni jepet në mënyrë indirekte duke ofruar pointerin për informacionin. Në C++, pointeri është një objekt i cili ruan adresën(d.m.th,. një lokacion në memorie), ku është ruajtur një e dhënë tjetër. Pra,thjesht, pointeri ruan adresë. Është e pritshme që adresa të jetë një numër i plotë (integer), ashtu që pointeri zakonisht mund të reprezentohet në mënyrëinterne si një unsigned “int” (numër pa shenjë). Ajo që e bën pointerin më

shumë sesa vetëm një numër i thjeshtë, është se përmes tij ne mund t’i qasemi tëdhënës (vlerës) në të cilën pointon ai. Ky veprim, ndryshenjihet edhe sidereferencim  i pointerit. Pointeri ruan adresën në të cilën banon një e dhënëtjetër.

 Një përmbledhje (angl. aggregate-grumbullim, shumë totale, etj) është njëkoleksion i objekteve të ruajtura në një njësi. Vargu është mekanizmi bazik iruajtjes së një koleksioni/bashkësie të objekteve të tipit të njëjtë. Një përmbedhës tjetër është struktura, e cila ruan një koleksion të objekteve që nuk janë të tipit të njëjtë. Si shembull abstrakt, shqyrtoni planin e banesave në një

ndërtese. Secili kat mund të ketë banesa një-dhomëshe, dy-dhomëshe, tri-dhomëshe etj. Në këtë rast, secili kat ruhet si strukturë, ndërsa e tërë ndërtesaështë nëj varg i kateve.

Vargjet dhe stringjet

 Në C++ mund të deklarojmë dhe përdorim vargjet në dy mënyra themelore.Metoda primitive është që të përdoret një varg i brendshëm (built-in).Alternativa tjetër është përdorimi i librarisë vector. Sintaksa për të dy metodatështë pak a shumë e njëjtë, mirëpo vector është shumë më e lehtë dhe pak mëe sigurtë sesa vargu primitiv dhe preferohet për shumicën e aplikacioneve.

Dallimi më i madh filozofik ndërmjet dy metodave është se vector sillet si tipi klasës së parë (angl. first-class type) edhe pse është i implementuar në librari,gjersa vargu primitiv është tipi i klasës së dytë (angl. second-class type). Ngjashëm, C++ ofron të dy opcionet edhe për variablat tekstuale (string).Stringjet primitive (janë thjeshtë vargje primitive char) dhe atë shumë më të preferuarën string.

Page 47: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 47/699

Algoritmet dhe strukturat e të dhënave

47

1.2.1 Objektet “First-Class” dhe “Second-Class” 

Studiuesit e gjuhëve programuese shpesh i përcaktojnë konstrukteve të një gjuhe

 programuese, emërtimin “firs-class objects” ose “second-class objects”.Definicioni është paksa jopreciz, por në përgjithësi, idea është që objektet eklasit të parë mund të manipulohen në të gjitha “mënyrat e zakonshme”, pa rastespeciale dhe përjashtime, ndërsa objektet e klasës së dytë mund të manipulohenvetëm në ndonjë mënyrë të përcaktaur dhe të kufizuar.

Cilat janë “mënyrat e zakonshme”? Në rastin specifik të C++, ato mund të përfshijnë gjërat si kopjimi. Rikujtoni se vargu ruan një koleksion të objekteve.Është e pritshme që kopjimi i një vargu të bëjë kopjimin e tërë koleksionit (tëgjithë anëtarëve të tij), mirëpo kjo nuk ndodhë në rastin e vargjeve primitive.

Gjithashtu, do të mund të prisnim që një varg ta dijë se sa anëtarë i ka nëkoleksionin e tij. Me fjalë të tjera, do të ishte e pritshme që madhësia e vargutështë pjesë e vet qenies së tij. Përsëri, kjo nuk është e vërtetë për vargjet primitive. Arsye për këtë ë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ë. Gjithashtudo të mund të prisnim që kur vargjet e alokuara më nuk janë të nevojshme (përshembull kur funksioni në të cilin ato janë deklaruar, ka kthyer rezultatin dhe kambaruar punë), atëherë memoria që ata e zënë, të lirohet. Kjo nuk është e vërtetë për vargjet (ndonjëherë edhe është), kështu që i bën shumë të ngatërruar për

kodim.Stringu primitiv mund të konsiderohet i nivelit edhe të ulët, sesa objekti i klasittë dytë, sepse i mungojnë të gjitha vetitë e vargut të klasës së dytë. Për më tepër,operatorët e tij të krahasimit (p.sh., == dhe <) nuk bëjnë atë që normalisht ështëe pritshme prej tyre dhe prandaj duhet të trajtohen si raste speciale.

Për të siguruar trajtim të klasit të parë për vargjet dhe stringjet, duhet përdorurvector  dhe string. Klasa vector  përmbanë operacionet themelore tëvargut primitiv plus tiparet/veçoritë shtesë. Prandaj, ai më shumë sillet sistrukturë e të dhënave sesa si varg i thjeshtë. Sidoqoftë, përdorimi 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++, mirëpodisa kompajlerë akoma nuk i përkrahin. Si pjesë e STL, klasat vector  dhestring 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 operatorëtrelacional, ==, <, etj. Edhe vector edhe string mund të kopjohen përmes“=”. Prandaj, përveq rasteve speciale, duhet evituar vargjet dhe stringjet e brendshme (built-in) të C++-it. Pasi mënyra e implementimit të librarive nukkërkon njohjen e implementimit të tyre të brendshëm, mjafton të dihet si

Page 48: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 48/699

Avni Rexhepi

48

funksionojnë dhe të mund të përdoren. Kështu string  dhe vector  janë tëimplementuara duke ofruar interfejsin i cili i fshehë sjelljet e klasit të dytë tëtipeve të brendshme.

Përdorimi i vector-it 

Për të përdorur vector-in  standard,  programi duhet të përfshijë “headerfile”-in e librarisë, përmes: #include <vector>. Njësoj siq duhet deklaruarvariabla para përdorimit të saj në ndonjë shprehje dhe të inicializohet para se të përdoret vlera e saj, edhe vargu duhet gjithashtu. vector  deklarohet duke idhënë një emër, në pajtim me rregullat e zakonshme të identifikatorëve dhe dukei treguar kompajlerit se të çfarë tipi janë elementet e tij. Mund t’i përcaktohetedhe madhësia, mirëpo nëse nuk është bërë kjo, madhësia është zero, porvector-it duhet t’i ndryshohet madhësia më vonë. 

Secili objekt në koleksionin e objekteve që e paraqet vargu, mund të qaset përmes përdorimit të “[ ]” - array indexing operator (operatorit të indeksimit tëvargut). Thuhet se operatori [ ] e indekson vargun, që nënkupton se e specifikoncili 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 hapësirën për ruajtjen e tre integer-ave, d.m.th., a[0], a[1] dhe a[2]. Nëvector-in e STL-it nuk kryehet/bëhet verifikimi i rangut të indeksave, kështuqë qasja jashtë kufijve të indeksave të vargut nuk “kapet” (nuk hetohet, nukvërehet) nga kompajleri, prandaj duhet kujdes i veçantë nga programerët. (Nërastin e mëparshëm, indeksa legal janë vetëm 0, 1 dhe 2). Kontrollimi i kufijvetë rangut (angl. range checking) mund të bëhet përmes përdorimit tëfunksionit/operatorit “at”, kështu që; a.at(i), është njësoj si a[i], mirëpo

në këtë rast sinjalizohet gabimi (error), nëse indeksi “i” është jashtë kufijve. Madhësia e vector-it mund të “fitohet” përmes funksionit “size” (angl. size-madhësia). Për kodin e mëparshëm, fragmenti a.size( ), do të kthente 3.Vëreni sintaksën: dot operatori përdoret për të thirrur funksionin size, tëvector-it.

Madhësia e vector-it mund të ndryshohet duke thirrur funksionin “resize”(angl. resize - ricakto madhësinë). Kështu, si alternative e deklarimit fillestarë,do të ishte:

Page 49: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 49/699

Algoritmet dhe strukturat e të dhënave

49

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

 Në programin 1.1, ilustrohet përdorimi i vector-it. Programi në mënyrë të

 përsëritur e zgjedhë një numër përbrenda kufijve 1 dhe 100 (inkluziv, duke përshirë edhe vlerat kufitare 1 dhe 100). Si rezultat fitohet numri i herave të paraqitjes së secilit numër.

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 përmes vector-it..

Rreshti 17 deklaron një varg të integjerave të cilët numërojnë numrin e paraqitjeve të secilit numër. Pasi që vargjet indeksohen duke filluar prej 0,atëherë “+1” është kritike nësë dëshirojmë t’i qasemi elementit në pozitënDIFFERENT_NUMBERS. Pa të, do të kishim një varg, rangu i indeksueshëm i

të cilit do të ishte prej 0 deri në 99, dhe kështu qasja tek indeksi 100 do të mund

Page 50: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 50/699

Avni Rexhepi

50

të ishte memorie e ndarë për ndonjë objekt tjetër. Në këtë rast do të paraqiteshinrezultate jokorrekte, varësisht prej detajeve të implementimit të vector-it. (mundtë ndodhë që në ndonjë platformë programi të funksionojë mirë, por në të tjeratdo të jepte rezultate të gabuara). Duhet pasur kujdes me kufijtë, sepse gabimet për një pozitë, janë të zakonshme dhe shumë të vështira për t’u detektuar. 

Pjesa tjetër e programit është relativisht e thjeshtë. Rutina “rand”, e deklaruar në“stdlib.h” jep numër 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 fushëveprimi (angl. scope-horizont, brezi, fushë,shtrirje, etj) i “i” në rreshtin 20 përfundon me unazën “for”. (Me fjalë tjera, “i”nuk duhet të jetë e dukshme në rreshtin 24). Prandaj, përdoren emra të ndryshëm për numratorët e unazave.

1.2.3 Ndryshimi i madhësisë së vektorit

 Një kufizim i vargjeve primitive është se kur të deklarohen një herë, madhësia etyre nuk mund të ndryshohet. Shpeshherë ky është kufizim i rëndësishëm.Sidoqoftë, ne e dijmë se mund të përdorim resize për të ndryshuar madhësinëe vector-it. Teknika e përdorur ilustron disa prej çështjeve të rëndësishme tëefikasitetit. Ajo që ndodhë është se pointerët (të cilët do të diskutohen në detaje pak më vonë) përdoren për të krijuar iluzionin e një vargu që mund të ndryshojëmadhësinë (rritet). Nuk nevojitet njohuri e C++ për të kuptuar algoritmin: të

gjitha detajet janë të fshehura brenda implementimit të vector-it.Idea themelore është e treguar në figurën 1.13, ku, arr reprezenton vector-inme 10 elemenete. Diku, brenda implementimit, alokohet memoria për 10elemenete. Supozojmë se dëshirojmë të zgjerojmë këtë memorie në 12 elemente.Problemi është se elementet e vargut duhet të memorohen (ruhen) në lokacionetë afërta (të njëpasnjëshme –  angl. contiguous locations –  lokacione të afërta, të puthitura) dhe se memoria që ndodhet menjëherë pas arr mund të jetë zënëndërkohë. Atëherë, veprojmë si vijon:

Page 51: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 51/699

Algoritmet dhe strukturat e të dhënave

51

arr 

arr 

original

arr 

original

arr 

original

(a)

(b)

(c)

(d)

 

 Figura 1.13. Zgjerimi i vargut, fillimisht: (a) Në pikën fillestare, arr

reprezenton 10 integer-a. (b) pas hapit 1, original reprezenton të njëjtit 10

integer-a; (c) pas hapave 2 dhe 3, arr reprezenton 12-integer-a, ku 10 të parët janë ata që u kopjuan prej originial-it; dhe (d) pas hapit 4, 10-integer-at e fillimit,

lirohen. 

1.  E mbajmë mend (e ruajmë) ku ka qenë memoria për vargun me 10elemente (synimi, qëllimi i original-it).

2.  Krijojmë një varg të ri me 12 elemente dhe bëjmë që vargu arr ta përdorëatë.

3.  Kopjojmë 10 elementet nga original në arr; dy elementet shtesë në arr tëri kanë ndonjë vlerë të nënkuptuar (default),4.  E informojmë sistemin se vargu 10 elementësh (memoria e tij) mund të

ripërdoret sipas nevojës (si e sheh ai të arsyeshme).

Pas një momenti mund të bindemi se ky është një operacion i shtrenjtë, sepsekopjohen të gjitha elementet prej vargut të alokuar fillimisht në vargun e ri. Nëse, për shembull, ky zgjerim i vargut është reagim në leximin e një vlerehyrëse, atëherë zgjerimi i vargut secilën herë që lexohet ndonjë element do tëishte shumë joefikas. Prandaj, kur implementohet zgjerimi i vargut, gjithmonë

duhet të bëhet për “ndonjë konstantë multiplikative” herë më i madh. P.sh., dy

Page 52: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 52/699

Avni Rexhepi

52

herë më i madh. Në këtë mënyrë, kur zgjerojmë vargun pre N elementeve në 2Nelemente, kostoja e N kopjimeve mund të shpërndahet proporcionalisht përgjatë N elementve të ardhshme të cilat mund të insertohen në var pa pasur nevojë përzgjerim. Si rezutat, ky zgjerim dinamik është vetëm pak më i “shtrenjtë” sesafillimi me një madhësi fikse, por është shumë më fleksibil.

 Në programin 1.2, paraqitet programi i cili lexon një numër të palimituar tëintegjerave prej hyrjes standarde (tastierës) dhe i ruan rezultatet në vargundinamik (që zgjerohet dinamikisht). Deklarimi i funksionit getInts tregon sevector është parametër. Shenja “&” në deklarimin e funksionit para array,specifikon se kjo është referencë për parametrin aktual, e jo kopje e tij. Për këtëarsye, të gjitha ndryshimet në parametrin formal reflektohen edhe në argumentinaktual (Referencat do të sqarohen në vazhdim).

1 #include <iostream>2 #include <vector>3 using namespace std;45 // Lexo nje numer te pakufizuar te int-ev pa perpjekje perrregullim gabimesh6 // mbushni parametrat e vektorit me te dhena; madhesia e tij7 // pas kthimit (return) tregon se sa elemente u lexuan8 void getInts( vector<int> & array )

9 {10 int itemsRead = 0;11 int inputVal;1213 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 }2223 int main( )24 { 25 vector<int> array;2627 getInts( array );28 for( int i = 0; i < array.size( ); i++ )

29 cout << array[ i ] << endl;

Page 53: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 53/699

Algoritmet dhe strukturat e të dhënave

53

3031 return 0;32 }

Programi 1.2 Kodi  për leximin e një numri të pakufizuar të “int -ave” dhe shtypja e

tyre, duke përdorur dyfishimin e vargut.

 Në fillim të getInts, itemsRead vendoset në 0. Në mënyrë të përsëritur

lexojmë elementet e reja në rreshtin 14. Nëse vargu është i mbushur, sisinjalizohet përmes testit të suksesshëm në rreshtin 16, atëherë vargu zgjerohet,në rreshtin 17. E zgjerojmë për dy herë madhësinë e vjetër. E shtojmë 1 ashtu qëdyfishi fillestar konverton vargun e madhësisë 0 në varg të madhësisë 1. Nërreshtin 18, elementi aktual hyrës i ndahet vargut dhe numri i elementeve tëlexuara inkrementohet (rritet për 1). Në rreshtin 20, e ricaktojmë (ndryshojmë)

madhësinë e vargut për të ju përshtatur numrit të elementeve që u lexuan. Njëalternativë është që itemsRead  të bëhet parametër referencë që pëfundimishtvendoset në madhësinë e re të vargut. Kur hyrja dështon (për çfarëdo arsyeje),thjesht kthehemi (return). Rutina (funksioni) main e thërret getInts, duke ia përcjellur vector-in. Madhësia fillestare e vector-it ndodhë të jetë 0.

Teknika e përdorur në programin 1.2 është aq e zakontë, saqë vector  kafunksionalitetin e brendshëm për të imituar këtë. Idea themelore është qëvector  të mirëmbajë jo vetëm madhësinë (angl. size), por gjithashtu edhekapacitetin (angl capacity). Kapaciteti është sasia e memories që ai e karezervuar. Kapaciteti i vector-it është vërtetë një detal i brendshëm, jo diçka përçka që ju duhet të brengoseni.

Funkscioni push_back( ), (angl. push back   –   shtyje prapa), e rritëmadhësinë për një dhe e shton elementin e ri në pozitën e duhur. Ky është njëveprim i thjeshtë nëse nuk është arritur kapaciteti, por nëse është arritur, atëherëkapaciteti zgjerohet/rritet automatikisht, duke përdorur strategjinë e përmendurmë herët. (Disa kompajlerë nuk e dyfishojnë madhësinë, por e rrisin për njëvlerë të vogël konstante, gjë që rezultaon në performansë të dobët), Në mënyrëtipit, e fillojmë vector-in me madhësi 0.

Kodi në Programin 1.3, tregon se si mund të përdoret 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;56 // Lexo nje numer te pakufizuar te int-ev pa perpjekje per

rregullim gabimesh

Page 54: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 54/699

Avni Rexhepi

54

7//mbush parametrat e vektorit me te dhena; madhesia e tij8//pas kthimit (return) tregon se sa elemente u lexuan9 void getInts( vector<int> & array )10 {

11 int inputVal ;1213 array.resize( 0 );14 cout << "Enter any number of integers: ";15 while( cin >> inputVal )16 array.push_back( inputVal );17 }

Programi 1.3 Kodi për të lexuar një numër të pakufizuar të int-eve dhe për t’i

 shkruar përmes përdorimit të push_back. 

Rreshti 13 e ricakton madhësinë e vector-it në 0 elemente. Kjo mundet osemundet të mos e redukojë kapacitetin e tij, varësisht prej implementimit të brendshëm të vector-it. Vëreni që nëse nuk e bëni resize, atëherë elementete 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 përcjelljes së parametrave të funksionit

Supozojmë se dëshirojmë të përcjellim një vektor në funksionin i cili e gjenëvlerën 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 nënkuptuar) i përcjelljes së parametrave tek funksioni, kur thirretfunksioni, është “call by value” (angl. call by value –   thirrja sipas/përmesvlerës). Në këto raste krijohet një kopje e argumentit aktual (përbrendafunksionit) dhe përdoret si parametër formal për secilën thirrje të funksionit

findMax. Pasi që a mund të jetë i madh, ky operacion është i shtrenjtë (ikushtueshëm), kështu që thirrja sipas vlerës është e papërshtatshme. Njëalternativë, është që të përdoret thirrja sipas referencës.

int findMax(vector<int> &a);

Tani, mund të evitohet mbingarkesa e kopjimit. Sidoqoftë, as ky funksionakoma nuk është perfekt, sepse deklarimi i tregon lexuesit dhe poashtukompajlerit, se argumenti aktual mund të ndryshohet si rezultat i thirrjes së

funksionit findMax. Kur parametri përcillet sipas vlerës, e kishim të garantuar

Page 55: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 55/699

Algoritmet dhe strukturat e të dhënave

55

që parametri aktual nuk do të ndryshohet, pasi që krijohej kopja e tij përfunksionin. Për të fituar sjellje të tillë, mund të përdorim formën e tretë të përcjelljes së parametrave, thirrja sipas referencës konstante (angl. call byconstant 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, përmes “mekanizmit përcjellës të parametrave” atij ipërcillen parametrat aktual për të cilët funksioni e kryen punën. Ato janë vlerat,për të cilat thirret funksioni. Varësisht prej mënyrës së thirrjes, efekti në

parametrat aktual ndryshon. Thirrjet mund të bëhen, sipas vlerës (ezakonshmja), sipas referencës dhe sipas referencës konstante.

Thirrja përmes vlerës (Call by value)  –  është mekanizmi i nënkuptuar ipërcjelljes së parametrave. Me rastin e thirrjes së funksionit, argumenti aktual,kopjohet në parametrin formal. 

Thirrja përmes referencës (Call by reference)  – është mekanizmi i përcjelljessë parametrave i cili eviton kopjimin. Mirëpo, lejon ndryshimin e parametraveaktual.

Thirrja përmes adresës (Call by address)  – është mekanizmi i përcjelljes sëparametrave sipas adresës. Me rastin e thirrjes së funksionit, argumenti aktualështë pointer, i cili e përcjellë adresën e parametrit aktual. Edhe në këtë rast,funksionit i lejohet/mundësohet ndryshimi i parametrave aktual, pasi që ështëpërcjellur adresa në mëmorie e parametrit aktual, kështu që ndryshimetndodhin në origjinalin e përcjellur si parametër tek funksioni.

Thirrja përmes referencës konstante (Call by constant reference)  – ështëmekanizmi i përcjelljës së parametrave, i cili eviton kompjimin dhe garanton që

parametri aktual nuk do të ndryshohet.

Zgjedhja e mekanizmit të përcjelljes së parametrave (thirrjes së funksionit) ështënjë detyrë e nënvlerësuar shpesh nga programerët. Tek e fundit, cila do mënyrëqë zgjedhet, programi është korrekt, mirëpo në C++ zgjedhja me kujdes emekanizmit të përcjelljes së parametrave është e rëndësishme për efikasitet,lexueshmëri dhe mirëmbajtje të programit.

Thirrja sipas referencës kërkohet për objektet të cilat mund të ndryshohen ngafunksioni.

Page 56: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 56/699

Avni Rexhepi

56

Thirrja sipas vlerës është e përshtatshme për objektet e vogla të cilat nuk duhettë ndryshohen nga funksioni.

Thirrja sipas referencë konstante është e përshtatshme për objektet e mëdha të

cilat nuk duhet të ndryshohen nga funksioni. Në disa raste më komplekse, thirrja sipas vlerët duhet të evitohet. Programimund të dështojë të kompajlohet nëse janë bërë zgjedhjet e gabuara. Pasi qëstring dhe vector, reprezentojnë objekte të mëdha, thirrja sipas vlerës është në përgjithësi e papërshtatshme, e pavend. Në vend të kësaj, kur këto objekte janë parametra të funksionit, ato zakonisht përcillen sipas referencës ose referencëskonstante, varësisht prej asaj se a pritet që funksioni t’i ndryshojë vlerat e parametrave apo jo.

1.2.6 Vargjet primitive të konstanteve

 Ndonjëherë, i kthehemi vargjeve primitive kur kemi konstante globale. Arsye për këtë është forma e përshtatshme e shkruarjes përmes shkurtesave, siilustrohet në deklarimin vijues të ditëve të muajit:

const int DITET-NE-MUAJ[ ]={31,28,31,30,31,30,31,31,30,31,30,31};

Këtu, madhësia e vargut primitiv inicializohet automatikisht dhe madhësia e tijnxjerret në përfundim nga numri i inicializuesve që janë prezent. Nëse ky varg

është global, numri i elementeve mund të përcaktohet duke ndarë sasinë ememories së përdorur nga vargu primitiv sizeof (DITET-NE-MUAJ)  mesasinë e memories së përdorur nga njëri 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 shumëdimensionale (multidimensionale)

 Ndonjëherë 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 dheversioni primitiv i tij është i klasës së dytë. Klasa matrix mund të përdoret përtë implementuar vargjet dydimensionale. Nuk ka version të klasit të parë nëSTL. Madhësitë e indeksave të saj janë të specifikuara dhe secili element qasetduke vendosur indekset përkatëse në kllapa të mesme të veçanta. Për 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 

Page 57: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 57/699

Algoritmet dhe strukturat e të dhënave

57

rezervon gjashtë lokacione të memories për 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 

Për të përdorur tipin string të librarisë standarde (STL), duhet të përfshihetdirektiva:

#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” nëse dhe vetëm nëse stringjet janë të njëjta. 

Secili karakter i string-ut mund të qaset duke përdorur operatorin e indeksimittë vargut (si zakonisht, indeksat fillojnë prej zeros). string  ofron shumëfunksione të dobishme.

 Nëse s është string, atëherë s.length() kthen gjatësinë e tij (d.m.th., numrin ekaraktereve), ndërsa s.c_str()  kthen stringun primitiv. Stringu primitivnevojitet nganjëherë për të ndërvepruar me pjesët tjera të librarive. Përshembull, për të hapur fajllin, duhet të përcillet stringu primitiv. Së fundi,operatorët +  dhe +=  për string  janë të definuar që të bëjnë bashkimin estringjeve (njëri string lidhet në fundin e tjetrit). Këto operacione janë ilustruar

në kodin në Programin 1.4.

1 #include <iostream>2 #include <string>3 using namespace std;45 int main( )

6 {

7 string a = "hello";8 string b = "world";

9 string c; // duhet te jete " "1011 c = a + “ “ // duhet te jete "hello " 12 c += b; // duhet te jete "hello world"1314 // Shtype c ne menyren e lehte.15 cout << "c eshte: " << c << endl;1617 // Shtype c ne menyren primitive.18 cout << "c eshte: " << c.c_str() << endl;

19

Page 58: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 58/699

Avni Rexhepi

58

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;2526 return 0;27 }Programi 1.4 Ilustrim i funksioneve të string.

Sintaksa e pointerëve në C++

Që pointeri të pointojë në një objekt, ne duhet të dijmë adresën e memories së

objektit cak (d.m.th., vendin ku është ruajtur). Për, (pothuajse) secilin objektobj, adresa e tij memorike jepet përmes aplikimit të operatorit unar “&”address-of (angl. address-of  –  adresa e) ose themi operatori i adresës. Pra,operatori i adresës e kthen adresën e objektit. Prandaj, &obj është lokacioni nëmemorie kur ruhet obj. Objektet e ruajtura përmes përdorimit të klasës sëruajtjes “register” nuk mund të jenë cak (angl. target-cak) i operatorit të adresës.

 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ë tipevetë zakonshme, ky deklarim nuk e inicializon pointerin ptr në ndonjë vlerë tëveçantë, kështu që përdorimi i ptr para se ti ndahet ndonjë vlerë, prodhonrezultate të këqija (programi bllokohet). Supozojmë se kemi bërë deklarimet:

int x = 5;int y = 7;

 Në mund ta bëjmë 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 këtë në dy mënyra. Në pjesën (a) modeli i memories tregon ku ruhet secili objekt. Në pjesën (b) përdoret një shigjetë për të treguar pointimin.

Vlera e të dhënave që pointohen (tregohen me pointer) fitohet përmes operatoritunar të dereferencimit “*”. Operatori unar i dereferencimit i qaset vlerët përmes pointerit. Në Figurën 1.6 *ptr do të ketë vlerën 5, e cila është vlera e variablës x

në të cilën pointohet. Dereferencimi i objektit që nuk është pointer është ilegal.

Page 59: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 59/699

Algoritmet dhe strukturat e të dhënave

59

Operatori * është e kundërta e operatorit & (d.m.th., *&x=5 është njësoj si x=5, për aq sa &x është legal). Dereferencimi funksionon jo vetëm për leximi evlerave nga objekti, por edhe për shkruarjen e vlerave të reja në objekt. Prandaj,nëse kemi:

*ptr = 10 //e lejueshme (tekstualisht: aty ku tregon pointeri, vendose vlerën 10

i bie që kemi ndryshuar vlerën e x-i në 10. Figura 1.157 paraqet ndryshimet qërezultojnë dhe problemin me pointerët: ndryshimet e pakufizuara janë tëmundshme dhe pointeri që “e huqë” adresën mund të mbishkruaj vlerat nëmemorie të çfarëdo variable, pa qëllim.

Kemi mundur ta inicializojmë pointerin ptr në kohën e deklarimit, duke bërë qëai të pointojë në x:

int x = 5;int y = 7;int *ptr = &x; // OK

(a)

(&x) 1000

(&y) 1004

(&ptr) 1200

  x = 5

  y = 7

  1000

5 7

ptr    x   y

(b) 

 Fig. 1.14 –  Ilustrim i pointerit. 

(a)

(&x) 1000

(&y) 1004

(&ptr) 1200

  x = 10

  y = 7

  ptr = &x = 1000

10 7

ptr    x   y

(b) 

 Fig. 1.15 –  Rezultati i *ptr = 10.

Page 60: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 60/699

Avni Rexhepi

60

Deklarimi thotë që x është një “int” i inicializuar në 5, y është një “int” iinicializuar në 7 dhe ptr është pointer në vlerë të tipit “int” dhe është inicializuarqë të pointojë në x. Lë të shikojmë se ku mund të kemi gabuar. Sekuenca vijuesee deklarimit është jokorrekte:

int *ptr = &x; // ILLEGAL: x nuk eshte deklaruar akomaint x = 5;int y = 7;

Këtu jemi duke përdorur x-in para se të jetë deklaruar, kështu që kompajleri dotë “ankohet”.

Ja edhe një gabim i zakonshëm (i shpeshtë):

int x = 5;int y = 7;int *ptr = x; // ILLEGAL: x nuk eshte adresë

 Në këtë rast, jemi duke tentuar që pointeri ptr të pointojë në x, por kemi harruarse pointeri mbanë një adresë. Prandaj, duhet të kemi adresë në anën e djahtë tëurdhërit. Kompajleri do të lajmërojë gabim pasi është harruar operatori i adresës& para x-it.

Duke vazhduar me shembullin e njëjtë, 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ë Figurën 1.15, vlera është e padefinuar, pasi që nuk ka qenë e inicializuar asnjëherë. Prandaj, vlera e *ptr është poashtu e padefinuar. Pointeri duhet të jetë duke pointuar diku para se të dereferencohet.Sidoqoftë, përdorimi 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 kështu duke shkaktuarqë programi të bllokohet nëse pointeri dereferencohet. Edhe më keq, ptr mund të jetë duke pointuar në një adresë e cila është e qasëshme: në të cilin rast,

 programi nuk do të bllokohet menjëherë, por do të jetë i gabueshëm dhe jeprezultate të pasakta. Nëse *ptr është cak i ndonjë përcaktimi të vlerës, atëherë aido të ndryshojë aksidentalisht ndonjë të dhënë tjetër, e cila mund të rezultojë në bllokim të mëvonshëm të programit. Ky është lloj i vështirë për t’u detektuar igabimit sepse shkaku dhe simptomet e gabimit mund të kenë distancë të madhekohore mes veti.

Më herët është paraqitur sintaksa korrekte për ndarjen (përcaktimin) e vlerës: 

ptr = &x; // LEGAL

Supozojmë se kemi harruar operatorin e adresës. Atëherë urdhëri: 

Page 61: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 61/699

Algoritmet dhe strukturat e të dhënave

61

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

do të gjenerojë gabim kompajleri. Janë dy raste kur kompajleri nuk reagon. Njëri është përdorimi i operatorit të adresë në anën e djathtë, si në sintakësn

korrekte. Tjetri është i gabuar:*ptr = x; // Semantikisht jokorrekt

Kompajleri do të heshtë, sepse urdhëri thotë që “int” në të cilin pointon ptr duhet të merr vlerën e x. Për shembull, nëse ptr është &y, atëherë y i ndahetvlera e x. ky urdhër është tërësisht legal, por nuk e bën atë që synohej, që ptr të pointojë në x. për më tepër, nëse ptr është i painicializouar, dereferencimi do tëshkaktojë gabim gjatë kohës së ekzekutimit, si u diskutua më herët. Ky gabimështë i dukshëm nga Figura 1.8. si rregull e preferuar, është që në shenjën e parëtë problemeve me pointer, të vizatohet figura, gjë që e sqaron situatën.

Përdorimi i *ptr=x në vend të ptr=&x është gabim i shpeshtë për dy arsye. Së pari, sepse heshtja e kompajlerit, bën që programeri të ndjehet komod përkundërsemantikës jokorrekte. Së dyti, duket ngjashëm sikur sintaksa e përdorur përinicializimin në kohën e deklarimit. Dallimi është se * në kohën e deklarimitnuk është * i dereferencimti por vetëm një tregues se objekti është i tipit pointer.

 Ndonjëhere duhet përcaktuar në mënyrë eksplicite se pointeri nuk është duke pointuar askund, si rast i kundërt me atë të lokacionit të padefinuar. Në këtoraste përdoret “NULL pointeri”, i cili pointon në lokacion që është e garantuar

se është i paaftë që të mbajë ndonjë vlerë. Rrjedhimisht, NULL pointeri nukmund të dereferencohet. NULL pointeri ka vlerën 0 dhe nuk duhet tëdereferencohet asnjëherë. Përdoret për të treguar se pointeri nuk pointon askund.Konstanta simbolike NULL është e definuar në disa header fajlla dhe mund të përdoret kjo ose edhe vlera zero. Më së miri është që pointerët të inicializohennë NULL pointer, sepse në shumë raste nuk kanë vlera fillestare të nënkuptuara(rregulla e njëjtë vlewn edhe për tipet tjera të predefinuara).

Pointeri i dereferencuar njësoj si objekti në të cilin pointon. Prandaj, pas treurdhërave vijues, vlera e ruajtur në x do të jetë 15:

x = 5;ptr = &x; //ptr merr adresen e x*ptr += 10; //aty ky pointon ptr (tek x), shto 10

Sidoqoftë, duhet të jemi të vetëdijshëm për rregullat e prioritetit sepse është emundur kryerja e veprimeve aritmetikë jo vetëm në vlerat e dereferencuara poredhe në vetë pointerët (vlerat e padereferencuara). Kjo aftësi është një pasojë e pafat e rregullave shumë liberale të C++-it, të cilat lejojnë aritmetikën e pointerëve, duke përfituar nga fakti që pointerët në mënyrë interne ruhen siintegjer. Megjithatë, evitoni përdorimin e aritmetikës së pointerëve në tekste.

Page 62: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 62/699

Avni Rexhepi

62

Për shembull, dy urdhërat vijues janë shumë të ndryshëm.

*ptr += 1;*ptr++;

 Në urdhërin 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 ndërrohet që të pointojë në lokacion të memories për një njësimemorike më të madhe sesa që kishte më herët.

 Nëse ptr1 dhe ptr2 janë pointerë në tip të njëjtë, atëherë

ptr1 = ptr2; 

 përcakton që ptr1 të pointojë në të njëjtin lokacion si ptr2, ndërsa

*ptr1 = *ptr2;

ia ndjanë ptr1-shit të dereferencuar, vlerën e ptr2-shit të dereferencuar. Figura1.16 ilustron se si këta dy urdhëra janë tërësisht të ndryshëm. Për më tepër, kur përdoret gabimisht forma e gabuar, pasojat mund të mos jenë të dukshmemenjëherë.

5

ptr1   x

(b)

7

ptr2   y

(a)

5

ptr1   x

7

ptr2   y

(c)

7

ptr1   x

7

ptr2   y

 

 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 urdhërit, *ptr1 dhe *ptr2 janë të dy 7. Ngajshëm,shprehja:

ptr1==ptr2;

është e vërtetë nëse të dy pointerët pointojnë në të njëjtin lokacion të memories,ndërsa

*ptr1==*ptr2;

është e vërtetë nëse vlerat e ruajtura në të dy adresat e treguara janë të barabarta.

Përdorimi i formës së gabuar është gabim i shpeshtë.

Page 63: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 63/699

Algoritmet dhe strukturat e të dhënave

63

Kërkesa që ptr1 dhe ptr2 të pointojnë në të njëjtin tip është pasojë e faktit qëC++ është “strongly typed” (me kontrollë të lartë të tipeve të variablave dhelokacioneve të tyre, si dhe kufizim të shndërrimit të brendshëm të tipeve, pakontrollë dhe pa përdorim të operatorëve eksplicit të konvertimit të tipeve): tipete ndryshme të pointerëve nuk mund të përzihen pa një konvertim eksplicit tëtipit, përveq nëse shftytëzuesi ofron një konvertim implicit të të dhënave.

Së fundi, kur deklarohen pointerët, vendosja e * dhe zbrazëtirave përreth tijë janë të parëndësishme për kompajlerin, prandaj përdoreni stilin që ju pëlqen.

1.4 Menaxhimi dinamik i memories (Dynamic Memory

Management)

Deri tani, të gjitha variablat që i kemi përdorur janë variabla automatike. Kyterm (i përdorur rrallë) tregon se variablat lokale krijohen kur të arrihen nëfunksion dhe asgjësohen (shkatërrohen) kur më nuk janë në fushëveprimin efunksionit (p.sh., kur funksioni kthen vlerën me return). Ndonjëherë, objektetduhet të krijohen në mënyrë tjetër. Mënyra tjetër është alokimi dinamik imemories.

1.4.1. Operatori “new” 

Objektet mund të krijohen në mënyrë dinamike duke thirrur operatorin “new”

(angl. new - i ri, e re). Operatori new alokon memorien në mënyrë dinamike(gjatë ekzekutimit të programit) dhe kthen pointerin për 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 përfshira në alokimin dinamik të memories.Sidoqoftë, shembulli është një përdorim “i varfër” i memories dinamike, pasi qëdo të duhej të përdoret një string automatik. Në këtë rast përdoret vetëm sa përtë ilustruar alokimin dinamik në kontekst të thjeshtë. Aplikimi më i arsyeshëmdo të paraqitet më vonë (në pikën 1.6.2).

 Në programin 1.5, rreshti 9 krijon një string të ri në mënyrë dinamike. Vëreni sestrPtr është pointer në tipin string, ashtu që vet string qaset përmes *strPtr, siështë treguar në rreshtat 10-13. Kllapat janë të nevojshme në rreshtin 11 përshkak të rregullave të prioritetit (precedencës).

1 #include <iostream>2 #include <string>3 using namespace std;4 5 int main( )6 {

Page 64: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 64/699

Avni Rexhepi

64

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 igrumbullimit automatik “të mbeturinave” (angl. garbage collection –  pastrim imbetjeve, mbledhje mbeturinash). Programeri nuk duhet të brengoset për këtë problem (Është fjala për mos zënien e hapësirës në memorie, ngavariablat/objektet të cilat më nuk janë të nevojshme në program). Mirëpo, C++nuk e ka mekanizmin e grumbullimit automatik të mbeturinave. Kur një objektqë alokohet me new, nuk referencohet më tutje, atëherë duhet të aplikohet

operatori “delete” (angl. delete-fshije) duhet të aplikohet në objekt (përmes pointerit). Përndryshe, memoria të cilën ai e konsumon humbet (deri sa programi të ndalet), gjë që njihet si “memory leak” (angl. leak -rrjedhje, pikim,humbje, etj.). fatkeqësisht, rrjedhjet e memories janë dukuri e shpeshtë në shumë programe në C++. Për fat të mirë, shumë burime të rrjedhjes së memories mundtë largohen automatikisht, me kujdes, si do të shihet në vazhdim.

 Një rregull e rëndësishme është që të mos përdoret new kur mund të përdoretvariabla automatike. Variabla automatike pastrohet automatikisht (prandaj edhequhet ashtu). Nuk duhet përdorur kurrë delete në një objekt që nuk është krijuar

me new; përndryshe, do të rezultojë me një “shkatërrim” gjatë kohës së  ekzekutimit. Operatori delete është ilustruar në rreshtin 15 (të programit 1.5).

1.4.3 “Stale” Pointerët, fshirja e dyfishtë dhe problemet tjera

 Një arsye që programerët mund të gjinden në telashe gjatë përdorimit të pointerëve është fakti që një objekt mund të ketë disa pointerë të cilët pointojnënë të. Shqyrtoni kodin në vijim:

string *s = new string( "hello" ) ; // s pointon në

Page 65: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 65/699

Algoritmet dhe strukturat e të dhënave

65

//stringun e ristring *t = s; // t pointon aty, poashtudelete t; // Objekti nuk është më

Askush nuk do t’i shkruante qëllimisht këta tre urdhëra njëri pas tjetrit, mirëposupozoni situatën kur ata janë të shpërndarë në një kod të gjatë e në një funksionkompleks. Para thirrjes së delete, kemi një objekt të alokuar në mënyrë dinamikei 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. Mirëpo, 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ë dërgojë në rezultate të paparashikueshme. Ajo

që i bën këto gjëra veçanërisht të vështira është se, edhe pse është e qartë që tështë stale pointer, fakti që edhe s është i tillë është më pak i dukshëm, sidomosnëse keni parasysh supozimin se këto urdhëra mund të jenë të shpërndarë nëndonjë funksion kompleks. Për më tepër, në disa situata, memoria që ishte ezënë nga ob jekti është e pandryshuar deri në një thirrje të mëvonshme të “new” për kërkesë të memories, gjë që mund të jep iluzionin se nuk ka ndonjë problem.

sHello

 Fig. 1.17 Stale pointerët: pas urdhërit delete t, pointerët s dhe t tani pointojnë

në një objekt që nuk ekzisto më; urdhëri delet s do të ishte fshirje e dyfishtë

ilegale

Problem tjetër është i ashtuquajturi “double-delete” (fshirja e dyfishtë). Ky problem ndodhë kur tentohet të fshihet i njëjti objekt më shumë se një herë. Kjodo të ndodhte nëse do të jepej në vazhdim urdhëri:

delete s; // fshirje e dyfishtë

sepse s është “stale” dhe objekti në të cilin pointon nuk është valid (nukekziston). Është mundëia shumë e madhe që do të paraqiten probleme të kohëssë ekzekutimit (angl. run-time error).

Këto janë rreziqet e alokimit dinamik të memories. Duhet të jemi të sigurtë qëkurrë të mos e thërrasim urdhërin delete më shumë se një herë për një objekt dheatë vetëm pasi të mos jetë i nevojshëm. Nëse nuk thirret delete fare, edhe pseobjekti më nuk është i nevojshëm, atëherë do të ketë rrjedhje të memories.Gjithashtu, nëse kemi variabël pointer dhe synojmë ta fshijmë me delete, duhet

të jemi të sigurtë që objekti në të cilin pointohet ka qenë i krijuar me urdhërin

Page 66: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 66/699

Avni Rexhepi

66

“new”. Kur kemi thir rje të funksionit prej funksionit, përcjellja e të gjithaelementeve bëhet më e vështirë.

Së fundi, pointerët mund të bëhen “stale pointer” edhe nëse nuk është bërë

alokimi dinamik. Shqyrtoni kodin në programin 1.6.Për ndonjë arsye me kuptim (përveq për ilustrim të gabimit), kemi funksioninstupit i cili kthen pointerin në string. Nëse funksioni stupit e thërret new për tëkrijuar stringun, atëherë thirrësi do të jetë përgjegjës për thirrje të delete. Nëvend se të ngarkohet thirrësi, gabimisht kemi vendosur që funksioni stupid të përdorë një string automatik dhe të kthejë adresën e tij. Programi kompajlohet por mund të mos funksionojë, sepse përmbanë gabim. Problemi është se vlera tëcilën e kthen funksioni stupid është pointer. Por pointeri është duke pointuar nës, e cila më nuk ekziston, sepse është variabël automatike dhe funksioni stupid

(angl. stupid-torollak), veç ka kthyer me return (ka përfunduar punën). Kur tëkthehet vlera pointer, të jeni të sigurtë që keni diçka në të cilën pointoni dhe seajo ekziston edhe pasi kthimi (return) të jetë kompletuar.

1 string *stupid( )2 {3 string s = "stupid";4 return &s;5 }6 7 int main( )8 {9 cout << *stupid( ) << endl;10  return 0;11 }

Programi 1.6 - Stale pointer: i pointuari, s, nuk ekziston pasi funksioni stupid

kthen rezultatin.

1.5 ReferencatPërveç tipit pointer, në C++ ekziston edhe tipi reference (referencë). Referencaështë një alias (pseudonim, nofkë) për një objekt tjetër dhe mund të shihet edhesi pointer konstant që dereferencohet gjithmonë në mënyrë implicite. Përshembull, në kodin vijues, cnt bëhet sinonim për një variabël me një emërshumë të gjatë dhe të vështirë për t’u shkruar: 

int emeriGjateiVariables = 0;int & cnt = emeriGjateiVariables;cnt += 3: 

Page 67: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 67/699

Algoritmet dhe strukturat e të dhënave

67

Referenca duhet të inicializohet kur të deklarohet. Ajo nuk mund të ndryshohet për të ju referuar një variable tjetër sepse një tentim i ricaktimit të referencës përmes urdhërit:

cnt = njeObjektTjeter;do të ja ndante/caktonte objektit emeriGjateiVariables  vlerën e objektitnjeObjektTjeter. Kjo qasje reflekton me saktësi se si ata përdoren në rastemë të përgjithshme në të cilat fushëveprimi i variablës referente është indryshëm prej atij të objektit që referohet. Një rast i rëndësishëm është përdorimi i variablës referente si parametër formal i funksionit, me ç’rast ajovepron si një alias për argumentin aktual, me rastin e thirrjes së funksionit. Kjoështë diskutuar në kontekst të përcjelljes së parametrave të funksioneve, nërastin e përcjelljes së vektorëve (1.2.5).

Le të rishikojmë përcjelljen e parametrave.

 Në programin 1.7 ilustrohet procedura/funksioni swapGabim( ) e cila nukfunksionon për shkak të kurfizimeve të “thirrjes sipas vlerës” (përcjelljes sipasvlerës) tek funksionet. Dy alternativat korrekte janë ajo me pointer dhe mereferencë. Thirrja përmes pointerit, që është “C style” (në stil të C-së) ështëtradicionale për gjuhën C, bëhet për të evituar kufizimet e thirrjes përmes vlerës.Thirrja përmes referencës, e cila përdorë parametrat referent të C++, ështëopcioni tjetër, tërësisht identik për nga funksionimi.

Dallimet mes tipeve me referenca dhe me pointer janë të përmbledhura si vijon:-   Në deklarim të funksionit, parametrat referencë përdoren në vend të pointerëve.

-   Në definicion të funksionit, parametrat referencë dereferencohen nëmënyrë implicite, ashtu që nuk ka nevojë për operatorët * (vendosja etyre do të gjenerojë gabim sintaksorë).

-   Në thirrjen e funksionit sëapRef, nuk nevojitet &, sepse adresa përcilletnë mënyrë implicite mbështetur në faktin se parametrat formalkorrespondues janë referenca.

-  Kodi i cili përfshinë përdorimin e parametrave referentë është shumë mëi lexueshëm.

1 #include <iostream>2 using namespace std;3 4 // Nuk funksionon.5 void swapGabim ( int a, int b )6 {7 int tmp = a;8 a = b;

Page 68: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 68/699

Avni Rexhepi

68

9 b = tmp;10 }11 12 // C Style – duke perdorur pointeret.

13 void swapPtr( int *a, int *b )14 {15  int tmp = *a;16  *a = *b;17  *b = tmp;18 }19 20 // C++ Style – duke perodrur referencat.21 void swapRef( int &a, int &b )22 {

23  int tmp = a:24  a = b;25  b = tmp;26 }27 28 // Programi per testim te funksioneve “swap” (shkëmbe)29 int main( )30 {31  int x = 5;32  int y = 7;

33 34  swapGabim ( x, y );35  cout << "x=" << x << " y=" << y << endl;36  swapPtr ( &x, &y ) ;37  cout << "x=" << x << " y=" << y << endl;38  swapRef ( x, y );39  cout << "x=" << x << " y=" << y << endl;40 41 return 0;42 }

Programi 1.7 Call-by-reference kundrejt call-by-pointer. Referencat janë sikur konstantet pointer në atë se vlera të cilën e ruajnë ështëadresa e objektit të cilit i referohen. Ato janë ndryshe në atë se aplikohet njëoperator automatik i padukshëm i dereferencimit në referencë. Ky dallim përkthehet në lehtësi të notacionit, posaqërisht pasi që mundëson që parametrattë përcillen sipas referencës pa “bagazhin shtesë” të operatorit & në argumentetaktuale dhe operatorin * i cili shkakton “rrëmujë” në programet dhe funksionetnë stilin e C-së.

Page 69: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 69/699

Algoritmet dhe strukturat e të dhënave

69

Përndryshe, edhe pointerët mund të përcillen sipas referencës. Kjo metodë përdoret për t’i mundësuar funksionit që të ndrysho jë vendin ku pointon pointerii përcjellur si parametër. Pointeri që përcillet me thirje sipas vlerës nuk mund tëndryshohet që të pointojë në lokacion të ri (sepse parametri formal ruan vetëmnjë kopje të vlerës së atij vendi).

 Një çështje tjetër me rëndësi është zgjedhja ndërmjet përcjelljes së parametravesipas vlerës ose sipas referencës. Edhe pse kjo më herët u diskutua në konteksttë vektorëve, kjo vlenë për të gjitha tipet e parametrave.

1.6 Strukturat dhe pointerët

Rikujtojmë se vargu është koleksion i objekteve (vlerave, variablave) të tipit të

njëjtë. Vargu ka dy përparësi kryesore: së pari vargu indeksohet dhe mund tëlëvizim nëpër secilin element të vargut me anë të unazave dhe së dyti, kur përdoren funksionet, mund të përcillet emri i vargut dhe kështu të përdoretvetëm një parametërt për të dërguar tërë përmbledhjen.

 Një tip tjetër i tipit përmbledhës në C++ është struktura. Struktura ruan njëkoleksion të objekteve të cilat nuk është e domosdoshme të jenë të tipit të njëjtë.Pasi që objektet në koleksion nuk janë të tipit të njëjtë, nuk mundemi që thjeshtëtë kalojmë me unaza nëpër to, sikur në rastin e vargjeve.

Secili objekt në strukturë është anëtarë (angl. member) dhe qaset përmesoperatorit pikë të anëtarit (angl. dot member operator). Deklarimi i strukturës bazë bëhet përmes përdorimit të fjalës së rezervuar “struct”, emrit të strukturësdhe listës së anëtarëve të përmbyllur në kllapa të mëdha. Për shembull:

struct Student{

string emri;string mbiemri;int numriStudentit;double notaMesatare;

}

emri

mbiemri

numriStudentit

notaMesatare

  Fig. 1.18 –  Struktura Student

Page 70: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 70/699

Avni Rexhepi

70

Figura 1.18. ilustron se struktura Student përbëhet prej katër objekteve tëndryshme. Nëse kemi deklarimin,

Student s;

atëherë, për notën mesatare do të kemi s.notaMesatare. Programi 1.8ilustron deklarimin e strukturës, qasjen e anëtarëve të të dhënae dhe përcjelljen etyrë si parametra të funksionit. Vëreni se strukturat zakonisht nuk përcillen sipasvlerës për shkak se mbingarkimi i përcjelljes sipas vlerës mund të jetë shumë i“shtrenjtë” (për shkak të krijimit të kopjeve të parametrave, në këtë rast).Mekanizmi i përcjelljes së parametrave përcaktohet sipas asaj që u theksua tekdiskutimi për këtë çështje (1.2.5).

1 // Shtypi informacionet per studentin2 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);

222  return 0;23 } Programi 1.8 - Programi që ilustron deklarimin e strukturës, qasjen e anëtarëve

të saj dhe përcjelljen e parametrave

Struktura e C++ është zgjeruar dukshëm nga homologia e saj në C, për tëmundësuar funksionet si anëtarë dhe për qasjen në anëtarë.

Page 71: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 71/699

Algoritmet dhe strukturat e të dhënave

71

 Në diskutimin për teknikat e avansuara të programimit, hyn edhe deklarimi i pointerit në strukturë, për të ju qasur anëtarëve të strukturës së pointuar.Supozojmë se kemi:

Student *ptr = &s; //ptr pointon në sturkuturën sAtëherë ne mund t’i qasemi notës mesatare përmes (*ptr).notaMesatare.Kllapat janë të domosdoshme në këtë rast, për të rregulluara prioriteitn, pasi qeoperatori i anëtarit, duke qenë operator postfix, ka prioritet më të lartë sesaoperatori prefix i dereferencimit. Meqenëse përdorimi i kllapave bëhet imërzitshëm, C++ ofron një postfix operatorë tjetër plotësues, operatorin ->

(member selection operator –  operatori për selektim të

anëtarit), i cili i qaset anëtarëve të strukturës në të cilën pointohet.

Pra, operatori ->  përdoret për të ju qasur anëtarëve të strukturës së pointuar, prandaj, edhe forma ptr->notaMesatare  jep qasjen e njëjtë si urdhëri më parë.

1.6.1 Të dhënat ekzogjene kundrejt atyre indigjene dhe kopjimi i

cekët kundrejt atij të thellë

C++-i i lejon shfrytëzuesit që të definojë operatorët në struktura. Për shembull,shfrytëzuesi mund të shkruaj funksionin me deklarimin:

bool operator<(const Student &lhs, const Student &rhs);

i cili kthen “true” nëse Studenti i parë (lhs: left-hand side –  i anës së majtë) ështëmë i vogël se studenti i dytë (rhs: right-hand side  –  i anës së djathtë), bazuar nëndonjë kriter të definuar përbrenda funksionit, nga ana e shfrytëzuesit.(Shkurtesat lhs dhe rhs, për left-hand side dhe right-hand side do të përdrorennëpër shembuj, respektivisht). Përdorimi i mekanizmit të klasave, mundëson qëky funksion të përfshihet si anëtarë i strukturës, ngjashëm me anëtarët e tëdhënave.

Operatori i ndarjes së vlerës me kopjimi = dhe operatori i barazisë == gjithashtumund të definohen, por nëse nuk bëjmë asgjë, definicioni standard përdoret përkopjim dhe krahasimi i barazisë bëhet ilegal. Në mënyrë specifike, në mënyrëstandarde, kopjimi i strukturës implementohet si kopje anëtarë për anëtarë. Mefjalë tjera, secili anëtarë kopjohet nga njëra strukuturë në tjetrën, e jo në nivel tëstrukturës, si tërësi.

Problemi me këtë mekanizëm është ilustruar në deklarimin vijues:

struct Profesor

(

Page 72: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 72/699

Avni Rexhepi

72

string *emri;string "mbiemri;int IDPunetori;

} ;

Supozojmë se kemi:

Profesor s, t;

 Nëse supozojmë se t  është inicializuar, atëherë s=t  është kopjim anëtarë përanëtarë. Mirëpo, dy anëtarët e parë të strukturës janë thjeshtë pointerë, kështu qëvetëm adresat kopjohen. Prandaj rezultati është që s.emri  tani është duke e bashkëndarë memorien (angl. sharing memory) me t.emri, dhe këto nuk janëkopje të pavarura të stringut. Nëse më vonë jepet urdhëri:

delete t.emri;

 për të recikluar memorien e alokuar dinamikisht, s ndodhet në telashe serioze.Ky problem është i ilustruar në Figurën 1.19, e cila thekson dallimin ndërmjet tëdhënave ekzogjene dhe atyre indigjene. (indigjene-vendëse, të brendshme;ekzogjene-jo vendëse, të jashtme).

s

“Meri”

12345

t

12345

“Shala”

emri

mbiemri

IDPunetori

  Fig. 1.19 –  Kopjimi i cekët, ku kopjohen vetëm pointerët

Të dhënat indigjene (të vendit) janë tërësisht të përmbajtura nga ana estrukturës. Për shembull, në strukturën Student, anëtarët emri dhe mbiemri  janë stringje dhe janë tërësishtë të vetë-përmbajtur. Disavantazh i reprezentimittë objektit në mënyrë indigjene është se madhësia e objektit është fikse,zakonisht është e madhe dhe prandaj e kushtueshme për t’u kopjuar (kur përcillet në funksionet, etj).

Të dhënat ekzogjene (të jashtme), përkundrazi, qëndrojnë jashtë strukturës dhe janë të qasura përmes pointerit. Avantazh i të dhënave ekzogjene është se të

dhënat e zakonshme (përbashkëta) mund të bashkëndahen (angl. shared) mes

Page 73: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 73/699

Algoritmet dhe strukturat e të dhënave

73

disa instancave; kur përdoret operatori standard i ndarjes së vlerës, kopja ështëvetëm kopje e pointerëve, jo edhe e vlerave të pointuara. Zakonisht kjo sjelljeështë e dëshirueshme. Për shembull, në Java, kjo është standarde.

Kopjimi i cekët (angl. shallow copy) nënkupton kopjen e pointerëve, e jo të tëdhënave të cilat pointohen. Ngjashëm, krahasimet për barazi për të dhënatekzogjene janë të cekëta në mënyrë standarde, sepse ato krahasojnë vetëmadresat. Edhe pse kopja e cekët është korrekte në raste të caktuara, lejimi ikopjimit të cektë kur nuk është i garantuar mund të dërgojë nëshkatërrim/rrëmujë.

Kopjimi i thellë  (angl. deep copy), në të cilin kopjohen vlerat në të cilat pointohet, në përgjithësi nevojitet për të alokuar hapësirë memorike shtesë dhe pastaj për të kopjuar pointerët e dereferencuar. Kjo kërkon rishkrimin e

operatorit të ndarjes së vlerës me kopjim (angl. copy assignment operator).Detajet e implementimit të kësaj procedure jepen më vonë. Normalisht, duhet tësigurojmë edhe operatorin e krahasimit të thellë, për të implementuar testin ethellë (natyrisht, kur gjejmë se jemi duke përdorur së shumti operacionet ethella, mund të kthehemi në përdorimin e të dhënave indigjene).

1.6.1 Listat jo të vazhduara –  listat e lidhura

Do të diksutojmë një teknikë të përdorur në strukturat e të dhënave. Më parë u

tregua se duke përdorur vargjet dinamikisht të zgjerueshme, mund të lexojmënjë numër arbitrar të të dhënave hyrëse (elementeve hyrëse). Kjo teknikë ka një problem serioz. Supozojmë se jemi duke lexuar rekorde 1000-bajtëshe dhe kemi1,000,000 bajta të memories në dispozicion (të lirë). Gjithashtu, supozojmë senë një moment, vargu përmbanë 400 rekorde dhe është i mbushur në tërësi.Atëherë, për të dyfishuar atë, e krijojmë një varg të ri me 800 rekorde, ikopjojmë në të 400 rekordet ekzistuese dhe pastaj i fshijmë ato 400 rekordet (“tëvjetrat”). Problemi është se në këtë hapin e ndërmjetëm (kalimtarë), kemi në përdorim të dy: vargun me 400 rekorde dhe vargun me 800 rekorde dhe kështukemi në total 1200 rekorde, që i tejkalon kufijtë e memories. Në fakt, mund të

mbesim pa memorie pas bashkëndarjes së përafërsisht një të tretës së memoriesnë dispozicion.

Zgjidhja e këtij problemi, është që të lejohet që lista e rekordeve të ruhet nëfomë jo të vazhduar (angl. non-contiguously  –   jo e afërt, jo e puthitur, jo evazhduar, është fjala për dallim nga rasti kur të gjithë anëtarët e vargut, janë nëlokacione të njëpasnjëshme, të vazhdueshme në memorie). Për secilin rekord, embajmë një strukturë e cila ruan rekordin (vlerën) dhe një pointer “next” (angl.next-tjetri, i ardhshmi), për në strukturën e ardhshme në listë. Shembulli

Page 74: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 74/699

Avni Rexhepi

74

themelor është treguar në figurën 1.20. Struktura rezultuese është lista e lidhur klasike, e cila i ruan të dhënat me një kosto (çmim) të një pointeri për element.

Definicioni i strukturës është:

//Node=Nyje; item=elementi; next=tjetri,i/e ardhshme;struct Node{

Object item; // elementiNode *next;

}l;

 Në çdo pikë, ne mund të shtypim listën, duke përdorur iteracionin (unazën)

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,eRelast->next = new Node;// Shto një nyje të relast = last->next; // Përshtate të fundit-lastlast->item = x; // Vendose x-in në nyjelast->next = NULL; // Ky është i fundit, kështu që bëje

// next=NULL

A0

First

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

Last

(Fundi, i/e fundit)

  A1 A2   A3

  Fig. 1.20 –  Lista e lidhur

Kështu, elementet mund të mos jenë në lokacione të njëpasnjëshme në memorie,mirëpo për të gjetur një element të listës, më nuk mundmi me vetëm një çasje, si

në rastin e vargut të zakonshëm, kur përmes indeksit, secili element mund tëgjindej direkt, me vetëm një qasje. Në vend të kësaj, duhet të “skenojmë” (angl.scan-hetim, kërkim, kqyrje etj.) listën prej fillimit e tutje. Dallimi është ingjashëm me atë të qasjes në të dhënat (p.sh., këngët) në CD (një qasje) dhe nëshirit (sekuenciale). P.sh., për të dëgjuar këngën e 3, në CD mundemi direkt,kurse në kasetofon të vjetër, duhet rrotulluar shiritin prej fillimit e deri te këngae tretë.

 Në anën tjetër, insertimi i një elementi të ri ndërmjet dy elementeve ekzistuesekërkon shumë më pak lëvizje të të dhënave në listën e lidhur sesa në një varg.

P.sh., për të shtuar një element të ri në mes të dy anëtarëve ekzistues, duhet

Page 75: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 75/699

Algoritmet dhe strukturat e të dhënave

75

kopjuar pjesa prapa e vargut, për t’u ruajtur dhe zhvendosur në pozitat pasinsertimit të anëtarit të ri, kurse, në rastin e listës së lidhur, kjo gjë realizohetvetëm duke i ndërruar pointerët e elementit para dhe atij pas elementit të ri mestyre, që të krijohet renditja e re. Avantazhi i listave të lidhura është më pakhapësirë e përdorur për objektet e mëdha sesa në teknikën e dyfishimit të vargut.“Dënimi” 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 Kontejnerët

Kontejneri është strukturë e të dhënave e cila mbanë disa objekte të cilatzakonisht janë të tipit të njëjtë (angl. contain-përmbaj, zë, përfshij; angl.container  –   enë, kuti). Tipet e ndryshme të kontejnerëve organizojnë objektet

 përbrenda tyre në mënyra të ndryshme. Edhe pse numri i organizimeve tëndryshme teoritikisht është i pakufizuar, vetëm një numër i kufizuar i tyre karëndësi praktike dhe ato që përdoren më së shpeshti janë të përfshira në STL.STL i përmbanë kontejnerët vijues: deque, list, map, multimap, set, multiset,stack, queue, priority_queue dhe vector.

Kontejnerët e STL-it janë të implementuar si klasa shabllone (template classes)të cilat përfshijnë një numër funksionesh të cilat specifikojnë se cilat operacionemund të kryhen në elementet e ruajtura në strukturën e të dhënave të specifikuar prej kontejnerit ose në vetë strukturën e të dhënave. Disa operacione mund të

gjinden në të gjithë kontejnerët, edhe pse ato mund të jenë të implementuarndryshe. Funksionet e zakonshme të të gjithë kontejnerëve përfshijnëkonstruktorin e zakonshëm, konstruktorin e kopjimit (copy constructor),destruktorin, empty() (zbraze), max_size() (madhësia maksimale), size()(madhësia), sëap() (shkëmbe), operatorin = dhe përveq ‘priority_queue’ gjashtëoperatorët relacional të mbingarkuar. Për më tepër, funksionet e zakonshme nëtë gjithë kontejnerët, përveq stack, queue dhe priority_queue, përfshijnë edhefunksionet: begin() (fillimi), end() (fundi), rbegin(), rend(), erase() (fshije) dheclear() (pastro).

Elementet e ruajtura në kontejnerë mund të jenë të çfarëdo tipi dhe ato duhet tëofrojnë së paku konstruktorin e zakonshëm, destruktorin dhe operatorin endarjes së vlerë (=). Kjo është posaqërisht e rëndësishem për tipet e definuara prej shfrytëzuesit. Disa kompajlerë mund të kërkojnë mbingarkimin e disaoperatorëve (së paku ‘= =’ dhe ‘<’, por ndoshta edhe ‘!=’ dhe ‘>’ poashtu) edhe pse programi nuk i përdorë ato. Gjithashtu, ‘copy construcor-i’ dhe operatori ifunksionit ‘=’ duhet të ofrohen nëse të dhënat janë pointerë, sepse operacionet einsertimit përdorin kopjen e një elementi që është duke u insertuar, e jo vetëelementin.

Page 76: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 76/699

Avni Rexhepi

76

1.7.2 Iteratorët

Iteratori (angl. interate –  përsëris), është një objekt që përdoret për të ju referuar

një elementi të ruajtur në kontejner. Prandaj, iteratori është një përgjithsim i pointerit. Një iterator mundëson qasjen në informacionin e përmbajtur nëkontejner ashtu që opercioni i dëshiruar të mund të kryhet në këto elemente.

Si përgjithësim i pointerëve, iteratorët mbajnë notacionin e njëjtë tëdereferencimit. Për shembull, ‘*i’ është një element i referencuar nga iteratori‘i’. Poashtu, aritmetika e iterator ëve është e ngjashme me atë të pointerëve, edhe pse të gjitha operacionet në iteratorë nuk lejohen në të gjithë kontejnerët.

Për kontejnerët: stack, queue dhe priority_queue nuk përkrahet asnjë iteratorë.Operacionet e iteratorëve për klasat list, map, multimap, set dhe multiset, janë sivijon (i1 dhe i2 janë iteratorë, n është numër):

i1++, ++i1, i1--, --i1i1=i2i1 == i2, i1 != i2,*i1

Përveq këtyre operacioneve, operacionet e iteratorëve për klasat deque dhevector janë si vijon:

i1 < i2, i1 <= i2, i1 > i2, i1 >= i2

i1 + n, i1 - ni1 += n, i1 -= n,i1[n]

1.7.3 STL Algoritmet

STL ofron afër 70 funksione të përgjithshme, të cilat mund të aplikohen nëkontejnerë dhe vargje, duke u përfshirë në program përmes direktivës #include<algorithms>.

Këto funksionie (të quajtura edhe algoritme) janë operacione që përdoren shumëshpeshë në shumicën e programeve, si p.sh. lokalizimi i një elementi nëkontejner, insertimi i elementeve, largimi i elementeve, modifikimi ielementeve, krahasimi i elementeve, gjetja e vlerës bazuar në sekuencën eelementeve, sortimi i elementeve, e kështu me radhë. Pothuajse të gjitha STLalgoritmet përdorin iteratorët për të treguar rangun e elementeve në të cilat atooperojnë. 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ë.

Për shembull, thirrja e funksioneve:

Page 77: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 77/699

Algoritmet dhe strukturat e të dhënave

77

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

i renditë në mënyrë të rastit të gjitha elementet e kontejnerit ‘c’. Thirrja: 

i3 = find (i1, i2, el);

kthen një iteratorë i cili tregon pozitën e elementit ‘el’ në rangun prej i1 deri nëi2. Thirrja:

n = count_if(i1, i2, oddNum);

numëron përmes algoritmit ‘count_if’ elementet në rangun e treguar përmesiteratorëve i1 dhe i2, për të cilët funksioni me një argument, i definuar ngashfrytëzuesi, ‘oddNum()’, kthen ‘true’.

Algoritmet e STL-it janë funksione të cilat janë plotësim për funksionet eofruara nga kontejnerët. Sidoqoftë, disa algoritme jantë të definuara si funksioneanëtare të klasave, për të ofruar performansë më të mirë.

Aritmetika e pointerëve

Pointerët përdoren për të kaluar nëpër memorie sekuencialisht duke përdoruraritmetikën e pointerëve dhe operatorin e inkrementimit (++) dhe operatorin edekrementimit (--). Operatori i inkrememntimit e rritë vlerën e variablës për 1,ndërsa operatori i dekrementimit e zovëlon vlerën e variablës për 1.

 Në shembullin vijues, vlera e variablës numriStudentit rritet për 1, duke bërë qëvlera finale të jetë 1235:

int numriStudentit = 1234;numriStudentit++;

ngjashëm, shembulli vijues, e zvogëlon vlerën e variablës numriStudentit për 1,duke rezultuar në velrën finale 1233.

int numriStudentit = 1234;numriStudentit--;

Aritmetika e pointerëve përdorë operatorin e inkrementimit dhe atë tëdekrementimit, në mënyrë të ngjashme, por pak më ndryshe. Urdhërat vijuesdeklarojnë dy variabla të përdorura për të ruajtur numrat e studentëve dhe dy pointerë, ku secili pointon në njërën prej këtyre variablave.

int numriStudentit1 = 1234;int numriStudentit2 = 5678;int *ptNumriStudentit1;int *ptNumriStudentit2;

ptNumriStudentit1 = &numriStudentit1;

Page 78: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 78/699

Avni Rexhepi

78

ptNumriStudentit2 = &numriStudentit2;

Sa do të jetë vlera e ruajtur në pointerin ptNumriStudentit1 nëse ptNumriStudentit1 inkrementohet për 1 duke përdorur urdhërin vijues:

ptNumriStudentit1++;

Kjo është pak problematike, sepse vlera e ptNurmriStudentit1 është 0. Nëseajo inkrementohet për 1, vlera e re do të duhet të ishte 1. Mirëpo, adresa ememories 2 është pjesa e dytë e lokacionit të memories së rezervuar përnumriStudentit1. Kjo do të thotë se ptNumriStudentit1 do të pointontenë mes të vlerës së numriStudentit1, gjë që nuk ka kuptim.

Ja se çka ndodhë në realitet. Kompjuteri përdorë aritmetikën e pointerëve. Vleratinkrementohen dhe dekrementohen në aritmetikë të pointerëve duke përdorur

madhësinë e tipit të të dhënave. Pra, nëse adresa e memories përmbanë një vlerëinteger dhe adresa e memories inkrementohet, kompjuteri e shton madhësinëe një integeri  ndaj lokacionit aktual të adresës së memories. Kjo i bie, qëlëvizja para/prapa bëhet me hapin e tipit të pointerit, e secili tip i pointerit kamadhësinë e hapit varësisht prej tipit të variablës në të cilën pointon. Kjo i bieqë pointeri i tipit int, ka hapin 4 bajta (kalon nga 4 bajta), ai i tipit double kahapin 8 bajta (kalon nga 8 bajta) dhe ai i tipit karakter  ka hapin 1 bajt(gjegjësisht, lëvizë me hap prej 1 bajti). Në rastin e strukturave, pointeri istruktorës lëvizë me hapin e madhësisë totale të strukturës.

Figura 1.21 paraqet një varg a, pointerin ptr  dhe urdhërin ptr=a. Këtu përforcohet idea se vlera e ruajtur në a  është vetëm lokacioni i memories kuqëndron elementi i “zero-t” (elementi i pare, me indeks 0) i vargut dhe seelementet e vargut garantohet të jenë të ruajtura në lokacione të njëpasnjëshme(konsekutive, të afërta) dhe në rritje të memories. Nëse a është një varg ikaraktereve, a[1]  është i ruajtur në lokacionin e memories a+1, sepsekarakteret përdorin një bajt. Për ketë arsye, ++ptr do të rriste pointerin ptr për1, duke rezultuar në lokacionin e memories së a[1].  Prandaj, shtimi i njëintegjeri në variablën pointer ka kuptim në një varg të karaktereve.

 Nëse a do të ishte varg i integjerëve 4-bajtësh, shtimi i 1-shit në ptr do të dukejse bën që pointri të lëvizë një bajt më tutje. Mirëpo, për interpretim më të lehtë,thuhet se: ++ptr  ia shton adresës së pointerit ptr, madhësinë e objektit në tëcilin pointon. Ky interpretim bartet në operacionet tjera me pointer. Kështu,shprehja x=&a[3]  bën që pointeri x të pointojë në a[3]. Nuk ka nevojë përkllapa të vogla përreth, si është thënë më herët. Shprehja y=x+4, bën që pointeriy të pointojë në a[7]. Kështu, do të mund të përdorim pointerët për të përshkuar vargun, në vend të metodës së zakonshme të iteracionit me indeksat evargut.

Page 79: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 79/699

Algoritmet dhe strukturat e të dhënave

79

a

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]

ptr x y

 

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

Nëse p është pointer dhe x është i tipit integer, g+x vlerësohet si adresa ‘g’  objektepërtej 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 pointerëve nuk ka kuptim. Mirëpo, zbritja e dy pointerëvefunksionon: y-x do të vlerësohej 4 (në shembullin e mëparshëm, lartë  –  sepsezbritja është operacioni invers i mbledhjes). Prandaj, pointerët mund të zbriten, por jo të mblidhen.

Për dy pointerë, x dhe y, x<y është e saktë nëse objekti në të cilin pointon pointeri x është në adresë më të ulët sesa objekti në të cilin pointon pointeri y. Nëse supozojmë se asnjëri nuk pointon në NULL, kjo shprehje është pothuajse e pakuptim përveç nëse të dy pointojnë në elementet e vargut të njëjtë. Në këtërast, x<y do të jetë e saktë nëse x pointon në elementin me indeks më të ulët se

y, sepse si është treguar, elementet e vargut është e garantuar se ruhen nëlokacione të njëpasnjëshme dhe në rritje të memories. Krahasimi i vlerave të pointerëve që pointojnë në të njëjtin varg është i vetmi përdorim legjitim ioperatorëve të krahasimit në rastine pointerëve. Përdorimet tjera duhet evituar.

Mos përdorni operatorët relacional (të krahasimit) në pointerë, përveç nëse të dypointerët pointojnë në pjesët e vargut të njëjtë. 

Si përmbledhje, kemi këto operacione të pointerëve:

-  Pointerëve mund të ju ndahet vlera, të krahasohen për barazi (jobrazi)

dhe të dereferencohen (në C++ dhe në shumicën e gjuhëve programuese). Operatorët 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.Operatorët janë: ++,--, +,-,+= dhe -=.

-   Në pointerë mund të aplikojmë operatorët relacional, por rezultati kakuptim vetëm nëse të dy pointerët pointojnë në pjesët e vargut të njëjtëose së paku njëri prej tyre pointon në NULL. Operatorët janë: <, <=,> dhe >=.

Page 80: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 80/699

Avni Rexhepi

80

-  Mund të testojmë me aplikimin e operatorit ! ndaj NULL  (sepse NULL  pointeri ka vlerën 0).

-  Mund të indeksojmë dhe të fshijmë pointerët përmes [ ] dhe delete.-  Mund të aplikomë operatorët trivial, si

&dhe

sizeof, për të gjetur

informacion për pointerin (jo për objektin në të cilin ai pointon).-  Mund të aplikojmë edhe disa operatorë të tjerë, si ->.

Pointerët në pointerë

Imagjinoni rastin kur kemi një listë me 1 milion studentë me notat e tyre dhenumrat e tyre, dhe këkohet që të sortohet lista sipas emrit, mbiemrit dhe numrittë studentit. Intuitivisht, mund të mendohet për krijimin e dy kopjeve të listës,

secila me një renditje të sortimit. Mirëpo, kjo shkakton humbje të panevojshmetë memories. Ka një qasje më të mirë për të sortuar listat: përdorimi i pointerëvetë pointerëve.

Dihet se pointeri është variabël e cila përmbanë adresën e memories së njëvariable tjetër. Pointeri i pointerit, gjithashtu është variabël që përmbanë adresëne memories, por në këtë rast adresën e memories së një pointeri tjetër.

 Nëse ju duket e koklavitur, nuk jeni i vetmi! Koncepti i pointerit në pointer nukështë mjaft intuitiv. Mirëpo, kjo sqarohet duke deklaruar variablat dhe dukeruajtur vlerat në memorie.

Le të fillojmë me deklarimin e katër variablave dhe inicializimin e tyre meshkronja të alfabetit. Kjo është treguar në urdhërin e parë të shembullit në vijim.Urdhëri i dytë deklaron pointerin e quajtur ptInitial dhe pointerin në pointer tëquajtur ptPtInitial. Pointeri deklarohet duke përdorur shenjën asterisk (*).Pointeri në pointer deklarohet duke përdorur dy asterisk-a (**).

char inital1='D', inital2='A', inital3='C', inital4='B';char *ptInitial, **ptPtInitial;ptInitial = &inital1;ptPtInitial = &ptInitial;

Me variablat e deklaruara, dy urdhërat vijues ua ndajnë vlerat pointerit dhe pointerit të pointerit. Në të dy rastet përdoret operatori i adresës (&).

Pointerit ptInitial i ndahet adresa e variablës intial1, e cila vendoset në memorienë adresën 1. Pointerit të pointerit, ptPtIntial, i ndahet adresa e memories së pointerit ptInitial. Adresa e ptInitial është adresa 5 e memories. Në figurën 1.22 paraqitet meoria e alokuar pas ekzekutimit të këtyre urdhërave.

Page 81: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 81/699

Algoritmet dhe strukturat e të dhënave

81

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

 pointerit ptInitial.

Programerët e përdorin pointerin në pointer për t’i treguar kompjuterit që të përdorë përmbajtjen e adresës së memories në variablën pointer, në të cilën pointon pointeri në pointer. Kjo është më lehtë të sqarohet me një shembull:

Përmbajtja e variablës intitial1 mund të përdoret duke ju referuar variablës ptPtInitial. Ja si bëhet kjo:

cout << **ptPtInitial;

Urdhëri cout i përdorur në këtë shembull, bën që të paraqitet përmbajtja evariablës initial1, edhe pse nuk duket se bënë këtë. Ky urdhëri është duke i

treguar kompjuterit që të shkojë në adresën e memories të ruajtur në pointerin ptPtInitial, që pointon në variablën pointer, e cila është adresa 5 e memories(Figura 1.23). Përmbajtja e asaj adrese të memories, është një adresë tjetër ememories, e që është adresa 1. Kompjuterit i është thënë të shkojë në adresën ememories 1 dhe të paraqesë përmbajtjen e asaj adrese të memories, e që ështëshkronja D.

 Figura 1.23: Dy adresa të memories referohen kur përdoret pointeri në pointer,

 për të paraqitur vlerën në ekran. 

Page 82: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 82/699

Avni Rexhepi

82

Vargjet primitive

Sikur variablat e zakonshme, që duhet të deklarohet para se të përdoret në

ndonjë shprehje dhe të inicializohet përa se të përdoret vlera e saj, ashtu duhetedhe vargu. Vargu deklarohet duke i dhënë emrin dhe duke i treguar kompajlerittipi e elementeve të tij. Nëse definohet vargu, duhet të jepet edhe madhësia e tij.Madhësia mund të anashkalohet nëse bëhet inicializimi direkt i vargut, ekompajleri pastaj i numëron vlerat e inicializuara dhe atë e merr si madhësi 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 ëprmesoperatorit 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. Mirëpo, C++, nuk bën kontrollim të kufijve, prandaj duhet kujdes sepse qasja me indeks jashtëkufijve nuk hetohet nga kompajleri. Nuk gjenerohet as gabim eksplicit i kohëssë ekzekutimit, mirëpo ndodhin sjellje dhe velra të “çuditshme” të programit.Për më tepër, nëse vargu përcilet se argument aktual në funksion, atëherefunksioni nuk ka ide për madhësinë e vargut, përveç nëse i përcilllet edhe një parametër plotësues. Vargjet, nuk mund të kompjohen me operatorin =, bazuarnë tiparet themelore të gjuhës për vargjet dhe pointerë dhe kufizimet për to.

Emri i vargut është Pointer

Kur alokohet një varg, kompajleri e shumëfishon madhësinë në bajta të tipit tëdeklaruar me madhësinë e vargut (të shënuar brenda [ ]), për të përcaktuar se samemorie duhet rezervuar. Në esencë, ky është përdorimi i vetëm i komponentëssë madhësisë. Në fakt, pasi të alokohet vargu, me përjashtime të vogla,madhësia e tij është e parëndësishme, sepse emri i vargut reprezenton një pointernë fillimin e memories së alokuar për atë varg, ashtu si paraqitet në figurën D.1.

Supozojmë se kemi deklarimet:

int a[3];int i; 

Kompajleri e alokon memorien si vijon. Së pari, tre integjera ndahen/rezervohen për vargun dhe referohen përmës a[0], a[1]  dhe a[2].  Objektet në vargështë e garantuar se do të ruhen në blloqe të njëpasnjëshme të memories.Prandaj, nëse a[0]  është ruajtur në lokacionin 1000  dhe integjeri kërkon 4 bajta, atëherë a[1]  është e garantuar se do të jetë i lokalizuar në lokacionin1004 dhe a[2] në lokacionin 1008. Në fund, kompajleri alokon memorien për

objektin tjetër, integjerin i. Një mundësi është paraqitur në figurën 1.24., ku i

Page 83: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 83/699

Algoritmet dhe strukturat e të dhënave

83

alokohet në sllotin e ardhshëm në dispozicion (angl. slot-vend i caktuar, ndarje,vrime, etj).

Për çdo i, ne mund të nxjerrim përfundim 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. Vëreni se kuralokohet vargu a, vlera e a është konstante; nuk krijohet për të pointeri. Pasi qëky rast e trajton a si konstantë e jo si objekt, &a merr kuptim special (konstantetnormalisht nuk kanë adresa) dhe në vetëm në këtë rast, vlera e &a është a.

&a[0] (1000) a[0]

a[1]

a[2]

i

a=1000

. . .

&a[1] (1004)

&a[2] (1008)

&i (1012)

konstantet lokale

 

 Fig. 1.24. Modeli i memories për vargjet

Tani, për të ju qasur elementit a[i], kompajlerit vetëm i duhet të merr vlerën ea dhe t’ia shtojë 4i.

Duke pasur parasysh këtë mënyrë të manipulimit të vargjeve në C++, mund tëshihet se përse vlejnë kufizimet e përmendura për vargjet dhe si përcillen vargjetsi parametra të funksioneve.

Së pari, kemi problemin e verifikimit se a është indeksi i caktuar brenda rangut.Kryerja e verifikimit të kufijve do të kërkonte që të ruhet madhësia e vargut në

një parametër plotësues. Sigurisht që kjo është e mundur, por kjo gjë shkaktonkosto shtesë të kohës dhe hapësirës. Në aplikimet e zakonshme të vargjeve(stringjet e shkurtëra), kjo mbingarkesë do të ishte e dukshme. Si është thënë më parë, nëse shftyrëzuesi dëshiron të bëjë verifikimin e kufijve, mund shkruhet njëklasë dhe të përdoret thuase do të ishte varg i predefinuar (ky është shabllonivector). Prandaj, nuk na mbetet që të diskutojmë për vendimin e dizajnerëvetë C++ që të mos mandatojnë verikimin e rangut, edhe pse kjo mungesë mund tëshkaktojë probleme serioze. Shqyrtoni fragmentin vijues të kodit, që përdorëdeklarimet e mëparshme të a dhe i:

Page 84: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 84/699

Avni Rexhepi

84

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

 Në këtë rast, programeri bën gabimin e zakonshëm të qasjes në a[3], duke

harruar se madhësia 3 e vargut përfaqëson vetëm indeksat prej 0 deri në 2. Kur i bëhet 3, kompajleri e ekzekuton urdhërin a[3]=0 pa e verifikuar nëse indeksiështë valid. Supozojmë se memorja është alokuar, ashtu si është paraqitur nëfigurën 1.24. Efekti i kësaj është që lokacioni i memories 1012 mbishkruhet me0 dhe kështu duke e “shkatërruar” i-në. rezultati i kësaj, d.m.th., resetimi i i në 0,shkakton unazë të pafund. Mirëpo, nëse kompajleri ka vendosur (si bëjnë disakompajlerë) që të lerë lokacionin 1012 të lirë dhe të vendosë i-në diku tjewtr, programi duket se punon. Prandaj, gabimet për nga një pozitë në indekset evargjeve mund të dërgojnë në bug-a që shumë vështirë vërehen. Në shembullin e paraqitur, unaza është e pafund, mirëpo i-ja nuk është ndryshuar direkt

asnjëherë.

Kufizimi i dytë për vargun themeor (që mund të përmirësohet vetëm përmesklasës së definuar prej shfrytëzuesit) është kopjimi i vargut. Supozojmë se a dhe b janë vargje të tipit të njëjtë. Në shumë gjuhë programuese, nëse vargjetgjithashtu janë të madhësisë së njëjtë, urdhëri a=b do të kryente kopjiminelement për element të bargut b në vargun a. Në C++ ky urdhër është ilegal,sepse a dhe b përfaqësojnë pointerë konstant në fillimin e vargjeve të tyrerespektive, në mënyrë specifike të &a[0] dhe &b[0]. Atëherë 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 bën urdhërin ilegal, e jo legal por të gabuar, është fakti se a-ja nukmund të ricaktohet që të pointojë diku tjetër sepse ajo në esencë është objektkonstant. Mënyra e vetme për të kopjuar dy vargje është që kjo të bëhet element për element; nuk ka shkurtesë. Argumenti i ngjashëm tregon se shprehja a==bnuk vlerësohet në true nëse dhe vetëm nëse secili element i a-së i përshtatatetelementit përkatës të b-së. Në vend të kësaj, kjo shprehje është legale. Shprehja jep true nëse dhe vetëm nëse a dhe b reprezentojnë lokacionin e njëjtë tëmemories (d.t.th, i referohen vargut të njëjtë).

 Në fund, vargu mund të përdoret si parametër 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 parametër një varg të tipitint. atëherë pamja nga këndvështrimi i thirrësit dhe të thirrurit janë si vijon:

functionCall(varguAktual) ; // thirrja e funksionitfunctionCall(int varguFormal[]) //deklarimi i funksionit 

Vëreni që në deklarimin e funksionit, kllapat shërbejnë vetëm si deklarim i tipit,në mënyrë të njëjtë si bën edhe int. në thirrjen e funksionit, përcillet vetëm emrii vargut; nuk ka kllapa fare. Në pajtim me rregullat e thirrjes/përcjelljes sipas

vlerës në C++, vlera e varguAktual kopjohet në varguFormal. Pasi që

Page 85: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 85/699

Algoritmet dhe strukturat e të dhënave

85

varguAktual përfaqëson lokacionin e memories ku i tërë varguAktual ështëruajtur, varguFormal[i] i qaset varguAktual[i]. me fjalë tjera, variablat e përfaqësuar përmes vargut të indeksuar janë të modifikueshme. Prandaj vargu,kur konsiderohet si “aggregate” (përmbledhës), përcillet sipas referencës. Përmë tepër, çfarëdo komponente e madhësisë në deklarimin e varguFormalinjorohet dhe madhësia e bargut aktual është e panjohur. Nëse madhësia është enevojshme, ajo duhet të përcillet si një argument shtesë.

Vëreni se përcjellja e përmbledhësit sipas referencës nënkupton që funksionimund të ndryshojë elementet në varg. Atëherë, mund të përdoret direktiva const, për të tentuar që të mos lejohet ndryshimi i tillë:

functionCall ( const int varguFormal[ ] );

Tipi char*, pointeri konstant dhe stringu konstant

 Një përdorim i rëndësishëm i pointerëve dhe vargjeve në C++, ështëimplementimi i stringjeve. C++ bazë ofron përkrahje minimale për stringjet, të bazuar tërësisht në rregullat e gjuhës C dhe libraritë e saj. Rezultati është shumëminimal për të qenë i dobishëm për një gjuhë moderne programuese, si ështëedhe për vargjet. Programerët në C++ priren të bazohen në klasën e librarisë<string>. Mëgjithatë, është mirë të dihet se si implementohen stringjet nëlibrarinë bazë të C-së, sepse ata formojnë bazën për klasën string.

 Në C++ dhe në C, stringu është një varg i karaktereve. Si rezultat, kur t’i përcillet funksionit, stringu ka tipin char * ose const char *. Në shikim të parëmund të supozohet se “Nina” është një varg i katër karaktereve: ‘N’, ‘i’, ‘n’, dhe‘a’.  Problemi me këtë supozim është se nëse këtë varg ia përcjellni 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 vetëm pointerin dhe prandaj nukka ide se sa i madh është vargu aktualisht. Një zgjidhje për këtë problem është të përdoret një varg pak më i madh me një shenjë/marker të fundit të vargut.

Për shembull, mund të deklarojmë një varg prej 5 elementeve, duke vendosur

një zbrazëtirë në pozitën e fundit për të sinjalizuar se vetëm katër pozitat e para përfaqësojnë karaktere të rëndësishme. Nëse të gjitha funksioniet shkruhen përtë reflektuar këtë marrëveshje, atëherë kemi një zgjidhje të problemit i cilikërkon një ndryshim të vogël të gjuhës. Sepse, ne do të mund të dëshironim të përdorim zbrazëtirën brenda stringut (p.sh., për të ruajtur adresën e banimit),atëherë duhet të zgjedhim një shenjë për funind (angl. endmarker) që nuk kagjasa që të paraqitet tjetërkund në string. Në C++ ku karakter special është “nullterminator” ‘\0’ (shenja e përfundimit NULL). Simboli ‘\’ tregon se nullterminator-i gjithmonë është i reprezentuar në mënyrë interne si zero, gjë që çonnjë një stenografi të shkurtër kur kontrollohet kur shprehja kontrolluese është e

Page 86: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 86/699

Avni Rexhepi

86

shkruar në një urdhër ‘if’ ose në unazë. (Kujdes!. Është e zakonshme të harrohetshenja \ para zeros, duke lënë vetëm ‘0’, që në fakt është karakteri për shifrën 0).Prandaj, një varg prej 6 karaktereve ‘N’, ‘i’, ‘n’, ‘a’, dhe ‘\0’, përfaqësonstringun “Nina”, pa marrë parasysh se çka ka në karakterin e gjashtë.

Pra, problemi është se C++ nuk ofron absolutisht asgjë si përkrahje për stringjet.Për më tepër, nuk ofron drejtpërdrejtë disa gjëra të gjuhës, si në rastin kurdeklarojmë dy stringje str1 dhe str2, si në vijimL

char strl[10]; // gjatesia Max eshte 9char str2[10]; // gjatesia Max eshte 9

Atëherë urdhërat vijues nuk mund të jenë korrekt:

strl = str2; //Gabim!cond = (strl == str2); // Gabim !

Ky dështim vjen drejtpërdrejt prej faktit se str2 dhe str2 janë vargje dhe ndarja evlerës së vargjeve dhe krahasimi i vargjeve nuk është i përkrahur nga gjuha. Nëfakt, e tërë përkrahja ofrohet nga libraria e C++, e cila specifikon funksionet tëcilat punojnë me stringjet null-terminated. Prototipet për këto funksione janëdhënë në fajllin që duhet përfshirë me direktivën #include <string.h> (ose<string>). Ky fajll është i replikuar në <cstring>. Disa funksione të rëndësishme janë paraqitur në figurën në vijim.

1 size-t strlen( const char *str );

2 char * strcpy( char *lhs, const char *rhs );3 char * strcat( char *lhs, const char *rhs ) ;4 int strcmp( const char *lhs, const char *rhs 1; Figura D.2. Disa funksione në <string>

Funksioni  strlen(str)  jep gjatësinë e strungut str (pa përfshirë null-terminator-in); gjatësia e strungut “Nina” është 4. Në këtë dhe të gjithafunksionet, nëse përcillet NULL Pointeri, mund të pritet që programi të bllokohet. Vëreni se kjo qasje është e ndryshme nga përcjellja e pointerit në pozitën memorike e cila përmbanë karakterin ‘\0’, i cili  përfaqëson stringun e

zbrazët të gjatësisë 0. Funksioni strcpy(lhs, rhs) kryen ndarjen e vlerës sëstringjeve: karakteret e dhëna nga rhs kopjohen në vargun e dhënë me lhs, derisa të kopjohet null-terminatori. (Skurtesat ishin: lhs-left hand side –  ana e majtë,rhs-right hand side –  ana e djathtë). Nëse stringu i përfaqësuar në lhs nuk ështëmjaft i madh sa për të ruajtur kopjen, një pjesë tjetër e memories mbishkruhet(duke shkaktuar dëmtimin e të dhënave në atë pjesë). Pra, funksioni strcpy nukverifikon se a është caku mjaft i madh për të ruajtur kopjen.

Renditja e parametrave lhs dhe rhs mbahet mend lehtë, duke pasur parasysh që:strcpy( lhs, rhs )

Page 87: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 87/699

Algoritmet dhe strukturat e të dhënave

87

është menduar që të imitojë shprehjen:

lhs = rhs: 

Tipi i kthimit të rezultatit char * i mundëson funksionit strcpy që të zgjerohet

në formë zinxhirore, njësoj si urdhërat: strcpy (a,strcpy (b,c ))  që janë sikur   a=b=c.

Funksioni strcat(lhs, rhs) e shton (bashkangjet) kopjen e stringut rhs, në vazhdimtë atij lhs. Njësoj si në rastin e strcpy, është përgjegjësi e programerit që tësigurohet që lhs është duke pointuar në hapësirë të mjaftueshme të memories përtë ruajtur rezultatin. Funksioni strcmp, i krahason dy stringje dhe kthen numërnegativ, zero ose pozitiv, varësisht prej asaj se a është stringu i parëleksikografikisht më i vogël, i barabartë apo më i madh sesa i dyti.

Siç është përshkruar më herët, C++ ofron funksionet e librarive për stringje por jo edhe përkrahje nga ana e gjuhës. Në fakt, përkrahja e vetme nga gjuha ështënga string konstantja. Konstantja string është sekuencë e karaktereve të rrethuaranë thonjëza. Null terminatori i shtohet automatiksht. Konstantja string ofronmekanizëm të shkurtër për të specifikuar sekuencën e karaktereve. Ajoautomatikisht e përfshinë null terminator-in si një karakter të fundit të padukshëm. Cilido karakter (i specifikuar me shenjën speciale \, nëse është enevojshme) mund të paraqitet në konstanten string, kështu që “Nina”reprezenton vargun me pesë karaktere. Për më tepër, string konstanta mund të

 përdoret si inicializues për vargun e karaktereve, prandaj:char namel[]="Nina"; //namel - varg prej 5 karakterevechar name2[9]="Nina"; //name2 - varg prej 9 karakterevechar name3[4]="Nina"; //name3 - varg prej 4 karaktereve

 Në rastin e parë, madhësia e vargut të alokuar për name1 është e përcaktuarnëmënyrë implicite. Në rastin e dytë kemi alokuar hapësirë të tepërt (e cilanevojitet nëse synojmë që më vonë të kopjojmë ndonjë string më të gjatë nëname2). Rasti i tretë është gabim, sepse nuk kemi alokuar hapësirë tëmjaftueshme të memories për null terminatorin. Inicializimi me konstante string

është përjashtim sepcial”. Nuk mund të themi: char name4[8] = namel; // ILEGALE!

Konstanta string mund të përdoret në cilindo vend ku mund të përdoret objektistring dhe konstant. Për shembull, mund të përdoret si parametër i dytë nëstrcpy, por jo si parametër i parë. Arsye për këtë është se deklarimi i strcpy nuk pamundëson mudnësinë që parametri i parë të mund të ndryshohet (në të vërtetë,ne e dijmë se është). Pasi që konstanta string mund të ruhet në memorie“readonly” (vetëm për lexim), të mundësuarit që ajo të jetë cak i funksionit

Page 88: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 88/699

Avni Rexhepi

88

strcpy do të mund të rezultonte në gabim hardveri. Vëreni se gjihmonë mund tëdërgojmë string jokonstant në parametrin që pret string konstant. Prandaj, kemi:

strcpy( name2, "Mark" ); // LEGALE

strcpy( "Mark", name2 ); // ILEGALE!strcpy( name2, name1 ) ; // LEGALE 

Deklarimet për string funksionet indikojnë se parametrat janë pointerë sepseemri i një vargu është pointer. Parametri i dytë në strcpy është string konstant,që nënkupton se cilido string mund të përcillet me garancionin se nuk do tëndryshohet. Parametri i parë është thjeshtë string dhe mund të ndryshohet.Rrjedhimisht, stringu konstant, duke përfshirë edhe konstantet e stringjeve, nukmund të përcillet në funkson.

Fillestarët synojnë të çojnë ekuivalencën e vargjeve dhe pointerëve një hap më

larg. Rikujtoni se dallimi fundamental ndërmjet vargut dhe pointerit është sedefinicioni i vargut alokon memorie të mjaftueshme për të ruajtur vargun,ndërsa pointeri pointon në memorien e cila është e alokuar tjetërkund. Pasi qëstringjet janë gjithmonë vargje të karaktereve, ky dallim aplikohet në stringje.Gabim i zakonshëm ë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 për për name, duke inicializuar në të kopjen estringut konstant “Nina” (duke përfshirë edhe null terminatorin). Deklarimi idytë thjeshtë thekson se name1 pointon në karakterin zero të stringut konstant“Nina”. Në fakt, deklarimi iështë i gabuar sepse jemi duke përzier tipet e pointerëve: ana e djathtë është const t char*, por ana e majtë është thjeshtë char*. Disa kompajlerë do të ankohen. Aryesja është se një urdhër pasues

namel[ 3 ] = 'e';

është një tentim për të ndryshuar konstanten string. Konstanta string supozohet

të jetë konstante, ashtu që ky veprim nuk do të lejohej. Mënyra më e lehtë përkompajlerin që të pamundësoj këtë veprim është që të përcjellë rregullën që,nëse a është varg konstant, atëherë a[i] është konstante poashtu dhe nuk mund t’indahet vlerë. Nëse urdhëri

char *name1 = "Nina";

do të lejohej, name1[3] do të lejohej. Duke detyruar const-antësinë nësecilën ndarje të vlerës, problemi bëhet i menagjueshëm. (Mund të përdoret“type cast” për const-antësinë, por kështu programeri do të humbiste mbrojtjen eofruar nga C++-i). Në mënyrë legale mund të përdoret:

Page 89: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 89/699

Algoritmet dhe strukturat e të dhënave

89

const char *name1 = "Nina";

 por kjo nuk është njësoj si deklarimi i një vargu për të ruajtur kopjen e stringutaktual; për më tepër, name1[3]=’e’ lehtë përcaktohet nga kompajleri se është

ilegal në këtë rast. Shembull i shpeshtë ku mund të përdoret deklarimi const char* është:

const char *message = "Welcome to FIEK!";

 Një pasojë e zakonshme e deklarimit të pointerit në vend të vargut është urdhërivijues (në të cilin supozojmë se name2 është deklaruar më herët):

strcpy ( name2, name ) ;

Këtu programeri pret që të kopjojë name në name2, por është mashtruar sepsedeklarimi i strcpy tregon se duhet të përcillen dy pointerë. Thirrja e tillë e

funksionit dështon sepse name2 është vetëm pointer, e jo pointer në lokacion tëmjaftueshëm të memories, për të mbajtur kopjen e name. nëse name2 është NULL pointer, pointon në konstanten string të ruajtur në memorie vetëm përlexim (angl read-only memory), ose pointon në një lokacion ilegal tëzakonshëm, strcpy është e sigurtë se do të tentojë ta dereferencojë atë, dukegjeneruar gabim. Nëse name2 pointon në varg të modifikueshëm (p.sh.,ekzekutohet urdhëri name2=name), atëherë nuk ka problem.

Edhe pse këto procedura duken shumë restriktive dhe të ngatërruara, C++-iofron tipin <string> dhe e bënë të duket njësoj sikur një tip i predefinuar, siç

është tipi int. Rrjedhimisht, nuk kemi nevojë të brengosemi për kufizimet edetyruara në C++, sepse ato janë të “fshehura” përbrenda string-ut.

Vargu dhe stringu

Vargu ësthë strukturë bazike e të dhënave që prezenton një grup të elementevetë ngjashme, që kanë qasje sipas indeksit. Struktura e vargut mund të ruhet nëmënyrë efektive në kompjuter dhe ofron qasje të shpejtë në të gjitha elementet e

tij. Vargjet kanë përparësite dhe të metat e tyre.

Përparësitë

  S’ka mbingarkesë (overhead) për element.   Cilido element i një vargu mund të qaset në kohë O(1) përmes indeksit

të tij.

Page 90: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 90/699

Avni Rexhepi

90

Të metat

  Vargu si strukturë e të dhënave nuk është tërësisht dinamik. Shumëgjuhë programuese ofrojnë mundësinë e alokimit të vargjeve me madhësiarbitrare (vargje të alokuara në mënyrë dinamike), por kur kjo hapësirëtë shfrytëzohet në tërësi (kur mbushet vargu), duhet të alokohet një vargme madhësi më të madhe dhe të dhënat e vjetra të kopjohen në të.

  Insertimi dhe fshirja e një elementi të vargut, kërkon zhvendosjen (shift-imin) e mesatarisht O(n) elementeve, ku n është madhësia e vargut.

Vargjet statike dhe dinamike

Ekzistojnë dy tipe të vargjeve, të cilat dallojnë për nga mënyra e alokimit. Vargustatik ka madhësi konstante dhe ekziston gjatë tërë kohës së ekzekutimit tëaplikacionit. Vargu dinamik (i alokuar në mënyrë dinamike) krijohet gjatëekzekutimit të programit dhe mund të fshihet kur të mos jetë më i nevojshëm.Vargjet e alokuara në mënyrë dinamike mund të jenë shumë të mëdha, edhe mëtë mëdha sesa madhësia e memories fizike. Sidoqoftë, vargut të alokuar nëmënyrë dinamike nuk mund t’i ndryshohet madhësia. Mirëpo, ju mund tarrisni (zgjeroni) vargun si në vijim:

1.  Krijoni varg të ri me madhësi më të madhe

2.  Kopjoni të dhënat nga vargu i vjetër në të riun3.  Lironi memorien, që ishte e zënë me vargun e vjetër

Madhësia fikse dhe vargjet dinamike

Si u tha më herët, vargjeve nuk mund të ju ndryshohet madhësia. Në këtë rast,vargu quhet varg me madhësi fikse. Mirëpo, ne mund të përdorim një dredhi(rreng, trik), për të konstruktuar një varg dinamik , të cilit mund t’i

ndryshohet madhësia.

Idea është e thjeshtë. Alokohet një hapësirë për vargun dinamik dhe nëmënyrë imagjinary e ndajmë në dy pjesë. Njëra pjesë përmbanë të dhënat(vlerat) dhe pjesa tjetër është e lirë. Kur të shtohet një element i ri, hapësira elirë zvogëlohet dhe anasjelltas. Kjo qasje rezulton më mbingarkesë përhapësirën e lirë, por ofron të gjitha përparësitë e vargjeve dhe mundësinë endryshimit të madhësisë në mënyrë dinamike. Disa definicione lidhur me këtëlloj të vargjeve janë si në vijim.

Page 91: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 91/699

Algoritmet dhe strukturat e të dhënave

91

Vargu dinamik ka kapacitetin e tij, i cili tregon numrin maksimal tëelementeve që mund ta përmbajë. Gjithashtu, një varg i tillë ka madhësinëlogjike, e cila tregon sa elemente në të vërtetë i përmbanë vargu. Përshembull, dëshirojmë të gjejmë minimumin e vlerave të cilat i jepshfrytëzuesi. Ne alokojmë hapësirë për ruajtjen e 15 elementeve, porshfrytëzuesi i jep vetëm 5 vlera. Në këtë rast, kapaciteti i vargut është 15elemente, por madhësia logjike është 5 elemente. Kur vargu dinamike tëmbushet tërësisht, ai duhet të zgjerohet duke krijuar një varg të ri më të madhdhe të kopjohen elementet nga vargu i vjetër në vargu e ri. Ta keni parasyshë,që kopjimi i vargjeve përkrahet nga hardveri dhe mund të bëhet në mënyrëshumë efikase.

Lidhja me stringjet

Marrim në konsiderim “null-terminated strings” (stringjet e krijuara prejkaraktereve dhe të  përmbyllura me karakterin “null”). Stringjet janë tëngjashme me vargjet dinamike, por madhësia e tyre logjike tregohet përmeskarakterit null. Prandaj, kapacitety i tyre është gjithmonë një element mëshume sesa madhesia logjike maksimale. Madhësia logjike e stringut njihet si“length” (gjatësia). 

Shembull. ASCII stringu "Hello!", i “përfaqësuar” 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 staticint vargu1[15];int n = 0;int vlera = 0;cout << "Jepni vlerat. Shtypni \"-1\" për fund: ";while (n < 15 && vlera != -1) {

cin >> vlera;if (vlera != -1) {

vargu1[n] = vlera;

n++;

Page 92: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 92/699

Avni Rexhepi

92

}}if (n == 0) {

cout << "Nuk keni dhënë 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 dëshirojmë të lexojmë një sekuencë të numrave dhe ta ruajmë sinjë varg për përpunim. Tipari themelor i një vargu ëkrkon që të deklarojmëmadhësinë ashtu që kompajleri të mund të alokojë sasinë korrekte të memories.Duhet të bëjmë këtë deklarim para qasjes së parë të vargut. Nëse nuk kemi idese sa elemente mund të priten, zgjedhja e arsyeshme e madhësisë së vargut ështëe vështirë. Atëherë, na hyn në punë alokimi dinamik i vargut, që mundëson

zgjerimin/rritjen e tij nëse vlerësimi fillestar është shumë i vogël. Teknika ealokimit dinamik të vargjeve (angl. dynamic array allocation) na mudnëson tëalokojmë një madhësi arbitrare të vargut dhe pastaj ta rrisim ose zvogëlojmë atëgjatë ekzekutimit të programit.

Mënyra e deritashme e alokimit të vargut ishte:

int al[ SIZE ] ; // SIZE është konstante e kohës së kompajlimit

Gjithashtu e dijmë se mund të përdorim: 

int *a2;

si një varg, përveq se nuk alokohet memorie nga kompajleri për vargun.Operatori “new” na mundëson që të marrim memorie nga sistemi, gjatë kohës sëekzekutimit të programit. Ne mund të përdorim shprehjen:

new int [SIZE];

 për të alokuar memorie të mjaftueshme për të ruajtur “SIZE” copë objekte tëtipit int. Shprehja vlerësohet në adresën ku qëndron fillimi i asaj memorie. Ajomund t’i ndahet vetëm një objekti int *, si në

int *a2 = new int [ SIZE ];

Page 93: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 93/699

Algoritmet dhe strukturat e të dhënave

93

Si rezultat, a2  është virtualisht i padallueshëm nga a1. Operatori new është“type-safe” që do të thotë se 

int *a2 = new char[ SIZE ]; 

do të ishte detektuar si gabim i mospërshtatjes (angl. mismatch error) gjatëkohës së kompajlimit. Atëherë cili është dallimi, nëse ka ndonjë, ndërmjet dyformave të alokimit të memories për vargun. Dallimi teknik është që memoria për a1 është marrë nga një burim tjetër për dallim nga a2. Sidoqoftë, dallimiështë transparent për shfrytëzuesin. Dallimi i dytë është që a1  nuk mund të paraqitet në anën e majtë të operatorit të ndarjes së vlerës (=) sepse emri i vargutështë konstante, ndërsa a2 mundet. Dallimi është gjithashtu relativisht i vogël,nëse do të kishim deklaruar  

int * const a2 = new int [ SIZE ];

ky dallim do të zhdukej. Ç’është më e rëndësishme, kur përdorim new, SIZE nuk duhet të jetë konstante e kohës 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, tërë memorie e shoqëruar me a1, lirohet11 // Në kthim, vetëm pointeri a2 lirohet;12 // 10 int-a kanë rrjedhur13 // delete [ ] a2; // Kjo do të zgjidhte problemin e rrjedhjes14 ) Figura D.3 Dy mënyrë të alokimit të vargut –  njëra e rrjedhë memorien

a1 a2

 

 Fig.1.25 Restaurimi i memories për programin në fig. D.3 

Page 94: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 94/699

Avni Rexhepi

94

Memoria e alokuar me “new” nuk reciklohet automatikisht. Dështimi për të recikluar atë,shkakton rrjedhje të memories (angl. memory leak).

Problemi ndodhë kur a1 është variabël lokale. Kur funksioni në të cilin ështëdeklaruar kthen rezultatin me return (d.m.th. kur a1  del prej fushëveprimit(angl. scope)), memoria e shoqëruar me vargun restaurohet automatikisht ngaana e sistemit; a1 del prej fushëveprimit kur blloku (funksioni) në të cilin ështëdeklaruar përfundon. Për shembull, në Fig. D.3 a1  është variabël lokale nëfunksionin f. Kur f kthen (me return), e tërë përmbajtja e a1, duke përfshirëmemorien e ndarë për vargun, lirohet. Në kontrast, kur a2  del prejfushëveprimit, vetëm memoria e shoqëruar me pointerin, lirohet. Memoria ealokuar me “new” tani është e paref erencuar dhe nuk përdoret për ndonjë urdhërtjetër. Situata është paraqitur grafikisht në Fig. 1.25.

Për të recikluar memorien, duhet të përdoret operatori “delete”. Sintaksa është: 

delete [ ] a2;

Operatori delete, reciklon memorien e alokuar dinamikisht, e që nuk është më enevojshme.

Kllapa [ ] është absolutisht e nevojshme këtu, për të siguruar se të gjitha objektetnë vargun e alokuar do të reciklohen. Pa kllapat [ ], vetëm 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 bëjë këtë për ne. Përse dotë ishim të interesuar për të vepruar kështu? Për arsye se, duke menagjuarmemorien vetë, mund të ndërtojmë 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ë vërtetë dëshirojmë 12 int-egjera, në vendtë 10. Në rastin e a1 do të ngecim, dhe thirrja e rreshtit 7 nuk do të funksionojë.Mirëpo, me a2, kemi një alternativë, si në vijim:

int *original = a2; //1. Ruaje pointer-in ne originala2=new int[12]; //2. Bëje a2 te pointojë ne me shume

// memoriefor(int i=0;i<10;i++) // 3. Kopjo te dhenat e vjetra

a2[i]=original[i];delete[] original; // 4. Reciklo vargun origjinal

Page 95: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 95/699

Algoritmet dhe strukturat e të dhënave

95

Vargjet dinamike

 Një prej problemeve që ndodhin gjatë punës me strukturën e vargut, është faktiqë madhësia e vargut nuk mund të ndryshohet gjatë ekzekutimit të programit.

 Nuk ekziston ndonjë zgjidhje e drejtpërdrejt, por mund të enkapsulohetmenagjimi i kapacitetit.

Reprezentimi intern (i brendshëm)

Idea është e thjeshtë. Aplikacioni e alokon një sasi të memories (fizikisht) dhe endanë atë logjikisht në dy pjesë. Njëra pjesë i përmbanë të dhënat dhe pjesatjetër është e lirë. Fillimisht, e tërë hapësira e alokuar është e lirë. Gjatëfunksionimit të strukturës së të dhënave, kufiri nërmjet pjesës së përdorur dheasaj të lirë, ndryshon. Nëse nuk ka më hapësirë të lirë për përdorim, hapësirazgjerohet duke e krijuar një varg të ri me madhësi më të madhe dhe duke ekopjuar përmbajtjen e vjetër në lokacionin e ri. Struktura e vargut dinamik kafushat vijuese:

  depoja (hapësira për ruajtje) (storage): hapësira e alokuar në mënyrëdinamike, për ruajtje të të dhënave;

  vlera e kapacitetit (capacity  value): madhësia e hapësirës për

ruajtje;  vlera e madhësisë (size value): madhësia e të dhënave 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;

Page 96: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 96/699

Avni Rexhepi

96

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 përbëhet prej dyfunksioneve: siguro kapacitetin dhe paketo.

Sigurimi i kapaciteit

Para se të shtohet një ose më shumë vlera, duhet të sigurohemi që kemi kapacitettë mjaftueshëm për ruajtjen e tyre. Realizoni hapat vijues:

  verifiko nëse kapaciteti aktual nuk është i mjaftueshëm për ruajtjen eelementeve të reja;

  llogarit kapacitetin e ri përmes formulës: kapacitetiiRi =(kapacitetiiVjeter* 3) / 2 + 1. Algoritmi krijon rezervë të hapësirës së lirëashtu që të mos ricaktohet shumë shpesh madhësia e hapësirës përruajtje.

  Verifiko nëse kapaciteti i ri është i mjaftueshëm për të ruajtur të gjithaelementet e reja dhe nëse jo, rrite atë për të ruajtur sasinë e saktë tëelementeve;

  aloko hapësirën e re dhe kopjo në të përmbajtjen nga e vjetra;  dealoko (liro) hapësirën e vjetër (në C++);  ndrysho vlerën e kapacitetit;

Page 97: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 97/699

Algoritmet dhe strukturat e të dhënave

97

Koeficienti i zmadhimit mund të zgjedhet në mënyrë 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, dëshirojmë të shtojmë një element të ri.

Paketimi

Kur largohen(fshihen) elementet, sasia e hapësirës së lirë rritet. Nëse ka shumë pak vlera në vargun dinamik, hapësira e pashftyrëzuar bëhet shpenzim i kotë.

Për të ruajtur hapësirën, zhvillojmë mekanizmin e zvogëlimit të kapacitetit, kurai është i tepërt.

  verifiko, nëse madhësia është më e vogël ose baraza me gjysmën ekapacitetit;

  llogarit kapacitetin e ri përmes formulës: kapacitetiiRi = (madhesia * 3) /2 + 1. Algoritmi lë sasinë e saktë të hapësirës, thua se kapaciteti ihapësirës për ruajtje është “prerë” për madhësinë dhe pastaj është thirrurmetoda për sigurimin e kapacitetit.

  Aloko hapësirën e re dhe kopjo në të përmbajtjen nga e vjetra ;  dealoko hapësirën e vjetër (në C++);  ndrysho vlerën e kapacitetit.

Shembull.  kapaciteti = 12, madhesia = 6, bëje paketimin (ngjeshjen,kompresimin).

Page 98: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 98/699

Avni Rexhepi

98

Kufiri i poshtëm për madhësinë, pas së cilës paketimi është bërë, mund tëndryshojë. Në shembullin aktual ajo është 0.5 (gjysma) e vlerës së kapacitetit.Zakonisht, paketimi është metodë private, e cila thirret pas largimit (fshirjes).Gjithashtu, interfejsi i vargut dinamik ofron metodën e prerjes (angl. trim  –  shkurtoj majen, përshtas), e cila e zvogëlon kapacitetin ashtu që t’i përshtatetsasisë së saktë të elementeve në varg. Kjo bëhet jashtë implementimit, kur jenitë sigurtë se nuk do të shtohen më vlera të tjera, (p.sh., insertimi nga ana eshfrytëzuesit ka përfunduar).

Pjesë kodiGjuhët programuese ofrojnë vegla efikase për kopjim të memories, të cilat janë përdorur 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) {

Page 99: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 99/699

Algoritmet dhe strukturat e të dhënave

99

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 për qasje në të dhëna: Set, Get, InsertAt, RemoveAt

Set(Cakto), Get(Merr), InsertAt (InsertoNë), RemoveAt (LargoNë).

Struktura e vargut dinamik enkapsulon hapësirën themelore, por interfejsi duhettë ofrojë funksionet e qasjes për të punuar me të. Mund të shtohen edhefunksionet për verifikimin e rangut (kufinjëve).

Verifikimi i kufijëve 

Algoritmi për verifikim të kufinjëve, verifikon nëse indeksi është përbrendakufijëve: 0...madhsia-1 dhe nëse jo, e lajmëron problemin (në programim, angl.throës exception –  hedhë kundërshtimin).

Funksionet Get dhe Set Pasi të jemi siguruar se indeksi është përbrenda kufijëve të duhur, shkruajmëvlerën në hapësirën e ruajtjes ose e lexojmë atë nga hapësira e ruajtjes.

Funksioni InsertAt

Ky operacion mund të kërkojë zgjerimin e vargut, ashtu që algoritmi së pari ethërret metodën për sigurim të kapacitetit, e cila duhet të sigurojë kapacitetinminimal madhesia+1. Pastaj shiftoni (zhvendosni) për një element (pozitë) në tëdjathtë, të gjitha elementet prej i  deri te madhesia-1, ku i  është pozita e

insertimit. Vëreni se nëse elementi i ri insertohet pas elementit të fundit në varg,

Page 100: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 100/699

Avni Rexhepi

100

atëherë nuk ka nevojë për shiftim. Pas shiftimit, vendose vlerën në elementin e i-

të dhe rrite madhësinë për 1.

Funksioni RemoveAt

Shifto të gjitha elementet prej i deri te madhesia-1, ku i është pika e largimit, për një element (pozitë) në të majtë. Pastaj zvogëlo madhësinë për 1 dhe thirreoperacionin e paketimit. Paketimi bëhet nëse ka shumë pak elemente të mbetura pas largimit (fshirjes).

Page 101: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 101/699

Algoritmet dhe strukturat e të dhënave

101

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

}

Page 102: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 102/699

Avni Rexhepi

102

void DynamicArray::removeAt(int index){

rangeCheck(index);int moveCount = size - index - 1;

if (moveCount > 0)memmove(storage  + index, storage  + (index  + 1),

sizeof(int) * moveCount);size--;pack();

}

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  + index,

sizeof(int)* moveCount);storage[index] = value;size++;

}

Page 103: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 103/699

Algoritmet dhe strukturat e të dhënave

103

2. Analiza e algoritmeve

 Në përgjithësi, ne e përdorim kompjuterin sepse kemi nevojë të përpunojmë sasitë mëdha të të dhënave. Kur ekzekutojmë një program për sasi të mëdha tëvlerave hyrëse, ne duhet të jemi të sigurtë që programi përfundon përbrenda njëkohe të arsyeshme. Kohëzgjatja e ekzekutimit është pothuajse gjithmonë e pavarur prej gjuës programuese ose edhe prej metodologjisë së përdorur (p.sh., procedurale kundrejt asaj të orientuar në objekte).

Algoritmi është një grup i sepcifikuar qartë i urdhërave të cilët i përcjellëkompjuteri, për të zgjidhur një problem. Kur të jetë definuar algoritmi për një problem të caktuar dhe të jetë vërtetuar se është korrekt, hapi tjetër është që të

 përcaktohet sasia e resurseve, si koha dhe hapësira, të cilën do ta kërkojëalgoritmi. Ky hap quhet analizë e algoritmit. Algoritmi i cili kërkon disagigabajtë të memories kryesore nuk është i dobishëm në shumicën ekompjuterëve aktual, edhe nëse është tërësisht korrekt.

Pra, të shohim:-  Si të llogaritet/vlerësohet koha e nevojshme për një algoritëm-  Si të përdoren teknikat të cilat e zvogëlojnë në masë të madhe kohën e

ekzekutimit të një algoritmi,-  Si të përdoret përdoret korniza matematike për përshkrimin më rigoroz të

kohës së ekzekutimit të një algoritmi-  Si të shkruhet një funksion i thjeshtë për kërkim binar

Për të qenë me interes, algoritmi duhet të zgjidhë problemin e përgjithshëm tëspecifikuar mirë. Një problem algoritmik është i specifikuar duke përshkruarsetin komlet të instancave që duhet t’i punojë dhe cilat tipare duhet t’i ketë dalja(rezultati në dalje) si rezultat i ekzekutimit të ndonjërës prej këtyre instancave.Ky dallim ndërmjet problemit dhe një instance të problemit është fundamental.Për shembull, problemi algoritmik i njohur si sortim është i definuar si vijon:

Hyrja: një sekuencë e n vlerave (çelësave), a1, a2, ... an.

Dalja: Permutacioni (rirenditja) e sekuencës hyrëse ashtu që: a1’<a2<...<an’. Një instancë e sortimit mund të jetë një varg i numrave ose një listë e emrave.Përcaktimi nëse kemi në fakt një problem të përgjithshëm me të cilin duhet tëmirremi, kundrejt një instance të problemit, është hapi i parë drejt zgjidhjes sëtij. Kjo është e vërtetë për algoritmet ashtu si edhe në jetën reale.

Page 104: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 104/699

Avni Rexhepi

104

Algoritmi është procedura e cila merr cilëndo prej instancave të mundshmehyrëse dhe e transformon atë në daljen e dëshiruar. Ka shumë algoritme tëndryshme për zgjidhjen e problemit të sortimit. Për shembull, një prej metodavetë sortimit fillon me një element të vetëm (prandaj duke formuar kështu nëmënyrë triviale një listë të sortuar prej një anëtari) dhe pastaj në mënyrëinkrementuese (rritëse) inserton elementet e mbetura ashtu që lista qëndron esortuar. Ky algoritëm, i njohur si sorti i insertimit (anlg. insertion sort), është përshkruar në vijim:

InsertionSort(A)for i = 1 to n-1 dofor j = i+1 to 2 do

if ( A[ j] < A[ j-1]) “shkëmbe vendet” ( A[ j], A[ j-1])

Keni parasysh gjeneralizimin e këtij algoritmi. Ky punon njësoj mirë si për emraashtu edhe për numra, duke pasur të dhënë operacionin e duhur të krahasimit“<” për të testuar se cili prej dy çelësave (cila prej dy vlerave) duhet të paraqitetsë pari në renditjen e sortuar. Me definicionin e dhënë të problemit të sortimit,mund të verifikohet pa vështirësi se ky algoritëm i renditë në mënyrë korrekte tëgjitha instancat e mundshme hyrëse.

Duhet pasur parasysh edhe disa çështje praktike:

Algoritmet në dukje të arsyeshme lehtë mund të jenë jokorrekte. Korrektësia ealgoritmit është veti që duhet demonstruar me kujdes.

Algoritmet mund të kuptohen dhe studiohen në mënyra të pavarur ngakompjuteri (makina).

 Notacioni “Big Oh” dhe analiza e rastit më të keq janë vegla të cilat në masë tëmadhe e thjeshtojnë aftësinë tonë që të krahasojmë efikasitetin e algoritmeve.

Kërkojmë algoritme koha e ekzekutimit të së cilave rriten në mënyrëlogaritmike, sepse rritet shumë ngadale me rritjen e n-it (numrit të vleravehyrëse). Modelimi i aplikacionit në terma të algoritmeve dhe strukturave tëdefinuara mirë, është hapi më i rëndësishëm në drejtim të zgjidhjes.

RAM modeli i llogaritjes

Dizajni i algoritmit të pavarur prej makinës varet nga kompjuteri hipotetik iquajtur “Random Access Machine” ose RAM. Sipas këtij modeli të llogaritjes,

 jemi përballë kompjuterit ku:

Page 105: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 105/699

Algoritmet dhe strukturat e të dhënave

105

1.  Secili operacion “i thjeshtë” (+, *, -, =, if, call) merr saktësisht 1 hapkohor.

2.  Unazat dhe nënprogramet nuk konsiderohen operacione të thjeshta. Nëvend të kësaj, ata janë një kompozim (kombinim) i disa operacioneve tëthjeshta një-hapëshe. Nuk ka kuptim që operacioni i sortimit të jetëoperacion një-hapësh, pasi që sortimi i një milion elementeve do të merrshumë më tepër kohë sesa sortimi i 10 elementeve. Koha që kërkohet përt’u ekzekutuar në unazë ose për t’u ekzekutuar nënprogrami varet nganumri i përsëritjeve të unazës ose natyra specifike e nënprogramit.

3.  Secila qasje e memories merr saktësisht një hap kohor dhe kemi aqmemorie sa kemi nevojë. RAM modeli nuk parasysh nëse elementindodhet në cache (kesh) ose në disk, gjë që e thjeshton analizën.

Sipas RAM modelit, ne masim kohën e ekzekutimit të një algoritmi (angl. runtime) duke nuëmruar numrin e hapave të cilët i merr për një instancë të dhënë të problemit. Duke supozuar se RAM-i ekzekuton një numër të caktuar të hapave për sekond, operacioni i numërimit konvertohet me lehtësi 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 këto supozime bëjnë që konkluzionet dhe analizat të jenë tepër të vrazhdëta për t’u besuar në

 praktikë.

Për shembull, shumëzimi i dy numrave merr më shumë kohë sesa mbledhja e dynumrave në shumicën e procesorëve, gjë që thyen supozimin e parë të modelit.Kohët e qasjes së memories dallojnë shumë varësisht prej faktit nëse të dhënatndodhen në disk apo në cache, duke thyer kështu supozimin e tretë. Përkundërkëtyre ankesave, RAM është një model i shkëlyeshëm për të kuptuar se si do të performojë një algoritëm në një kompjuter real. Kështu, aplikohet një balans imirë i përvetësimit të sjelljes themelore të kompjuterëe duke qenë njëkohësishttë thjeshtë për të punuar me ta. Modeli RAM përdoret pasi që është i dobishëmnë praktikë.

Secili model ka një rang të madhësisë përgjatë të cilit ai është i dobishëm. Tëmarrim për shembull modelin se toka është e rrafshët. Mund të argumentohet seky model është i keq, pasi që toka nuk është e rrafshët. Mirëpo, kur të hidhetthemeli i një shtëpie, modeli i tokës së rrafshët është mjaftueshëm i saktë dhemund të përdoret me besueshmëri. Për më tepër, është shumë më e lehtë që tëmanipulohet modeli i tokës së rrafshët që është i pabesueshëm sesa që tëtentohet të mendohet për një model sferik kur nuk ka nevojë.

Page 106: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 106/699

Avni Rexhepi

106

Situata e njëjtë është e vërtetë edhe me RAM modelin e llogaritjes. Ne bëjmë njëabstraksion që në përgjithësi është shumë i dobishëm. Është plotësisht e vështirëqë të dizajnohet një algoritëm i tillë që RAM modeli të jep rezultatesubstancialisht të gabuara, duke performuar ose shumë më mirë ose shumë mëkeq në praktikë sesa që modeli sygjeron. Fuqia (angl. robustness) e RAM namundëson që të analizojmë algoritmet në modelin e pavarur nga makina.

Kompleksiteti i rastit më të mirë, rastit mesatar dhe rastit më të keq

Duke përdorur RAM modelin e llogaritjes, mund të numërojmë se sa hapa do tëmerr algoritmi i jonë në cilëndo instancë të hyrjes, thjeshtë duke e ekzekutuaratë në një hyrje të dhënë. Sidoqoftë, për të kuptuar me të vërtetë se sa i mirë apoi keq është një algoritëm, duhet ta dijmë se si punon ai përgjatë të gjitha

instancave.

Për të kuptuar nocionet e komplesitetit më të mirë, mesatar dhe më të keq, njeriuduhet të mendojë lidhur me ekzekutimin e algoritmit në të gjitha instancat emundëshme të të dhënave të cilat mund t’i jepen atij. Për problemin e srotimit,seti i instancave të mundëshme hyrëse të të gjitha aranzhimeve të mundshme tënumrave të çelësave. Ne mund të reprezentojmë secilën instancë hyrëse si një pikë në graf, ku boshti x është madhësia e problemit (për sortimin, numri ielementeve që sortohen) dhe në boshtin y është numri i hapave të ndërmarrë nga

algoritmi në këtë instancë. Këtu ne supozojmë, tëresisht me arsye, se nuk ështëme rëndësi se çfarë janë vlerat e çelësave, por vetëm sa janë dhe si janë tërenditura. Për shembull, nuk do të duhej të merr më shumë kohë sortimi i 1,000emrave anglez sesa sortimi i 1,000 emrave shqip.

1. Kompleksiteti i rastit më të keq të algoritmit është funksioni i definuarnga numri maksimal i hapave të ndërmarrë në cilëndo instancë tëmadhësisë n.

2. Kompleksiteti i rastit mesatar të një algoritmi është funksioni i definuarnga numri mesatar i hapave që ndërmirren në cilëndo instancë tëmadhësisë n.

3. Kompleksiteti i rastit më të mirë të një algoritmi është funksioni idefinuar ng anumri minimal i hapave që ndërmirren në cilëndo instancëtë madhësisë n.

 Në praktikë, më e dobishmja prej këtyre tri matjeve vërtetohet të jetëkompleksiteti i rastit më të keq, të cilin shumë njerëz e konsiderojnë kundër-intuitive. Për të ilustruar se përse analiza e rastit më të keq është e rëndësishme,shqyrtoni tentimin e projektimit të asaj që do të ndodhë me ju nëse shkoni në

kazino (bastore) për të vënë bast me n euro.

Page 107: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 107/699

Algoritmet dhe strukturat e të dhënave

107

Rasti më i mirë, se ju do të dilni si pronar i vendit (e keni fituar tërë kazinon),është i mundshëm por aq i pashpresë (pak ka të ngjarë të ndodhë) saqë nukduhet të bazohen në të (të keni besim se do të ndodhë). Rasti më i keq, që ju dotë humbisni të gjitha n eurot, është lehtë i llogaritshëm dhe shumë me gjasë që tëndodhë. Rasti mesatar, që një person tipik humbë 87.32% të parave që i sjellë nëkazino, është vështirë të llogaritet dhe do të thotë që është subjekt i diskutimit.Mirëpo, çka nënkupton në të vërtetë “mesatar”? Njerëzit e padijshëm humbasinmë shumë se të menqurit, kështu që a jeni më i menqur apo më “trullan” sesa personi mesatar dhe për sa? Ata që luajnë në zare humbasin më shumë sesa ataqë luajnë në rulet. Kështu, evitojmë të gjitha këto kompleksitete dhe marrimrezultat shumë më të dobishëm duke marrë në konsideratë vetëm rastin më tëkeq.

Gjë me rëndësi për t’u kuptuar është se secili prej këtyre kompleksitetevekohore definon funksion numerik, që reprezenton kohën në raport me madhësinëe problemit. Këto funksione janë poaq mirë të definuara se edhe funksionet etjera numerike. Kompleksitetet kohore janë funksione të komplikuara, sidoqoftë.Për të thjeshtuar punën me funksionet e tilla të çrregullta, na duhet Big-Oh Notacioni.

Çka është analiza e algoritmit

Algoritmi është një bashkësi e instruksioneve ose logjikës, e shkruar për tërealizuar një detyrë të predefinuar. Algoritmi nuk është një kod i kompletuar osenjë program, por është vetëm logjika bazë (zgjidhja) e problemit, e cila mund tëshperhet çoftë si përshkrim logjik i nivelit të lartë si pseudokod  ose dukë përdorur bllok diagramet.

Algoritmi thuhet se është efikas dhe i shpejtë, nëse merr më pak kohë për t’uekzekutuar dhe konsumon më pak hapësirë të memories. Performansa ealgoritmit matet në bazë të:

1.  Kompleksitetit kohor,

2.  Kompleksitetit hapësinorKompleksiteti hapësinor

Kompleksiteti hapësinor është hapësira e memories që kërkohet prej algoritmit,gjatë ekzekutimit të tij. Kompleksiteti hapësinor duhet të mirret seriozisht përsistemet me shumë shfrytëzues (angl. multi-user systems) dhe në situatat ku kanë dispozicion vetëm memorie të kufizuar.

 Një algoritëm në përgjithësi kërkon hapësirë për komponentet vijuese:

Page 108: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 108/699

Avni Rexhepi

108

-  Hapësira për instruksione: hapësira e kërkuar për ruajtjen e versionitekzekutiv të programit. Kjo hapësirë është fikse, por ndryshon varësisht prej numrit të rreshtave të kodit në program.

-  Hapësira për të dhëna: kjo është hapësira e kërkuar për të ruajtur vlerat etë gjitha konstanteve dhe variablave.

-  Hapësira e ambientit punues: kjo është hapësira e kërkuar për ruajtjen einformatave të nevojshme për rikthimin nga funksioni i ndërprerë përkohësisht.

Kompleksiteti kohor

Kompleksiteti kohor është mënyrë për të reprezentuar sasinë e kërkuar të kohësqë programi të ekzekutohet deri në kompletim (përfundim).

Llogaritja e kompleksitetit kohor

Llogaritja e kompleksitetit kohor bëhet zakonisht me metrikën e zakonshme tënjohur si “Big O notation” (Notacioni O e madhe), i cili i largon t ë gjithëfaktorëtë konstant, ashtu që koha e ekzekutimit të mund të përafrohet në relacionme N (numrin e vlerave hyrëse), gjersa N i afrohet infinitit. Në përgjithësi, muneta konsideroni si në vijim.

urdhëri;

 Në këtë rast, kemi një urdhër të vetëm 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++){urdhëri;

}

Kompleksiteti kohor i këtij algoritmi do të jetë linear. Koha e ekzekutimit ështëdrejtpërdrejt e varur nga N (në proporcion të drejtë me N). Kur rritet N, ashturritet edhe koha e ekzekutimit. Kur dyfishohet N, dyfishohet edhe koha eekzekutimit.

for (i=1; i<N; i++){

for (j=1; i<N; j++){urdhëri;

}}

 Në këtë rast, kompleksiteti kohor i këtij kodi është kuadratik . Koha eekzekutimit të dy unazave është proporcionale me katrorin e N-it. Kur N

dyfishohet, koha e ekzekutimit rritet për N*N.

Page 109: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 109/699

Algoritmet dhe strukturat e të dhënave

109

 Në përgjithësi, kryerja e veprimeve me secilin element (N) në një dimensionështë lineare, kryerja e veprimeve me secilin element në dy dimensione ështëkaudratike, ndërsa ndarja e hapësirës punuese përgjysme, është logaritmike.

Pra, sasia e kohës që e merr për t’u ekzekutuar cilido algoritëm pothuajsegjithmonë është e varur nga sasia e vlerave hyrëse të cilat ai duhet t ’i përpunojë. Ne presim për shembull që sortimi i 10,000 elementeve kërkon më shumë kohësesa sortimi i 10 elementeve. Koha e ekzekutimit të një algoritmi pra ështëfunksion i madhësisë së hyrjes. Vlera e saktë e funksionit varet nga shumëfaktorë, siç janë shpejtësia e kompjuterit, kualiteti i kompajlerit dhe në disa rastekualiteti i programit. Për një program të caktuar në një kompjuter të caktuar,mund të vizatojmë grafikun e funksionit të kohës së ekzekutimit. Figura 2.1 paraqet grafikun për disa funksione.

 Figura 2.1 Koha e ekzekutimit për numër të vogël hyrjesh 

Më shumë të dhëna do të thotë programi merr më shumë kohë për t’u ekzekutuar. 

Lakoret paraqesin katër funksione të zakonshme të hasura në analizën ealgoritmeve: linear, logaritmik, kaudratik dhe kubik. Madhësia hyërse N ështënë rangun prej 1 deri në 100 elemente, ndërsa koha e ekzekutimit në rangun prej0 deri në 10 milisekonda. Një vështrim i shpejtë në figurën 2.1 dhe në tabelën2.1, sygjeron se funksionet kaudratike dhe kubike, kanë shkallën shumë më të

Page 110: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 110/699

Avni Rexhepi

110

lartë të rritjes, në krahasim me funksionet lineare dhe ato logaritmike, ndërsa atoeksponenciale, edhe shumë shumë më të lartë.

N lg n n n lgn n2

  n3

  2n

 

1 0.0 1.0 0.0 1.0 1.0 2.0

2 1.0 2.0 2.0 4.0 8.0 4.0

5 2.3 5.0 11.6 25.0 125.0 32.0

10 3.3 10.0 33.2 100.0 1000.0 1024.0

15 3.9 15.0 58.6 225.0 3375.0 32768.0

20 4.3 20.0 86.4 400.0 8000.0 1048576.0

30 4.9 30.0 147.2 900.0 27000.0 1073741824.0

40 5.3 40.0 212.9 1600.0 64000.0 1099511627776.0

50 5.6 50.0 282.2 2500.0 125000.0 1125899906842620.0

60 5.9 60.0 354.4 3600.0 216000.0 1152921504606850000.070 6.1 70.0 429.0 4900.0 343000.0 1180591620717410000000.0

80 6.3 80.0 505.8 6400.0 512000.0 1208925819614630000000000.0

90 6.5 90.0 584.3 8100.0 729000.0 1237940039285380000000000000.0

100 6.6 100.0 664.4 10000.0 1000000.0 1267650600228230000000000000000.0

Tabela 2.1 –  Shkalla e rritjes për 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 (për të vendosur lidhjen), pas së cilës shkarkimi vazhdon meshpejtësinë 1.6 K/sec. Atëherë nëse fajlli ka N kilobajtë, koha e shkarkimit do të përshkruhet me formulën T(N)=N/1.6+2. Ky është funksion linear. Shkarkimi ifajllit me madhësi 80K do të merr përafërsisht 52 sekonda, ndërsa shkarkimi ifajllit me madhësi të dyfishtë (160K) do të marr përafërsisht 102 sekonda, oseshikuar vrazhde, pothuajse dyfish më shumë. Kjo karakteristikë, në të cilën kohaështë në esencë në proporcion të drejtë me madhësin e hyrjes, është e algoritmitlinear. Në krahasim me funksionet jolineare (kuadratike dhe kubike), funksionilinear është shumë më efikas.

 Nga tabela,mund të shihni se kur hyrja është e vogël, nuk ka ndryshim tërëndësishëm (të dukshëm) në vlera, por atëherë kur vlera hyrëse bëhet e madhe,ka dallim/ndryshim të madh. Kjo riforcon atë cka pamë në grafin në Fig. 2.1.Për shkak të kësaj, gjithnjë shqyrtohet se çka ndodhë kur madhësia e hyrjesështë e madhe, për shkak se setet e vogla të hyrjes mund të fshehin ndryshimetshumë dramatike.

Të dhënat në ilustrojnë edhe faktin se pasi që funksionet me rritje më të shpejtërriten në shkallë/shpejtësi të rrijes kaq domethënëse, shumë shpejt i dominojnëfunksionet me rritje të ndagalshme. Kjo do të thotë se nëse ne përcaktojmë se

kompleksiteti i një algoritmi është kombinim i dy prej këtyre klasave, ne shpesh

Page 111: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 111/699

Algoritmet dhe strukturat e të dhënave

111

do të injorojmë të gjitha termat përveq termave me rritje më të shpejtë. P.sh.,nëse analizojmë një algoritëm dhe gjejmë se ai i bën n3  –  30n krahasime, ne dot’i referohemi këtij algoritmi vetëm si algoritëm që rritet me shkallë/shpejtësi tën3. Kjo për arsye se edhe në një madhësi hyrëse prej vetëm 100, diferencandërmjet n3 dhe n3-30n është vetëm 0,3%. Kjo ide është e formalizuar nëklasifikimin e shkallëve të rritjes.

Klasifikimi i rritjes

Pasi që shkalla/shpejtësia e rritjes së një algoritmi është e rëndësishme dhe pasiqë kemi parë që shkalla/shpejtësia e rritjes është e dominuar nga termi më imadh në një ekuacion, ne do t’i hedhim poshtë termat që rriten më ngadalë. Kur

i largojmë të gjitha këto gjëra, mbesim me atë që e quajmë rendi i funksionit osealgoritmit të ndërlidhur. Pastaj mund t’i grupojmë algoritmet së bashku në bazëtë rendit të tyre. Ne i grupojmë ato në tri kategori  –   ato që rriten së paku aqshpejtë sa disa funksione, ato që rriten me shkallë/shpejtësi të njëjtë dhe ato qërriten më shpejt.

Kompleksiteti kohor i një algoritmi ka të bëjë me përcaktimin e një shprehjejeme numrin e hapave të nevojshëm si funksion i madhësisë 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ë kësaj, tentohet që vetëm të gjinden

kufinjtë asimptotik në numrin e hapave. Analiza asimptotike përdorë notacionetsi O notacionin, që njihet si “Big Oh notation” (Big Oh notacioni). Dykonstrukte tjera notacionale të përdorur nga shkencëtarët e kompjuterikës nëanalizën e algoritmeve janë: Θ notacioni (Big Theta notacioni) dhe Ω notacioni(Big Omega notacioni).

Vlerësimi i performansës së një algoritmi fitohet duke mbledhur totalin e“ngjarjeve” (paraqitjeve) të çdo operacioni gjatë ekzekutimit të algoritmit.Performansa e një algoritmi vlerësohet si funksion i madhësisë së hyrjes n dheduhet të konsiderohet si modul i konstantës multiplikative.

 Notacionet vijuese janë notacionet e përdorura zakonisht në analizën e performansave dhe të përdorur për të karakterizuar kompleksitetin e njëalgoritmi.

O-Notation (Kufiri i epërm/lartë)

Big O  notacioni e jep kufirin e epërm/lartë për funksionin brenda faktoritkonstant. 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) nëse ekzistojnë konstantet pozitive n0 dhe c të tilla që në

Page 112: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 112/699

Avni Rexhepi

112

të djathtë të n0, vlera e f(n) gjithmonë shtrihet nën cg(n). Pra, big O është kufiri iepërm për të gjitha funksionet që i takojnë kësaj klase.

cg(n) 

f(n) 

n 0    n 

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

 Fig. 2.2 –  Big O

Θ-Notation (Rendi i njëjtë)

Big Teta notacioni e kufizon funksionin në faktorë konstant. Themi se f(n) =Θ(g(n)) nëse ekzistonjnë konstantet pozitive n0, c1 dhe c2, të tilla që në anën edjathtë të n0 vlera e f(n) gjithmonë shtrihet ndërmjet c1g(n) dhe c2g(n)(inkluzive).

c 1 g(n) 

f(n) 

n 0    n 

f(n)=  

(g(n)) 

c 2 g(n) 

  Fig. 2.3 –   Big Θ 

Page 113: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 113/699

Algoritmet dhe strukturat e të dhënave

113

Ω-Notation (Kufiri i poshtëm/ulët)

Big Omega  notacioni jep kufirin e poshtëm për funksionin deri në faktorinkonstant. Shkruajmë: f(n) = Ω(g(n)) nëse 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).

cg(n) 

f(n) 

n    n 

f(n)=   (g(n)) 

0

  Fig. 2.4 –   Big Ω 

Rastet e analizave

Kompleksiteti i një algoritmi është funksioni g(n) i cili jep kufirin e epërm të

numrit të operacioneve (ose kohës së ekzekutimit) të kryera nga një algoritëmkur madhësia e hyrjes është n. Ekzistojnë dy interpretime të kufirit të epërm

Rasti-më i keq (Worst-case Complexity)

Koha e ekzekutimit për hyrje të cilësdo madhësi të dhënë do të jetë me evogël/ulët sesa kufiri i epërm përveq mundësisë për disa vlera të hyrjes kuarrihet maksimumi.

Rastit-mesatar (Average-case Complexity)

Koha e ekzekutimit për hyrje të cilësdo madhësi të dhënë do të jetë sa mesatarjae operacioneve mbi të gjitha instancat e problemit për një madhësi të dhënë.

Rastit-më i mirë (Best-case Complexity)

Kemi edhe rastin më të përshtatshëm, që njihet si rasti më i mirë (Best-casecomplexity), që ka të bëjë me rastet si: kërmimi sekuencial, i vlerës që gjendet

Page 114: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 114/699

Avni Rexhepi

114

në pozitën e parë të vargut, ose kompleksiteti i algoritmit të sortimit, kur nëfillim jepet vargu i sortuar.

Pasi që është shumë e vështirë që të vlerësohet sjellja statistike e hyrjes, të

shumtën e herave ne kënaqemi më sjelljen e rastit më të keq. Të shumtën ekohës, kompleksiteti i g(n) përafrohet me familjen e tij O(f(n)) ku f(n) është një prej funksioneve vijuese: n (kompleksiteti linear), log n (kompleksitetilogaritmik), na ku a≥2 (kompleksiteti polinomial), an (kompleksitetieksponencial).

Optimaliteti

Kur një herë të jetë vlerësuar kompleksiteti i një algoritmi, parashtrohet pyetja a

është ky algoritëm optimal. Një algoritëm për problemin e dhënë është optimalnëse kompleksiteti i tij arrinë kufirin e poshtëm përgjatë të gjitha algoritmeve qëe zgjidhin këtë problem. P.sh., cilido algoritëm që e zgjidhë problemin e“pikëprerjes së n segmenteve” do të ekzek utoj së paku n2 operacione në rastinmë të keq edhe nëse ai nuk bën asgjë përveq shtypjes së rezultati (daljes). Kjoshkurtohet duke thënë se problemi ka kompleksitet Ω(n2). Nëse gjindet njëalgoritëm O(n2) i cili e zgjidhë këtë problem, ai do të jetë optimal dhe ikompleksitetit Θ(n2).

Reduktimi Një teknikë tjetër për vlerësimin 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 poshtëm të një problemi A dhe ne do tëdëshironim të vlerësojmë kufirin e poshtëm të problemit B. Nëse mund tëtransformojmë problemin A në problemin B me disa hapa transformimi, çmimi itë cilëve është më i vogël se ai për zgjidhjen e problemit A, atëherë B ka kufi tënjëjtë si A.

Algoritmika

Është e qartë se temat e algoritmeve dhe strukturave të të dhënave nuk mund tëndahen pasi që janë të ndërlidhura pazgjidhshëm. Kështu, para se të flasim përstrukturat e të dhënave, duhet të fillojmë me një rishikim të shpejtë tëalgoritmeve themelorë dhe në veçanti, si të matet efikasiteti relativ ialgoritmeve. Çështja themelore në studimin e efikasitetit të algoritmeve ështësasia e resurseve që ato i shfrytëzojnë, zakonisht e matur në kohë ose hapësirë.Zakonisht janë dy mënyra për të matur këto madhësi. Njëra është analiza

matematike e algoritmit të përgjithshëm të përdorur, e quajtur analiza

Page 115: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 115/699

Algoritmet dhe strukturat e të dhënave

115

asimptotike, e cila mund të zë aspektet e përgjithshme të efikasitetit për të gjithahyrjet e mundshme, por jo kohët e sakta të ekzekutimit. E dyta është analizaempirike e një implementimi aktual për të përcaktuar kohën e saktë tëekezekutimit për mostra të hyrjeve speciale, por kjo nuk mund parashikojë performansën e algoritmit në të gjitha hyrjet.

Është edhe aspekti i kompleksitetit të programit, pasi që disa struktura të tëdhënave mund të jenë krejtësisht të thjeshta për t’u implementuar, kurse disa  tëtjera shumë më komplekse. Çështja se cila struktura të përdoret mund të varetnga çështje të cilat nuk kanë të bëjnë fare me çështjen e kohës se ekzekutimit, por në vend të kësaj me çështjet se cilat struktura të të dhënave janë mëfleksibile, pastaj më të lehtat për t’u implementuar dhe mirëmbajtur, etj.Zakonisht shqyrtohet koha e ekzekutimit, edhe pse ajo që thuhet për kohën,mund të vlejë edhe për hapësirën, por hapësira është më e lehtë për t’u trajtuar.Për një program të caktuar, koha e tij e ekzekutimit nuk është një numër fiks, por zakonisht një funksion. Për secilën hyrje (ose instancë të strukturës së tëdhënave), mund të ketë kohë tjetër të ekzekutimit. Supozohet se me rritjen emadhësisë hyrëse rritet edhe koha e ekzekutimit, kështu që shpeshherë kohën eekzekutimit e përshkruajmë si funksion të hyrjes/madhësisë së strukturës së tëdhënave ‘n’, të shënuar si T(n). Në duam që nocioni i jonë i kohës të jetë i pavarur prej pajisjes (makinës, kompjuterit), kështu që në vend se të matensekondat për CPU (procesor), është më e zakonishme të maten hapat themelor tëcilët i bën algoritmi (p.sh., numri i urdhërave që ekzekutohen ose numri i

qasjeve në memorie). Kjo nuk do të parashikojë saktësisht kohën e ekzekutimit, pasi që disa kompajlerë do të bëjnë optimizim më të mirë se disa të tjerë, mirëpokjo do të ndodhë me një faktor të vogël dhe konstant të kohës së saktë tëekzekutimit për shumicën e kohës.

Edhe matja e kohës së ekzekutimit si funksion i madhësisë hyrës nuk është edefinuar mirë, sepse për 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. Përkëtë arsye, zakonisht flitet për kohën e rastit më të keq (angl. ëorst case) tëekzekutimit. Përgjatë të gjitha hyrjeve të mundshme, cila është koha maksimale

e ekzekutimit. Është më e arsyeshme të shqyrtohet rasti më i pritshëm, kështu qënxirret mesatarja e të g jitha hyrjeve të madhësisë ‘n’. Kjo është analiza e rastitmesatar (angl. average case). Rasti më i lehtë, si p.sh, sortimi i listës së sortuar,është rasti më i mirë (angl. besta case).

Rishikim i asimptotës

Ka një mori “lojrash” të cilat i përdorin analizuesit e algoritmeve, për të studiuarkohën e ekzekutimit të algoritmit, por elementi i parë dhe themelor është nocioni

Page 116: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 116/699

Avni Rexhepi

116

asimptotik. Supozojmë se është bërë analiza e algoritmit dhe është zbuluar sekoha e rastit më të keq është:

T(n) = 13n3 + 42n

2 + 2n log n +3   n  

 Nëse nuk përcaktohet ndryshe, supozojmë se algoritmet janë marrë me bazë 2.Kur vlera e ‘n’ është e vogël, ne nuk brengosemi shumë për këtë funksion, sepseai nuk do të jetë shumë i madhe, por gjersa ‘n’ rritet, do të duhet të brengosemi për kohën e ekzekutimit. Sa më shumë që rritet ‘n’, madhësia ‘n3’ është shumëmë e madhe se ‘n2’, që është shumë më e madhe sesa ‘n log n’ (vëreni se 0 < logn < n, sa herë që n > 1), e që është më i madh se n . Prandaj, termi‘n3’dominon vlera të mëdha të ‘n’. Gjithashtu, vëreni se faktori i parë 13 ështëkonstante. Faktorët e tillë konstant mund të ndikohen nga shpejtësia e procesoritose kompajleri, kështu që mund të injorohen (për sa kohë që janë realitivisht tëvegjël). Pra, këtë funksion do ta përgjithsonim në formë të ngjeshur duke thënëse koha e tij e ekzekutimit është “afërsisht e rendit n 3” dhe kjo shkruhet si T(n) O(n3).

Joformalisht, shprehja T(n)   O(n3) do të thotë që, “kur injorohen faktorëtshumëzuese konstant dhe kur shqyrtohet termi me rritjen më të shpejtë, fitohetn3”. Kjo është intuita më standarde që përdoret dhe më e lehta për t’u aplikuar për shumicën e rasteve.

Sipas definicionit, T(n)   O(f(n)), nëse limn->∞  T(n)/f(n) është ose zero ose

konstant (por jo ∞). Për shembull, ne thamë që funksioni i mëparshëm ishte T(n) O(n3). Duke përdorur definicionin, do të kemi:

3

23 3log24213lim

)(

)(lim

n

nnnnn

n f  

nT 

nn

 

)3log242

13(lim   5.2nn

n

nn

 

= 13. 

Pasi që kjo është konstantë, vlerësojmë se T(n)  O(n3).

Pra, notacioni O është i mirë për të caktuar një kufi të epërm të funksionit.Vëreni se nëse T(n)  O(n3) është poashtu edhe O(n4), O(n5), etj., pasi që limitido të shkojë në zero. Për të fituar ndjenjën e shkallëve të ndryshme të rritjes, janjë përmbledhje e tyre:

T(n)  O(1) Shumë e mirë. Kjo do të thotë se algoritmi merr kohë konstante. Nuk mund të bëni më mirë se kaq.

T(n)  O(log log n) Shumë e shpejtë. Për shumicën e synimeve, kjo është poaq e

shpejtë sa koha konstante.

Page 117: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 117/699

Algoritmet dhe strukturat e të dhënave

117

T(n)  O(log n)  Shumë e mirë. Kjo quhet koha logaritmike. Është koha eekzekutimit të kërkimit binar dhe lartësia e pemës binare të balansuar. Është afërsisht më e mira që mund të arrihet përstrukturat e të dhënave të bazuara në pemët binare. Vini re se

log2100010, dhe log2,1,000,000 20. T(n)  O((log n)k )  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(n p)  (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 vogël p), por akoma më e shpesjtë se koha lineare, e cilaështë e pranueshme për përdorim të strukturave të të dhënave.

 Një shembull është O(   n )..

T(n)  O(n) Kjo quhet koha lineare. Është pothuajse më e mira që mund tëshpresohet në rastet kur algoritmi duhet t’i “shikojë” të gjitha tëdhënat. (Megjithatë, në rastin e strukturave të të dhënavezakonisht qëllimi është që kjo të evitohet).

T(n)  O(n log n)  Kjo është e famshme për arsye se është koha e nevojshme përsortimin e një liste të numrave. Paraqitet edhe në një numër të problemve të tjera, poashtu.

T(n)  O(n2)  Koha kuadratike. Është në rregull, nëse n është i rendit tëmijërave, por e padëshirueshme kur n kalon në të miliontat.

T(n)  O(nk )  Koha polinomiale. Praktike, nëse k nuk është shumë e madhe.

T(n)   O(2

n

),O(nn), O(n!)  Koha eksponenciale. Algoritmet që marrin kaq shumë kohë janëtë përshtatshme vetëm për vlerat më të vogla të n-it. (p.sh., n≤10ose ndoshta n≤20). 

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 njësoj, pa marrë parasysh hyrjen. Përshembull, algoritmi i cili gjithmonë kthen një vlerë të njëjtë, pa marrë parasyshhyrjen mund të konsiderohet algoritëm me efikasitet O(1). Qasja direkte e rastit,në cilindo element të vargut, është poashtu O(1).

O(logn)Algoriteme e bazuar në pemët binare janë zakonisht të rendit O(log2 n). Kjo përarsye se pema e balansuar binare ka (log2  n) shtresa dhe kërkimi i cilitdoelement në pemën binare kërkon përshkimin e vetëm një nyjeje në secilënshtresë.

Page 118: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 118/699

Avni Rexhepi

118

Algoritmi i kërkimit binar është poashtu shembull i rendit O(log n). Në kërkimin binar, kërkohet vlera në vargun e sortuar dhe duke filluar në mes, mundësohetqë në hapin tjetër, të kërkohet vetëm gjysma e epërme ose e poshtme. Kështuvazhdohet, në çdo hap pasues, me pjesën e mbetur. Vargu mund të ndahet nëgjysma, vetëm log n herë, para se të arrihet në një element të vetëm, i cili në faktdo të jetë elementi që kërkohej (me kusht që të jetë në varg).

O(n)Algoritmet me efikasitet n kërkojnë vetëm një kalim nëpër tërë hyrjen. Përshembull, algoritmi i kërkimit linear, i cili kërkon një vlerë në vargun e pasortuar, duke verifikuar secilin element me radhë, është O(n). shpesh, qasja nëelementet e listës së lidhur është O(n), sepse lista e lidhur nuk përkrahë qasjen erastit.

O(nlogn)Shpeshherë, algoritmet e mira të sortimit janë të rendit O(n log n). shembull ialgoritmit me këtë efikasitet është “merge sort” (sorti i bashkimit), i cili e ndanëvargun në dy pjesë, i sorton dy gjysmat me thirrje rekursive, duke e thirrurvetveten në to dhe pastaj duke i bashkuar rezultatet prapa në një varg të vetëm.Pasi që e ndanë vargun në gjysmë, secilën herë, unaza e jashtme ka efikasitetinlog n, dhe për çdo nivel të vargut që është ndarë (kur vargu është në dy gjysma,

 pastaj në qerek, pastaj në të tetën, e kështu 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 arsyeshëm, akoma në rangun e kohës polinomiale, ështëtipik për shembujt e disa algoritmeve të sortimit, si p.sh., algoritmi i sortit meselektim.

O(2n)

Efikasiteti më i rëndësishëm jo-polinomial është kjo rritja eksponenciale kohore.Shumë probleme të rëndësishme mund të zgjidhen vetëm me algoritme të kësajshkalle të efikasitetit (ose edhe më të keqe). Një shembull do të ishte faktorizimii numrave të mëdhenj, të shprehur në formë binare. Mënyra e vetme është prova/gabimi dhe qasja naive do të përfshinte ndarjen/pjestimin e secilit numërmë pak sesa numri që faktorizohet në atë numër, deri sa të gjendet plotpjestimi.Për secilën rritje të një shifre, kjo do të kërkonte dyfish testime.

Page 119: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 119/699

Algoritmet dhe strukturat e të dhënave

119

Parakushtet matematike

Baza e mirë matematike është një vegël që do të nevojitet për të arsyetuar lidhurme strukturat e të dhënave dhe algoritmet me të cilat punohet. Të kuptuarit e

matematikës ndihmon në aftësinë e dizajnimit të strukturave të mira të tëdhënave, pasi që përmes matematikës është e mundur që të fitohet pasqyrë më eqartë e natyrës së strukturave të të dhënave dhe ndjenja e përgjithshme përefikasitetin e tyre kohor dhe hapsinor. Le të shohim disa nocione preliminare tëshumave dhe provave përmes induksionit.

Shumat: shumat janë të rëndësishme ën analizën e programeve të cilat operojnëme përsëritje (iterativisht). Për shembull, fragmenti vijues i kodit:

for (i=1; i<n ; i++) { ... };

ku trupi i unazës ({...}) merr kohën f(i) për të kaluar kohën totale të ekzekutimit, jepet përmes shumës:

T(n) =

1

0

)(n

i

i f   .

 Nëse kemi unaza të ndërthurura (unazë brenda unazës), atëherë kemi edheshuma të ndërthurura (shumë brenda shumës). Zgjidhja e shumave ndahet në dyhapa themelorë. Së pari duhet thjeshtuar shumën sa më shumë që të jetë e

mundur, duke larguar termat konstant (vini re, këtu konstante do të thotë çdo gjëqë nuk varet nga variabla e unazës, 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ë rëndësishme për t’u ditur jane: 

n

i

n1

1  (Seritë konstante)

n

i

nni

1   2

)1( (Seritë aritmetike) 

n

i

Oni1

)1(ln1

(Seritë harmonike) 

n

i

ni

c

cc

0

1

1

1  c 1 (Seritë gjeometrike)

Vëni re se shumat komplekse shpesh mund të ndahen në terma të thjeshta, të cila pastaj mund të zgjidhen më lehtë. Për shembull:

Page 120: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 120/699

Avni Rexhepi

120

Shuma e fundit është me gjasë më e rëndësishmja për strukturat e të dhënave.Për shembull, supozojmë se doni të dini sa nyja ka në pemën komplete 3-are melartësi h. (Akoma nuk është përkufizuar definicioni i pemës, por merrni parasyshfigurën vijuese).

 Fig. 2.5 –  Pema 3-are e kompletuar, me lartësi h=2. 

Lartësia e pemës, h, është numri maksimal i degëve prej rrënjës, deri tek gjethja. Një mënyrë për të copëtuar këtë llogaritje është që të shikohet pema nivel pasniveli. Në nivelin fillestar (niveli 0), kemi një nyje, në nivelin 1 kemi 3 nyje, nënivelin 2 kemi 9 nyje dhe në përgjithësi, në nivelin ‘i’ kemi 3i nyje. për të gjeturnumrin total të nyjeve, do të mbledhim përgjatë të gjitha niveleve, prej 0 deri në‘h’. Duke zëvendësuar në shprehjen e mëparshme n=h, do të kemi:

h

0

1

3 13

133  

h

i

hi  

 Në anën e kundërt, nëse dikush do të ju thoshte se keni një pemë 3-are me n nyje, ju do të mund të përcaktonit lartësinë e pemës, duke invertuar këtë. Pasi qën = (3(h+1) –  1)/2, do të kemi:

3(h+1) = (2n+1)

duke lënë të kuptohet se

h = (log3(2n+1) - 1  O(log n).

Page 121: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 121/699

Algoritmet dhe strukturat e të dhënave

121

 Një fakt tjetër i rëndësishëm që duhet të mbahet mend lidhur me shumat, ështëse ato mund të përafrohen përmes përdorimit të integraleve.

   

b

ai

b

a x

dx x f  i f     )()(  

Me një shumë të dhënë të komplikuar, shpesh është e mundur që të gjindet nëlibrin e integraleve dhe të përdoret formula përkatëse për të përafruar shumën.

Rekurrenca

Konstrukt tjetër matematik i cili paraqitet gjatë studimit të algoritmeve rekurziveështë ai i rekurrencës. Rekurrenca është formula matematike e cila definohet

rekurzivisht. Për shembull, le të kthehemi tek shembulli i pemës 3-are, melartësi h. ka edhe një mënyrë tjetër për të përshkruar numrin e nyjeve në pemën3-are. Nëse h=0 atëherë pema përbëhet prej një nyjeje të vetme (rrënjës).Përndryshme, ajo pemë përbëhet prej rrënjës dhe 3 kopjeve të pemës 3-are melartësi h-1. Kjo sygjeron rekurrencën vijuese që definon numrin e nyjeve N(h)në pemën 3-are me lartësi h:

 N(0) = 1

 N(h) = 3N(h−1) + 1 if h _  1.

Edhe pse definicioni duket të jetë cirkularë, është i bazuar mirë pasi që përfundimisht 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 kështu me radhë.Janë dy metoda themelore për zgjidhjen e rekurrencave. Njëra (e cila funksiononmirë për rekurrencat e thjeshta të rregullta) është që të zgjerohet definicioni irekurrencës me përsëritje, duke e redukuar përfundimisht në një shumë dhetjetra është që thjeshtë të hamendësohet përgjigja dhe të përdoret induksioni. Janjë shembull i teknikës së parë:

 N(h) = 3N(h−1) + 1 

Page 122: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 122/699

Avni Rexhepi

122

= 3(3N(h − 2) + 1) + 1 = 9N(h − 2) + 3 + 1 

= 9(3N(h − 3) + 1) + 3 + 1 = 27N(h−3) + 9 + 3 + 1 

...

= 3kN(h−k) + (3k−1 + ... + 9 + 3 + 1)

Kur përfundon e gjithë kjo? Ne e dijmë se N(0)=1, kështu që le të caktojmë k=hduke implikuar që

 N(h) = 3hN(0) + (3h−1 + ... + 3 + 1) = 3h + 3h−1 + ... + 3 + 1 = 

h

i

i

0

3  

Pra, kjo është e njëjta gjë si më parë, por e derivuar (nxjerrur) në mënyrë tjetër.

Prova përmes induksionit: teknika tjetër e rëndësishme matematike është ajo e provës (vërtetimit) përmes induksionit. Vërtetimet (provimet) me induksion janëkritike për të gjitha aspektet e shkencave kompjuterike dhe strukturave të tëdhënave, e jo vetëm prova të efikasitetit. Në veçanti, virtualisht të gjithaargumentet e korrektësisë janë të bazuara në induksion. Prej mësimeve ngamatematika diskrete keni mësuar qasjen përmes induksionit. Keni një teoremë tëcilën doni ta vërtetoni dhe është e formës: “Për të gjitnë nurmat e plotë n ≥ 1,vlenë se ...” kur shprehja e teoremës përfshinë në njëfarë forme ‘n’-in. Idea është

që në hapin e parë, të njohur si “rasti bazë”, të vërtetohet se teorema vlenë përnjë bashkësi themelore të vlerave ‘n’, (p.sh., n=1 në k ëtë rast). Pastaj tregohet senëse teorema vlenë për rastin bazë, atëherë hapi i dytë, i njohur si hapi induktiv,është që të vërtetohet që shprehja e dhënë për cilindo numër natyral ‘n’,implikon që shprehja e dhënë vlenë edhe për cilindo numër natyral të ardhshëm,n+1. Prej këtyre dy hapave, induksioni matematik është rregulla për të cilënkonkludojmë se shprehja e dhënë vlenë për të gjithë numrat natyral.

 Në strukturat e të dhënave, posaqërisht kur punohet me pemët, ky tip iinduksionit nuk është shumë i dobishëm. Në vend të kësaj, përdoret versioni i

ngjashëm,i quajtur induksioni i fortë, që duket të jetë më relevant. Idea është qëtë supozohet se nëse teorema vlenë për të gjitha vlerat e ‘n’ të cilat janë nëmënyrë strikte më të vogla sesa ‘n’, atëherë ajo vlenë edhe për ‘n’.

Le t’i kthehemi rastit të mëparshëm dhe ta vërtetojmë me induksion. 

Teoremë: le të jetë T pema komplete 3-are me n-1 nyje. Le të tregojë H(n)lartësinë e kësaj peme. Atëherë:

H(n) = (log3(2n + 1)) − 1 

Rasti bazë: marrim vlerën më të vogël legale për ‘n’, n=1 në këtë rast. Pema me

një nyje të vetme ka lartësinë zero, kështu që H(1)=0. Duke vendosur n=1 në

Page 123: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 123/699

Algoritmet dhe strukturat e të dhënave

123

formulën e dhënë na jep: (log3(2 · 1 + 1))  –  1=(log33)  –  1=0, që ishte vlera edëshiruar.

Hapi i induksionit: duam të vërtetojmë se teorema vlenë për një vlerë specifike

të n>1. Vëreni se në këtë rast nuk mund të aplikojmë induksionin standard, pasiqë nuk ka pemë komplete 3-are me dy nyje në të (vlera e ardhshme më e madheka 4 nyje).

Do të supozojmë hipotezën e induksionit, për të gjitha vlerat më të vogla n’, 1≤n’<n, H(n’) është dhënë me formulën më lart. (Kjo quhet ndonjëherë“induksioni i fortë” dhe është mirë të mësohet, sepse shumica e provave tëinduksionit për strukturat e të dhënave funksionojnë në këtë mënyrë).

Le të shqyrtojmë pemën komplete 3-are me n>1 nyje. pasi që n>1, duhet të përbëhet prej rrënjës dhe plus tri nënpemëve të tjera identike, ku secila është

 pemë komplete 3-are me n’<n nyje. Sa nyje janë në këtë nënpemë? Pasi që ato janë identike, nëse përjashtojmë nyjën rrënjë, secila nënpemë ka një të tretën enumrit të mbetur të nyjeve, kështu që n’=(n-1)/3. Pasi që n’<n, mund tëaplikojmë hipotezën 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.

Vëreni se lartësia e pemës së tërë është një më shumë sesa lartësitë enënpemëve, kështu që H(n) =H(n’) + 1. Prandaj, kemi:

H(n) = log3(2n + 1) - 2 + 1 = log3(2n + 1) - 1;

siç dëshirohej.

Kjo mund të duket një mënyrë shumë e komplikuar për të vërtetuar një fakt kaqtë thjeshtë, por induksioni është teknikë e fuqishme për vërtetimin e fakteveshumë më komplekse të cilat paraqiten në analizën e strukturave të të dhënave.

Duhet pasur kujdes gjatë tentimit të vërtetimit me induksion të rasteve që përfshijnë notacionin O(n) (Big-oh notacionin). Ja një shembull i gabimit tëshpeshtë.

Teoremë. (False!): për n≥1, le të jetë T(n) e dhënë me shumën vijuese: 

T(n)=

n

i

i

0

 

Atëherë, 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 vërtetë (false). (A mund ta

Page 124: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 124/699

Avni Rexhepi

124

lokalizoni gabimin në“vërtetimin” vijues?) 

Rasti bazë: për n=1, kemi T(1)=1 dhe 1 është O(1).

Hapi i induksionit: duam të vërtetojmë teoremën për një vlerë specifike të n>1.Supozojmë se për çfarëdo n’<n, T(n’)    O(n’). Tani, për rastin ‘n’, sipasdefinicionit kemi:

T(n)=   nnT niin

i

n

i

 

  

   

)1(00

 

Tani, pasi që n-1<n, mund të aplikojmë hipotezën e induksionit, duke dhënëT(n-1)  O(n-1).

Duke vendosur këtë prapa, do të kemi:T(n)  O(n-1)+n.

Mirëpo, (n - 1) + n ≤ 2n - 1  O(n), kështu që kemi T(n)  O(n).

Atëherë, ku është gabimi?! Rikujtoni që notacioni asimptotik aplikhet vetëm përvlera arbitrarisht të mëdhaja të n-it (për n kufitare). Mirëpo, prova me induksion prej vetë natyrës së tyre vlejnë vetëm për vlera specifike të n-it. Mënyra e duhur për ta vërtetuar këtë përmes induksionit do të ishte që të dilet me një shprehjekonkrete, e cila nuk e përfshinë O-notacionin.

2.3. Rritja e funksioneve

Shumica e algoritmeve parametrin primar N i cili ka ndikimin më tërëndësishëm në kohën e ekzekutimit. Parametri N mund të jetë shkalla e polinomit, madhësia e fajllit që duhet kërkuar ose sortuar, numri i karaktereve tëstringut ose ndonjë masë tjetër abstrakte e madhësisë së problemit që analizohet:më së shpeshti është direkt proporcionale më madhësinë e bashkësisë së tëdhënave që procesohet. Kur ka më shumë se një parametër të tillë (për shembull

M dhe N në algoritmet për gjetje në union), shpeshherë analiza redukohetzakonisht në analizën e vetëm njërit parametër, duke shprehur njërin parametërsi funksion të tjetrit ose duke e konsideruar një parametër në kohë (duke mbajturtjetrin konstant), ashtu që të mund të kufizohemi në analizimin e një parametri N, pa humbur në përgjithësim. Qëllimi është që të shprehen kërkesat për resursetë programit (koha e ekzekutimit dhe hapësira në memorie), në terma të N-it,duke përdorur formulat matematike sa më thjeshtë që të jetë e mundur dhe që janë të sakta për vlera të mëdhaja të parametrave. Shumica e algoritmeve kanëzakonisht kohe të ekzekutimit proporcionale me njërin prej funksioneve vijuese:

Page 125: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 125/699

Algoritmet dhe strukturat e të dhënave

125

1 Shumica e instruksioneve (urdhërave) të shumicës së programeveekzekutohen nga një herë ose në të shumtën vetëm disa herë. Nëse tëgjitha instruksionet e programit kanë këtë veti, atëherë themi se koha

e ekzekutimit të programit është konstante.log N Kur koha e ekzekutimit të programit është logaritmike, programi

 bëhet pak më i ngadalshëm gjersa N rritet. Kjo kohë e ekzekutimitzakonisht paraqitet në programet të cilat zgjidhin një problem të madhduke e transformuar në seri të problemeve të vogla, duke “prerë”madhësinë e problemit për një faktorë konstant në secilin hap. Përsferën tonë të interesit, mund të konsiderojmë kohën e ekzekutimit të jetë më e vogël se një konstante e madhe. Baza e logaritmit ndryshonkonstantën, por jo shumë. Kur N është 1000, log N është 3 nëse baza

është 10, ose është afër 10-shit, nëse baza është 2; kur N është 1milion, log N është vetëm dyfishi i vlerave paraprake. Sa hërë që Ndyfishohet, log N rritet për një konstantë, por log N nuk dyfishohetderi sa N të rritet sa N2.

 N Kur koha e ekzekutimit të programit është lineare, është zakonishtrasti kur një sasi e vogël e procesimit kryhet për secilin element tëhyrjes. Kur N është 1 milion, atëherë aq është edhe koha eekzekutimit. Sa herë që N dyfishohet, ashtu bën edhe koha eekzekutimit. Kjo situatë është optimale për një algoritëm 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ë nënprobleme të vogla dhe duke izgjedhur ato në mënyrë të pavarur, e pastaj duke i kombinuarzgjidhjet. Thjeshtë, themi se koha e ekzekutimit të një algoritmi tëtillë është N log N (sepse mungon ndonjë shprehje që do të ishtekombinim i linear-algoritmik, si p.sh., linearitmik!). Kur N është 1milion, 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 algoritëmështë praktik vetëm për probleme relativisht të vogla. Koha eekzekutimit kuadratike në mënyrë tipike paraqitet në algoritmet tëcilat procesojnë të gjitha çiftet e të dhënave (ndoshta në unazë tëdyfishtë). Kur N është 1000, koha e ekzekutimit është 1 milion. Saherë që N dyfishohet, koha e ekzekutimit katërfishohet (rritetkatërfish).

Page 126: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 126/699

Avni Rexhepi

126

 N  Ngjashëm, algoritmi që proceson “treshe” të të dhënave (ndoshta nëunazë të trefishtë), ka kohë kubike të ekzekutimit, që është praktikevetëm për përdorim në probleme të vogla. Kur N është 100, koha e

ekzekutimit është 1 milion. Sa herë që N dyfishohet, koha eekzekutimit tetëfishohet.

2 Shumë pak prej algoritmeve me kohë eksponenciale të ekzekutimit dotë jenë të përshtatshëm për përdorim praktik, edhe pse algoritmet etilla 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ë veçantë zakonisht është ndonjë konstantee shumëzuar me një nga këto terma (termi kryesor) plus ndonjë term më i vogël.Vlera e koeficientit konstant dhe termave të përfshirë varen nga rezultatet eanalizës dhe detajet e implementimit. Në terma të vrazhdët, koeficionti i termitkryesor (të parë) ka të bëjë me numrin e instruksioneve në unazën e brendshme:në cilindo nivel të dizajnit të algoritmit, është menquri të limitohet numri iinstruksioneve të tilla. Për N të madh, efekti i termit kryesor dominon; për N tëvogël ose për algoritme të ndërtuara me kujdes, mund të kontribuojnë më shumëterma dhe krahasimet e algoritmeve janë më të vështira. Në shumicën e rasteve,kohëve të ekzekutimit të programeve thjeshtë ju referohemi si “lineare”,“kaudratike”, “kubike”, “N log N”, etj. 

Përfundimisht, për të zvogëluar kohën e ekzekutimit të programit, fokusoheminë minimizimin e instruksioneve në unazën e brendshme. Secili instruksionshkon në vëzhgim të kujdesshëm: a është më të vërtetë i nevojshëm? A kamënyrë më efikase për të përmbushur detyrën e njëjtë? Disa programerë besojnëse pajisjet automatike të ofruara nga kompajlerët modernë mund të prodhojnëkodin më të mirë të makinës; të tjerët besojnë se rruga më e mirë që të përpunohen me kujdes dhe të kodohen unazat e brendshme në kod të makinësose në asembler. Normalisht, ajo që na intereson pa hyrë në aspektet eoptimizimit, është që të dijmë sa instruksione nevojiten për operacionet e

caktuara dhe të kuptojmë se përse në praktikë një algoritëm mund të jetë më ishpejtë se një tjetër.

Për problemet e vogla, ka dallim të vogël se cilën metodë e përdorim, pasi qëkompjuterët modern super të shpejtë, e kryejnë punën në çast. Por me rritjen emadhësisë së problemit, numrat bëhen shumë të mëdhenjë. Pasi që numri iinstruksioneve që duhet të ekzekutohen nga një algoritëm i ngadalshëm bëhetëvërtetë shumë i madh, koha e nevojshme për të ekzekutuar këto instruksione bëhet e pazbatueshme, edhe për kompjuterët e shpejtë. Në figurën 2.1 shihendisa faktorë të konvertimit prej një numri mjaft të madh të sekondave, në ditë,

Page 127: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 127/699

Algoritmet dhe strukturat e të dhënave

127

muaj, vite, e kështu me radhë. Tabela 2.2 jep disa shembuj të cilët paraqesin sesi algoritmet e shpejta janë më të prirur a të jenë të afta t’i zgjidhin problemetsesa komjuterët e shpejtë, pa u përballur me kohëra “mizore” të ekzekutimit. 

lg N  N     N N lg N N(lg N)2

  N3

/2 N2

 3 3 10 33 110 32 1007 10 100 664 4414 1000 10000

10 32 1000 9966 99317 31623 100000013 100 10000 132877 1765633 1000000 10000000017 316 100000 1660964 27588016 31622777 1000000000020 1000 1000000 19931569 397267426 1000000000 1000000000000

Sekonda Koha

102  1.7 minuta

104  2.8 orë

105  1.1 ditë

106  1.6 javë

107  3.8 muaj

108  3.1 vjet

109  3.1 dekada

1010  3.1 shekuj

1011

  kurrë :)

Tabela 2.2. Konvertimi në sekonda

Dallimi i madh ndërmjet numrave si 104 dhe 108 është më i qartë kur shqyrtohennë aspektin e gjatësisë së kohës në sekonda dhe kur bëhet konvertimi i tyre nënjësi të zakonshme të kohës. Është e kuptueshme se do të prisnim përekzekutimin e një programi për 2.8 orë, por pak ka gjasa që të pajtohemi me programin që do të kërkonte 3.1 vite për t’u kryer. Pasi që 210 është përafërsishtsa 103, kjo tabelë do të ishte e dobishme edhe për fuqitë e 2-shit. Për shembull,

232

 sekonda është përafërsisht 124 vite.Për shumë aplikacione, gjasa e vetme për të zgjidhur instanca të problemeveshumë të mëdha është përdorimi i algoritmit efikas. Tabela në vijim paraqetkohën minimale të nevojshme për zgjidhjen e problemeve me madhësi të rendit1 milion dhe 1 miliard, duke përdorur algoritmet lineare (N), kuadratike (N2)dhe N herë logaritmik (N lg N), në kompjuterët me shpejtësi të ekzekutimit të 1milion, 1 miliard dhe 1 bilion instruksione për sekond. Algoritmi i shpejtë namundëson zgjidhjen e problemeve në kompjuter të ngadalshëm, por kompjuteri ishpejtë nuk ndihmon dot kur përdoret algoritmi i ngadalshëm.

Page 128: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 128/699

Avni Rexhepi

128

Operacionepër sekond

Madhësia e problemit 1 milionMadhësia e problemit 1

miliard

N N lg N N2  N N lg N N2 

106  sekonda sekonda javë orë orë kurrë

109  në çast në çast orë sekonda sekonda dekada

1012  në çast në çast sekonda në çast në çast javë

Tabela 2.2. Koha për zgjidhjen e problemeve shumë të mëdhaja

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 algoritëm N3/2.Gjithashtu, disa algoritme kanë dy shkallë të dekompozimit të nënproblemit, tëcilat çojnë në kohë të ekzekutimit proporcionale me N log2 N. Është e dukshmenga tabela 2.1 se të dy këto funksione janë shumë më të afërta me N log N sesame N2.

Funksioni logaritmik luan rol të veçantë në dizajnin dhe analizën e algoritmeve,kështu që është me rëndësi të njihet mirë dhe të shqyrtohet detaje. Pasi qëshpesh kemi të bëjmë me rezultate analitike përbrenda faktorit konstant, përdorim notacionin “log N” pa e specifikuar bazën për logaritmin. Ndryshimi i bazës prej një konstante në një tjetër e ndryshon vlerën e algoritmit vetëm përnjë faktor konstant, por disa baza specifike sygjerohen vetvetiu për kontekste të

veçnta. Në matematike, logaritmi natyral (me bazë e=2.71818...) është shumë irëndësishëm dhe shkurtesa e tij speciale është loge N ≡ ln N. Poashtu, logaritmi binar (me bazë 2) është shumë i rëndësishëm dhe zakonisht përdoret si log2  N ≡lg N.

 Numri më i vogël i plotë (integer) më i madh se lg N është numri i bitave tënevojshëm për të reprezentuar N-in si numër binar, në mënyrë të njëjtë siqnumri i plotë më i vogël (integer) më i madh sesa log10N është numri i shifravetë nevojshme për të reprezentuar N-in si decimal. Urdhëri në C++

for (lgN = 0; N > 0; lgN++, N /= 2) ;

është mënyrë e thjeshtë për të llogaritur integer-in më të vogël, më të madh sesalg N. Mënyra e ngjashme për llogaritjen e këtij 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 vogël më i madhsesa lgN.

 Nganjëherë, e përsërisim logaritmin: e aplikojmë atë në mënyrë tënjëpasnjëshme në një numër shumë të madh. Për shembull: lglg 2256 = lg 256 =

Page 129: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 129/699

Algoritmet dhe strukturat e të dhënave

129

8. Si ilustrohet këtë shembull, zakonisht e konsiderojmë loglogN si konstant, përqëllime praktike, pasi që është shumë i vogël, edhe kur N është shumë i madh.

Poashtu, shpeshherë hasim një numër të funksioneve speciale në notacionet

matematike prej analizës klasike, që janë të dobishme në ofrimin e shpjegimevekoncize (të ngjeshura) të tipareve të programeve. Tabela 2.3 përmbledhëfunksionet më të zakonshme:

Funksioni  Emri Vlera tipike Përafrimi

x  Kufiri i poshtëm 3.14 = 3  x

 x   Kufiri i epërm 3.14 = 4  x

lg N Logaritmi binar lg 1024 = 10 1:44 ln N

FN  Numrat Fibonacci F10 = 55 N=/   5  

HN  Numrat Harmonik H10 2:9 ln N+ 

N! Faktorieli 10! = 3628800 (N/e)N 

lg(N!) Lg(100!)  520 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ë bëjnë me njësi diskretë, kështuqë shpeshherë na duhet që për funksionet speciale vijuese të konvertojmënumrat real në numra të plotë (integer):

x: numri i plotë më i madh, që është më i vogël ose baraz me x

x: numri i plotë më i madh, që është më i vogël ose baraz me xPër 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ë përdorim tjetër i rëndësishëm ikëtyre funksioneve paraqitet kur dëshirojmë të ndajmë përgjysmë bashkësinë prej N elementeve. Nëse N është tek, këtë nuk mund ta bëjmë saktësisht, por përtë qenë preciz, e ndajmë njërën nënbashkësi në  N/2  dhe tjetrën në  N/2. Nëse N është numër çift, të dy nënbashkësitë janë me madhësi të barabarta( N/2 =  N/2), mirëpo nëse N është tek, ato dallojnë në madhësi për 1 ( N/2 +1=  N/2). Në C++, këto funksione mund t’i llogarisim drejtpërdrejt kur jemi

duke operuar me numra integer (p.sh., nëse N≥0, atëherë N/2 është dhe N-(N/2)

Page 130: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 130/699

Avni Rexhepi

130

është ), ndërsa nëse jemi duke operuar me numra jo të plotë, mund të përdorimfunksionet ‘floor’ dhe ‘ceil’ nga libraria <math.h> (angl. floor -dyshemeja, dhe‘ceil’, shkurtesa për ceiling-tavani, pra kufiri i epërm dhe kufiri i poshtëm).

Versioni i diskretizuar i funksionit të logaritmit natyral, i quajtur numratharmonik, paraqitet shpesh në analizën e algoritmeve. Numri harmonik i N-të(seria e harmonikëve) definohet përmes ekuacionit:

 N  H  N 

1...

3

1

2

11    

Logaritmi natyral ln N është zona nën lakoren 1/x ndërmjet 1 dhe N; harmonikuH N  është zona nën funksionin shkallë (angl. step function) që definohet duke

vlerësuar 1/x në pozitat e numrave të plotë ndërmjet 1 dhe N. Ky relacion ështëilustruar në figurën 2.6.

Figura 2.6. Numrat harmonik

Formula H N   ln N++1/(12N), ku  = 0.57721... (konstanta e Euler-it) jep një përafrim të shkëlqyeshëm të H N. Në anën tjetër, për  lg N  dhe lg N është mëmirë të  përdoret funksioni ‘log’ i librarisë matematikore për të llogaritur H N  sesa tëllogaritet direkt prej definicionit.

 Numrat harmonik (seria e harmonikëve) janë një përafrim i zonës nën lakoreny=1/x.

Konstanta e Euler-it definohet si limiti i diferencës ndërmjet serisë së

harmonikëve dhe logaritmit natyral (pjesa e shkallëzuar, përmbi lakore 1/xn):

Seria e numrave:

0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 ...

të cilët definohen përmes formulave:

FN = FN–1 + FN–2, për N ≥ 2, me F0 = 0 dhe F1 = 1

Page 131: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 131/699

Algoritmet dhe strukturat e të dhënave

131

 Njihet si “Numrat Fibonacci” dhe kanë disa tipare shumë interesante. Përshembull, raporti i dy termave të njëpasnjëshme, i përafrohet raportit të artë:

are knoën as the Fibonacci numbers, and they have many interesting properties.For example, the ratio of tëo successive terms approaches the golden ratio ø = (1

+ 5   )/21.61803.... Analiza e detajuar tregon se F N  është ø N/   5 , errumbullaksuar në numrin e plotë më të afërt.

Poashtu, do të kemi rastin e manipulimit të funksionit të njohur të faktorielit, N!. Ngjashëm me funksionin eksponencial, faktorieli paraqitet në zgjidhjet “brute-force” të problemeve dhe rritet shumë shpejt, sa që zgjidhjet e tilla nuk janë meinteres praktik. Poashtu paraqitet në analizën e algoritmeve sepse përfaqëson të

gjitha mënyrat e mundshme të aranzhimit të N objekteve. Për të përafruarfaktorielin N!, përdoret formula e Stirling-ut:

 N e N  N  N  N     2lglglg!lg    

ose versioni zakonisht i përdorur nëpër alikacione:

Formula e Stirling-ut tregon se numri i bitave në reprezentimin binar të N! është përafërsisht N lg N.

Shumë prej formulave të shqyrtuara në analizën e algoritmeve shprehen nëterma të funksioneve të përmendura, prandaj janë me interes.

Page 132: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 132/699

Avni Rexhepi

132

Algoritmet për nga teknika dhe qasja

Për rastet kur natyra e problemit është e thjeshtë dhe ka zgjidhje të lehta, përdoren algoritmet standarde. Për problemet e komplikuara dhe për të cilat nuk

ka ndonjë zgjidhje standarde, përdoren metodat e optimizimit.Kur fillohet zgjedhja ideore e algoritmit, varësisht prej problemit, mund të përdoren qasje të ndryshme. Zgjidhjet ideore të algoritmeve kryesisht janë tëorientuara kah gjetja e zgjidhjeve optimale për problemin e caktuar, kështu qëshpesh herë flitet për algoritmet optimizuese. Metodat më të njohura të qasjes për algoritmet janë: brute-force (metoda që provon me radhë të gjitha rastet), përçaj e sundo (angl. Divide and Conquer), metoda e eliminimit ose e turneutme eliminime (angl. tournament), rekursioni, “back -tracking” (angl. back  –  prapa,angl. tracking –  përcjellja, gjurmimi; kështu që mund ta quajme rishikimi,

 prapaveprimi, prapakthimi, etj), algoritmet dinamike, algoritmet evolutive, etj.

Copëtimi i problemeve

 Një prej teknikave më të fuqishme për zgjidhjen e problemeve është copëtimi ityre në probleme më të vogla dhe në pjesë më lehtë të zgjidhshme. Problemet evogla janë më të lehta dhe na lejojnë të fokusohemi në detajet të cilat humbenkur studiohet problemi i tërë. Për shembull, sa herë që të mund ta copëtojmë problemin në instanca më të vogla të problemit të tipit të njëjtë, fillon të bëhet idukshëm algoritmi rekurziv.

Dy paradigma të rëndësishme të dizajnit të algoritmeve janë të bazuara nëcopëtimin e problemeve në probleme më të vogla:

-   Programimi dinamik  në mënyrë tipike largon një element nga problemi,e zgjidhë problemin më të vogël dhe pastaj e përdorë zgjidhjen e këtij problemi më të vogël për të ja shtuar përsëri elementin në mënyrën eduhur.

-   Përqaj e sundo në mënyrë tipike e ndanë problemin në gjysmë, e zgjidhësecilën gjysmë dhe pastaj i bashkon prapa të dy gjysmat për të formuar

zgjidhjen e plotë.Të dy këto teknika janë të rëndësishme për t’i njohur. Në veçanti, programimidinamik është teknikë e keqkuptuar dhe e nënvlersuar.

Shumë objekte kanë një renditje të qenësore nga e majta në të djathtë përelementet e tyre, si karakteret në string, elementet e permutacioneve, pikat përreth poligonit ose gjethet në pemën e kërkimit. Për cilindo problem tëoptimizimit në objekte të tilla nga e majta në të djathtë, programimi dinamik megjasë do të dërgojë në një algoritëm efikas për gjetjen e zgjidhjes më të mirë. Panjë renditje të nënkuptuar nga e majta në të djathtë të objekteve, programimi

Page 133: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 133/699

Algoritmet dhe strukturat e të dhënave

133

dinamik zakonisht është i “dënuar” që të kërkojë kohë dhe hapësirë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ë kërkohen. Optimumi global (i gjetur, për shembull, duke përdorur programimin dinamik)është shpeshherë dukshëm më i irë sesa ndonjë zgjidhje e gjetur me heuristikëtipike. Se sa është i rëndësishëm ky përmirësim varet prej aplikacionit tuaj, porkurrë nuk mund të jetë i dëmshëm.

Kërkimi binar dhe variantet e tij janë algoritme më të sofistikuara përqaj-e-sundo.

Programimi dinamik

 Ndonjëherë ka kuptim që të punohet drejt zgjidhjes së problemit duke krijuar njëtabelë të zgjidhjeve për versionet më të vogla të problemit. Për arsye të përshkruara si “historike” ky proces është i njohur me emrin programimidinamik ndërsa algoritmet përkatëse quhen algoritmet dinamike. Ka aplikim në problemet e ndryshme të ndërlidhura me kërkimin kombinatorik  –   ndoshta problemi më i thjeshtë do të ishte shembulli i llogaritjes së koeficientëve binomial përmes ndërtimit të trekëndëshit të Paskalit, rresht pas rreshti, deri satë arrihet të koeficienti i dëshiruar.

Pasi ta keni kuptuar mirë, programimi dinamik me gjasë është teknika më e

lehtë e dizajnimit të algoritmeve për t’u aplikuar në praktikë. Në fakt, algoritmete programimit dinamik janë zakonisht më të lehta për t’u rizbuluar sesa tëtentohet të kërkohen nëpër libra. Mirëpo, deri sa të kuptohet, programimidinamik duket si magji. Para se të përdoret, duhet të kuptohet “forja” e tyre. 

 Në problemet algoritmike, si sortimi, korrektesia është e prirur të vërtetohet mëlehtë sesa efikasiteti. Kjo nuk është rasti në problemet e optimizimit, kukërkojmë të gjejmë zgjidhjen e cila maksimizon ose minimizon funksionin. Nëdizajnimin e algoritmeve për një problem të optimizimit, duhet të japim prova sealgoritmi gjithmonë jep zgjidhjen më të mirë të mundshme.

 Algoritmet lakmitare, të cilat marrin vendimin më të mirë lokal në secilin hap,rastësisht ndodhë që të prodhojnë optimume globale për disa probleme. Këto janë efikase në mënyrë tipike. Mirëpo, duhet provë për të treguar se gjithmonëdo të përfundoni me përgjigjen më të mirë. Algoritmet e kërkimit shterrues, tëcilat provojnë të gjitha mundësitë dhe zgjedhin më të mirën, sipas definicionit,gjithmonë duhet të prodhojnë rezultat optimal, por zakonisht me kosto penguesenë terma të kompleksitetit kohor.

Programimi dinamik kombinon më të mirat nga të dy botët. Teknika

sistematikisht shqyrton të gjitha vendimet e mundshme dhe gjithmonë zgjedhë

Page 134: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 134/699

Avni Rexhepi

134

atë që vërtetohet se është më e mira. Duke ruajtur konsekuencat e të gjithavendimeve të mundshme deri në momentin aktual dhe duke përdorur këtëinformatë në mënyrë sistematike, minimizohet puna totale. Programimi dinamikmësohet më së miri duke studiuar një numër të shembujve, gjersa të kuptohet siduhet. Shembuj përkatës janë: Numrat Fibonacci, Problemi i particionimit (The partition problem), përshtatja e përafërt e stringjeve (approximate stringmatching), sekuenca më e gjatë rritëse (longest increasing sequence),trekëndëzimi me peshë minimale (minimum ëeight triangulation), etj.

Numrat Fibonacci

“Pazari” ndërmjet hapësirës dhe kohës, i eksploatuar në programimin dinamik,më së miri ilustrohet në vlerësimin e relacioneve të rekurrencës, siç janë numratFibonacci. Numrat Fibonacci fillimisht u definuarn nga matematikani italian

Fibonacci, në shekullin e trembëdhjetë, për të modeluar rritjen e popullacionit tëlepujve.

Lepujt shumohen shpejtë dhe Fibonacci hamendësoi se numri i çifteve të lepujvetë lindur në një vit është i barabart me numrin e çifteve të lepujve të lindur nësecilin prej dy viteve paraprake, nëse vitin e parë fillohet me një çift lepujsh. Përtë numëruar numrin e lepujve të lindur në vitin e n-të, ai definoi relacioninvijues të rekurrencës:

Fn = Fn-1 + Fn-2 

me rastet fillestare F0=0 dhe F1=1, kështu që seria vazhdon si:{3,5,8,13,21,34,55,89,144...}. Siç shihet, formula e Fibonacci-t nuk dha rezultattë mirë sa i përket numërimit të lepujve, mirëpo ka përdorim për aplikacione tëtjera dhe tipare interesante.

Pasi që janë definuar përmes formulës rekurzive, është e lehtë të shkruhet programi rekurziv për llogaritje të numrit të n-të Fibonacci dhe pseudokodi ialgoritmit 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])

Page 135: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 135/699

Algoritmet dhe strukturat e të dhënave

135

F(1)F(2)

F(3)

F(1) F(0)

F(2)

F(1) F(0)

F(4)

F(5)

F(6)

F(1)F(2)

F(3)

F(1) F(0)

F(1)F(2)

F(3)

F(1) F(0)

F(2)

F(1) F(0)

F(4)

 Figura: Pema e llogaritjes për llogaritjen rekurzive të numrave Fibonacci 

Sa kohë merr ky algoritëm për të llogaritur Fibonacci[n]?

Pasi që

61803.12

)51(1

 

n

n

 F 

 F 

 

kjo do të thotë që: Fn>1.6n 

Pasi që pema rekurzive e ilusturar më sipër ka vetëm gjethe 0 dhe 1, dukembledhur deri në një numër të madh duhet të kemi së paku gjethet ose thirrjet e procesurave! Ky program i vogël merr kohë eksponenciale për t’u ekzekutuar! 

 Në fakt, mund të bëhet shumë më mirë. Mund të llogarisim të njëjtën në kohëlineare, duke ruajtur të gjitha vlerat. Kështu shkëmbejmë hapësirën për kohëndhe algoritmi vijues:

Fibonacci[n]F[0]=0F[1]=1For 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 dyinteger-ave në kohë totale O(n), që është shumë më mirë sesa kohaeksponenciale e rastit rekurziv.

Kufizimet e programimit dinamik

Programimi dinamik mund të aplikohet në cilindo problem që zbaton principin eoptimalitetit. Thënë 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. Për shembull, për të vendosur se a të zgjerohet

Page 136: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 136/699

Avni Rexhepi

136

një kërkim i përafërt i stringut me zëvendësim, insertim ose fshirje, ne nuk keminevojë ta dijmë saktësisht cila sekuencë e operacioneve është kryer deri më tani. Në fakt, mund të ketë disa sekuenca edituese të ndryshme të cilat arrijnë kosto Cnë p karakteret e para të mostrës P dhe t karakteret e stringut T. Vendimet në tëardhmen do të bëhen bazuar në konsekuencat e vendimeve paraprake, e jo nëvetë ventimet aktuale.

Problemet nuk kënaqin principin e optimalitetit nëse operacionet aktuale kanërëndësi, kundrejt vetëm kostos së operacionit. Shqyrtoni një formë të editimit tëdistancës ku nuk na lejohet të përdorim kombinime të operacioneve në ndonjërenditje të caktuar. Formuluar si duhet, sidoqoftë, shumica e problemeve tëkombinatorikës e respektojnë principin e optimalitetit.

Kufizimi më i madh në përdorimin e programimit dinamik është numri i

zgjidhjeve parciale të cilat duhet të përcillen (ruhen). Për shembujt që u panë,zgjidhjet parciale mund të përshkruhen tërësisht duke specifikuar pozitat endalimit në hyrje. Kjo për 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 –  përziej) pa ndryshuar problemin në tërësi. Kur një herë të jetëfiksuar renditja, ka relativisht pak pozita të ndaljes ose gjendje, ashtu që përfitojmë algoritëm efikas. Nëse objektet nuk mund të renditen me vendosmëri(qëndrueshëm), sidoqoftë, kemi një kohë eksponenciale të zgjidhjeve tëmundshme parciale dhe jemi të “gjykuar” që të kemi nevojë për një sasi të pa -arritshme të memories.

Për të ilustruar këtë, shqyrtoni algoritmin vijues të programimit dinamik për problemin e shitësit ambulant (agjentit tregtar, angl. Traveling SalesmanProblem  – TSP). Zgjidhja e problemit TSP nënkupton gjetjen e renditjes me tëcilën vizitohen lokacionet (qytetet, pikat) saktësisht një herë, me distancën totaleminimale ose me koston minimale. Le të jetë C(i,j) kostoja e degës së përshkuar prej pikës i në pikën j. Definoni T(i; j1, j2...jk ) të jetë kostoja e turit optimal prej inë 1 që kalon nëpër secilin qytet j1, j2...jk   saktësisshtë një herë, me radhë.

Kostoja e turit optimal TSP është prandaj e definuar të jetë T(1,2,...n) dhe mundtë llogaritet rekurzivisht duke identifikuar nyjen e parë në këtë sekuencë:

),...,,,(),(min),...,,,( 111

11   k mmk m

k    j j j jT  jiC  j j jiT     

duke përdorur rastet bazike

)1,(),(),(   jC  jiC  jiT     

Kjo rekurrencë, edhe pse pak e komplikuar për t’u kuptuar, në fakë ështëkorrekte. Mirëpo, secila zgjidhje parciale është e përshkruar me nënbashkësinë e

nyjeve: j1, j2, ..., jk . Pasi që ka 2n

 nënbashkësi të n nyjeve, kërkohet kohë dhe

Page 137: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 137/699

Algoritmet dhe strukturat e të dhënave

137

hapësirë W(2n), për të vlerësuar këtë rekurrencë. Kjo është e menagjueshme, pasiW(2n) është vërtetë përmirësim i dukshëm në krahasim me të gjitha O(n!) turet emundhshme TSP. Megjithatë, programimi dinamik është më së shumti efikas nëobjektet e renditura mirë.

Programimi linear –  zgjidhja për kutinë e zezë

Mënyra më e lehtë për zgjidhjen e një problemi të optimizimit është që tëshkruhet specifikacioni për hapësirën e zgjidhjeve të realizueshme dhe tëfunksionit objektiv, e pastaj të përdoren ndonjë paket ekzistues softverik për tëgjetur zgjidhjen optimale.

Algoritmet lakmitare –  kurrë mos shiko prapa

Algoritmet lakmitare (angl. Greedy Algorithms, greed-lakmi) përfshijnë ndonjë

lloj të optimizimit. Idea e “lakmisë” është që të fillohet me kryerjen e cilitdooperacion që kontribon sa një hap i vetëm në drejtim të zgjidhjes përfundimtaredhe qëllimit final. Hapi i ardhshëm pastaj do të ishte hapi më i mirë që do tëmund të ndërmirrej prej pozitës së re, e kështu me radhë. Shembull i mirë përshkrues do të ishte algoritmi i gjetjes së pemës minimale të shtrirjes tekgrafet.

Prapaveprimi

Prapaveprimi ose rishikimi (angl. Back-tracking, back-prapa; tracking-

 përcjellje/ndjekje/ gjurmim) është rasti kur është i nevojshëm rishikimi irezultateve të arritura paraprakisht. Nëse algoritmi që ju nevojitet përshinëkërkimin, mund të jetë që rishikimi është ajo çka nevojitet. Kjo e ndanë dizajninkonceptual të funksionit të kërkimit në dy pjesë: së pari vetëm shkon përpara përtë hulumtuar atë çka mendon se është shtegu më i arsyeshëm për t’u hulumtuar.Kjo pjesë me gjasë do të arrijë në rrugë pa dalje dhe ky është momenti kur fillon pjesa e dytë, prapaveprimi (rishikimi). Në këtë rast ai ka mbajtur informacioneshtesë përreth asaj kur pjesa e parë ka bërë zgjidhjet dhe “çpshtjellon” prapa tëgjitha llogaritjet deri në pikën e zgjidhjes paraprake të fundit e pastaj rifillon

kërkimin nëpër një shteg tjetër. Kjo metodë është shumë e dobishme në shumë probleme të ndërlidhura me grafet. Është i njohur problemi i ashtuquajtur“Problemi i tetë mbretëreshave” në tabelë shahu, ku tetë mbretëresha duhet të pozicionohen në tabelën e shahut, ashtë që asnjëra nuk e sulmon tjetrën.

Prapaveprimi është mënyrë sistematike e kalimit nëpër të gjitha konfiguracionete mundshme të hapësisrës së zgjidhjeve. Këto konfiguracione mund të jenë tëgjitha renditjet/rregullimet e mundshme të objekteve (permutacionet) ose tëgjitha mënyrat e ndërtimit të bashkësive të tyre (nënbashkësitë). Ndonjëaplikacion tjetër mund të kërkojë të gjitha pemët e shtrirjeve të grafeve ose të

Page 138: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 138/699

Avni Rexhepi

138

gjitha shtigjet e mundshme mes dy nyjeve ose të gjitha mënyrat e ndarjes sënyjeve në grupe sipas ngjyrave.

Kërkimi lokal –  Mendo globalisht, vepro lokalisht

Algoritmet e optimizimit janë të aplikueshme në disa rrethana të veçanta.Programimi dinamik, kërkon strukturë speciale për problemin dhe mund tëkërkojë shumë hapësirë dhe kohë. Kërkimi sistematik është zakonisht shumë ingadalshëm për hyrje të mëdha. Algoritmet lakmitare janë të shpejta, porshpeshherë japin vetëm zgjidhje të kualitetit të ulët. Kërkimi lokal është procedurë iterative gjerësisht e aplikueshme. Ai fillon me ndonjë zgjidhje tërealizueshme dhe pastaj lëvizë nga zgjidhja e realizueshme në zgjidhje tërealizueshme me modifikime lokale. Kërkimi lokal mbanë zgjidhjen aktuale tërealizueshme x dhe zgjidhjen më të mirë të gjetur deri në atë moment x’. Në

secilin hap, kërkimi lokal lëvizë nga zgjidhja aktuale në një zgjidhje fqinje. Çka janë zgjidhjet fqinje? Cilado zgjidhje që mund të përfitohet prej zgjidhjesaktuale, duke bërë një ndryshim të vogël në të, është zgjidhje fqinje.

Hill Climbing

Hill Climbing  (angl. Hill  –  kodër, Climbe  –  ngjitje, alpinizëm), është poashtumetodë/teknike që përdoret për problemet e optimimzimit. Hill climbing ështëversioni lakmitar i kërkimit lokal. Kjo kërkon që së pari të gjeni (në ndonjëmënyrë) ndonjë formë të realizueshme (por me presupozimin që nuk është

optimale) të zgjidhjes së problemit. Pastaj, shikon për mënyrat në të cilat do tëmund të bëheshin ndryshime të vogla në këtë zgjidhje, për ta përmirësuar atë. Një varg i këtyre përmirësimeve të vogla do të mund të dërgojë në fund nëoptimumin e kërkuar. Natyrisht, propozimi i mënyrës për të gjetur përmirësimete tilla nuk është vetvetiu garancë që optimumi global do të arrihet ndonjëherë: sizakonisht algoritmi i dizajnuar nuk është i kompletuar deri sa të keni vërtetuar(provuar) që ai gjithmonë përfundon me arritjen e saktësisht rezultatit që junevojitet/duhet.

Kalitja e simuluar –  të mësuarit nga natyra

 Nëse dëshirojmë t’i ikim zgjidhjeve të padëshiruara lokale, duhet të gjindetmënyra për të ikur prej tyre. Kjo do të thotë që ndonjëherë duhet të pranohenlëvizjet të cilat zvogëlojnë vlerën objektive. Qëllimi është që të iket prejoptimumeve lokale, ashtu që të lëvizet drejt cakut, për të gjetur optimumetglobale. Një mënyrë e e asthuquajtura ‘kalitja e simuluar’ (angl. simulatedannealing).

Kjo medotë simulon procesin e kalitjes së metaleve, në të cilin substanca nxehet përmbi temperaturën e saj të shkrirjes dhe pastaj gradualisht ftohet duke prodhuar rrjetën kristaline, e cila minimizon shpërndarjen e probabilitetit të

Page 139: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 139/699

Algoritmet dhe strukturat e të dhënave

139

energjisë së saj. Kjo rrjetë kristalore, e përbër nga miliona atome të renditura nëmënyrë perfekte, është shembull i mirë i gjetjes natyrore të strukturës optimale.Megjithatë, ftohja e shpejtë ose shuarja e prishë formacionin kristalor dhesubstanca bëhet masë amorfe me gjendje energjie më të lartë sesa optimumi.Çelësi i formimit të kristalit është kontrollimi i kujdesshëm i shkallës sëndryshimit të temperaturës.

Algoritmi analog me këtë proces fillon me një hamendësim të rastit të vlerave tëvariablave të funksionit të kostos. Nxehja nënkupton modifikimin me rastësi tëvlerave të variablave. Nxehja e lartë kërkon luhatje të rastit më të mëdha.Funksioni i kostos kthen daljen, të shoqëruar me setin e variablave. Nëse daljazvogëlohet, atëherë seti i variablave zëvendëson setin e vjetër të variablave.Edhe nëse seti i variablave dërgon në kosto më të keqe, mund të pranohet me një probabilitet të caktuar.

Variabla kontrolluese vendosë hapin e shkallës ashtu që, në fillim të procesitalgoritmi detyrohet të bëjë ndryshime të mëdha në vlerat e variablave. Me kohë,ndryshimet lëvizin algoritmin tuje nga optimumi, gjë që e detyron algoritmin tëhulumtojë regjione të reja të hapësirës së kërkimit. Pas një numri të caktuar të përsëritjeve, seti i ri i variablave nuk dërgon më në kostot e ulëta. Algoritmingalet kur T   0. Zvogëlimi i T është i njohur edhe si orari i ftohjes. Janë tëmundshme shumë orare të ndryshme të ftohjes. Shpresohet që në këtë mënyrë tëarrihet në regjionet e zgjidhjeve të mira optimale dhe pastaj në fakt të gjindet

zgjidhje afër optimales, në fazën e temperaturës së ulët.Pragu i pranueshëm  –  është pragu i vlerave të pranueshme të zgjidhjeve.

Tabu lista  –  Tabu lista ose “Tabu search” (tabu k ërkimi) na paraqitet në rastetkur gjatë kërkimitë të zgjidhjeve optimale, arrijmë gjithmonë në zgjidhjen enjëjtë sub-optimale, duke u sillur në cikël të mbyllur. Randomizimi (marrja evlerave të rastit) do të paraqiste një rrugëdalje prej optimumeve lokale, dukembajtur një listë tabu të “elementeve të zgjidhjes ”evitohet” në zgjidhjet e reja, për momentin. Tabu listat janë shumë të suksesshme dhe mund të përdoren siteknikë bazë për një variantë ta pavarur të zgjidhjes lokale, të quajtur “tabu

search” (k ërkimi tabu).Restartimi  –   sjellje tipike e algoritmeve të rafinuara të kërkimit lokal ështëlëvizja 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ë. Mirëpo, mund tëndodhë që ekzistojnë zgjidhje shumë më të mira, në zona të tjera të largëta. Disaekzekutime të njëpasnjëshme të pavarura në disa kompjuterë të ndryshëm mundtë dërgojnë në një formë të lehtë të paralelizmit.

Page 140: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 140/699

Avni Rexhepi

140

Algoritmet Evolutive

Qeniet e gjala janë shumë adaptive në ambient dhe i tejkalojnë problemet e jetëssë përditshme. Aplikimi i principeve të jetës 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ëcilët dërgojnë në performansë më të mirë: mutacionet, rekombinimet, mbijetesae atyre me aftësi më të mira (të ashtuquajtur fitnes, më të mirë). Qasja evolutivenë zgjidhjen e problemeve, nënkupton krijimin e zgjidhjeve të përafërtafillestare, me rastësi, dhe pastaj aplikimin e ndryshime dhe rekombinimevenëpër “gjenerata” të ekzekutimit të programit, për të fituar zgjidhje optimale.

Algoritmet me rastësi - Las Vegas dhe Monte Carlo

Algoritmet e randomizuara (me rastësi) ndahen në dy kategori kryesore:Algoritmet Las Vegas dhe ato Monte Carlo.

Algoritmi Las Vegas gjithmonë llogaritë përgjigjen korrekte por koha e tij eekzekutimit është variabël e rastit.

Algoritmi Monte Carlo gjithmonë ka kohë të njëjtë të ekzekutimit, por ka një probabilitet jo-zero të kthimit të përgjigjes jo-korrekte. Probabiliteti që përgjigjaështë jokorrekte është pothuajse 1/4. Pra, kthen përgjigje, por ndonjëherë jo tësaktë.

Page 141: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 141/699

Algoritmet dhe strukturat e të dhënave

141

3. Implementimi i strukturave themelore  –  Stack dhe Queue

Stack (Steku)Steku (angl. Stack-mullar, grumbull, raft etj) është struktura në të cilënelementet renditen sipas radhës 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 imbullur në njërin skaj, etj. Pra në të gjitha këto raste, kemi qasje vetëm nëelementin e fundit të vendosur në “listë”. 

Stack - Steku

Radha e pritjes (angl. Queue) është struktura e të dhënave e cila përbëhet ngalista e elementeve dhe dy pointerëve, në elementin e “front”-it (fillim, para) dheelementin “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, për ndonjëshërbim të caktuar (i pari që vjen, i pari shërbehet), lista e punëve në printer,tuneli, një gyp i hapur në të dy anët, por me qarkullim vetëm një një kahje, kuelementet hyjnë nga njëri skaj, e dalin nga skaji tjetër, etj.

Vargjet dhe listat e lidhura sigurojnë mekanizmat themelore të cilat mundësojnë

insertimin dhe largimin e elementeve. Në të vërtetë, vargjet dhe listat e lidhura janë strukturat themelore të të dhënave, të cilat qëndrojnë përfundi, si bazë endërtimit të disa implementimeve të disa tipeve të përgjitshsuara të radhëve(queue) dhe strukturave të tipeve abstrakte të të dhënave. Dihet se kostoja einsertimit dhe fshirjes është e varur nga strutura specifike dhe elementi specifikqë insertohet ose fshihet. Për një ADT të dhën, sfida është se si të zgjedhetstruktura që lejon kryerjen efikase të këtyre veprimeve.

Tipet e të dhëave të cilat përbëhen nga objektet abstrakte, janë objekt qëndror nëstudimin e shkencave kompjuterike, sepse ato përkrahin në mënyrë direkte

 paradigmën themelore të llogaritjeve. Për shumë llogaritje, e gjejmë veten në

Page 142: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 142/699

Avni Rexhepi

142

 pozita kur kemi shumë objekte me të cilat duhet punuar, por kemi mundësi tëqasjes së vetëm një elementi në kohë. Prandaj, duhet të ruhen të tjerat, deri sa të përpunohet ai i zgjedhuri. Ky përpunim mund të përfshijë ekzaminimin e disaobjekteve të ruajtura më parë dhe shtimin e të tjerave në koleksion, poroperacionet e ruajtjes së objekteve dhe marrjes së tyre në bazë të ndonjë kriteritë caktuar, janë baza e llogaritjes kompjuterike. Shumë struktura klasike të tëdhënave dhe shumë algoritme, i përkasin këtij “modeli”. 

 Në C++, klasat të cilat implementojnë koleksione të objekteve abstrakte quhen“container classes” (klasa bartëse, kontejnerë). Shumë struktura të të dhënave janë të implementuara në libraritë e C++-it ose në versionet e bazuara nëshabllonet standarde STL (Standard Template Library).

Steku përmes vargjeveTermi “Stek” mund të duket abstrakt për t’u kuptuar, por në jetën e përditshme e përdorim gjatë lojës me letra, kur bëjme palpeta, kur vendosim një grup librashnjë mbi një, etj. Steku pra është një grup i gjërave të vendosura njëra mbi tjetrëndhe me mundësi të largimit të vetëm një elementi në moment kohor, vetëm ngamaja e stekut. Edhe pse e strukturë shumë e thjeshtë, steku gjenë përdorim 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ë pjesën e poshtme, pa i larguar së pari një nga një të gjitha sipër. Edhe pse në dukje, strukturë joefikase, nëse objektiv do të ishte qasja e rastit nëelementet përbërëse, për qëllime të tjera, steku del të jetë strukturë ideale.

Mirëpo, nëse qëllimi është marrja e gjërave, sipas radhës së vendosjes në stek, siështë puna me instruksionet e kompjuterit, atëherë steku është strukturë efikase.

Steku është një prej strukturave fundamentale të të dhënave në shkencatkompjuterike dhe përdoret në shumë algoritme dhe aplikacione. Për shembull,steku përdoret në: vlerësimin e shprehjeve, në mënyrë implicite në rekurzion, për të verifikuar korrektësinë e sekuencës së kllapave, etj.

Së pari do të përshkruajmë ADT-në e stekut dhe pastaj do të paraqitenimplementimet e ndryshme.

Stack ADT

Ilustrim i mirë nga jeta reale do të ishte një grumbull i librave (të vendosuranjëra mbi tjetren), pra një stek i librave, ku mund të veproni vetëm me “majën” estekut. (angl. top-maja, kulmi, kreu, kreshta, etj. P.sh., shpesh përmendet “Top

lista”, pra lista e kryesoreve, më të mirave, etj.). 

Page 143: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 143/699

Algoritmet dhe strukturat e të dhënave

143

 Në stek janë të përcaktuara gjashtë veprime standarde. Kemi mundësi tëvendosim një libër në maje (push;  angl. push-goditje, shtytje, hedhje, etj.); tëshikojmë cili libër ndodhet në maje (peek, top; angl. peek-shikim vjedhurazi, përgjim); të largojmë librin në majë (pop; angl. pop-nxjerrë (kuptimi që i përshtatet këtij veprimi)); dhe të verifikojmë a është steku i zbrazët (isEmpty).Gjithashtu, kemi dy operacionet themelore për krijim (create) dhe asgjësim(destroy) të stekut.

Operacionet  Stack create() 

krijon stekun e zbrazët  boolean isEmpty(Stack s) 

tregon a është steku s i zbrazët  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 zbrazët

  pop(Stack s) largon elementin që ndodhet në maje, nga steku s  Parakusht: s nuk është i zbrazët

 

destroy(Stack s) asgjëson stekun s 

 Në literaturë, operacioni peek  quhet edhe top,  për të simbolizuar shikimin nëmaje të stekut. 

Aksiomat për stekun

  steku i sapokrijuar është i zbrazët;

   pasi të vendoset një element në stekun e sapokrijuar, ai bëhet “jo i zbrazët”;    peek kthen elementin e fundit të vendosur në stek;  steku mbetet i paprekur, pas çiftit të komandave “push” dhe “pop”, të

ekzekutuara njëra pas tjetrës.

Pra, të gjitha janë definicione formale. Përmes figurës në vijim ilustrohenoperacione në stek.

Skematikisht, steku mund të paraqitet si vijon:

Page 144: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 144/699

Avni Rexhepi

144

 Fig. 3.1 - Steku

Vargu dhe steku shpeshherë duken si koncepte të cilat ngatërrohen në diskutimetë njëjta, mirëpo në esencë janë dy gjëra të ndryshme. Vargu i ruan vlerat nëmemories; ndërsa steku vetëm e përcjellë se cili është elementi në krye të stekut(angl. top). Kur “nxirret” vlera nga steku, ajo në fakt mbetet në memories, sepseajo akoma është element i vargut. Nxjerrja e elementit nga steku, vetëm endryshon elementin që ndodhet në krye të stekut.

 Fig. 3.2 –  Steku dhe vargu janë dy gjëra të ndryshme: vargu i ruan vlerat në

memorie; steku përcjellë cili element ndodhet në krye të stekut. 

Programerët përdorin vargjet, për të ruajtur vlerat të cilat referencohen ngasteku. Si është thënë, vargu përbëhet nga seria e elementeve të vargut, që janë tëtipit të njëjtë, në koncpet të variablave. Steku e përmbanë indeksin e elementit tëvargut i cili ndodhet në krye të stekut.

Page 145: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 145/699

Algoritmet dhe strukturat e të dhënave

145

Figura 3.2 është mënyra se si disa programerë e përfytyrojnë një varg të përdorur me stek. Shembulli paraqet një varg të quajtur stek, me 8 elemente tëvargut. I tërë vargu, përmbanë vlerat të cilat janë të referencuara nga steku. Trielemente të vargut, janë vlera të përcaktuara, gjersa të tjerat janë akoma tëzbrazëta dhe mund të përdoren kur të vendosen në stek elemente të reja.

Moni është vlera e parë e vendosur në stek. Kjo dihet për 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

Për vendosjen e elementit në stek, përdoret termi “push” (angl. push-shtyje,detyroje, godite, etj). Push është udhëzimi se e dhëna është duke u shtuar nëstek. Imagjinojeni këtë si shtyerje të elementeve teposhtë stekut, duke i lëvizurelementet që veç janë brenda stekut teposhtë, për të krijuar vend/hapësirë përelementin e ri.

Ja çka ndodhë në realitet. Vlera e re caktohet në pozitën e ardhshme të lirë tëvargut (pozita e lirë që është në dispozicion) dhe indeksi i atij elementi të vargut bëhet top (kreu) i stekut, si në figurën 3.3. Programi e inkrementon (e rritë përnjë) indeksin aktual të stekut. Në këtë shembull, indeksi rritet për 1, dukerezultuar në indeksin 3 si top i stekut, që është indeksi vlerës së re të caktuar tëvargut.

 Fig. 3.3 –  Vlera e re (angl. New Value) i ndahet elementit të ardhshëm të vargut

dhe indeksi i tij bëhet top i stekut.

Pop

Procesi i kundërt i “Push” është “Pop” (angl. Pop –   nxirre, Pop Up  –   tërhiqe

lartë). Me “pop” largohet një element nga steku. Është me rëndësi të kuptohet se

Page 146: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 146/699

Avni Rexhepi

146

tërheqja e elementit nga steku nuk e kompjon elementin. Kur një element tëtërhiqet nga steku, ai më nuk është në dispozicion në stek, edhe pse vlera e tijakoma 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ë figurën 3.3, indeksi 3 është nëmajte të stekut, që do të thotë se elementi 3 i vargut, “Vlera e re”, është në majetë stekut.

Kur nxirret (pop) Vlera e re prej stekut, dekrementohet (zvogëlohet për 1)indeksi në “top” të stekut. Kjo do të thotë se indeksi i top tash bëhet 2, në vendtë 3. Kjo bën që “ beni” të jetë vlera e re në maje të stekut (Figura 3.4). vëreni se“Vlera e re” dhe elementi i tretë i vargut mbesin të paprekur, sepse tërheqja(popping) e vlerës prej stekut vetëm e ndryshon stekun, e jo vargun që ndodhet

“përfundi, nën”.

 Figura 3.4: Kur tërhiqet elementi top nga steku, të gjitha vlerat lëvizin te lartë,

kah maja e stekut.

Krijimi i stekut në C++

Steku mund të krijohet në C++ duke definuar klasën Stack dhe duke deklaruarinstancat e klasës. Klasa Stack kërkon tri atribute dhe disa funksione (memberfunctions). Do të fillojmë me definimin e klases themelore, që përmbanë vetëmkomponentet e nevojshme për krijimin e stekut.

Klasa do të emërtohet Stack, edhe pse normalisht mund të zgjedhni emrin sipasdëshirës. Klasa përmbanë pjesën private dhe atë publike. Komponentet private

kanë qasje vetëm përmës funksioneve anëtare të klasës.

Page 147: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 147/699

Algoritmet dhe strukturat e të dhënave

147

 Në pjësën private janë tri atribute: size (madhësia), 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ë këtë shembull është stek inumrave të plotë, por mund të përdoret vargu i çfarëdo lloji të vlerave, varësisht prej natyrës së programit.

 Në fillim, për qëllime thjeshtësie dhe për t’u kuptuar lehtë, fillojmë me vetëmnjë funksion anëtarë të klasës. Ky funksion, quhet Stack dhe është konstruktori iklasës. Konstruktor është funksioni që ka emrin e njejtë me vetë klasën dhe qëthirret automatikisht kur të krijohet një instancë e klasës.

Brenda konstruktorit ndodhin disa gjëra. Së pari, konstruktori e merr një vlerëinteger si argumet, i cili përcillet kur të deklarohet një instancë e klasës. Kjo

vlerë integer përcakton numrin e elementeve në stek dhe i ndahet variablës size.Urdhëri i parë në konstruktor mund të duket pak i paqartë, pasi që duket thua sevlera e variablës size nga lista e argumenteve po i ndahet vetëvetes, por kjo nukështë kështu. Në fakt, variaba size nga lista e argumenteve, është variabël lokale përbrenda funksionit Stack. Kombinimi: this->size i referohet atributit size tëklasës Stack, si në vijim:

this->size = size;

Programerët e përdorin pointerin “this” (angl. this –   ky, kjo), përbrenda

funksionit të klasës për të ju referuar instancës aktuale të klasës. Në këtëshembull, pointeri this përdorë referencën e pointerit ->, për t’i treguarkompjuterit që të përdorë atributin size të klasës. Si dihet, në C++, referenca e pointerit përdoret (->) kur punohet në mënyrë indirekte me anëtarin e klasësndërsa operatori “dot” (.) përdoret kur punohet direkt me anëtarin e klasës. 

Kjo i mundëson kompajlerit që të bëjë dallimin ndërmjet variablës së kalsës dhevariablës lokale që ka emër të njëjtë. Kjo do të thotë se vlera e variablës size që përcillet si argument në funksionin Stack, i ndahet atributit size, duke e bërëvlerën të disponueshme për anëtarët e tjerë të klasës Stack.

Atributi size përdoret në urdhërin e ardhshëm. Ky urdhër i bënë dy gjëra. Së pari, e alokon memorien për stekun duke përdorur opreratorin “new” (newint[size]). Operatori new kthen pointerin për në lokacionin e rezervuar tëmemories. Size është atributi size i klasës dhe përcakton madhësinë e vargut.Vargu është një varg i numrave të plotë (integer).

Pastaj, pointeri i vargut të integjerave i ndahet atributit values të klasës. Atributivalues është variabël pointer që është definuar në pjesën private të klasës Stack.

Urdhëri i fundit në funksionin Stack ia ndanë vlerën a-1 atributit top. Vlerat e

atributit top është indeksi i elementit top të stekut. Vlera -1 do të thotë që steku

Page 148: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 148/699

Avni Rexhepi

148

nuk ka elemente. Rikujtoni se vlerat e indeksave janë zhvendosjet e memories(angl. memory offsets) prej fillimit të vargut. Indeksi 0 do të thotë “lëviz 0 bajta prej fillimit të vargut”. Kështu, indeksi -1 është vetëm një lehtësirë për të thënëqë vargu është i zbrazët.

Definicioni i klasës Stack do të zgjerohet, mirëpo tani për tani le të krijojmë njëinstancë të klasës Stack. Instanca e klasës është deklaruar përbrenda funksionitmain( ). Këtu ndodhin tri gjëra. Së pari, operatori new krijon një instancë tëstekut në memorie. Operatori new kthen pointerin në atë lokacion të memories.

Pastaj, urdhëri definon një pointer për në stek, që është emërtuar myStack. Hapii fundit është përcaktimi (ndarja) e pointerit të kthyer nga operatori new, pointerit myStack. Tutje, myStack përdoret si emër për të ju referuar instancëssë klasës Stack, në program.

public class Stack{

private:int size; //size=madhesiaint top; //top=kreuint* values; //values=vlerat

public:Stack(int size){

this->size = size;values = new int[size]; //’size’ anëtartop = -1;

}};void main(){

Stack *myStack = new Stack(10);}

Funksioni Push

Pas definimit të klasës që e krijon stekun, do të shohim funksionin i cili imundëson klasës që të mbushë stekun (që të shtyjë vlerat në stek).Vendosja/shtyrja e vlerës në stek është proces dy-hapësh. Së pari, duhet të përcaktohet nëse ka vend në stek për një vlerë të rë. Nëse ka, atëherë vlerashtyhet (angl. push) në stek; përndryshe, jo.

Do të krijojmë funksione anëtare për secilin hap, duke filluar me definimin efunksioni i cili përcakton nëse ka vend në stek. Do ta quajmë, isFull( ) (angl. si

full - është i mbushur) dhe do ta definojmë në kodin vijues. Funksioni isFull()

Page 149: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 149/699

Algoritmet dhe strukturat e të dhënave

149

është funksion i thjeshtë, i cili krahason vlerën e kreut të stekut, atributi top, menjë më pak sesa vlera e atributit size.

Vlera e atributit top është -1, kur deklarohet instanca e stekut. Supozojmë se

madhësia është 10 (pra atributi size është 10). Shprehja e kushtëzimit nëurdhërin if të funksionit isFull() përcakton nëse vlera top, e cila është -1, ështëmë e vogël se size-1. Pasi që vlera e size është 10, shprehja e kushtit krahason -1<9. Nëse top është më e madhe ose baraz me 9, atëherë kthehet “true”(plotësohet kushti), përndryshe kthehet “false”. 

Pra, duhet të zbritet 1 nga vlera size, pasi që vlerat e atributit top është njëindeks i anëtarit të vargut. Pasi që indeksat fillojnë prej zeros, kemi dhjetëindeksat: 0 deri në 9. Përndryshe, vlera size në fakt është numri i elementeve tëstekut.

bool isFull() //isFull = eshtePlot{

if(top < size-1){

return false;}else{

return true;}

}

Me funksionin e definuar isFull(), vazhdojmë definimin e funksionit push( ), sinë shembullin vijues. Funksioni push() shtyen vlerën në stek (e vendosë vlerën ere në stek). Vlera që shtyhet në stek, përcillet si argument në funksionin push()dhe i ndahet variablës x (në këtë shembull).

Para se të bëjë ndonjë gjë tjetër, funksioni push() e kontrollon se a ka vend nëstek, duke e thirrur funksionin isFull(), në shprehjen e kushtëzimit if. Kjosh prehje mund të duket pak e çuditshme, pasi paraprihet nga shenja ‘!’ (shenja e

negacionit), mirëpo kjo është bërë për të siguruar plotësimin e kushtit, pasi qëisFull() kthente “false”, kur kishte vend në stek. Pra, me logjikën e negacionit,kthejmë “true”, kur kemi vend në stek, dhe vazhdojmë me shtytjen e elementit tëri në stek.

Brenda urdhërit të kushtit ‘if’ kemi dy urdhëra. I pari, e inkrementon vlerën eatributit top, që është indeksi i vlerës së fundit të vendosur në stek. Nëse stekuështë i zbrazët, athëerë vlera aktuale që është në fillim -1, e bën vlerën top 0, qëedhe është indeksi i elementit të parë të vargut të stekut. Urdhëri tjetër në bllokun e kushtit ‘if’ ia ndanë (përcakton) vlerën e përcjellur në funksionin

 push(), elementit të ardhshëm në dispozicion të vargut.

Page 150: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 150/699

Avni Rexhepi

150

void push(int x){

if(!isFull())

{top++;values[top] = x;

}}

Funksioni Pop

 Na duhet edhe funksioni për largimin e elementeve/vlerave nga steku. Për të

 bërë këtë, duhet të definojmë edhe dy funksione shtesë: isEmpty() (angl. isempty –  është i zbrazët) dhe pop(). Funksioni isEmpty() e verifikon se a ka vleranë stek. Funksioni pop e largon vlerën top nga steku.

Së pari definojmë funksionin isEmpty(), i cili përmbanë një urdhër tëkushtëzimit ‘if’. Shprehja e kushtit të urdhërit ‘if’ krahason vlerën e atributit toptë stekut, me -1. Rikujtoni se -1 ishte vlera fillestare e top, kur deklarohet steku. Nëse atributi top është baraz me -1, atëherë kthehet ‘true’ sepse steku është izbrazët; përndryshe, kthehet ‘false’. 

bool isEmpty() //isEmpty=eshteBosh

{if(top == -1){

return true;}else{

return false;}

}

Funksioni pop() i klasës Stack ka për detyrë të bëjë ndryshimin e indeksit qëndodhet në krye të stekut (top) dhe të kthejë vlerën e vargut korrespondues nëurdhërin që e thërret funksionin pop(). Shembulli vijues, e definon funksionin pop().

Urdhëri i parë deklaron një variabël të tipit integer, të quajtur retVal, e cila ruanvlerën e kthyer nga funksioni pop(). Vlera retVal inicializohet në zero.

Pastaj, thirret në shprehjen e kushtëzimit ‘if’, thirret funksioni isEmpty(), për të përcaktuar nëse ka vlerë në krye të stekut (në top). Vëreni, përseri me negacion

(‘!’), përmes logjikës së kundërt, veprohet si në rastin e funkionit  push().

Page 151: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 151/699

Algoritmet dhe strukturat e të dhënave

151

Urdhërat përbrenda urdhërit ‘if’ duhet të ekzekutohen nëse funksioni isEmpty()kthen false, që do të thotë se steku nuk është i zbrazët.

Brenda kushtit ‘if’ ndodhin dy hapa. Së pari, vlera top i ndahet variablës retVal

duke iu referuar vlerës së vargut përmes përdorimit të indeksit të përmbajtur nëatributin top. Pastaj, vlera e atributit top dekrementohet. Vlera retVal atëherëkthehet përmes funksionit pop().

int pop(){int retVal = 0; //retVal = Vlera qe kthehetif(!isEmpty()){

retVal = values[top];top--;

}return retVal;

}

Steku në veprim

Tani që e kemi parë se si krijohet dhe si përdoret steku, do të shohim një stek nëveprim. Tri gabime të zakonshme të cilat duhet të përcillen në rastin e stekut, janë alokimi i memories për stekun, reagimi në rastin e stekut të mbushur dhereagimi në rastin e stekut të zbrazët.

Do të shohim një shembull të programit që krijon dhe përdorë stekun. Programi përmbahet përbrenda tre fajllave: stack.h, stack.cpp dhe stackDemo.cpp. Fajllistack.h është ‘header file’ që përmbanë definicionin e klasës Stack, e cilashërben si shabllon për krijimin e instancave të stekut. Fajlli stack.cpp ështëkodi burimor i cili përmbanë implementimin e funksioneve të klasës Stack.Fajlli stacDemo.cpp përmbanë kodin burimor për programin në C++ i cilideklaron instancën e klasës Stack dhe thërret funksionet anëtare 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 përmbanë definicionet dhe instruksionet/direktivat preprocesorike. Preprocesoriështë një program i cili aplikon direktivat preprocesorike në kodin burimor, parase te kompajlohet kodi.

Header fajlli stack.h përmbanë një direktive preprocesorike, #define, e cila nëkëtë rast definon një simbol. Këtu është definuar simboli DEFAULT_SIZE dheatij i është dhënë vlera 10. Preprocesori pastaj i zëvendoson të gjitha paraqitjet eDEFAULT_SIZE me 10, para se të kompajlohet kodi. DEFAULT_SIZE është

madhësia standarde e stekut nëse nuk përcillet ndonjë argument tek konstruktori.

Page 152: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 152/699

Avni Rexhepi

152

Parametrave të funksioneve mund të ju ndahen vlera të nënkuptuara (standarde,default) në protoptipin e funksionit, nëse argumentet ndodhen në funkd të listëssë argumenteve. Nëse vlera size nuk përcillet, aje e merr vlerën e nënkutpuarnga DEFAULT_SIZE, e cila në këtë shembull është 10.

Fajlli stack.h gjithashtu përmbanë definicionin e klasës Stack. Definicioni iklasës Stack ka të njëjtat vlera për atributet size, top dhe values, si nëshembullin e mëparshëm. Mirëpo, definicioni i funksioneve anëtarëe është indryshëm, sepse funksionet janë implementuar jashtë definicionit të klasës nëfajllin e kodit burimor stack.cpp. Header fajlli përmbanë vetëm protoptipet efunksioneve, të cilat krijojnë shabllonet e klasave.

 Nga klasat në C++, dihet se vetëm protoptipi i funksioneve anëtare është enevojshme që të përfshihet në definicionin e klasës. Implementimi i funksioneve

mund të bëhet jashtë definicionit të klasës. Janë dy arsye për të mbajturdefinicionin (header fajllin) dhe implementimin (burimin) në fajlla të ndarë:

  E mbanë ambientin zhvillimor (angl development environment) më të pastër dhe më të lehtë për t’u kuptuar.

  Ju mundëson që të ju ofroni programerëve të aplikacioneve softverikekomerciale vetëm interfejsin, pa pasur nevojë t’u jepni kodin tuaj burimor. Ju i ofroni/siguroni programerit header fajllat e juaj, të cilët atado t’i përdorin për të kompajluar kodin e tyre (atyre ju duhen vetëmheader fajllat për të komapjluar kodin). Ju e ofroni kodin tuaj burimor nëformë të librarive të prekompajluara të cilat referohen nga programet e programerëve të tjerë gjatë linkimit.

Definicioni i klasës përmbanë prototipet e gjashtë funksioneve anëtare.Funksioni i parë është quajtur Stack, i cili është konstruktori që u paraqit në pjesën paraprake. Më parë u tregua se konstruktorit i përcillet një integer i cili përfaqëson madhësinë e stekut, size. Në versionet reale, programi e vendosë njëvlerë të nënkuptuar (default) e cila mund të mbishkruhet kur instanca e klasëskrijohet në program. Madhësia e nënkuptuar specifikohet duke përdorur

DEFAULT_SIZE, që është 10 (e caktuar me #define).

Funksioni tjetër është ~Stack() dhe është destruktori i klasës. Destruktori ështëfunksioni i fundit që thirret kur instanca e klasës del jashtë fushëveprimit dhe“vdes”. Destruktori gjithmonë duhet të ketë emrin e njëjtë më atë të klasës dhetë paraprihet nga shenja ‘tilde’ (~). Sipas definicionit, destruktori nuk mund të pranojë argumente. Qëllimi i destruktorit është të lirojë memorien e përdorurnga steku ose të bëjë ndonjë lloj tjetër ‘spastrimi’,që mund të kërkohet. 

Funksionet tjera janë ato të njëjtat që u paraqitën paraprakisht: isFull(),

isEmpty(), push() dhe pop().

Page 153: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 153/699

Algoritmet dhe strukturat e të dhënave

153

//stack.h#define DEFAULT_SIZE 10class 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ë përmbanë implementimin efunksioneve të klasës Stack. Është vendosur në fajll tjetër prej definicionit tëklasës sepse është më lehtë të lexohet dhe të mirëmbahet si dhe për arsyet e përmendura më herët.

Fajlli fillon me direktivën preprocesorike #include e cila i tregon kompjuterit qëtë ‘vlerësojë’ përmbajtjen e fajllit stack.h para se të kompajlojë fajllin stack.cppashtu që ai “të dijë” lidhur me definicionin e klasës Stack para se të kompajlohet programi.

Funksionet anëtare në fajllin stack.cpp janë të njohura (përveç njërit) sepse janëato që u paraqitën në pjesën e përparshme. Sidoqoftë, emrat e funksioneve nëshikim të parë mund të duken të çuditshme, sepse të gjitha fillojmë me emrin eklasës të pasuar nga simboli ‘::’ (katër pika, ose dy dy-pikësha). Ky simbolnjihet si ‘scope resolution operator’ (operatori për zbërthimin e fushëveprimit)dhe përcakton se në cilën klasë është deklaruar funksioni përkatës (gjegjësishtcilës klasë i përket funksioni).

Emri i funksionit duhet të paraprihet me operatorin ‘::’ nëse funksioni definohet jashtë definicionit të klasës. Pra, brenda klasës deklarohet vetëm protoptipi(paralajmërimi se funksioni ndodhet në këtë klasë), kurse definicioni i plotë ifunksionit (trupi i funksionit me urdhërat përkatës) jepet jashtë klasës.Mendojeni këtë si mënyrë për t’i terguar kompjuterit se funksioni i përket klasës përkatëse, në këtë rast klasës Stack.

Funksioni ~Stack() (Destruktori) e liron memorien e përdorur nga steku. Ai e bën këtë duke përdorur operatorin ‘delete’ dhe duke ju referuar emrit të vargut të përdorur nga steku. Në këtë shembull, emri i vargut është ‘values’. 

Page 154: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 154/699

Avni Rexhepi

154

Për të evituar rrjedhjet e memories (memory leaks), lirimi i memories është irëndësishëm sa herë që bëhet alokimi dinamik i memories. Kllapat e mesme, [ ], përdoren me urdhërin ‘delete’, sepse objekti që duhet të fshihet/largohet ngamemoria ka qenë i krijuar dinamikisht.

Fajlli stack.cpp kompajlohet si kompajlohet çdo kod burimor i zakonshëm.Rezultati i fituar është një objekt fajll që është i bashkuar nga linkeri me fajllin ekompajluar të kodit burimor të stackDemo.cpp, për të krijuar programinekzekutiv.

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

Page 155: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 155/699

Algoritmet dhe strukturat e të dhënave

155

{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ë krijoninstancën e klasës Stack. Urdhëri i parë krijon stekun në proceson tre-hapësh.Hapi i parë është përdorimi i operatorit neë për të alokuar hapësirën në memorie për kalsën Stack, duke thirrur konstruktorin e kësaj klase. Operatori neë kthenlokacionin 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ë këtë shembull, kemi përdorur madhësinë standarde ëpr stekun, që është 10elemente. Konstruktorit Stack() mund t’ia përcjellim një vlerë integer për tëndryshuar madhësinë e stekut.

Funksioni push() thirret tri herë. Secilën herë, në stek vendoset vlerë endryshme. Vëreni se në vend të dot operatorit është përdorur pointeri ->. Kjoduhet të bëhet për arsye se stack është pointer në një instancë të klasës dhe jovetë instanca.

Pjesa e fundit e programit stackDemo.cpp e thërret tri herë funksionin pop()(brenda unazës). Secilën herë, vlera përkatëse largohet nga kreu i stekut (top)dhe paraqitet në ekran.

//stackDemo.cppvoid main() {

Stack *stack = new Stack();stack->push(10);stack->push(20);stack->push(30);for(int i=0; i<3; i++)

Page 156: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 156/699

Avni Rexhepi

156

{cout << stack->pop() << endl;

}}

Implementimi i stekut të bazuar në vargje

Supozojmë se kapaciteti i stekut është i kufizuar në një vlerë të caktuar dhembingarkimi (tejmbushja) e stekut do të shkaktojë “error” (gabim). Megjithatë,duke pasur parasysh idetë nga implementimi i vargjeve dinamike, ky kufizimmund të tejkalohet lehtësisht, përmes mengaxhimit të kapacitetit të vargjevedinamike.

Pavarësisht nga kufizimi i kapacitetit, implementimi stekut të bazuar në vargje përdoret gjerësisht në praktikë. Në një numër të rasteve, kapaciteti i kërkuar istekut është i ditur paraprakisht dhe hapësira e alokuar i përmbushë saktësishtkërkesat e detyrës së veçantë. Në rastet tjera, kapaciteti i stekut thjeshtë tentohettë përcaktohet që të jetë i mjaftueshëm. 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 përdorëvariablën top për të pointuar në elementin e majes së stekut në varg.

1.  Fillimisht top = -1; 2.  push  operacioni, e rritë top për 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 ndryshëm nga -1 dhe e zvogëlon

vlerën e top për 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;

Page 157: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 157/699

Algoritmet dhe strukturat e të dhënave

157

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;

}};

Page 158: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 158/699

Avni Rexhepi

158

Përdorimi i stekut

Steku gjenë zbatime të shumëfisht. Në rastin më të thjeshtë, nëse dëshirojmë tëndryshojmë (rrotullojmë) renditjen e elementeve të një vargu, mund t’i

 përcjellim në stek dhe t’i tërheqim nga steku, duke fituar renditjen e kundërt tëvlerave.

Steku mund të përdoret edhe në konvertimin e numrave, p.sh., nga numratdecimal në numër binar.

 Konvertimi i numrave

Steku përdoret në shumë aplikacione, gjatë kohës së ekzekutimit të tyre:

•  “Runtime stack” përdoret nga proceset (programet gjatë ekzekutimit) përtë përcjellur funksionet në punë

•  Për problemet e kërkimit (angl. Search problems)•  Për operacionet ‘undo’, ‘redo’ (zhbërja dhe ribërja e veprimeve,

nëprograme të ndryshme), pastaj për operacionet: ‘ back ’, ‘forward’(lëvizja para, prapa, p.sh., gjatë shfletimit të ëeb-faqeve, në shfletuesit einternetit etj).

Steku përdoret edhe nga compiler-et të cilët i testojne programet për gabimesintaksore. Shpeshhere, mungesa e nje simboli të vetëm (p.sh., * / ose 1) bën qëkompajleri të “kthejë” një mori rreshtash te diagnozës pa e identifikuar gabimine vërtetë.

Përmes stekut, kontrollohet edhe përmbajtja e programit, e urdhërave dhefunksioneve. Duke i vendosur elementet “hapëse” (kllapa e hapur) në stek, melehtësi mund të verifikojmë se a ka kuptim simboli “mbyllës” (kllapa e mbyllur). Në mënyrë specifike, kemi algoritmin vijues:

1.  Krijo nje stek te zbrazet

Page 159: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 159/699

Algoritmet dhe strukturat e të dhënave

159

2.  Lexo simbolet deri në fund të fjallita.   Nesë “token”-i është simbol hapës, shtyje në stek b.   Nëse është simbol mbyllës dhe steku është i zbrazët, raporto gabim.c.  Përndryshe, “tërhiqe” stekun. Nëse simboli i tërhequr nuk i

 përgjigjet simbolit hapës, raporto gabim.3.   Në fund të fajllit, nëse steku nuk është i zbrazët, raporto gabim.

Algoritmi i perdorur per te verifikuar simbolet e balansuara sygjeron një mënyre për thirrjen e funksioneve. Problemi është që kur thirret një funksion i ri, të

gjitha variablat lokale për në funksionin e thirrur duhet të ruhen nga sistemi; përndryshe, funksioni i ri do t’i mbishkruante variablat e rutinës thirrëse. 

Për më tepër, lokacioni aktual ne rutinen thirrëse duhet të ruhet ashtu qëfunksioni i ri të dije ku të shkoje/kthehet, pasi të ketë mbaruar punën. Variablatnë pergjithsi janë caktuar nga kompajleri në regjistra të makinës dhe konfliktetmund të paraqiten.

Arsyeja perse ky problem është i ngjashëm me balansimin e simboleve ështësepse thirrja e funksionit dhe kthimi i funksionit (return) janë në esencë të njëjtë

me kllapen e hapur dhe atë të mbyllur, prandaj duhet aplikuar idetë e njëjta. Një aplikim tjetër i rëndësishëm i stekut është vlerësimi i shprehjeve në gjuhët programuese. Në shprehjen 1+2*3, në pikën ku haset *, veq e kemi lexuaroperatorin + 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ë vlerësojmë 2*3 si6 dhe pastaj të aplikojmë operatorin +.

Ky proces sygjeron se operandet dhe rezultatet intermediate duhet të ruhen ne

stek.

Page 160: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 160/699

Avni Rexhepi

160

Gjithashtu sygjeron se operatoret duhet të ruhen në stek (si + qe mbahet gjersa prioriteti me i larte * të vlerësohet).

Shqyrtoni shprehjen aritmetike vijuese:

x = a * b + c (Notacioni “infix” –  operatorët ndërmjet operandëve).

Kompajleri duhet të gjeneroj instuksionet e makinës (angl. “machineinstructions”) si vijon:

1. LOAD a2. MULT b3. ADD c4. STORE x

Gjuhët programuese dhe kompajlerët përdorin notacionin polak (angl. PN -

Polish Notacion, të definuar nga matematikani polak, Jan Lukasieëicz) dhenotacionin revers polak (angl. RPN-Reverse Polish Notation). Kështu, shprehjastandarde shndërrohet në forma më të përshtatshme për llogaritje:

(Infix): 1+2;PN (Prefix): +12;RPN (Postfix): 12+

Shumica e kompajlerëve konvertojnë shprehjet nga notacioni inf ix  në postfix  operatorët shkruhen pas operandëve.

Kështu: a * b + c bëhet a b * c +Përparësia e këtij notacioni është se shprehjet mund të shkruhen pa kllapa.

 Në notacionin POSTFIX, operatori vendoset menjëhere pas operandëve të tij.

INFIX POSTFIX a + b ab+ a + b * c abc*+ a * b + c ab*c+ (a + b) * c ab+c* 

 Në këtë rast, për vlerësim mund të përdoret teknika “Me laps/dorë" (Teknika enënvizimit):

1.  Skeno shprehjen nga e majta në të djathtë, për të gjetur një operator.

Page 161: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 161/699

Algoritmet dhe strukturat e të dhënave

161

2.  Lokalizo (“nënvizo") dy operandët paraprak dhe kombinoji ata me këtëoperator.

3.  Përsërit, deri sa të arrihet fundi i shprehjes.

Vlerësimi i shprehjeve RPN bëhet njësoj edhe përmes stekut:

Përmes algoritmit me stack1.  Inicializo stekun e zbrazët2.  Përsërit sa vijon deri sa të arrihet fundi i shprehjes

a)  Merr token-in e ardhëshëm (const, var, operator) në shprehje b)  Operand –  push në stack

Operator –  bëj sa vijoni.  Pop 2 vlera nga stack-u

ii.  Apliko operatorin në dy vlerat (Vërejtje: nëse ka mbeturvetëm 1 vlerë në stack, kjo RPN shprehje është jo-valide)

iii.  Push vlerën rezultuese prapa në stack3.  Kur arrihet fundi i shprehjes, vlera e shprehjes është numri i vetëm i

mbetur në stack

Pra, procedura e thjeshtë është:

Operand: pushOperator: pop 2 operandë, llogarit rezultatin,

 push rezultatin prapa në stack.P.sh., për shprehjen: 1 2 3 + *

Page 162: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 162/699

Avni Rexhepi

162

Rreshti - Queue (Kju)

Queue (angl. queue –  rreshti, radha, radha e pritjes, etj. Lexohet/theksohet: Kju)është njësoj si radha e pritjes për blerjen e biletave ose radha e pritjes për

kryerjen e pagesave, në dalje të supermarketit. I pari që vjen, renditet në fillim tëradhës, i dyti pozicionohet pas tij e kështu me radhë deri tek klienti i fundit nëradhë, në fund të radhës. Klientët, shërbehen sipas radhës me të cillën kanëarritur në rreshtin e pritjes. Kjo është, i pari që vjen, i pari shërbehet, e njohur siFIFO (angl. First In, First Out).

I njëjti koncept aplikohet edhe në radhën (queue) në programim. Queue ështënjë organizim sekuencial i të dhënave. Të dhënat qasen sipas parimit FIFO. Kjodo të thotë që, vlera e parë në queue është e vlera e parë që është e qasshme prej programit. Në fillim do të shohim radhën e thjeshtë, me madhësi fikëse, e cila

realizohet përmes përdorimit të vargut. Më vonë do të shohim mundësinë erealizimit të radhës me prioritet (angl. Priority Queue) e cila realizohet përmes përdorimit të listës së lidhur. Në radhën me prioritet, elementet largohen bazuarnë dy faktorë: radha me të cilën janë vendosur në queue dhe prioritetit ielementit.

Programerët përdorin njërin prej llojeve të queue-ve varësisht prej objektivave të programit, radhë e thjeshtë apo radhë me prioritet. Radha e thjeshtë i organizontë dhënat në rend, ku elementi i parë është në fillim të rendit dhe elementi ifundit është në pjesën e prapme (në fund) të rendit. Secili element procesohet në

radhën në të cilën 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 kamënyrë që një element të prejë rendin dhe të procesohet jashtë radhës.

Radha me prioritet është e ngjashme me radhën e thjeshtë në atë se elementetorganizohen në rend dhe procesohen sekuencialisht. Mirëpo, elementet nëradhën me prioritet mund të kërcejnë në fillim të rendit nëse kanë prioiritet mëtë lartë. Prioriteti është një vlerë që i shoqërohet secilit element në radhë.Programi e proceson radhën duke e skanuar atë për elementet me prioritet më tëlartë. Këto procesohen të para, pa marrë parasysh pozitën në rend. Të gjithaelementet tjetra pastaj procesohen sekuencialisht, pasi të jenë procesuarelementet me prioritet të lartë.Tani për tani do të mirremi me radhën e thjeshtë.

 Në botën reale, radhët përdoren në programet të cilat procesojnë transaksionet.Transaksioni është një bashkësi e informacioneve si p.sh., një formular iurdhëresave. Informacioni për transaksionin pranohet nga programi dhe pastajvendoset në një radhë të pritjes për t’u procesuar nga një pjesë tjetër e programit. 

 Nëse i kthehemi radhës së pritjes për pagesë në supermarket, arka ekompjuterizuar e pagesave është një kompjuter që ekzekuton një program të

Page 163: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 163/699

Algoritmet dhe strukturat e të dhënave

163

transaksioneve, i cili ndër të tjera, proceson barkodin e secilit produkt të skanuarnë arkë.

 Një prej hapave të parë të procesimit të barkodit, është kërkimi i çmimit. Mund

të ndodhë që janë 10 ose më shumë arka të pagesave në një supermarket meshumë klientë dhe të gjitha duke kërkuar çmime të produkteve, në të njëjtënkohë. Mirëpo, kompjuteri mund të procesojë vetëm një barkod në kohë.Programi që i kërkon çmimet menagjon kërkesat duke përdorur një radhë tëthjeshtë të pritjes (queue) në të cilën secila kërkesë e re vendoset në fund tëlistës dhe programi proceson barkodin që ndodhet në fillim të radhës së pritjes.

Shumë aplikacione përdorin radhën e thjeshtë të pritjes për të mirëmbajturradhën në të cilën procesohen elementet. Këto përfshijnë programet të cilat procesojnë bursat, hipotekat dhe ato që procesojnë studentët të cilët regjistrohen

 për një kurs. Radhët e pritjes poashtu përdoren në kompjuter për të menagjuarshtypjen e dokumenteve në shtypës (printer).

Queue përmes vargjeve

Të dhënat e organizuara në radhë të pritjes mund të ruhen në një varg. Queue përcakton elementin që ndodhet në fillim të radhës dhe atë në fund të radhës.Vargu nuk është radhë e pritjes (queue) dhe as anasjelltas. Pra janë dy gjëra tëveçanta. Ky është një koncept që duhet kuptohet dhe zotërohet si duhet, edhe pse fillimisht mund të duket e vështirë për t’u kuptuar. 

Figura 3.5 është një ilustrim se si vargu dhe queue janë të ndryshëm porsidoqoftë janë të ndërlidhur së bashk u për t’i organizuar të dhënat. Vargu(realiteti fizik në memorie) vizatohet si bllok i elementeve. Queue (konceptilogjik) është vizatuar si rreth. Fushat (kutitë) e zbrazëta janë lokacionet ku ruhenvlerat në queue dhe numrat rendorë i korrespondojnë indeksave të vargut qëështë i shoqëruar me queue. Në të djathtë të rrethit janë paraqitur tri vlera. Vleratfront (fillimi) dhe back (fundi) ruajnë indeksat e fillimit dhe fundit të radhës(queue). Vlera size është numri i elementeve në queue, që në këtë rast është 8.

Page 164: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 164/699

Avni Rexhepi

164

 Figura 3.5 - Queue është i ndryshëm nga vargu që përdoret për të ruajtur të

dhënat që ndodhen në queue.

Enqueue (vendose në radhë)

 Një vlerë vendoset në queue duke kryer procesin ‘enqueue’ (enkju –  vendosja nëradhë), i cili përbëhet prej dy hapave. Hapi i parë është që të identifikohetelementi i vargut që është në fund të queue. Mirëpo, ky nuk është

domosdoshmërisht elementi i fundit të vargut. Rikujtoni, queue nuk është vargu.Fundi (back) i queue-s llogaritet duke përdorur formulën vijuese:

 back = (back+1) % size

Figura 3.6 paraqet se si përdoret formula dhe jep vlerat për front (fillimi), back(fundi) dhe size (madhësia) të queue-s. Variablat front dhe back janë vendosurnë zero sepse queue është i zbrazët dhe size është 8, sepse vargu ka 8 elemente.

Page 165: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 165/699

Algoritmet dhe strukturat e të dhënave

165

 Figura 3.6 - Procesi ‘ enqueue’ vendosë  vlerën e re në fund (back) të

radhës(queue).

Fusha e ardhshme paraqet formulën që identifikon fundin (back) e queue-s dheia ndanë atij vlerën 90. Në të djathtë të kësaj fushe është formula e njëjtë kuemrat e variablave janë zëvendësuar me vlerat aktuale. Le të shohim së afërmise si është llogaritur vlera back e radhës së pritjes (queue).

Opercioni i parë ndodhë brenda kllapave, ku 1 i shtohet vlerës së variablës back.Operatori i modulit përcakton se ku duhet të vendoset elementi i ardhshëm nëqueue duke bërë pjestimin e plotë dhe duke kthyer mbetjen nga pjestimi i plotë.

Edhe pse më parë kemi thënë se queue është si radha e pritjes në supermarket,aktualisht queue është rrethor. Kjo është ilustruar në llogaritjen e përdorur për të përcaktuar vlerën 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). Kështu, pas elementit last në varg, sillemidhe arrijmë në fillim të vargut, si fund (back) i radhës (queue). Si do të shihet nëvazhdim, para se të vendoset një element në fund (back) të radhës së pritjes

Page 166: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 166/699

Avni Rexhepi

166

(queue), verifikohet për të parë nëse jemi në fillim (front) të radhës, ashtu që tëmos mbishkruhet elementi në fillim (front) dhe të mos korruptohet queue-ja.

Hapi i dytë është që t’i ndahet vlera 90 elementit 1 të vargut. Kjo është, vendosja

e vlerës 90 në fund (back) të queue-s. rikujtoni se vlerat shtohen në queue ngaana e pasme, nga fundi (back), njësoj siç hyjmë në radhën e pritjes për pagesa nësupermarket. Vëreni se vlera 90 i është ndarë vargut në figurën 3.6.

Dequeue (nxjerrja nga queue)

Dequeue është procesi i largimit të vlerës nga fronti (fillimi) i queue-s. Është merëndësi të kuptohet që vlera largohet prej radhës (queue), jo prej vargut. Vleragjihmonë 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ë figurën 3.7. Hapi i parëështë llogaritja e indeksit të elementit të vargut në fillim të radhës, duke përdorur shprehjen vijuese:

front = (front+1) % 8

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

Vëreni se shprehja është shumë e ngjashme me shprehjen e përdorur në procesin

vendosjes në radhë, për llogaritjen e indeksit të elementit të vargut në fund të

Page 167: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 167/699

Algoritmet dhe strukturat e të dhënave

167

radhës. Operacioni i parë në këtë shprehje e inkrementon vlerën e variablës‘front’ (fillimi). Si mund të shihet në fig. 3.7, variablës front në fillim i ështëndarë vlera zero. Prandaj, rezultati i operacionit të parë është 1. Operacioni iardhshëm është aplikimi i operatorit të modulit, i cili është i njëjtë me atë tëaplikuar në procesin e vendosjes (enqueue). Rezultati i këtij operacioni është 1,që do të thotë se front-i i radhës është elementi i vargut që ka indeksin 1. Kjovlerë pastaj i ndahet variablës front. Në fillim, e pamë se nëse ndodhemi nëindeksin 7 të vargut, rezultati i llogaritjes do të ishte 0 ((7+1)%8=0), kështu qëdo të silleshim nëpër rreth.

Hapi i fundit në procesind dequeue është përdorimi i vlerës së lokalizuar nëfront. Në mënyrë tipike, procesi deque është funksion (metodë) dhe vlera front eradhës i kthehet urdhërit i cili e thërret funksionin (metodën).

 Në figurën 3.7, elementi i vargut values[1] është në front (fillim) të queue-s(radhës). Vlera e ndarë për këtë element është 90, që ishte vendosur në back(fund) të radhës (queue-s), nga procesi i mëparshëm enqueue (i vendosjes nëradhë).

Vëreni që velra 90 mbetet e caktuar në elementin values[1] të vargut në fig. 3.7,sepse vlerat e caktuara të vargut, të shoqëruara me queue nuk ndikohen kur vleralargohet nga fronti (fillimi) i queue-s (radhës). Queue përcjellë elementet evargut të cilat ndodhen në front (fillim) dhe në back (fund) të queue-s, e jo nëfillim dhe në fund të vargut. Në këtë rast, jemi duke përdorur varg të thjeshtë me

numra të plotë (integer-a) për të ilustruar principet e implementimit të strukturësqueue. Mund të hasen edhe implementime më komplekse, ku secili element nëvarg është pointer në objektin e klasës ose strukturës. Në këto raste, duhet tëkujdeseni për menagjimin e memories gjatë kryerjes së operacioneve enque dhedeque (vendosja në radhë dhe largimi nga radha e pritjes).

Queue përmes vargut në C++

Tani që e pamë se si punon radha e pritjes (queue) përmes vargut, le të shohim

krijimin e queue në C++.Programi është i organizuar në tre fajlla: queue.h, queue.cpp dhequeueProgram.cpp. Fajlli queue.h, cakton madhësinë standarde të vargut dhedefinon klasën Queue. Klasa Queue deklaron atributet size, front dhe back, tëcilat ruajnë madhësinë e vargut dhe indeksin e elementeve front dhe back tëqueue-s (radhës). Klasa Queue gjithashtu deklaron një pointer që do të pointojnë varg. Përveq këtyre, klasa Queue definon edhe funksionet anëtarëe të cilatmanipulojnë queue-të (radhët).

Page 168: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 168/699

Avni Rexhepi

168

//queue.h#define DEFAULT_SIZE 8class Queue{

private:

const int size;int front; //front=fillimiint back; //back=fundiint* 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 përmbanë implementimin e funksioneve anëtare për klasënQueue. Janë gjashtë funksionet të definuara në këtë fajll: Queue(), ~Queue(),isFull(), isEmpty(), enqueue() dhe dequeue().

Funksioni Queue() është konstruktor, të cilit i përcillet madhësia (size) e vargutkur të deklarohet një instancë e klasës Queue. Nëse konstruktori thirret pa parametra, atëherë përdoret madhësia standarde (default), përndryshe, përdoretvlera e cila i përcillet konstruktorit. Vlera e madhësisë së vargut i ndahetatributit size përmes urdhërit të parë në konstruktor.

Urdhëri i dytë e përdorë operatorin ‘new’ për të deklaruar një varg të numrave të plotë (integer-ave), madhësia e të cilit përcaktohet nga vlera ‘size’ që i përcilletkonstruktorit. Operator i ‘new’ kthen pointerin në varg, i cili i ndahet pointerit tëvlerave (values). Dy urdhërat e fundit në konstruktor, inicializonë atributet frontdhe back, në zero.

Funksioni ~Queue() është destruktori dhe përdorë operatorin ‘delete’ për tëlarguar vargun nga memoria, kur instanca e Queue-s del nga fushëveprimi

(përdorimi).Funksioni isFull() (shih Figurën 3.8) përcakton nëse ka vend në queue (radhë),duke krahasuar vlerën e llogaritur ‘back’ me vlerën e ‘front’-it të queue-s(radhës), si në fig. 3.8. Vëreni se shprehja që llogaritë ‘back’ është shumë engjashme me shprehjen e përdorur në procesin enqueue dhe të dyja prodhojnërezultat të njëjtë. Queue është i mbushur (angl. full), kur indeksi ‘back’ është 1 prapa ‘front’. Vendosja e një elementi tjetër në queue do të mbishkruanteelementin ‘front’ dhe do të korruptonte (prishte) queue-n (radhën e pritjes).Operatori i modulit përdoret përsëri për ta bërë këtë ‘circular queue’ (radhë

Page 169: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 169/699

Algoritmet dhe strukturat e të dhënave

169

rrotulluese, rrethore, qarkore), ashtu që kur jemi në elementin 7 në ‘back’, elementi i ardhshëm në të cilin duhet të shikohet është elementi 0.

 Figura 3.8 - Funksioni isFull() përcakton (kontrollon) nëse ka vend për një

tjetër element në fund (back) të ‘ queue’ -s.

Funksioni isFull() thirret nga funksioni enqueue() para se të tentohet vendosja evlerës në back (fund) të queue-s (radhës). Funksioni isFull() kthen ‘true’ nësenuk ka më vend në queue ose ‘false’ nëse ka vend të lirë, në dispozicion. 

Funksioni isEmpty() përcakton (shiko Figurën 3.9) nëse queue është i zbrazët

duke krahasuar variablat ‘back’ dhe ‘front’. Nëse ato kanë vlera të njëjta,kthehet ‘true’; përndryshe, kthehet ‘false’. Funksioni isEmpty() thirret brendafunksionit dequeue() para se ai të tentojë të largojë elementin front nga queue(radha).

Page 170: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 170/699

Avni Rexhepi

170

 Figura 3.9 - Funksioni isEmpty() përcakton (kontrollon) nëse ‘queue” ka vlera.

Funksioni enqueue() e vendosë një element në fund të radhës. Funksionitenqueue() i përcillet vlera që duhet të vendoset në radhë. Mirëpo, para se të bëhet kjo gjë, thirret funksioni isFull(), për të kontrolluar nëse ka vend në radhë.Vëreni në shembullin vijues se funksioni isFull() thirret si shprehje ekushtëzimit (kusht) në urdhërin ‘if’. Poashtu, vëreni se operatori i negacionit endryshon në të kundërt vlerën bool-ane të kthyer nga isFull(). Do të thotë, nëseka vend të lirë në radhë, nga funksioni isFull() kthehet ‘false’. Shperhja ekushtëzimit në urdhërin ‘if’ e rrotullon logjikën në ‘true’ ashtu që urdhërat tëekzekutohen urdhërat brenda urdhërit ‘if’, për të vendosur elementin e ri në fund

të radhës.Funksioni dequeue() e largon një element nga radha e pritjes dhe kthen (return)atë element në urdhërin e programit i cili e thërret funksionin dequeue().Mirëpo, përbrenda funksionit dequeue(), në shprehjen e kushtëzimit të urdhërit‘if’, thirret funksioni isEmpty(), si në kodin vijues. 

Operatori i negacionit (!-not) e rrotullon logjikën e kthyer nga funksioniisEmpty(). Funksioni isEmpty() kthen ‘false’ nëse radha nuk është e zbrazët.Operatori e kthen atë në ‘true’, duke u mundësuar urdhërave brenda kushtit ‘if’

Page 171: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 171/699

Algoritmet dhe strukturat e të dhënave

171

që të largojnë elementin ‘front’ nga radha dhe t’ia kthejnë atë urdhërit që ethërret 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;

}

Page 172: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 172/699

Avni Rexhepi

172

}int Queue::dequeue(){

if(!isEmpty())

{front = (front+1) % size;return queue[front];

}return 0;

}

Programi queueProgram.cpp është vendi ku ndodhin veprimet. Këtu deklarohetdhe manipulohet instanca e klasës Queue. Si mund të shihet në shembullinvijues, urdhëri i parë i programit përdorë operatorin ‘new’ për të deklaruar

instancën e klasës Queue dhe për të caktuar madhësinë (size) në 8 elemente.Operatori ‘new’ kthen një pointer që i ndahet një pointeri në instancë të klasësQueue.

Tre urdhërat vijues e thërrasin tri herë funksionin enqueue(), për të vendosurvlerat 10, 20 dhe 30 në queue, respektivisht. Programi përfundon me thirrjen efunksionit dequeue() tri herë, për të paraqitur përmbajtjen e queue-s (radhës së pritjes). Figura 3.10 paraqet queue-n (radhën) dhe vargun, pas thirrjes së fundittë funksionit enqueue().

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

Page 173: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 173/699

Algoritmet dhe strukturat e të dhënave

173

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

}

Page 174: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 174/699

Avni Rexhepi

174

4. Listat e lidhura

Lista e lidhur është një listë e elementeve që pointojnë të dhënat aktuale, paraprake dhe të ardhshme. Lista mund të jetë e lidhur njëfish (me pointerët

vetëm për elementin e ardhshëm) dhe e lidhur dyfish (me dy pointerë, njëri përelementin paraprak dhe tjetri për të ardhshmin).

Lista e lidhur është strukturë e të dhënave që e bënë të lehtë rirregullimin(rirenditjen, korrigjimin) e të dhënave, pa pasur nevojë lëvizjen e të dhënave nëmemorie. Edhe pse tingëllon çuditshëm, merrni parasysh rastin e një klase mestudentë, të cilët janë ulur sipas një renditjeje të caktuar. Një numër unikidentifikon secilën ulëse, si në figurën 4.1. Janë përfshirë edhe të dhënat përgjatësinë relative të secilit student, të cilat do të përdoren në shembullin vijues.

Figura 4.1: Studentët janë ulur në renditje të rastit

Le të themi se profesori duhet të vendosë emrat e studentëve në renditjealfabetike, ashtu që të mund t’i gjejë më lehtë emrat në listë. Një opcion është qëstudentët të ndryshojnë vendet dhe të ulen me radhë sipas alfabetit. Mirëpo, kjomund të jetë situatë problematike, nësë është në pyetje një numër i madh istudentëve në një klasë.

 Një opcion tjetër është që studentët të mbesin të ulur në vendet e tyre, por tëkrijohet një listë e numrave të ulëseve, të cilat i korrespodojnë renditjesalfabetike të studentëve. Lista do të dukej diçka si: 3, 1, dhe 2 (ashtu si është paraqitur në fig. 4.1). Studenti në ulësen 3, është i pari në listën alfabetike, i pasuar nga studenti në ulësen 1, e kështu me radhë. Vëreni se ky opsion nukshkaktonë shqetësime për klasën.

Supozojmë se dëshirojmë të rirendisim studentët, sipas madhësisë (gjatësisë).

Përsëri, kemi mundësi që të mos i lëvizim studentët nëpër klasë, mirëpo do të

Page 175: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 175/699

Algoritmet dhe strukturat e të dhënave

175

krijojmë një listë tjetër të numrave të ulëseve të cilat reflektojnë gjatsinë e secilitstudent. Tani lista është: 2, 3 dhe 1 (fig. 4.1). Lista mund të lexohet prej funditkah fillimi, për renditjen prej gjatësisë më të vogël kah ajo më e madhe dheanasjelltas, prej të gjatës kah e shkurta.

Kur të krijohet një herë lista, profesori mundet thjeshtë të kalojë me radhë nëpërlistë, për të parë se cila ulëse e ka studentin e ardhshëm. Për t’i pyetur studentëtnë bazë të renditjes alfabetike, profesori do të përdorte listën alfabetike, për të parë që studenti në ulësen 3 është i pari, i pasuar nga ai në ulësen 1. Mund tëfillohet edhe nga ana e kundërt dhe pastaj të kërkohet 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ë ardhshëm. Kjo do të thotë që,ulësja e studentit aktual është e lidhur me ulësen e studentit të përparshëm dhe

atë të atij të ardhshëm, në listë.Përderi sa mësohen listat e lidhura, është me rëndësi të kihet parasyshë situatanë botën reale, sepse përndryshe mund të fitohet ideja se lista e lidhur është njëkoncept abstrakt që ka pak përdorim në botën reale. Në fakt, listat e lidhuraluajnë rol kritik në aplikacionet të cilat u mundësojnë shumë kompanive dheqeverive, menagjimin dinamik të të dhënave.

Kemi dy versione të listave të lidhura: lista e lidhur njëfish (angl. single link, osesingly linked list) dhe lista e lidhur dyfish (angl. doble link, ose doubly linkedlist). Lista e lidhur njëfish i mundëson programit që të lëvizë nëpër listë në njëdrejtim (kahje), i cili zakonisht është prej fillimit të listës, kah fundi i listës, aposi thuhet ndryshe, lëvizje para. Lista e lidhur dyfish, i mundëson programit që tëlëvizë nëpër listë në të dy drejtimet (kahjet), apo si thuhet ndryshe, lëvizje paradhe prapa.

Edhe pse është thënë që një ‘hyrje’ (element, nyje), në listën e lidhur përmbanëtë dhënën (vlerën) dhe pointerët për në elementin e përparshëm dhe atë tëardhshëm në listë, ky është një thjeshtim i tepruar. Të dhënat për të cilat e kemifjalën, zakonisht janë një bashkësi e të dhënave, si p.sh, të dhënat e klientit. Tëdhënat e klientit mund të jenë: ID e klientit, emri, mbiemri, adresa, qyteti, shteti,kodi postal, etj. Programerët e quajnë këtë ‘record’ (rekord). Kjo do të thotë senjë element në listë, mund të përmbajë disa të dhëna. Në shembullin në vijim,sidoqoftë, do të ruhet vetëm një vlerë numerike e tipit integer, ashtu që tëfokusohemi në principet e punës së listave të lidhura. Në realitet, për secilënnyje në listë mund të shtohen atribute shtesë sipas dëshirës ose sipas nevojës.

Programerët e zgjedhin listën e lidhur ndaj vargut, sepse lista e lidhur mund tëzgjerohet (rritet, zgjatet) ose ngushtohet (zvogëlohet, shkurtohet) për ngamadhësia gjatë kohës së ekzekutimit. Një element i ri mund të vendoset në fund

të listës së lidhur thjeshtë duke ia ndarë (caktuar) një referencë (pointer) për në

Page 176: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 176/699

Avni Rexhepi

176

elementin e ri në fund të listës së lidhur. Edhe për të shtuar nyje të reja (anëtarëtë rij, vlera të reja) ndërmjet nyjeve ekzistuese, mjafton vetëm të përshtaten pointerët përkatës, për të krijuar vendin për anëtarin e ri në listë.

 Ngjashëm, elementi në cilëndo poiztë në listë apo edhe i fundit në listë, mund tëlargohet nga lista e lidhur, thjeshtë duke e larguar referencën (poinerin) për nëelementin e ardhshëm nga elementi i parafundit në listën e lidhur.

Kjo është shumë më efikase sesa përdorimi i vargut dhe ndryshimi i madhësisësë vargut gjatë kohës së ekzekutimit. Kjo për arsye se, nëse duhet të ndryshohetmadhësia e vargut, sistemi operativ tenton që të rrisë vargun duke përdorurlokacionet e memories në vazhdim të vargut. Nëse kjo nuk është e mundur(lokacionet në vazhdim nuk janë në dispozicion), atëherë sistemi operativ egjenë një lokacion tjetër, në pjesën tjetër të memories, me madhësi tëmjaftueshme për të mbajtur elementet e vargut dhe elementet e reja të vargut.

Page 177: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 177/699

Algoritmet dhe strukturat e të dhënave

177

Atëherë, elementet e vargu, kopjohen në lokacionin e ri. Ky proces ka koston evetë.

 Nëse ndryshohet madhësia e listës së lidhur, sistemi operativ vetëm ndryshon

referencat (pointerët) për elementin e përparshëm dhe atë të ardhshëm në listë,që është numër shumë më i vogël i hapave sesa ndryshimi i madhësisë së vargut.

Struktura e listës së lidhur

Secili element në listën e lidhur quhet ‘node’ (angl. node –  nyje). Paramendojeninyjën si një element me tri nënelemente. Njëri përmbanë vlerën, e cila mund të jetë një atribut ose shumë atribute. Tjetri pointon në nyjen e përparshme dhe ifundit pointon në nyjen e ardhshme. Kur vendoset një element i ri në listën e

lidhur, alokohet nyja e re dhe caktohen pointerët për nyjen e përparshme dhe atëtë ardhshme.

 Në C++ nyja krijohet duke përdorur objektin strukturë (struct) ose klasë (class). Në shembullin në vazhdim, për krijimin e nyjes do të përdoret struktura, e cila sidihet është tip i të dhënave i definuar prej shfrytëzuesit. Nyja është paraqitur nëfigurën 4.2.

 Figura 4.2 - Nyja përmbanë pointerin për nyjen e përparshme dhe pointerin për

në nyjen e ardhhsme në listën e lidhur, si dhe përmban të dhënat e shoqëruara

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ë këtë shembull, struktura në dy prejatributeve, ka pointerin në vetë strukturën. Elementi i parë deklaron një integer

Page 178: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 178/699

Avni Rexhepi

178

që ruan vlerën aktuale të nyjes (data, vlera). Dy urdhërat tjerë deklarojnë pointerët për në nyjen e përparshme dhe në atë të ardhshme, në listën e lidhur.

Struktura e cila përmbanë komponente që është pointer i tipit të njëjtë me vetë

strukturën quhet  strukturë vetë-referente (ang. self-referential structure). Nëse për definimin e nyjeve shfrytëzohen klasat, atëherë bëhet fjalë për klasa vetë-

referente (ang. self-referencial classes).

Konstruktori inicializon elementet e nyjes kur të krijohet instanca e nyjes. Kjofunksionon në mënyrë të ngjashme me konstruktorin e klasës. Si do të shihet mëvonë, vlera aktuale i sigurohet strukturës kur të krijohet nyja e re. Kjo vlerë indahet të dhënës në listën e argumenteve. Vlera e të dhënës pastaj i ndahetelementit të instancës së strukturës. Gjithashtu, pointeri për në nyjen e përparshme dhe atë të ardhshme fillimisht inicializohet në NULL, gjë që i tregon

 programit se nuk ka elemente të tjera në listën e lidhur. NULL zëvendësohet me pointerët për në nyje kur të shtohet nyja e re në listën e lidhur.

Lista e lidhur njëfish ndaj listës së lidhur dyfish

Lista e lidhur dyfish quhet edhe bidireksionale (angl. bidirectional-dydrejtimëshe, figura 4.3), sepse secila nyje përmbanë pointer në nyjen e përparshme dhe të ardhshme në listën e lidhur. Kjo i mundëson programerëe qëtë përshkojnë listën e lidhur në të dy drejtimet duke ju referuar nyjes së përparshme dhe asaj të ardhshme. Lista mund të transformohet në listë të lidhur

njëfish (Fig. 4.3) duke pasur vetëm një pointer në strukturë, që përmbanëadresën e nyjes së ardhshme. Në mënyrë tipike, nyja e listës së lidhur njëfish ireferohet vetëm nyjes së ardhshme, e jo edhe asaj të përparshme, edhe pse asgjënuk e ndalon që të krijohet vetëm referenca prapa, duke përdorur vetëm pointerin për nyjen e përparshme.

Page 179: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 179/699

Algoritmet dhe strukturat e të dhënave

179

Ose:

 Figura 4.3 - Listat e lidhura. Lista e lidhur dyfish përmbanë dy pointera,

ndërsa ajo e lidhur njëfish vetëm një pointer, për në nyjen e ardhshme.

Deklarimi vijues është afërsisht i njëjtë me të përparshmin, përveq se nyja është

e lidhur vetëm në një drejtim, sepse mungon pointeri në nyjen e përparshme.Kjo do të thotë se mund të lëvizet vetëm “teposhtë” listës së lidhur, e jo në të dydrejtimet.

struct Node{int data;Node* next;};

struct Nyje{

int vlera;Nyje* eArdhshme;

};

Për shembull, për krijimin e një nyjeje të vetme, mund të veprojmë si vijon:

Page 180: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 180/699

Avni Rexhepi

180

#include "stdafx.h" #include <iostream> using namespace std;struct nyje 

{ int vlera;nyje *eArdhshme;

};

int main(){nyje *n; //Pointer në strukturën nyjen=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 tjetër 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;}

 Nëse e konsiderojmë një nyje tëvetme si listë me vetëm një anëtarë,

 pasi që për listën zakonishtdefinohen edhe dy pointerë të cilët e përcjellin fillimin dhe fundin e listës,do të kishim situatën kur të dy pointerët pointojnë në nyjen e njëjtë(të vetme), si në vijim:

10

● eArdhshme

fillimi

fundi

 Nëse insertojmë edhe një nyje të re, me vlerën 20, do të kemi:

fundi (eFundit)

10

fillimi (ePara)

20

● eArdhshme

 

Page 181: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 181/699

Algoritmet dhe strukturat e të dhënave

181

Pjesa e kodit për krijimin e dy nyjeve do të ishte si vijon:

...nyje *n,*ePara=NULL,*eFundit=NULL;n=new nyje; //krijojmë nyjen e re (të parën) 

n->vlera=10; //tek nyja e parë, vlera = 10 n->eArdhshme=NULL; //pointeri për në nyjen e ardhshme (NULL) 

ePara=n; //Pointer i nyjes së parë të listës=nyja e parë eFundit=n; // Pointer i nyjes së fundit të listës=nyja e parë n=new nyje; // Shtojmë një nyje të re (të dytën) eFundit->eArdhshme=n; //nyja e ardhshme e të parës=nyja e re 

n->vlera=20; //vlera = 20 n->eArdhshme=NULL; //pointeri për në nyjen e ardhshme (NULL) 

eFundit=n; //Fundi i listës bëhet nyja e re (e dyta)//Pointer i nyjes së fundit të listës=nyja e re (e dytë) 

...

 Në mënyrë të ngjashme, mund të shtojmë nyje të reja, duke kërkuar hapësirë tëre nga memoria, me operatorin ‘neë’ dhe duke marrë pointerin ‘n’ për atëlokacion të memories. Pasi të shtohet nyja e re, vetëm ndryshojmë pointerët, qëtë pointojnë në nyjet adekuate.

Për të shtypur anëtarët e listës, fillojmë nga nyja e parë dhe pastaj përmes pointerëve, kalojmë me radhë nëpër nyjet tjera. Zakonisht, këtë e bëjmë përmesnjë funksioni, i cili e ka argument (parametër) adresën e nyjes së parë dhe pastaj

vazhdon prej nyjes në nyje, deri në fund të listës (duke verifikuar a është pointeri për nyjen e ardhshme i ndryshëm prej 0, gjegjësisht NULL, (n!=0) ).Deri sa vlenë kushti, që pointeri për nyjen e ardhshme nuk është null, do të thotëkemi nyjet tjetër 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;

}...

Për të krijuar më shumë nyje, mund të përdorim unazë si në shembullin vijues:

Page 182: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 182/699

Avni Rexhepi

182

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++) //për çdo ‘i’ nga 1 deri në ‘k’ {

n=new nyje; //krijojmë nyje të re (pointeri n për nyjen e re) cout << "Vlera ne nyjen e " << i << ": ";cin >> x; //lexojmë vlerën x n->vlera=x; //nyjes aktuale ia ndajmë vlerën e x-it n->eArdhshme=NULL; //nyjes së aktuale, ia caktojmë

//pointerin për në nyjen e ardhshme NULL if (ePara==NULL) //nëse akoma nuk është filluar/krijuar lista ePara=n; //kjo është nyja e parë e listës 

else eFundit->eArdhshme=n; // eArdhshme, pointon në nyjen e re ‘n’ eFundit=n; //nyje e fundit e listës bëhet nyja e re që u shtua 

}cout<<endl;cout << "Fillimi: " << ePara << "\n"; //Fillimi i listëscout << "Fundi : " << eFundit<< "\n"; //Fundi i listës cout << "\n\nPermbajtja e listes:";

cout << "\n\n Adresa Vlera Adresa e Ardhshme\n";

Page 183: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 183/699

Algoritmet dhe strukturat e të dhënave

183

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, për menagjimin e listës së lidhur, në C++ krijohet klasa LinkedList.Definicioni i klasës LinkedList përbëhet prej dy anëtarëve të të dhënave dhegjashtë funksioneve anëtare, si është paraqitur në shembullin në vijim. Dyanëtarët e të dhënave janë pointerë për në instancat e strukturës Node (Nyja) ecila u definua më herët. Pointeri i parë, front (fillimi) i referohet nyjes së parë nëlistën e lidhur. Pointeri i dytë, back (fundi), i referohet nyjes së fundit në listën elidhur.

Gjashtë funksionet shërbejnë për manipulimin e listës së lidhur. Funksioni i parëështë konstruktori i klasës LinkedList dhe thirret kur të deklarohet instanca eklasës. Pas konstruktorit është destruktori. Për t’ia kthyer memorien sistemitoperativ, përmes përdorimit të operatorit ‘delete’, thirret destruktori. Nëse nukthirret operatori ‘delete’, atëherë destruktori nuk thirret asnjëherë dheaplikacioni shkakton rrjedhje të memories (memory leak).

Funksioni appendNode() (angl. append –  shto, bashkangjit) e vendosë nyjen e renë fund të listës së lidhur. Funksioni appendNode() kërkon një integer që

 përfaqëson vlerën aktuale të nyjes (sepse në këtë shembull, thamë që elementi ilistës së lidhur është një numër i plotë, integer).

Dy funksionet e ardhshme paraqesin përmbajtjen e listës së lidhur. FunksionidisplayNodes() paraqet listën e lidhur në renditjen natyrale (prej fillimit kahfundi). (angl. display  –   paraqes, shfaqë). Funksioni displayNodesReverse() paraqet listën e lidhur në renditje të kundërt.

Funksioni i fundit është destroyList() (angl. destroy-shkatërro, rrëno, asgjëso)dhe thirret për të larguar instancën e listës së lidhur nga memoria.

Page 184: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 184/699

Avni Rexhepi

184

Specifikacioni i klasës LinkedList është definuar në ‘header file’ dheimplementimi i saj është i definuar në fajllin e kodit burimor. Implementimi ifunksioneve anëtare të klasës do të paraqitet në vazhdim.

//front=fillimi; back=fundiclass LinkedList{

private:Node* front;Node* back;

public:LinkedList();~LinkedList();void appendNode(int); //appendNode=shto nyje

void displayNodes(); //displayNodes=paraqitiNyjetvoid displayNodesReverse(); //ParaqitiReversvoid destroyList(); //destroyList=asgjesoListen

};

Konstruktori dhe destruktori i klasës LinkedList

Konstruktori LinkedList është funksioni i cili thirret (ekzekutohet automatikisht)kur deklarohet ndonjë instancë e klasës LinkedList. Qëllimi i konstruktorit ështëqë të inicializojë pointerët ‘front’ dhe ‘back’, si është treguar në definicionin

vijues. Të dy pointerëve u ndahet velra NULL, e cila përdoret nga funksioniappendNode() për të kontrolluar (përcaktuar) nëse lista e lidhur është e zbrazët.Më vonë do të shohim se si bëhet kjo.

LinkedList(){

front = NULL;back = NULL;

}

Destruktori është funksioni që thirret kur të fshihet instanca e klasës, përmes

 përdorimit të operatorit ‘delete’. Në shembullin në vijim, destruktori përmbanënjë urdhër i cili e thërret funksionin destroyList().

Funksioni destroyList() e fshinë përmbajtjen e listës së lidhur por nuk e fshinëvetë listën e lidhur (strukturën e saj). Do të thotë, i largon të gjitha nyjet nga listae lidhur. Funksioni destroyList() i reseton (rivendosë) pointerët ‘front’ dhe‘back’ në NULL, duke treguar se lista është e zbrazët. Destruktori është përgjegjës për dealokimin e memories që është alokuar për listën e lidhur. Nëkëtë rast, të gjitha nyjet.

Page 185: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 185/699

Algoritmet dhe strukturat e të dhënave

185

Mund të pyesni veten, e përse definohen dy funksione për kryerjen e parimishttë njëjtës punë? Kjo për arsye që të mundesohet zbrazja e listës ashtu që tëresetohet përmbajtja e listës së lidhur pa e shkatërruar instancën e klasësLinkedList.

~LinkedList(){

destroyList();}

Shtimi i nyjes në listë

Funksioni appendNode() e vendosë një nyje të re në fund të listës së lidhr. Janëdisa hapa që duhet kryer për të shtuar nyjen në listë. Këta 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() kërkon një argument, të quajtur data, i cili është vlera

aktuale për nyjen. Argumenti i përcillet instancës së strukturës Node. Si jukujtohet nga pjesa paraprake, vlera e përcjellur në strukturën Node i ndahetelementit të të dhënave (data) të nyjes.

Urdhëri i parë në funksionin appendNode(), deklaron një instancë të strukturës Node duke përdorur operatorin ‘new’, i cili kthen pointerin në instancë, i cili nëanën tjetër i ndahet variablës pointer të quajtur ‘n’. 

Kur të krijohet nyja e re, funksioni appendNode() e pozicionon nyjen e re nëlistën e lidhur. Së pari, ai e kontrollon nëse lista e lidhur është e zbrazët, dukekrahasuar nyjen ‘back’ me NULL. Kjo pasi nyja ‘back’ caktohet në NULL kur

Page 186: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 186/699

Avni Rexhepi

186

të deklarohet instanca e klasës LinkedList dhe kur funksioni destroyList() ilargon të gjitha nyjet nga lista (e zbrazë listën).

 Nëse lista e lidhur ësthë e zbrazët, atëherë 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 përmbanë një nyje, që është nyja e re.

Sidoqoftë, nëse në listën e lidhur ndodhet së paku një nyje, atëherë duhet të bëhet një zhvendosje (shiftim; angl. shift-zhvendosë, lëvizë) e pointerëve.Urdhëri ‘else’ përmbanë tre urdhëra, të cilët e bëjnë zhvendosjen (shiftimin).Urdhëri i parë ia ndanë pointerin në nyjen e re, pointerit ‘next’ të nyjes së funditnë listën e lidhur. Pastaj pointeri ‘back’ i ndahet pointerit ‘previous” (i përparshëm) të nyjes së re. Në fund, nyja e re i ndahet pointerit ‘back’, duke e bërë nyjen e re si nyje të parë në listën e lidhur.

Kjo mund të duket pak konfuze, por shikoni figurën 4.4, në të cilën paraqitennyjet e listës së lidhur. Supozoni se lista e lidhur ka dy nyje para vendosjes sënyjes së re në listë. Kjo është paraqitur në bllokun e epërm.

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

listën e lidhur

Page 187: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 187/699

Algoritmet dhe strukturat e të dhënave

187

Hapi i parë ia ndanë adresën e memories së nyjes së re anëtarit ‘next’ të nyjes‘back’, i cili është paraqitur në bllokun e dytë të memories në figurën 4.4.

Hapi i dytë, i ndanë adresën e memories së nyjes ‘back anëtarit ‘previuous’ (i përparshmi) të nyjes së re (new). Kjo i lidhë të dy nyjet.

Hapi i tretë, e zëvendëson adresën e memories së nyjes ‘back’ në listën e lidhurme adresën e memories së nyjes së re (new). Kjo e vendosë nyjën e re në nëfund të listës së lidhur.

Paraqitja e listës së lidhur

Funksioni displayNodes(), paraqet secilën nyje të listës së lidhur, duke filluarme nyjen në fillim të listës së lidhur dhe duke përfunduar me nyjen në fund të

listës, 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 fjalës “Nyjet:” në ekran dhe pastaj deklaron pointerin për në nyje, i cili inicializohet me nyjen që paraqitet nëfillim (front) të listës së lidhur.

Para se të tentoj të paraqes vlerën që i është ndarë nyjes, funksionidisplayNodes() kontrollon nëse ka nyje në fund (back) të listës së lidhur. E bënkëtë, duke përcaktuar nëse nyja e pointuar nga pointeri ‘temp’ është NULL. Nësë po, lista e lidhur është e zbrazët dhe aty nuk ka asgjë për të shfaqur. Nëse

 jo, funksioni vazhdon dhe paraqet të dhënat e ndara për nyjen që ndodhet nëfund të listës së lidhur.

Pastaj paraqitet një zbrazëtir, e përcjellur me vlerën (të dhënën) që i është ndarënyjes. Funksioni displayNode() përdorë anëtarin ‘next’ të nyjes për t’ia ndarë pointerin në nyjen e ardhshme pointerit ‘temp’.

Ky proces përfundon pasi të paraqitet nyja në fund të listës së lidhur, pasi qëanëtari ‘next’ i saj është NULL. 

Page 188: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 188/699

Avni Rexhepi

188

“Rrotullimi” i listës së lidhur

Funksioni displayNodesReverse() paraqet përmbajtjen e listës së lidhur nërenditje të kundërt, duke filluar nga nyja në fund të listës së lidhur dhe duke

vazhduar deri sa të paraqitet nyja e parë. Shembulli vijues tregon se si realizohetkjo:

void displayNodesReverse(){

cout << "Nyjet në renditje të kundert:";Node* temp = back;while(temp != NULL){

cout << " " << temp->data;temp = temp->previous;

}}

Mund të vërehet se funksioni dispalyNodesReverse() është afërsisht i njëjtë mefunksionin displayNodes(), të përshkruar më herët. Mirëpo, janë dy dallime tërëndësishme ndërmjet këtyre dy funksioneve anëtare. FunksionidisplayNodesReverese() ia ndanë pointerin në nyjen në fund të listës, pointerittemp, duke bërë që nyja në fund të listës të paraqitet e para. FunksionidisplayNodes() e cakton pointerin ‘back’ në pointerin ‘temp’, duke shkaktuar paraqitjen e nyjes së fundit në listën e lidhur.

Dallimi tjetër ndërmjet funksioneve displayNodesReverse() dhe atijdisplayNodes() është se në funksionin displayNodesReveres(), anëtari i përparshëm (previuous) i nyjes përdoret për të përcaktuar nyjen e ardhshme përt’u paraqitur. Kjo mundëson paraqitjen e nyjeve në renditje të kundërt (inverse).Figura 4.5 ilustron se si “rrotullohet” lista e lidhur. 

 Figura 4.5 - Anëtari i përparshëm (‘previuos’) i secilës nyje, e rrotullon listën e

lidhur.

Page 189: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 189/699

Algoritmet dhe strukturat e të dhënave

189

Asgjësimi i listës së lidhur

Funksioni destroyList() i largon nyjet nga lista e lidhur pa e larguar vetë listën elidhur, si në shembullin vijues. Secila nyje deklarohet në mënyrë dinamike duke

 përdorur operatorin ‘new’, si është treguar më herët. Kjo mundëson largimin enyjes përmes përdorimit 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 përkohshëm (‘temp’),të cilit i ndahet pointerin në nyjen e cila ndodhet në fund (‘back’) të listës sëlidhur. Mirëpo, para se nyja të largohet, funksioni e përcakton (kontrollon) nëseka nyje në fund të listës së lidhur, duke testuar nëse pointeri ‘temp’ është NULL. Nëse është kështu, atëherë funksioni destroyList() supozon se nuk ka nyje në

listën e lidhur. Nëse pointeri ‘temp’ nuk është NULL, atëherë funksioni vazhdonme fshirjen e nyjes.

Deklarohet një tjetër nyje e përkohshme (temp2) dhe i ndahet pointeri në nyjen e pointuar nga nyja e përkohshme, ‘temp’. Kjo bëhet sepse pointerit ‘temp’ icaktohet (ndahet) nyja tjetër (‘next’) që duhet të fshihet nga lista e lidhur nëurdhërin e ardhshem.

Pointeri për në nyjen tjetër (‘next’) ndodhet në anëtarin tjetër (‘next’) të nyjes së përkohshme (‘temp’), e cila pastaj i ndahet pointerit ‘temp’. Kjo do të thotë se

temp2 pointon në nyjen në fund të listës së lidhur dhe ‘temp’ tani pointon nënyjen që ndodhet menjëherë para (‘previous’) nyjes në fund (‘back’) të listës sëlidhur. Atëherë nyja e pointuar nga ‘temp2’ fshihet, siç është ilustruar nëFigurën 4.6

Page 190: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 190/699

Avni Rexhepi

190

 Figura 4.6 - Funksioni destroyList() largon nyjet duke filluar me nyjen e fundit

në listën e lidhur dhe kryen punën e tij deri në fillim të listës së lidhur.

Procesi vazhdon deri sa të gjitha nyjet të largohen nga lista e lidhur. Hapi ifundit në funksionin destroyList() është që t’i ndahet vlera NULL pointerëve nëfillim (‘front’) dhe fund (‘back’) të listës, gjë që tregon (indikon) se lista e lidhurështë e zbrazët.

Listat e lidhura në C++Tani që i njohim pjesët e listës së lidhur dhe mënyrën e krijimit dhe manipulimittë saj përmes përdorimit të klasës, do të bashkojmë të gjitha pjesët në njëaplikacion të C++-it i cili përdorë listën e lidhur.

Lista e lidhur organizohet në tre fajlla. I pari është ‘header’ fajlli i cili përmbanëdefinicionin e struktorës së nyjes (‘ Node’) dhe definicionin e klasës LinkedList.Fajlli i dytë është kodi burimor i cili përmbanë implementimin e funksioneve tëklasës LinkedList. Fajlli i fundit është fajlli i aplikacionit i cili përmbanë kodinqë krijon dhe përdorë klasën LinkedList.

Page 191: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 191/699

Algoritmet dhe strukturat e të dhënave

191

Le të fillojmë me ‘header’ fajllin LinkedList.h. Ky fajll përmbanë dykomponente, definicionin e strukturës ‘ Node’ dhe definicionin e klasësLinkedList, të cilat programerët i quajnë specifikacioni i klaësë.

Do të vëreni se të dy komponentet janë diskutuar në detaje në pjesën paraprake.Gjithashtu do të shihni se definicioni i klasës LinkedList nuk përmbanëimplementimin e funksioneve anëtare. Në vend të kësaj, përmbanë prototipet efunksioneve anëtare të cilat janë implementuar në fajllin e kodit burimor.Mbajtja e specifikacioneve dhe implementimeve në fajlla të ndarë, të headerfajllit dhe atij burimor, është praktikë e zakonshme. Pjesëve të programit të cilat përdorin klasën, ju interesojnë vetëm funksionet interfejs, të definuara në headerfajll; atyre nuk ju intereson implementimi. Kjo mundëson që ju të parakompajloni kodin tuaj burimor në module të librarive ashtu që shfrytëzuesite kësaj klase të kenë nevojë vetëm për header-ët dhe modulet.

//LinkedList.h// Node=Nyje; data=vlera;// previous=ePerparshme; next=eArdhshmestruct 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 anëtare të klasës LinkedList janë të përmbajtura nëfajllin LinkedList.cpp, si është treguar në kodin në vijim. Fajlli fillon meurdhërin pr eporcesorik (#include) që i tregon preprocesorit që t’i referohet përmbajtjes së fajllit LinkedList.h gjatë paraprocesimit. Fajlli LinkedList.h përmbanë definicionin e klasës LinkedList dhe definicionin e strukturës Node, të

Page 192: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 192/699

Avni Rexhepi

192

cilat kërkohen për të zbërthyer urdhërat në LinkedList.cpp, të cilët i referohenklasës dhe nyjes.

Definicioni i secilit funksion në këtë shembull, praktikisht është i njëjtë me ato

të diskutuara më herët. Përjashtimi i vetëm është se referenca është bërë nëklasën LinkedList në emrin e definicionit të secilit funksion. Kjo e shoqëronsecilin definicion me klasën LinkedList për 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;

}}

Page 193: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 193/699

Algoritmet dhe strukturat e të dhënave

193

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

 përdorë listën e lidhur. Aplikacioni është shumë i shkurtër në krahasim me kodine përdorur për të definuar strukturën Node dhe klasën 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 instancës së klasës LinkedList. Si ju kujtohet prej pjesës më herët, konstruktori i inicializon pointerët ‘front’ dhe ‘back’. 

Instanca deklarohet duke përdorur operatorin ‘new’. Operatori  ‘new’ ktehn pointerin për në lokacionin e memories së instancës. I njëjti urdhër deklaron

Page 194: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 194/699

Avni Rexhepi

194

 pointerin që i referohet listës së lidhur (LinkedList). Pointeri emërtohet ‘list’ dhecaktohet për të ju referuar instancës së klasës LinkedList.

Pastaj, thirret tri herë funksioni appendNode(), i cili e shton nyjen e re në fund të

listës së lidhur dhe ia ndanë vlerën e përcjellur funksionit appendNode() anëtarittë të dhënës së nyjës (vlerës së nyjës).

Dy urdhërat e fundit në këtë shembull, paraqesin vlerën e secilës nyje në listën elidhur. Së pari, funksioni displayNodes() thirret për të paraqitur nyjet nërenditjen natyrale, duke filluar nga fillimi i listës së lidhur dhe duke përfunduarnë nyjen e fundit (back) të listës së lidhur.

Pastaj, funksioni displayNodesReverese() thirret për të bërë paraqitjen eanëtarëve të listës në renditje të kundërt, duke filluar nga nyja e fundit dhe duke përfunduar me nyjen e parë.

 Në fund, operatori ‘delete’ thirret për të fshirë instancat e klasës LinkedList, ngamemoria.

Rezultati i kodit të shembullit të paraqitur, është:

102030

302010

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

Lista e lidhur është strukturë dinamike e të dhënave shumë e rëndësishme.Parimisht, ka dy lloje të listave të lidhura: singly-linked list  (lista e lidhur

njëfish, vetëm në një kah/anë) dhe doubly-linked list  (lista e lidhur dyfish). Në listën e lidhur njëfish, secili element përmbanë një të dhënë (vlerë) dhelidhjen për tek elementi tjetër (i ardhshëm), e cila mundëson mbajtjen estrukturës. Në anën tjetër, secila nyje në listën e lidhur dyfish, vlerën, lidhjentek elementi i ardhshëm dhe lidhjen tek elementi i përparshëm. Lista e lidhurmund të jetë strukturë e të dhënave në të cilën bazohet zbatimi i stack-ut,radhës së pritjes (angl. queue) ose listës së sortuar.

Page 195: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 195/699

Algoritmet dhe strukturat e të dhënave

195

Shembull:

Lista njëfish e lidhur mund të paraqitet si vijon:

Secila qelulë (angl. cell) quhet nyje  (angl. node) e listës së lidhur njëfish. Nyja e parë quhet “kokë” (angl. head) dhe është nyje e dedikuar. Duke e diturkokën, mund të sigurohet qasja në secilën nyje tjetër në listë. Ndonjëherë,nyja e fundit, e quajtur“bisht” (angl. tail), gjithashtu ruhet, në mënrë që tëshpejtohet operacioni i shtimit/insertimit.

Opearcionet (Veprimet) në listën e lidhur njëfish

Implementimi konkret i operacioneve në listën e lidhur njëfish varet ngaqëllimi për të cilin përdoren.

Lista e lidhur njëfish, reprezentimi i brendshëm

Secila nyje e listës së lidhur njëfish përmbanë informacionet vijuese:

  vlera (e dhëna e shfrytëzuesit);  lidhja për në elementin e ardhshëm (e dhënë ndihmëse)

Skematikisht, mund të paraqitet si në vijim:

 Nyja e parë quhet koka (angl. head) dhe nuk ka ndonjë nyje tjetër që “pointon”(tregon me pointer) në të. Lidhja për tek koka zakonisht ruhet në klasën e cilaofron interfejsin për në strukturën rezultuese të të dhënave. Për listën e zbrazët,koka vendoset në “NULL”. 

Page 196: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 196/699

Avni Rexhepi

196

Poashtu, ka kuptim që të ruhet lidhja për tek nyja e fundit, e quajtur “bishti”(angl. tail). Edhe pse asnjë nyje nuk mund të qaset prej bishtit (sepse në listën elidhur njëfish mund të lëvizim vetëm përpara), ajo mund të shpejtojëoperacionin e insertimit (shtimit), kur të shtohet një nyje e re në fund të listës.Kur lista është e madhe, ajo e zvogëlon kompleksitetin e operacionit të shtimitnë mënyrë domethënëse, gjersa mbingarkimi i memories është i parëndësishëm. Në figurën vijuese, mund të shihet një reprezentimi i brendshëm i plotë i listëssë lidhur njëfish.

Pjesë kodi

Zakonisht, struktura e plotë e listës njëfish të lidhur vendoset në dy klasa. Klasa

kryesore,  SinglyLinkedList   është interfejs publik dhe SinglyLinkedListNode “mjet” për përdorim privat brenda klasës kryesore. Pasi qëSinglyLinkedListNode  është klasë ndihmëse, nuk është e nevojshme që tëenkapsulohen fushat e saj (të bëhen private). Vëreni që klasa interfejsSinglyLinkedList   mund të zëvendësohet nga një klasë tjetër, si klasa Stack ,gjersa implementimi i brendshëm i stekut mbetet listë njëfish 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;

}

}; 

Page 197: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 197/699

Algoritmet dhe strukturat e të dhënave

197

//SinglyLinkedList=ListaeLidhurNjefish class SinglyLinkedList {private:

SinglyLinkedListNode *head;SinglyLinkedListNode *tail;

public:SinglyLinkedList() {

head = NULL;tail = NULL;

}}

Përshkimi i listës së lidhur njëfish

Supozoni se kemi një listë me disa nyje. Përshkimi (angl. traversal  –  përshkimi, bredhja, kalimi me radhë) është operacion bazik, i cili paraqitet si pjesë e pothuajse çdo operacioni (veprimi) në listën e lidhur njëfish. Për shembull,algoritmi mund të përshkojë listën e lidhur njëfish për të gjetur një vlerë, për tëgjetur pozitën për insertim, etj. Për listë të lidhur njëfish, është i mundur vetëm përshkimi përpara.

Algoritmi i përshkimitDuke filluar nga fillimi (koka),

1.  verifiko, nëse nuk është arritur akoma fundi i listës;2.  kryej ndonjë veprim me nyjen aktuale, e cila është specifike për

algoritëm të veçantë;3.  nyja aktuale bëhet paraprake (e kaluar) dhe nyje a ardhshme bëhet

aktuale. Kalo në hapin 1.

Shembull 

Si shembull, le të marrim mbledhjen e vlerave të listës së lidhur njëfish.

Page 198: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 198/699

Avni Rexhepi

198

Për disa algoritme, përcjellja (ndjekja, gjurmimi) i nyjes së përparshme ështëesencial, por për disa është e panevojshme. Megjithatë këtu është paraqitur rastii zakonshëm (i përgjithshëm) dhe algoritmi konkret mund të përshtatet për të

 plotësuar kërkesat individuale.

Pjesë kodi

Edhe pse kemi dy klasa për listën e lidhur njëfish, klasa SinglyLinkedListNode  përdoret vetëm për ruajtje. I tërë algoritmi implementohet në klasënSinglyLinkedList .

//traverse=përshko, current=aktuale/momentale

int SinglyLinkedList::traverse()

Page 199: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 199/699

Algoritmet dhe strukturat e të dhënave

199

{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 njëfish –  Operacioni i shtimit (insertimit)

Insertimi në listën e lidhur njëfish ka dy raste speciale. Ato janë insertimi i nyjëssë re para kokës (krejt në fillim të listës) dhe pas bishtit/fundit (krejt në fund tëlistës). Për këtë qëllim, i përdorim dy pointer ë : head (koka) dhe tail (bishti),

përmes së cilëve përcjellim fillimin dhe fundin e listës. Në të gjitha rastet tjera,nyja e re insertohet ndërmjet nyjeve në listë, kështu që ka paraardhës dhe pasardhës në listë.

Rasti i listës së zbrazët

Kur lista është e zbrazët, gjë që tregohet përmes kushtit (head==NULL),insertimi është tërësisht i thjeshtë. Algoritmi i vendosë të dyja, fillimin dhefundin, që të pointojnë në nyjen e re. (angl. new node –  Nyja e re).

Page 200: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 200/699

Avni Rexhepi

200

Shtimi i nyjes në pozitë të parë

 Në këtë rast, nyja e re (new node) insertohet para nyjes së parë aktuale (parakokës).

Kjo mund të bëhet në dy hapa:

1.  Azhuro lidhjen “next” (i ardhshmi) të nyjes së re, për të pointuar nënyjen aktuale të filllimit (kokën).

2.  Azhuro lidhjen (pointerin) e kokës, që të pointojë në nyjen e re (newnode).

Page 201: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 201/699

Algoritmet dhe strukturat e të dhënave

201

Shtimi i nyjes në fund

 Në këtë rast, nyja e re insertohet menjëherë pas nyjes së fundit aktuale.

Kjo mund të bëhet në dy hapa:

1.  Azhoro lidhjen “e ardhshme” ( pointerin next) të nyjes së fundit aktuale, për të pointuar në nyjen e re.

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

Page 202: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 202/699

Avni Rexhepi

202

Rasti i përgjithshëm

 Në rastin e përgjithshëm, nyja e re gjithmonë insertohet ndërmjet dy nyjeve,të cilat veç janë në listë. Lidhjet e fillimit dhe fundit në këtë rast nuk janë të

azhuruara.

 Një insertim i tillë mund të bëhet në dy hapa:

1.  Azhoro lidhjen e nyjes “së përparshme”, që të pointojë në nyjen e re.

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

Page 203: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 203/699

Algoritmet dhe strukturat e të dhënave

203

Pjesë kodi

Të gjitha rastet e paraqitura më sipër mund të implementohen në një funksionme dy argumente, të cilat janë: nyja pas (prapa) së cilës duhet të insertohet nyja

e re dhe nyja e re. Për operacionin “add first” (shto të parin), argumentet janë(NULL, newNode). Për operacionin “add last” (shto të fundit), argumentet janë(tail, neëNode). Megjithatë, këto operacione specifrike (shto të parin dhe shto të

 fundit) mund të implementohen veçmas, në mënyrë që të evitohen verifikimet e

 panevojshme.

//addLast=shtoTëFundit, newNode=nyjeeRevoid 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=shtoTëParin, newNode=nyjeeRevoid 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;

}

Page 204: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 204/699

Avni Rexhepi

204

}}

//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 njëfish –  Operacioni i largimit (fshirjes)

Janë katër raste të cilat mund të paraqiten gjatë fshirjes (largimit) të nyjes dhe janë të ngjashme me rastet e insertimit. Kemi katër situatat e njëjtë, por merenditje të kundërt të veprimeve. Vëreni, se algoritmi i largimit përfshinë“shkatërrimin” e nyjes së fshirë, veprim i cili mund të jetë i  panevojshëm nëgjuhët programuese me mbledhje automatike të mbetjeve (si p.sh., Java).

Lista ka vetëm një nyje

Kur lista ka vetëm një nyje, gjë që tregohet nga kushti se koka pointon në tënjëjtën nyje sikur edhe fundi (bishti), largimi është krejt i thjeshtë. Algoritmi eeliminon nyjen e pointuar nga koka (ose bishti) dhe i vendosë të dyja, edhekokën edhe bishtin, në NULL.

Page 205: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 205/699

Algoritmet dhe strukturat e të dhënave

205

Largo nyjen e parë (fillimin)

 Në këtë rast largohet nga lista nyja e parë (nyja aktuale kokë).

Kjo mund të bëhet në dy hapa:

1.  Azhuro lidhjen e kokës që të pointojë në nyjen e ardhshme pas kokës.

2.  Elimino nyjen e larguar

Page 206: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 206/699

Avni Rexhepi

206

Largo nyjen e fundit

 Në këtë rast, largohet nga lista nyja e fundit (bishti aktual). Ky operacion është

 pak më i ndërlikuar 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ë bëhet në tre hapa:

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

Page 207: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 207/699

Algoritmet dhe strukturat e të dhënave

207

2.  Vendose lidhjen e bishtit të ri për tek nyja e ardhshme, në NULL.

3.  Elimino nyjen e larguar.

Rasti i përgjithshëm

 Në rastin e përgjithshëm, nyja që duhet larguar gjithmonë ndodhet ndërmjet dy nyjeve në listë. Në këtë rast, lidhjet e kokës dhe bishtit nuk azhurohen (nukndryshohen)

Page 208: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 208/699

Avni Rexhepi

208

Largimi i tillë mund të bëhet në dy hapa:

1.  Azhuro lidhjen “e ardhshme” të nyjes paraprake, që të pointojë në nyjene ardhshme të nyjes që largohet.

2.  Elimino nyjen e larguar.

Pjesë kodi

Të gjitha rastet e paraqitura më sipër, mund të implementohen në një funksionme një argument të vetëm, i cili është nyja “e përparshme” (paraprake) e nyjes

që duhet të largohet. Për operacionin remove first   (largo të parën), argumentiështë NULL. Për operacionin remove last  (largo të fundit), argumenti është nyjae parafundit (“e përparshme” e bishtit). Megjithatë, më mirë është që këto rastespeciale (largo të parë dhe largo të fundit) të implementohen në funksione tëveçanta. Vëreni, se largimi i nyjes së parë dhe të fundit kanë kompleksitete tëndryshme, pasi që “largo të fundit” duhet të përshkojë tërë listën. 

//removeFirst=largoTeParen, removeLast=largoTeFundit//removeNode=largoNyjen, removeNext=largoTeArdheshmen//removePrevious=largoTePerparshmen, previousToTail=paraBishtit 

void SinglyLinkedList::removeFirst()

Page 209: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 209/699

Algoritmet dhe strukturat e të dhënave

209

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

}}

Page 210: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 210/699

Avni Rexhepi

210

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ë vetëm. Në këtë shembull kemi programin që mundëson manipulimet me anëtarët (nyjet)

e listës së lidhur (insertimi, fshirja, paraqitja). Edhe për definimin e nyjes është përdorur klasa. Dritarja gjatë ekzekutimit do të duket si në vijim:

 Fig. 4.7 –  Dritarja gjatë ekzekutimit

Page 211: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 211/699

Algoritmet dhe strukturat e të dhënave

211

// ListaeLidhurShembull.cpp

#include "stdafx.h" #include<iostream> using namespace std;

class nyje  //Klasa nyje – definon nyjen {public :int vlera; //vlera,e dhënanyje *pNeA; // pNeA=pointeri ne Nyjen e Ardhshme

// (përmbanë adresën 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);

Page 212: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 212/699

Avni Rexhepi

212

system(“Pause”); return 0;

}

void listaeLidhur :: insertoNeFund(){

nyje *temp; //(temp-e Përkohshme); 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;

}

Page 213: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 213/699

Algoritmet dhe strukturat e të dhënave

213

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;

Page 214: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 214/699

Avni Rexhepi

214

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

Page 215: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 215/699

Algoritmet dhe strukturat e të dhënave

215

Lista e lidhur në STL

 Në STL ekziston libraria <list> që është kontejner për listën e lidhur dyfish.Lista e STL-it mund të insertojë elementet, të shtojë elementet në fillim më

shpejtë sesa vektorët, sepse lista nuk ka nevojë për zhvendosjen e elemenetevetë tjera. Listat poashtu janë efikase me rastin e shtimit të elementeve në fundsepse kanë pointerin e brendshëm (built-in) për në elementin e fundit në listë.

Funksionet e klasës list që përdoren për listat e STL-it, janë:

Funksioni Shembull-Përshkrimi

back cout << list.back() << endl;Kthen referencën për në elementin e fundit në listë.

erase list.erase(iter);

Elementi i listës i pointuar përmes iteratorit iter, do të fshihetlist.erase(firstIter, lastIter)

Fshirja e të gjitha elementeve të listës, nga i pari tek i funditempty if (list.empty())

Funksioni empty kthen ‘true’ nëse lista është e zbrazët, përndryshe nëse lista ka elemente, kthen ‘false’.

end iter = list.end();

end kthen iteratorin dy-drejtimësh për në fund të listës front cout << list.front() << endl; 

front kthen referencën për në elementin e parë në listëinsert list.insert(iter, x); 

funksioni insert, inserton një element në listë. Në këtë formë,inseron elementin me vlerë x, para elementin të pointuar meiter.

merge list1.merge(list2);  bashkon elementet nga list2 në list1. list1 zgjerohet për tëakomoduar anëtarët e rinj nga list2 (merge pret që të dy listat të jenë të sortuara)

pop_back list.pop_back();

 pop_back largon elementin e fundit të listëspop_front list.pop_front();

 pop_front largon elementin e parë të listës

Page 216: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 216/699

Avni Rexhepi

216

push_back list.push_back(x);

 push_back inserton një elementi të ri me vlerë x në fund tëlistës

push_front list.push_front(x); push_front inserton një element me vlerë x në fillim të listësreverse list.reverse();

kthen mbrapsht (rrotullon) renditjen e elementeve në listësize( ) size () - Kthen numrin e elementeve në listë

swap list1.swap(list2) Funksioni swap shkëmben elementet e ruajtura në dy lista. Përshembull, nëse list1 dhe list2 janë lista, urdhëri i dhënë do tëshkëmbejë vlerat e ruajtura në të dy listat.

unique list.unique();unique largon elementet me vlerë të njëjtë si ndonjë element para tij (largon duplikatet, e shndërron në listë me vlera unike)

Shembull:

// STL list container#include <iostream>

#include <list> //List nga STLusing 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 vleratfor (iter = ListaIme.begin(); iter != ListaIme.end();

iter++)cout << *iter << " ";cout << endl;

// Reverse – ndrysho renditjen e elemeneteveListaIme.reverse();

Page 217: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 217/699

Algoritmet dhe strukturat e të dhënave

217

// Paraqiti vlerat perserifor (iter = ListaIme.begin(); iter != ListaIme.end();

iter++)cout << *iter << " ";

cout << endl;}

Rezultati:

0 10 20 30 40 50 60 70 80 9090 80 70 60 50 40 30 20 10 0

Steku përmes listës së lidhur

Më herët e kemi parë krijimin e stekut përmes vargjeve. Mirëpo, përdorimi ivargjeve paraqet problemin e pamundësisë së përshtatjes së madhësisë së stekutgjatë ekzekutimit të programit. Zgjidhja për këtë do të ishte përdorimi i listës sëlidhur për krijimin e stekut. Në vazhdim do të shohim si të përdoret lista e lidhur për të krijuar stekun.

Steku

Steku është strkutura e të dhënave që organizon të dhënat sipas parimit FILO(First In Last Out), i pari brenda, i fundit jasht. Kjo i përngjanë vendosjes së pjatave një mbi një në kuzhinë, ku vetëm pjata e vendosur e fundit mund tëtërhiqet nga grupi. Për të marrë pjatën e tretë, së pari duhet larguar dy pjatat efundit. Nuk ka mënyrë tjetër për të nxjerrë elementet, përveq nga maja (top). Përtë siguruar qasje të drejtpërdrejtë ose qasje të rastit në cilindo element, duhet të përdoret tjetër strukturë e të dhënave.

Steku është i përshtatshëm kur duhet të ruhen dhe të tërhiqen të dhënat në

renditjen i pari brenda, i fundit jashtë. Për shembull, kompjuteri i procesonurdhërat përmes përdorimit të stekut, në të cilin instruksioni i ardhshëm për t’u  ekzekutuar ndodhet në krye të stekut.

Klasa LinkedList

Edhe pse diskutohet për vlerat e vendosura (palosura) si stek, fizikisht kjo nukndodhë aspak, në memorie të kompjuterit. Në vend të kësaj, të dhënat lidhen së bashku njëra pas tjetrës (në mënyrë sekuenciale) në një listë të lidhur, ku vlera efundit gjithmonë paraqitet në fillim (front) të listës. Vlerat largohen vetëm ngafillimi i listës.

Page 218: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 218/699

Avni Rexhepi

218

Kjo listë sekuenciale krijohet përmes përdorimit të listës së lidhur. Siç ështëthënë, lista e lidhur përmbanë elementet e quajtura nyje. Nyja ka tri nën-elemente: vlerën dhe dy pointera. Vlera është e dhëna e ruajtur në stek. Pointerët pointojnë në nyjen e përparshme dhe në atë të ardhshme (Figura 4.8). Kurvendoset një element i ri në listënë e lidhur, alokohet ny ja e re (‘new’) dhe pastaj caktohet prointeri në nyjen e përparshme dhe atë të ardhshme.

 Figura 4.8 - Nyja përmbanë pointerin në nyjen e përparshme dhe atë të

ardhshme në listën e lidhur dhe përmbanë të dhënën (vlerën) e shoqëruar me

nyjen aktuale.

 Nyja definohet në C++ përmes përdorimit të strukturës, e cila është tip i tëdhënave i definuar prej shfrytëzuesit. 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). Urdhëri i parë deklaron një integer që ruan vlerënaktuale të nyjes. Dy urdhërat tjerë në vazhdim, deklarojnë pointerët për në nyjene përparshme dhe të ardhshme në listën e lidhur.

Kontruktori inicializon elementet e nyjës kur krijohet nyja, gjë që është engjashme me mënyrën se si punon konstruktori në definicionin e klasës. Jusiguroni (ofroni) vlerën aktuale për strukturën kur të krijoni nyje të re. Kjo vlerëi ndahet vlerës në listën e argumenteve, e cila pastaj i ndahet elementit tëinstancës së strkuturës. Nyjet ‘previous” (e përparshme) dhe ‘next’ (e ardhshme)inicializohen në NULL, gjë që tregon se nuk ka elemente të tjera në listën elidhur. NULL zëvendësohet me pointerin për në nyje kur të shtohet nyja e re nëlistën e lidhur.

Si ju kujtohet nga pjesa e mëparshme, klasa LinkedList definohet për të krijuar

dhe menagjuar listën e lidhur. Janë dy anëtarë të të dhënave dhe gjashtë

Page 219: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 219/699

Algoritmet dhe strukturat e të dhënave

219

funksione anëtare, të definuara në klasën LinkedList. Anëtarët e të dhënave janë pointerë në instancat e strukturës Node. Pointeri i parë quhet ‘front’ dhe ai ireferohet nyjës së parë në listën e lidhur. Pointeri i dytë quhet ‘back’ dhe ireferohet nyjës së fundit në listën e lidhur.

Të dy këta pointerë, ‘front’ dhe ‘back’ deklarohen në pjesën e mbrojtur (angl. protected) të klasës, sepse klasa lista LinkedList trashëgohet nga klasaStackLindekList, për të cilën do të flasim në vazhdim. Klasa StackLinkedList përdorë pointerët ‘front’ dhe ‘back’. 

Gjashtë funksionet anëtare të klasës, manipulojnë listën e lidhur. Këto funksione janë konstruktori, destruktori, appendNode(), displayNodes(),displayRevereseNodes() dhe destrotyNodes(). Këto u paraqitën në pjesën emëparshme.

 Në vazhdim kemi definicionin e klasës LinkedList. Do të shihni se ështëafërsisht i njëjtë me definicionin e klasës Linkedlist të paraqitur më parë, por menjë dallim të lehtë. Gjersa më parë pointerët ‘front’ dhe ‘back’ ishin të definuarnë pjesën private të klasës, tani ata janë në pjesën e mbrojtur (protected), sepseklasa StackLinkedList do t’i përdorë ata: 

class LinkedList{

protected:Node* front;

Node* back;public:

LinkedList();~LinkedList();void appendNode(int);void displayNodes();void displayNodesReverse();void destroyList();

};

Klasa StackLinkedList

Programeri efikas nëse është e mundur nuk e përsëritë kodin. Në vend të kësaj,ai i trashëgon atributet dhe sjelljet e klasës tjetër, duke definuar klasënLinkedList për të krijuar dhe manipuluar listën e lidhur. Programeri efikasmundet poashtu të krijoj klasën StackLinkedList për të krijuar dhe manipuluarstekun-listë e lidhur. Klasa StackLinkedList trashëgon atributet dhe sjelljet eklasës LinkedList dhe definon sjelljet tjera të nevojshme për të punuar mestekun-listë e lidhur.

Page 220: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 220/699

Avni Rexhepi

220

Si shtesë ndaj atributeve të definuara në klasën LinkedList, klasaStackLindekList kërkon pesë sjellje plotësuese të definuara si funksione anëtare:konstruktorin, destruktorin, push(), pop() dhe isEmpty(). Definicioni i klasësStackLinkedList ë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 klasës StackLinkedList mund të duken tëçuditshëm në fillim, sepse të dy janë të zbrazët dhe nuk ka instruksione/urdhëratë specifikuara në trupin e tyre, si vijon:

StackLinkedList(){}

~StackLinkedList(){}

Konstruktori është i zbrazët sepse para konstruktorit të klasës StackLinkedListthirret konstruktori i klasës LinkedList. Ju kujtohet se klasa StackLinkedList etrashëgon klasën LinkedList. Konstruktori i klasës LinkedList inicializon pointerët ‘front’ dhe ‘back’ në NULL. Prandaj, nuk ka asgjë tjetër për të bërënga ana e konstruktorit të klasës StackLinkedList.

 Ngjashëm, destruktori i klasës LinkedList thirret para destrkutorit të klasësStackLinkedList. Destruktori i klasës LinkedList e fshinë tërë memorien eshoqëruar me nyjet e listës së lidhur. Prandaj, destruktori i klasësStackLinkedList poshtu nuk ka për të bërë asgjë.

Vendosja/shtyrja e nyjes në stekun-listë e lidhur

 Në pjesën për stekun është sqaruar se vlerat vendosen/shtyhen në krye të stekutdhe largohen/tërhiqen nga kreu i stekut (top). Këto veprime quheshin ‘push’ dhe‘pop’. Hapat e njëjtë ndodhin edhe gjatë përdorimit të listës së lidhur për stekun,

Page 221: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 221/699

Algoritmet dhe strukturat e të dhënave

221

 por në vend të vendosjes së vlerës në inkeksin e ardhshëm në dispozicion tëvargut, ajo vendoset në fund (back) të listës së lidhur.

Duhet të definoni funksionin push() të klasës StackLinkedList i cili thirret sa

herë që të shtohet një vlerë në stek. Mbani mend që në realitet jeni duke shtuarnjë nyje në listën e lidhur dhe jo thjeshtë vetëm vlerë. Vlera është e përmbajtur përbrenda nyjes (në nyje).

Për të shtyar një nyje në stek, përdoren hapat e njëjtë si në rastin e shtimit tënyjes në listën e lidhur. Kjo do të thotë që funksioni appendNode() i klasësLinkedList mund të përdoret për të vendosur nyjen e re në stek. Prandaj, krejtçka nevojitet është që nga ana e funksionit push(), të thirret funksioniappendNode(). Pasi që funksioni appendNode() është publik, ai do të mund tëthirrej edhe direkt, sa për të vendosur nyjen në stek, por vendosja e funksionit

 push() në klasën e stekut e bënë këtë më intuitive për përdoruesit e kësaj klase.Kjo poashtu ndihmon në fshehjen e implementimit të brendshëm, pradaj përdorimi i klasës është pak më i drejtpërdrejtë.

Si ju kujtohet, funksioni appendNode() kërkon një argument (parametër), i ciliështë e dhëna (vlera) që i ndahet nyjes së re. Duhet të definohet funksioni push()që të pranojë këtë vlerë të njëjtë si argument të tij, për të përcjellur këtë të dhënënë funksionin appendNode(). Kjo është ilustruar në shembullin vijues. Funksioni push() këkron një integer të përcjellur si argument. Ajo vlerë integer pastaj i përcillet funksionit appendNode() përmbrenda trupit të definicionit të funksionit

 push().void push(int x){

appendNode(x);}

Nxjerrja e nyjës nga steku-listë e lidhur

Është e nevojshme të definohet edhe funksioni për nxjerrjen (tërheqjen) e nyjës prej stekut, që do të quhet pop(). Pasi që jemi duke përdorur listën e lidhur si

stek, funksioni pop() duhet të largojë nyjen prej fundit të listës së lidhur.Fatkeqësisht, nuk është e mundur që thjeshtë të thirret funksioni i klasësLinkedList për 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 pasurfunksionin e klasës bazë për largim të nyjes së fundit ‘removeBack()’, do tëmund të thirrej ky funksion për të nxjerrur (duke e larguar) nyjen nga lista. Nëkëtë rast, do të duhet të definohet funksioni pop() në klasën StackLinkedList përtë bërë këtë gjë. Kjo do të sigurojë qasjen ‘last in, first out’, në stek. 

Page 222: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 222/699

Avni Rexhepi

222

Ja si do të jetë definicioni i funksionit pop(). Referojoni pamjes së stekut-listë elidhur, në figurën 4.9, për të pasur të qartë mënyrën e funksionimit të funksionit pop().

 Figura 4.9: Funksioni pop() largon nyjen në krye të stekut, e cila është nyja në fillim të listës 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;

Page 223: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 223/699

Algoritmet dhe strukturat e të dhënave

223

}delete temp;return retVal;

}

Së pari, duhet të përcaktohet/kontrollohet nëse ka ndonjë vlerë në stek, dukethirrur anëtarin isEmpty(). Ky funksion kthen vlerën Bool-eane ‘true’ nëse stekuështë i zbrazët dhe atë ‘false’ nëse nuk është i zbrazët. Nga figura 4.9 mund tëshihet se ka dy nyje në stek dhe kështu steku nuk është i zbrazët. Për këtë arsye,urdhëri ‘return’ në kushtin ‘if’ nuk do të ekzekutohet. 

Funksioni pop() i referohet atributit ‘back’ të klasës LinkedList. Është merëndësi të mbahet mend që atributi ‘back’ i referohet kr eut të stekut (pra atributit‘top’ të stekut). Nyjet do të largohen nga fundi (‘back’) i listës së lidhur, për të

kryer operacionin ‘pop’. Prandaj, vlera ‘front’ është Node2 (Nyja 2). Vlera e Node 2, i ndahet variablës ‘retVal’, e cila është vlera e kthyer   ngafunksioni pop(), nëse ka nyje në stek (steku nuk është i zbrazët). Kjo bën qëvlera të tërhiqet (nxirret) nga steku.

Pastaj, adresa e nyjës ‘back’, që është Node 2, i ndahet pointerit të përkohshëm‘temp’. Nyja në të cilën pointon pointeri i përkohshëm largohet nga memoria meoperatorin ‘delete’ në fund të funksionit pop(). 

Pastaj, përcaktohet nëse nyja në fund të stekut ishte nyja e vetme në listën e

lidhur. Kjo bëhet duke parë nëse atributi ‘previous’ i nyjes është NULL. Nëse pointeri ‘previous’ në f und të listës është NULL, kjo tregon se ka vetëm një nyjenë listën e lidhur.

Duhet të jeni të kujdesshëm kur analizoni funksionin pop(). Mbani mend qëfundi i listës së lidhur (back) është kreu i stekut (top) dhe se fundi i stekut ështëfillimi i listës së lidhur.

 Nëse funksioni pop() është duke larguar nyjen e vetme nga steku, atëherëatributet ‘front’ dhe ‘back’ të klasës LinkedList do të vendosen në NULL, duketreguar se nuk ka nyje të mbetura në listën e lidhur, pas ekzekutimit të funksionit

 pop().Sidoqoftë, nëse ka së paku një nyje në stek, ekzekutohen urdhërat përbrendaurdhërit ‘else’, si në figurën 4.9. Urdhëri i parë përbrenda urdhërit ‘else’ ecakton atributin ‘previous’ të atributit ‘back’ si ‘back’ i ri (si fundi i ri). Nëfigurën 4.9, atributi ‘previous’ është 1. Kjo tregon se Node 1 vjen para Node 2.Atëherë Node 1 caktohet si fundi i ri i stekut.

Mbani mend që nuk ka nyje tjetër (të ardhshme, next) në stek sepse gjithmonë jeni duke punuar me fundin (back) e listës së lidhur. Prandaj, duhet t’ia ndani

Page 224: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 224/699

Avni Rexhepi

224

 NULL atributit ‘next’ të nyjes ‘back’, që është Node 1. Kjo e bën Node 1 si kryetë stekut (top).

Urdhëri i parafundit e largon nyjen nga memoria duke përdorur operatorin

‘delete’. Pointeri i përkohshëm pointon në adresën e memories së nyjes që ështëlarguar nga steku. Urdhëri i fundit e kthen vlerën e nyjes që u largua nga steku.

Kontrollimi a është i zbrazët steku

Funksioni pop() duhet të kontrollojë nëse steku është i zbrazët ose do të tentojëtë largojë nyjen që nuk ndodhet në stek. Funksioni pop() e kontrollon a ështësteku i zbrazët duke thirrur funksionin isEmpty(), i cili duhet të definohet si pjesë e klasës StackLinkedList.

Funksioni isEmpty() është funksion i thjeshtë i cili kontrollon a është steku i

zbrazët duke shikuar nëse vlera e atr ibutit ‘front’ të klasës LinkedList është NULL. Nëse është, atëherë kthehet vlera bool-eane ‘true’, përndryshe kthehet‘false’. Nëse steku është i zbrazët, të dy atribuetet, ‘front’ dhe ‘back’ janë NULL, por mjafton të testohet vetëm njëra 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ë këtë 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 dheStackLinkedListDemo.cpp. të gjithë këta fajlla bashkohen sëbashku në kohën ekompajlimit për të krijuar fajllin ekzekutiv.

Header fajlli LinkedList.h është fajlli që përmbanë definicionin e strukturës Node adhe definicionin e klasës Linkedlist. LinkedList.cpp është kodi burimor icili përmbanë implementimin e funksioneve anëtare të klasës LinkedList, të cilat janë paraqitur më parë (në kapitullin 6).

Page 225: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 225/699

Algoritmet dhe strukturat e të dhënave

225

Fajlli StackLinkedList.h është header fajlli i cili përmbanë definicionin e klasësStackLinkedList. StackLindekList.cpp është fajlli i kodit burimor që përmbanëimplementimin e funksioneve të klasës StackLinkedList.

StackLinkedListDemo.cpp përmbanë aplikacionin dhe është vendi ku deklarohetinstanca e klasës StackLinkedList dhe ku thirren funksionet anëtare.

Funksionet LinkedList Header Fajlli dhe LinkedList

Fajlli LinkedList.h dhe fajlli LinkedList.cpp janë treguar në kodin vijues. Këtoduhet të duken të njohura sepse janë të njëjtat që janë deklaruar më parë (nëkapitullin 6). Mirëpo ka një përjashtim. Atributet ‘front’ dhe ‘back’ të definuaranë klasën LinkedList në fajllin LinkedList.h janë definuar në pjesën (seksionin)e mbrojtur (protected) të definicionit të klasës. Ata paraqiteshin më parë në pjesën private. Klasa StackLinkedList duhet të ju qaset këtyre variablave, ashtuqë ato vendosen nën specifikimin ‘protected’ për të qenë të dukshme për nën-klasën.

Për shpjegimet e plota të tyre, referojuni pjesës ku janë paraqitur më parë.

//LinkedList.h// Node=Nyje; data=vlera;// previous=ePerparshme; next=eArdhshmestruct 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

Page 226: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 226/699

Avni Rexhepi

226

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

}

Page 227: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 227/699

Algoritmet dhe strukturat e të dhënave

227

}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 përmbanë definicionin e klasës StackLinkedList, si nëvijim. Nën fajllin StackLinkedList.h ndodhet fajlli StackLinkedList.cpp i cili përmbanë definicionet e funksioneve anëtare.

Definicioni i secilit funksion është sqaruar në pjesën “Klasa StackLinkedList”. 

//StackLinkedList.hclass StackLinkedList : public LinkedList

{public:

StackLinkedList();virtual ~StackLinkedList();void push(int);int pop();bool isEmpty();

};

//StackLinkedList.cpp

StackLinkedList.hStackLinkedList::StackLinkedList(){}StackLinkedList::~StackLinkedList(){}void StackLinkedList::push(int x){

appendNode(x);

}

Page 228: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 228/699

Avni Rexhepi

228

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 përmbanë aplikimin aktual të stekut, si është paraqitur në kodin vijues. Aplikacioni fillon me deklarimin e instancës së klasësStackLinkedList. Rikujtoni që ky urdhër thërret (në mënyrë indirekte)konstruktorin e klasës LinkedList, që ësthë trashëguar në klasënStackLinkedList.

Pastaj aplikacioni e thërret funksionin push(), për të vendosur (shtyrë) në stekvlerat 10, 20 dhe 30. Për të paraqitur vlerat thirret funksioni displayNodes().

Page 229: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 229/699

Algoritmet dhe strukturat e të dhënave

229

Funksioni displayNodes() është anëtarë i klasës LinkedList dhe është sqaruar më parë.

Funksioni pop() thirret pastaj për të larguar nyjen e fundit në stek, e cila pastaj

 paraqitet në ekran (shih figurën 4.10). Programi pastaj thërret operatorin ‘delete’ për 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;

}

Page 230: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 230/699

Avni Rexhepi

230

Rezultati i programit është:

 Nodes: 10 20 30 10

Queue përmes listës së lidhur

Gjaët përdorimit të queue-s (radhës së pritjes), mund të ndodhë që nuk ka vendtë mjaftueshëm për vendosjen e të gjitha të dhënave të cilat duhet të përpunohen.Për të evituar këtë problem, për krijimin e radhës së pritjes, mund të përdorenlistat e lidhura.

Është thënë më herët se queue është organizim sekuencial i të dhënave, ku tëdhënat (vlerat) kanë qasje vetëm në parimin “First In, First Out” (I pari brenda, i pari jashtë), që është e ngjashme me radhën e pritjes për blerjen e biletave për

koncert ose për kryerjen e pagesës, tek arkat e pagesave në supermarket.

 Në pjesën ku është shpjeguar queue u krijua përmes përdorimit të vargut, përruajtjen e të dhënave. Si ju kujtohet, vargu ishte ‘i ndarë” nga queue. Vlerat indahen elementeve të vargut. Vetë queue përbëhet prej dy variablave të quajtura‘front’ dhe ‘back’. Secila pointon në elementin e vargut që ndodhe t në fillim(front) të radhës (queue-s) ose në fund (back) të radhës (queue-s). Kur largohete dhëna (vlera) nga fillimi i radhës, programi e ndryshon vlerën e variablës‘front’, për të pointuar në elementin e ardhshëm të vargut. Mirëpo, e dhëna(vlera) e larguar nga radha (queue) akoma mbetej në varg. Kjo do të thotë që edhëna (vlera) nuk largohet nga memoria.

Ëshët edhe një problem serioz me përdorimin e vargjeve për të ruajtur radhët(queues): kur të shkruhet programi, duhet të dihet madhësia e vargut. Vargumund të ruaj vetëm një numër të caktuar të elementeve në cilindo momentkohor. Programerët evitojnë këtë problem duke përdorur listën e lidhur në vendtë vargut, për të krijuar radhën e pritjes (queue-n), pasi si është thënë më herët,lista e lidhur mund të rritet ose zvogëlohet gjatë kohës së ekzekutimit bazuar nënevojat e programit.

Queue-Listë e lidhurKonceptualisht, queue listë e lidhur është i njëjtë sikur queue i ndërtuar duke përdorur vargun. Të dy ruajnë të dhëna (vlera). Mirëpo, në queue varg, e dhënaruhet në elementin e vargut. Në queue listë e lidhur, e dhëna ruhet në nyje tëlistës së lidhur. Queue listë e lidhur përbëhet prej tri komponenteve kryesore:nyja, definicioni i klasës LinkedList dhe definicioni i klasës QueueLinkedList.Bashkarisht, ato grupohen në të dhëna të organizuara në radhë (queue).

 Në C++ nyja krijohet si strukturë e e tipit të të dhënave të definuar prej

shfrytëzueusit dhe përmbanë tri elemente: vlera dhe pointerët në nyjen e

Page 231: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 231/699

Algoritmet dhe strukturat e të dhënave

231

 përparshme dhe në nyjen e ardhshme (Figura 4.11). Pjesa e kodit në vijim ështënyje e tipit të definuar prej shfrytëzuesit. Në këtë pjesë do të përdoret strukturavijuese e tipit të definuar prej shfrytëzuesit, për të krijuar queue-listë e lidhur.

 Figure 4.11 - Secila nyje pointon nyjen e përparshme dhe atë të ardhshme.

Emri i strukturës së definuar prej shfrytëzuesit është ‘Node’ dhe përdoret nëklasën LinkedList për të deklaruar instancat e nyjes. Tre urdhërat e fundit në

strukturë deklarojnë një integer që ruan vlerën aktuale dhe deklarojnë dy pointerë për të ju referuar nyjes së përparshme dhe nyjes së ardhshme në listën elidhur.

Secilën herë që krijohet një nyje, strukturës së definuar prej shfrytëzuesit i përcillet vlera për nyjen. Pointerët në nyjen e përparshme dhe të ardhshmecaktohen në NULL, që tregon se nuk ka nyje të përparshme ose të ardhshme. NULL zëvendësohet me pointerët përkatës kur të shtohet nyja e re në listë.

// Node=Nyje; data=vlera;// previous=ePerparshme; next=eArdhshme

Page 232: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 232/699

Avni Rexhepi

232

struct Node{

int data;Node* previous;

Node* next;};

Klasa LinkedList krijon dhe menagjon listën e lidhur. Rikujtoni se klasaLinkedList identifikon nyjen që ndodhet në fillim të listës së lidhur (front) dhenyjen që ndodhet në fund të listës (back).

Për më tepërt, klasa LinkedList definon funksionet anëtare të cilat menagjojnëlistën e lidhur. Këto janë funksionet e definuara në pjesën ku u shpjeguan listat elidhura (kapitullin 6): konstruktori, destruktori, appendNode(), displayNodes(),displayRevereseNodes() dhe destroyList(). Ja definicioni i klasës LinkedList qëdo të përdoret për 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 enjëjtë, LinkedList.h, si mënyrë për të mbajtur atë të organizuar. Programerët pastaj përdorin direktivën preprocesorike #include për të përfshirë LinkedList.hnë programin që përdorë listën e lidhur.

Komponenta e fundit e queue-listë e lidhur është definicioni i klasësQueueLinkedList. Kjo klasë trashëgon klasën LinkedList dhe pastaj definonfunksionet anëtare të cilat janë të dizajnuara në mënyrë specifike për tëmenagjuar queue-n (radhën).

Edhe pse duket se do të ishte mirë që të kombinohen klasat LinkedList dheQueueLinkedList 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 përsëritje të kodit, gjëqë programerët e evitojnë sa herë të jetë e mundur.

Page 233: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 233/699

Algoritmet dhe strukturat e të dhënave

233

Për shembull, definicionet e nyjes dhe klasa LinkedList do të lokalizoheshin nëdy vende. Nëse nevojitet përmirësim (angl. upgrade) i cilit do definicion, do tëduhet të mbahen mend të gjitha vendet ku ato janë definuar brenda kodit. Qasjemë e mirë është që të vendoset secili definicion në fajllin e vet (p.sh.,LinkedList.h, QueueLinkedList.h), ashtu që kodi nuk do të përsëritet.

 Në vijim paraqitet definicioni i klasës QueueLinkedList që do të përdoret për tëkrijuar queue-n. Ky definicion ruhet në fajllin QueueLinkedList.h. KlasaQueueLinkedList 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 klasës QueueLinkedList janë të zbrazët, si do tëshihet në kodin vijues. Konstruktori në mënyrë tipike inicializon anëtarët e një

instance të klasës. Në rast të queue-listë e lidhur, inicializimi bëhet ngakonstruktori i klasës LinkedList, i cili thirret para konstruktorit të klasësQueueLinkedList, prandaj kjo do të thotë se nuk ka asgjë për të bërë nga ana ekonstruktorit të klasës QueueLinkedList.

Destruktori në mënyrë tipike e liron memorien e përdorur nga instanca e klasës.Lista e lidhur e përdorur për queue largohet nga destruktori i klasës LinkedList, icili poashtu thirret para se të thirret destruktori i klasës QueueLinkedList.Prandaj, as për destruktorin e klasës QueueLinkedList, nuk ka asgjë për të bërë.

QueueLinkedList::QueueLinkedList(){}QueueLinkedList::~QueueLinkedList(){}

Page 234: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 234/699

Avni Rexhepi

234

Enqueue

Funksioni enqueue() (vendose në radhë), i klasës QueueLinkedList thirret saherë që një nyje e re të vendoset në queue (radhë). Si do të shihet nga definicioni

i funksionit në pjesën vijuese të kodit, funksioni enqueue() është “i rrallë” sepse përmbanë vetëm një urdhër, i cili thërret funksionin appendNode() të klasësLinkedList.

 Nuk ka nevojë që të shtohen urdhëra plotësues në funksionin enqueue() sepsevendosja e nyjes në queue është procesi i njëjtë si i vendosjes së nyjes në listën elidhur. Secila nyje e re vendoset në fund (back) të listës së lidhur. Prandaj, krejtçka nevojitet, është funksioni appendNode().

 Nyjet shtohen në fund të queue-s, për shkak se është duke u ripërdorur kodi iklasës LinkedList. Nyja e re vendoset në fund (back) të queue-s, kurse tërhiqet(largohet) nga fillimi i listës (front).

Funksioni enqueue() ka nëj argument, i cili është vlera (e dhëna) që do t’i ndahetnyjes së re. Në këtë shembull, nyja përdoret për të ruajtur një integer, mirëpomund të ruhet çfarëdo tipi i të dhënave. Në fakt, e dhëna mund të jetë pointer përnë setin e të dhënave, si p.sh. informacionet për studentit. Për të ndryshuar këtëshembull nga vlera integer në një tip tjetër të të dhënave, vetëm duhet ndryshuarelementi i të dhënave në strukturën Node, për të reflektuar tipin e të dhënave tëcilin dëshironi ta ruani në nyje.

Vlera e pranuar nga funksioni enqueue() i përcillet funksionit appendNode().Figura 4.12 ilustron se si funksioni appendNode() e vendosë nyjen e re në fund(back) të listës së lidhur. Në krye të ilustrimit është lista e lidhur që përmbanë dynyje. Pastaj thirret funksioni appendNode(), për të shtuar një nyje të re në fundtë listës së lidhur.

Page 235: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 235/699

Algoritmet dhe strukturat e të dhënave

235

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

Hapi i parë në këtë 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ë listën e lidhur. Kjo e bënë Node3 fund (back) të listës sëlidhur.

Hapi i dytë ia ndanë referimin në Node2 si vlerë të nyjës së përparshme

(previous) në Node3. Kjo do të thotë se programi shikon në vlerën e nyjes së përparshme të Node3 për të ditur se cila nyje vjen para Node3 në listën e lidhur.

Hapi i fundit është të caktohet Node3 si vlerë e re e anëtarit ‘back’ të klasësLinkedList.

void enqueue(int x){

appendNode(x);}

Page 236: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 236/699

Avni Rexhepi

236

Dequeue

Funksioni dequeue() (largoje nga radha) i klasës QueueLinkedList largon nyjennga fillimi (front) i queue-s (radhës). Për fat të keq, nuk ka ndonjë funksion në

klasën LinkedList i cili largon nyjen nga fundi (back) i listës së lidhur. Prandaj,funksioni dequeue() duhet të kruej punën.

Funksioni dequeue() fillon me kontrollimin nëse ka nyje në queue, duke thirrurfunksionin isEmpty(). Funksioni isEmpty() kthen vlerën bool-eane ‘true’ nësequeue është i zbrazët, rast në të cilin dequeue() kthen ‘-1’. Nëse është së pakunjë nyje në queue, kthehet ‘false’. 

Vërejtje: Në këtë rast, termi “dequeue” përdoret për funksionin për nxjerrjen eelementint nga fillimi i listës. Në fakt, DEQueue është edhe temri që përdoret përversionin e dytë të queue-s, i cili mundëson qasjen nga të dy anët, si për insertim ashtu

edhe për nxjerrje dhe rrjedh nga kombinimi: Double Ended Queue = dequeue.

Figura 4.13 paraqet mënyrën e funksionimit të funksionit dequeue(). Vërehet se janë tri nyje në queue, kështu që funksioni isEmpty() kthen vlerën bool-eane‘false’, duke bërë 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 vlerës së nyjes në fillim (front) të queuevariablës së quajtur retVal. Vlera e retVal kthehet nga funksioni dequeue(), nëurdhërin e fundit të funksionit.

Page 237: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 237/699

Algoritmet dhe strukturat e të dhënave

237

Pastaj, nyjes së fillimit (front) i ndahet referimi në varia blën pointer ‘temp’.Operatori ‘delete’ e përdorë variablën ‘temp’ më vonë në funksion, për tëlarguar nyjen e fundit (back) nga memoria.

 Në vazhdim, funksioni përcakton nëse ka nyje tjetër në queue duke kontrolluarvlerën e anëtarit ‘next’ të nyjes ‘front’. Nëse vlera e anëtarit ‘next’ është NULL,atëherë nuk ka nyje të tjera në queue. Në këtë rast, anëtarët ‘front’ dhe ‘back’ tëklasës LinkedList caktohen në NULL, duke treguar se queue (radha) është ezbrazët.

Mirëpo, nëse anëtari ‘next’ i nyjes ‘front’ nuk është NULL, vlera e anëtarit‘next’ të nyjes ‘front’ i ndahet anëtarit ‘front’ të klasës LinkedList. Në këtëshembull, Node2 është nyja e ardhshme për Node1. Node2 bëhet ‘front’ i ri përqueue.

Vëreni se anëtari i përparshëm i ‘Node2’ është caktuar në ‘Node1’. Mirëpo, Node1 nuk ekziston më!. Prandaj, anëtari i përparshëm (previuous) duhet tëcaktohet në NULL, sepse nuk ka nyje të përparshme (previuous). Node2 ështëfillimi (front) i queue-s (radhës së pritjes).

 Nyja ‘temp’ pastaj fshihet nga memoria. Rikujtoni se nyja ‘temp’ është pointeriqë pointon në Node1, dhe Node1 nuk ekziston më në memories. Urdhëri i funditkthen vlerën e variablës retVal, e cila është vlera (e dhëna) 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;

Page 238: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 238/699

Avni Rexhepi

238

Funksioni isEmpty() kontrollon nëse ka nyje në queue dhe ky funksion thirretnga funksioni dequeue(). Funksioni isEmpty() verifikon vlerën e anëtarit ‘front’të klasës LinkedList. Nëse vlera e ‘front’ është NULL, atëherë queue (radha e pritjes) është e zbrazët, përndryshe, queue ka së paku një nyje.

Funksioni isEmpty() kthen ‘true’ nëse vlera e ‘front’ është NULL, përndryshekthen ‘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 përdorur listën e lidhur, le të përmbledhim të gjitha pjesët për të krjijuar një queue funksional në C++.

Programerët e organizojnë aplikacionin në disa fajlla, të cilët përmbajnë pjesë tëveçanta të aplikacionit.

 Në rastin e ‘demo’ queue aplikacionit të ilustruar në vazhdim, janë pesëkonponente të veçanta: fajlli drejtues QueueLinkedListeDemo.cpp, header fajllii cili përmbanë definicionin e klasës së nyjes dhe klasës LinkedList(LinkedList.h), fajlli i cili përmbanë implementimin e funksioneve anëtare tëklasës LinkedList (LinkedList.cpp), header fajlli i cipi përmbanë definicionin eklasës QueueLinkedList (QueueLinkedList.h) dhe fajlli i cili përmbanëimplementimin e funksioneve anëtare të klasës QueueLinkedList(QueueLinkedList.cpp).

Aplikacioni quhet QueueLinkedListDemo dhe përdorë listën e lidhur për tëkrijuar queue-n, si është paraqitur në kodin vijues. Aplikacioni fillon medeklarimin e isntancës së klasës QueueLinkedList, duke përdorur operatorin‘new’. Pastaj deklaron pointerin në një instancë të QueueLinkedList. Pointeriështë quajtur ‘queue’, të cilit i ndahet referenca në instancën e krijuar ngaoperatori ‘new’. 

Page 239: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 239/699

Algoritmet dhe strukturat e të dhënave

239

Pastaj thirret tri herë funksioni enqueue(). Secilën herë një nyje e re vendoset nëqueue. Queue i paraqitur në Figurën 4.14 ilustron queue-n pas thirrjes së fundittë funksionit enqueue().

 Figura 4.14 - Queue, pas vendosjes së tri vlerave.

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

 Figura 4.15 - Queue pas thirrjes së funksionit dequeue()

Urdhëri i fundit në program, e largon queue-n nga memoria.

Secila komponentë tjetër veç është parë dhe diskutuar në pjesën e përparshme.

//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=eArdhshmestruct Node{

int data;Node* previous;Node* next;

};

class LinkedList

Page 240: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 240/699

Avni Rexhepi

240

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

Page 241: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 241/699

Algoritmet dhe strukturat e të dhënave

241

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)

Page 242: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 242/699

Avni Rexhepi

242

{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 plotësuese për stek dhe queue: Insert, Delete, Peek, Find

Sikur në rastin e mësimit të programimit, ku në fillim mësohen vetëm gjëratelementare e pastaj kalohet në programe më komplekse, edhe në rastin e listavetë lidhura, pasi u sqaruan funksionet themelore për punë me listat e lidhura,është radha të shohim edhe funksionet plotësuese, të fuqishme, të cilat nevojiten për të ndërtuar aplikacione të kompletuara. Do të shohim funksionet përinsertim, nxjerrje, fshirje dhe kërkim të cilat do të mundësojnë përdorimin e

stekut dhe queue-s nëpër aplikacione.

Page 243: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 243/699

Algoritmet dhe strukturat e të dhënave

243

Klasa e zgjeruar LinkedList

Për të rritur efikasitetin e klasës LinkedList dhe për ta bërë më të lehtë për

 përdorim, do të rrisim funksionalitetin e klasës së definuar më parë. Si ështëthënë më parë, klasa LinkedList krijon instancë të strukturës ‘Node’ që ështëdefinuar në header fajllin LinkedList.h. Struktura ‘Node’ ka tri elemente: tëdhënën (vlerën) e ruajtur në nyje, pointerin për në nyjen e ardhshme dhe pointerin për në nyjen e përparshme në listën e lidhur.

Klasa LinkedList që u përdor në pjesën e përparshme përmbanë dy anëtarë të tëdhënave (dy vlera) dhe gjashtë funksione anëtare. Anëtarët e të dhënave janëreferenca (pointerë) për në nyjen që ndodhet në fillim (front) të listës dhe përnyjen që ndodhet në fund (back) të listës së lidhur.

Klasa LinkedList ha funksionet të cilat e shtojnë nyjen në listën e lidhur dhe paraqesin të dhënat (vlerat) e nyjeve në renditje natyrale dhe të kundërt, si dhefunksionin për asgjësim të listës së lidhur. Si plotësim, klasa LinkedList ka edhekonstruktorin dhe destruktorin.

Kët anëtarë (vlera dhe funksione) janë skeleti i nevojshëm për operim të klasësLinkedList. Tani do të ndërmarrim disa hapa plotësues për të rriturfunksionalitetin e klasës LinkedList duke e bërë më të përdorshme dhe më tëdobishme për punë me aplikacionet me lista të lidhura.

Plotësimi i parë është definimi i anëtarit të r i të të dhënave, të quajtur ‘size’.Anëtari ‘size’ është një numër i plotë (integer) që përfaqëson numrin e nyjeve qëndodhen në listën e lidhur. (Poërdoret indeksi, por ‘size’ përcakton nëse ështëlista e zbrazët ose nëse është përcjellur indeksi jo valid). Kjo vlerë mund të përdoret sa herë që aplikacioni ka nevojë të dijë madhësinë e listës së lidhur.

Pastaj, duhet të definohen funksionet plotësuese, i pari prej të cilave do të jetëfunksioni removeNode() (angl. remove node  –   largo nyjen). FunksioniremoveNode() largon nyjen dhe e rilidhë listën e lidhur. Ky funksion është‘protected’ (i mbrojtur) sepse përdoret vetëm në mënyrë interne (përbrenda).Shfrytëzuesi i kësaj klase nuk do të dinte pointerët në nyjet individuale. Kyështë funksion i përshtatshëm për largimin e nyjeve.

 Një funksion tjetër i dobishëm që do të definohet, është funksioniremoveNodeAt(), i cili e largon nyjen që ndodhet në një lokacion të caktuar tëlistës së lidhur. Radha në të cilën paraqiten nyjet e listës referohet si ‘indexorder’ (renditja e indeksuar). Pozita e nyjes në listën e lidhur referohet si index-ii nyjes. Indeksi përcillet funksionit removeNodeAt() për të specifikuar nyjen ecila do të largohet nga lista e lidhur. Atëherë funksioni removeNodeAt() elargon nyjen dhe i rivendosë lidhjet në listën e lidhur. Nyja në fillim (front) të

Page 244: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 244/699

Avni Rexhepi

244

listës e ka indeksin 0; pastaj indeksi inkrementohet për një, duke lëvizur përparakah fundi (back) i listës. Funksioni appendNode() i shton nyjet në fund (back) tëlistës së lidhur, kështu që kjo mund të imagjinohet si varg dinamik që lëvizë prejfillimit kah fundi.

 Ndonjëherë mund të mos dihet indeksi i nyjes që dëshirojmë të lagrohet nga listae lidhur. Në këtë rast, duhet të definohet një funksion tjetër i cili e largon nyjen bazuar në vlerën (të dhënën) e nyjes, jo në indeksin e nyjes. Ky funksion do tëquhet deleteNode(). Funksioni deleteNode() dallon nga funksioniremoveNodeAt() për nga mënyra se si funksioni e identifikon nyjën që duhet tëlargohet nga lista e lidhur. Funksioni removeNodeAt() e lokalizon nyjen qëduhet larguar duke përdorur vlerën e indeksit të nyjes. Funksioni deleteNode() elokalizon nyjen që duhet larguar duke përdorur vlerën e të dhënës së nyjes, e cilai përcillet funksionit deleteNode().

Deri më tani, nyjet në listën e lidhur janë qasur në mënyrë sekuenciale. Mirëpo,në disa aplikacione reale, nyjet qasen edhe në mënyrë të rastit. Funksioni iardhshëm që do të definohet për klasën LinkedList mundëson qasjen në një nyjespecifike. Ky funksion do të quhet findNode() dhe përdoret kur dihet e dhëna(vlera) e përmbajtur në nyje por nuk dihet pozicioni i nyjës në listën e lidhur(nuk dihet indeksi). Për të lokalizuar nyjen, funksionit i jepet e dhëna (vlera) eruajtur në nyje, kurse funksioni findNode() e kthen indeksin e nyjes.

Lista origjinale LinkedList është e aftë që të shtojë një nyje të re në listën e

lidhur, duke e shtuar atë në fund të listës. Mirëpo, do të ketë situata kur duhet tëinsertohet një nyje e re diku në mest të listës së lidhur. Për të bërë këtë, duhet tëdefinohet funksioni insertNodeAt() (inserto nyjen në). Funksioni insertNodeAt()do të kërkojë dy parametra. Parametri i parë është indeksi i nyjes që do të lëvizet(zhvendoset) në listën e lidhur, për t’i bërë vend nyjes së re. Ky indeks, bëhetindeksi i nyjes së re. Parametri i dytë është vlera (e dhëna) që do t’i ndahet nyjessë re. Funksioni insertNodeAt() krijon nyjen e re dhe përshtatë referencat(pointerët) në listën e lidhur, për të lidhur nyjen e re me nyjet tjera në listën elidhur.

 Një plotësim i rëndësishëm i klasës LinkedList është “nxjerrja” (angl. retrieve-rigjej, rifitoj) e vlerës (të dhënës) që është e ruajtur në nyjen specifike. Më parëkishim dy funksionet ‘display’ të cilat përdoreshin për të “shtypur” të dhënat nëlistën e lidhur. Funksioni i ri, që do të quhet peek() (angl. peek-shikojvjedhurazi, përgjoj). Funksioni peek() kërkon që t’i përcillet indeksi i nyjes që e përmbanë vlerën të cilën dëshironi ta nxirrni (shihni, tërhiqni). Ai pastaj e kthenvlerën (të dhënën) që ndodhet në atë nyje.

Plotësimi i fundit që do të bëhet në klasën LinkedList është definimi i funksionitqë kthen numrin e nyjeve të listës së lidhur. Ky funksion do të quhet getSize()

dhe do të përdoret sa herë që ka nevojë për të ditur madhësinë e listës së lidhur.

Page 245: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 245/699

Algoritmet dhe strukturat e të dhënave

245

 Në vijim do të paraqitet header fajlli i ripërpunuar LinkedList.h, i cili përmbanëdefinicionet e strukturës së nyjes dhe klasës së zgjeruar LinkedList. Vëreni seanëtari ‘size’ dhe funksioni removeNode() janë të vendosur në zonën e qasjes sëmbrojtur (protected) të definicionit të klasës. Kjo për arsye se asnjëri nuk përdoret drejtpërdrejt nga aplikacioni. Në vend të kësaj, ata përdoren ngafunksionet anëtare të klasës LinkedList dhe nga funksionet anëtare të klasave qëe trashëgojnë klasën LinkedList.

Të gjitha funksionet janë vendosur në pjesën zonën me qasje publike tëdefinicionit të klasës LinkedList dhe janë në dispozicion për qasje direkte ngaaplikacioni. Më vonë do të paraqiten detajet për mënyrën e funksionimit tëfunksioneve anëtare.

//LinkedList.h

// Node=Nyje; data=vlera;// previous=ePerparshme; next=eArdhshmestruct 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();

}; 

Page 246: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 246/699

Avni Rexhepi

246

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

Largimi i nyjes nga lista e lidhur është operacion i ndërlikuar. Së pari duhet të“zgjidhet” nyja (të largohen lidhjet e nyjes) nga lista e lidhur. Mirëpo, duke bërë

këtë, prishen lidhjet, sepse më nuk ka asgjë që e lidhë nyjen e përparshme(previuos) dhe nyjen e ardhshme (next), sepse nyja që largohet ishte lidhjandërmjet tyre. Kjo do të thotë që, pas largimit të nyjes, duhet të lidhen mes vetinyja e përparshme dhe nyja e ardshme, e nyjes që largohet.

Klasa LinkedList mund të zgjerohet për të përmbajtur tri funksione të cilat elargojnë nyjen nga lista e lidhur dhe pastaj i lidhin mes veti nyjen e përparshmedhe nyjen e ardhshme të saj. Këto funksione janë: removeNode(),removeNodeAt() dhe deleteNode().

Funksionit removeNode() i përcillet referenca (pointeri) për në nyjen që duhetlarguar nga lista e lidhur dhe thirret nga funksioni removeNodeAt() dhefunksioni deleteNode(). Funksioni removeNode() nuk mund të thirret direkt ngaaplikacioni sepse është anëtarë i mbrojtur i klasës.

Funksioni removeNodeAt() e përdorë indeksin e nyjes për të lokalizuar nyjen qëduhet të largohet. Kur të gjindet nyja, referenca e saj (pointeri në të) i përcilletfunksionit removeNode(). Ngjashëm, funksioni deleteNode() e përdorë vlerën enyjes për të lokalizuar nyjen. Kur të gjindet nyja, funksioni deleteNode() enxjerrë referencën e nyjes (pointerin), i cili pastaj i përcillet funksionitremoveNode().

Për shembujt në këtë pjesë, do të përdoret lista e lidhur e paraqitur në figurën4.16, e cila ka pesë nyje: NodeA deri në NodeE, repsektivisht. Secila nyje embanë pozitën në listën e lidhur dhe secila pozitë identifikohet nga një indeks.Vlerat e indeksave fillojnë prej zeros dhe janë paraqitur përmbi emrin e secilësnyje (në figurën 4.16).

 Figura 4.16 - Lista e lidhur që përmbanë pesë nyje, ku secila nyje identifikohet

 përmes indeksit.

Fillojmë me definimin e funksionit removeNode(), i cili është ilustruar në kodin

Page 247: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 247/699

Algoritmet dhe strukturat e të dhënave

247

vijues. Referenca për në nyjen që do të largohet (pointeri) i përcillet funksionitremoveNode(). Funksioni removeNode() duhet të vendosë se cili prej katër proceseve duhet të përdoret për të larguar nyjen.

Procesi i parë që duhet të përcaktohet (kontrollohet, verifikohet) nga funksioniremoveNode() është nëse nyja është e vetmja nyje në listën e lidhur. Ky e bënkëtë përcaktim duke vlerësuar nëse nyjet: e përparshme (previous) dhe eardhshme (next) janë NULL. Nëse janë, nyja që fshihet është e vetmja nyje nëlistë. Nyja pastaj largohet duke ia ndarë vlerën NULL anëtarëve ‘front’ dhe‘back’ të klasës LinkedList. Si ju kujtohet, funksionet që i “nxjerrin” të dhënatnga lista e lidhur gjithmonë verifikojnë anëtarët ‘front’ dhe ‘back’ për të përcaktuar nëse janë të dy NULL. Nësë po, atëherë funksioni e di se lista elidhur nuk ka asnjë nyje.

 Nëse nyja nuk është e vetmja nyje në listën e lidhur, funksioni removeNode()duhet të verifikojë pastaj nëse nyja që largohet është në fillim (front) të listës sëlidhur. Ai e kontrollon këtë duke verifikuar anëtarin e përparshëm (previuous) tënyjes. Nëse nyja ndodhet në fillim (front) të listës së lidhur, atëherë anëtari‘previuous’ është NULL dhe funksioni removeNode() i ndërmerr hapat vijues për të larguar nyjen:

1.   Nyja në të cilën pointohet nga anëtari anëtari ‘next’ i nyjes së fshirë(larguar), i ndahet anëtarit ‘front’ të listës së lidhur. Kjo e bënë atë‘front’ (fillim) të listës së lidhur.

2.  Anëtarit ‘previous’ të nyjes që tani është në fillim (front) të listës sëlidhur i caktohet vlera NULL, për të treguar se nuk ka nyje të përparshme, sepse është larguar nyja e përparshme e saj. Kjo bëhet si nëvijim. Mund të duket pak konfuze, por është e lehtë të kuptohet nëse endani këtë urdhër:

node->next->previous = NULL;

3.  Le të themi se largohet NodeD. Nyja e ardhshme për të është NodeE.

Tani, zëvendësoni emrat për termat në këtë shprehjeje:

NodeD->NodeE->previous = NULL;

Është e qar të se anëtari ‘previous’ i përket nyjes NodeE.

 Në procesin e tretë, funksioni removeNode() e kontrollon (përcakton) nëse nyjaqë largohet është në fund (back) të listës së lidhur. Këtë e bënë duke krahasuarvlerën e anëtarit ‘next’ (të ardhshëm) me NULL. Nëse anëtari ‘next’ është

 NULL, atëherë nyja që largohet është nyja e fundit në listën e lidhur.

Page 248: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 248/699

Avni Rexhepi

248

Vlera e anëtarit ‘previuous’ (e përparshme) të nyjes pastaj i ndahet anëtarit‘back’ të klasës LinkedList. Kjo e lëvizë nyjen e përparshme në fund të listësdhe në fakt (si rezultat) e largon nyjen që i përcillet funksionit removeNode(),nga lista e lidhur.

Vlera e anëtarit ‘next’ të nyjes së përparshme pastaj caktohet në NULL, për tëtreguar se nuk ka nyje tjetër pas saj sepse ajo është në fund (back) të listës.Urdhëri që e kruen këtë operacion mund të duket konfuz, por duke zëvendësuarreferencat (pointerët) në nyje dhe ‘previuos’ me numrin e nyjes, duhet të jetë eqartë se çka ndodhë. Urdhëri është:

node->previous->next = NULL;

Le të themi se largohet nyja NodeC. Nyje e përparshme (previuous) është NodeB. Tani zëvendësoni emrat për termat në këtë urdhër:

NodeC->NodeB->next = NULL;

 Nëse nyja që fshihet nuk është nyja e vetme në listën e lidhur dhe nuk është nyjanë fillim ose në fund të listës së lidhur, atëherë mundësia e vetmë është që nyjandodhet diku tjetër përbrenda listës.

Procesi i katërt është largimi i nyjes në mes të listës së lidhur dhe pastaj lidhja enyjes së përparshme dhe të ardhsme të saj. Edhe kjo ilustrohet më mirë përmesshembullit.

Le të themi se do të largohet NodeC. Nyje e përparshme është NodeB dhe nyje eardhshme është NodeD. Së pari, lidhet nyja NodeB me NodeD, duke përdorururdhërin vijues:

node->previous->next = node->next;

Zëvendësoni ‘node’, ‘previuos’ dhe ‘next’ me emrat e nyjeve aktuale, për 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;Përsëri, zëvendësoni 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 përcjellur në funksionin removeNode() nuk është më në listën elidhur, ajo akoma mbetet në memorie. Prandaj, ajo duhet të largohet ngamemoria duke thirrur operatorin ‘delete’.

Page 249: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 249/699

Algoritmet dhe strukturat e të dhënave

249

Hapi i fundit është që të përshtatet vlera e anëtarit ‘size’ (madhësia) e klasësLinkedList, për të reflektuar një nyje më pak në listën e lidhur. Kjo do të bëhet përmes dekrementimit të vlerës ‘size’. 

Figura 4.17 paraqet listën e lidhur pas ekzekutimit të funksionit removeNode().Vëreni se NodeC nuk është më në listën e lidhur dhe vlerat e indeksave janë përshtatur për të reflektuar numrin e ri të nyjeve në listën 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--;

}

Page 250: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 250/699

Avni Rexhepi

250

Funksioni removeNodeAt()

Funksioni removeNodeAt() largon nyjen duke përdorur indeksin e nyjes (e jo

 pointerin për në nyje, në memorie). Mbani mend, indeksi është pozita e nyjes nëlistën e lidhur. Le të themi se dëshironi të largoni nyjen e tretë në listën e lidhur.Atëherë, thjeshtë ia përcillni indeksin 2 funksionit removeNodeAt() dheremoveNode() e kryen operacionin në mënyrë interne. Nuk mund ta thirrnidirekt funksionin removeNode(), pjesërisht për arsye se është i mbrojtur, poredhe për arsye se jashtë kësaj klase ju nuk keni njohuri për vlerat aktuale të pointerëve. Pasi që indeksat fillojnë prej zeros, ju nuk keni nevojë të dini pointerin aktual në nyje të cilën dëshironi ta largoni. Kjo është ilustruar në kodinvijues.

Hapi i parë në funksionin removeNodeAt() është të kontrollojë nëse indeksiështë valid. Për të bërë këtë, funksioni removeNodeAt() kontrollon nëse indeksiështë më i vogël se zero ose më i madh se madhësia e listës minus një. Ai e përdorë vlerën e anëtarit ‘size’ të klasës LinkedList për të përcaktuar madhësinëe listës së lidhur. Nëse ndonjëri prej kushteve është ‘true’, atëherë indeksi është jo valid dhe nuk tentohet të fshihet nyja.

Mirëpo, nëse të dy kushtet janë ‘false’, atëherë funksioni removeNodeAt() fillon procesin e largimit të nyjes prej listës. Ky proces i ka dy hapa. Së pari, indeksilokalizon referencën (pointerin) për në nyjen korresponduese dhe së dyti, thirretfunksioni removeNode() dhe i përcillet refernca (pointeri).

Funksioni removeNodeAt() fillon kërkimin për referencën (pointerin) në nyjeduke deklaruar pointerin e përkohshëm për në nyje, të quajtur temp_node dheduke ia ndarë atij referencën (pointerin) për në nyjen në fillim (front) të listës sëlidhur. Pastaj, unaza ‘for’ kalon nëpër secilën nyje në listën e lidhur deri sa tëgjindet nyja e reprezentuar nga indeksi. Gjatë secilës përsëritje (iteracion), pointerit temp_node i ndahet nyja e pointuar nga anëtari ‘next’ i ‘temp_node’aktual.

Kur të arrihet indeksi, vlera e temp_node është referencë (pointer) për në nyjen ecila i përgjigjet (korrespondon) indeksit që i përcjellet funksionitremoveNodeAt(). Thirret funksioni removeNode() dhe i përcillet ‘temp_node’. 

void removeNodeAt(int index){

if(index < 0 || index > size-1){

return;}

Page 251: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 251/699

Algoritmet dhe strukturat e të dhënave

251

Node* temp_node = front;

for(int i=0; i<index; i++){

temp_node = temp_node->next;}

removeNode(temp_node);}

Funksioni deleteNode()

Funksioni deleteNode() përdorë vlerën e ruajtur në nyje për të gjetur dhe larguar

nyjen gjegjëse prej listës së lidhur. Funksioni deleteNode() pastaj kërkon listëne lidhur për të lokalizuar dhe larguar nyjen.

Procesi funksionon si në vijim. Së pari, deklarohet nyja e përkohshme e quajturtemp_node dhe i ndahet referenca (pointeri) për në nyjen që ndodhet në fillim(front) të listës së lidhur. Nëse temp_node nuk është NULL, atëherë lista nukështë e zbrazët dhe funksioni përcakton (kontrollon) nëse vlera (e dhëna në të) përshtatet me vlerën (të dhënën) e nyjes aktuale.

 Nëse po, atëherë temp_node i përcillet funksionit removeNode() dhe funksioni përfundon. Nëse të dhënat (vlerat) nuk përshtaten, atëherë nyja e ardhshme

(next) i ndahet asaj temp_node dhe procesi vazhdon deri sa të gjindet nyja që përmbanë vlerën ose funksioni deleteNode() arrin fundin e listës së lidhur (dmthvlera 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;}

}

}

Page 252: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 252/699

Avni Rexhepi

252

Funksioni findNode()

 Nëse duhet të qaset një nyje e veçantë në listën e lidhur, por ju nuk e dinireferencën (pointerin) për në atë nyje e as pozitën e nyjës në listën e lidhur,

mirëpo e dini vlerën (të dhënën) që ruhet në nyje, atëherë nyja mund tëlokalizohet duke thirrur funksionin findNode() (angl. find-gjej).

Funksioni findNode() kërkon që t’ia përcjellni vlerën e ruajtur në nyje. Ai pastaje përdor vlerën (të dhënën) për të lokalizuar nyjen dhe kthen indeksin e nyjes,siç paraqitet në shembullin vijues.

Procesi i gjetjes së nyjes fillon kur deklarohet një variabël e indeksit të cilës nëfund do t’i ndahet indeksi i nyjes, nëse ajo gjindet. Deklarohet gjithasthtu edhenyja e përkohshme dhe i ndahet referenca (pointeri) për në nyjen në fillim(front) të listës së lidhur.

Gjersa temp_node nuk është NULL, funksioni findNode() iteron (kalon me përseritje, në unazë) nëpër listën e lidhur. Me secilin iteracion (përsëritje) vlera enyjes aktual krahasohet me vlerën e përcjellur si argument në funksioninfindNode().

 Nëse të dyja janë të barabarta, atëherë kthehet vlera aktuale e indeksit, që ështëindeksi i nyjes. Nëse nuk janë të barabartë, atëherë vlera e anëtarit të ardhshëmtë nyjes aktuale i ndahet asaj temp_node dhe inkrementohet indeksi. Nëse vlera(e dhëna) nuk është gjetur në listën e lidhur, kthehet ‘-1’, sepse vlera ‘-1’ nuk

mund të jetë asnjëherë 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;

}

Page 253: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 253/699

Algoritmet dhe strukturat e të dhënave

253

Funksioni insertNodeAt()

Funksioni insertNodeAt() inserton një nyje të re në lokacionin e caktuar të listës

së lidhur. Dihet se secila pozitë në listën e lidhur identifikohet përmes indeksitdhe lokacioni i parë ka vlerën e indeksit 0, i dyti 1, e kështu me radhë. Për tëspecifikuar lokacionin ku dëshironi të vendosni (insertoni, shtoni) nyjen e re nëlistën e lidhur, përdoret indeksi.

Funksioni insertNodeAt() kërkon dy argumente: lokacionin ku do të insertohetnyja në listën e lidhur dhe vlera (e dhëna) që do të ruhet në atë nyje. Shembullinë vijim paraqet mënyrën e vendosjes së nyjes në listën e lidhur.

Hapi i aprë është që funksioni insertNodeAt() të kontrollojë nëse indeksi i

 përcjellur në funksion është valid. Ai e bënë këtë duke kontrolluar nëse indeksiështë më i vogël se zero ose më i madh se madhësia e listës (kjo informatëgjindet në anëtarin ‘size’ të klasës LinkedList). Nëse indeksi është invalid,atëherë funksioni insertNodeAt() ndërprehet dhe kthen përgjigjen në urdhërin qëe ka thirrur. Ka një ndryshim të vogël në rastin e kontrollimit të indeksave, nëkrahasim me funksionin removeNodeAt(). Nëse indeksi do të ishte i barabartëme ‘size’, kjo do të bënte që nyja të shtohet në listën e lidhur. I bie që indeksiështë për 1 jashtë kufijve të indeksave të vargut, por kjo është në rregull, sepsenë fakt në këtë rast do të shtohet një nyje e re në fund të listës së lidhur, ndërsafunksioni removeNodeAt() kërkonte indeks valid në rangun nga 0 deri në ‘size -1’, që është nyja e fundit në listën e lidhur. 

Kur funksioni insertNodeAt() e verifikon se indeksi është valid, ai vazhdon mekrijimin e nyjes së re dhe e inserton nyjën në listën e lidhur. Ky proces fillon mekrijimin në instance të strukturës së nyjes (node) dhe duke i ndarë asaj vlerën e përcjellur në funksion si argument. Kjo instancë pastaj i ndahet pointeritnew_node (nyja e re).

Pastaj, duhet të verifikohet a ka ndonjë nyje në listën e lidhur. Kjo bëhet dukekontrolluar vlerën e anëtarit ‘size’ të klasës LinkedList. Nëse vlera është zero,

atëherë lista e lidhur është e zbrazët dhe nyja e re do të bëhet nyja e parë (evetme) në listë.

 Nyja e re vendoset në listë duke ia ndarë pointerin new_node të dy anëtarëve,‘front’ dhe ‘back’, të klasës LinkedList. Anëtarët ‘previous’ dhe ‘next’ të nyjesveq janë caktuar në NULL (si vlerë e nënkuptuar, default) ashtu që nuk duhet bërë asgjë në nyjen e re.

front = new_node;back = new_node;

Page 254: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 254/699

Avni Rexhepi

254

 Nëse lista e lidhur ka një ose më shumë nyje, atëherë funksioni insertNodeAt()kontrollon nëse nyja e re duhet të insertohet në pozitën e parë në listë, dukevlerësuar vlerën e indeksit të përcjellur në funksion. Nëse vlera e indeksit ështëzero, atëherë nyja e re do të bëhet nyje e parë në listën e lidhur.

Kjo bëhet si në vijim. Nyja e re (new_node) i ndahet anëtarit ‘previous’ (të përparshëm) të ny jes së ndarë anëtarit ‘front; të klasës LinkedList. Pastaj,anëtarit ‘next’ të new_node (nyjës së re) i ndahet nyja e ndarë anëtarit ‘front’ tëklasës LinkedList. Në fund, anëtarit ‘front’ i ndahet new_node. 

front->previous = new_node;new_node->next = front;front = new_node;

 Nëse nyja e re nuk do të bëhet nyje e parë e listës së lidhur, atëherë funksioni

insertNodeAt() vendosë nëse nyja do të bëhet nyje e fundit e listës së lidhur,duke krahasuar indeksin me madhësinë (anëtarin ‘size’) e klasës LinkedList. Nëse këto dy vlera janë të barabarta, atëherë nyja e re vendoset në fund të listëssë lidhur. Rikujtojmë se indeksi 0 është fillimi i listës (front) dhe indeksi (size-1)është fundi i listës (back).

Kjo bëhet si në vijim. ‘new_node’ i ndahet anëtarit ‘next’ të nyjes që aktualishtndodhet në fund të listës, ‘back’. Pastaj, nyja në fund, ‘back’, i ndahet anëtarit‘previous’ (të përparshëm) të nyjes së re. Në fund, nyja e re (new_node) i ndahetanëtarit ‘back’ të klasës LinkedList. 

back->next = new_node;new_node->previous = back;back = new_node;

 Në këtë pikë, nëse nyja e re nuk është insertuar as në fillim (front) e as në fund(back) të listës së lidhur, atëherë funksioni insertNodeAt() supozon se nyja e redo të insertohet diku në pjesën ndërmjet, të listës së lidhur.

Ky proces fillon me deklarimin e pointerit të quajtur ‘temp’ dhe në fillim duke indarë atij nyjen në fillim (front) të listës së lidhur. Pastaj, funksioni e gjenë

nyjen në indeksin e dhënë. Kjo nyje zhvendoset (lëvizet) djathtas (për të krijuarvendin për nyjen e re). sidoqoftë, nuk caktoeht në ‘previous’, caktohet në poiztën e indeksit dhe nyja në atë pozitë lëvizet djathtas. Kjo bëhet duke përdoruunazën ‘for’. Për secilin iteracion, nyja e ndarë anëtarit ‘next’ të nyjes ‘temp’ indahet nyjes ‘temp’. Kjo tingëllon çuditshëm, por do të jetë e qartë kur tëshiqohet se çka po ndodhë. 

Le të themi se janë pesë nyje në listën e lidhur, si është paraqitur në figurën4.16. Nyja e fillimt (front) është NodeA dhe vlera filestare e ‘temp’ është NodeA. Le të themi se dëshironi të insertoni nyjen e re NodeN në pozitën me

indeks 2.

Page 255: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 255/699

Algoritmet dhe strukturat e të dhënave

255

Para iteracionit të parë, front=NodeE. Gjatë iteracionit të parë, ja se çkandodhë:

temp = temp->next

temp = NodeA->NodeBtemp = 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), kështu që ekzekutohet edhe një iteracion.Ja se çka ndodhë:

temp = temp->nexttemp = NodeB->NodeCtemp = NodeC

Tani pointeri ‘temp’ pointon në NodeC dhe vlera e ‘i’-së është 2, që është e barabartë me vlerën e indeksit, kështu që nuk ka më iteracione plotësues dhe pointeri ‘temp’ pointon në NodeC. 

Tani që jemi në lokacionin e dëshiruar në listën e lidhur, është koha që tëshkëmbehen pointerët përreth, për të insertuar nyjen e re në listë. Ja si bëhet kjo:

new_node->next = temp;new_node->previous = temp->previous;temp->previous->next = new_node;temp->previous = new_node;

Për sqarim më të lehtë, le të ndërrojmë nyjet për pointerët:new_node->next = NodeC;new_node->previous = NodeC->NodeB;NodeC->NodeB->next = new_node;NodeC->previous = new_node;

Figura 4.18 paraqet listën e lidhur pas insertimit të nyjes së re NodeN, në pozitën me indeksin 2 në listë.

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

në listën e lidhur.

Page 256: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 256/699

Avni Rexhepi

256

Hapi i fundit është që të inkrementohet anëtari ‘size’ (madhësia) e klasësLinkedList, për të reflektuar nyjën e re. Definicioni i plotë i funksionitinsertNodeAt(), ë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++;

}

Page 257: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 257/699

Algoritmet dhe strukturat e të dhënave

257

Funksioni peek()

Funksioni peek() ‘nxjerrë’ (lexon, tërheqë) vlerën e ruajtur në nyjen especifikuar përmes indeksit që i përcillet funksionit peek(). Funksioni peek() e

kërkon një argument, i cili është indeksi i pozitës në listën e lidhur i cili përmbanë vlerën (të dhënën) që duhet nxjerrur. Në shembullin vijues do të ruhetdhe pastaj do të “lexohet” një vlerë  e tipi integer, por mund të ruhet dhe“lexohet” çfarëdo tipi i të dhënave, vetëm duke e ndryshuar tipin e të dhënave nëdefinicionin e nyjës.

Le të shohim si punon funksioni peek(). Ai fillon me vlerësimin e indeksit duke përdorur procedurën e njëjtë të vlerësimit sikur ajo e diskutuar në funksioninremoveNodeAt(), me ndryshimin që funksioni peek() i verifikon vlerat brendarangut. Nëse indeksi është jovalid, atëherë funksioni kthen zero.

 Nëse indeksi është valid, atëherë në fillim deklarohet pointeri i emërtuar ‘temp’dhe i ndahet nyja e cila ndodhet në fillim (front) të listës së lidhur. Funksioni peek() pastaj vazhdon të lëvizë nëpër listën e lidhur deri sa të arrinë tek nyja nëtë cilën jemi të interesuar. Ky proces është i njëjtë me atë në funksionininsertNodeAt().

Kur funksioni peek() del prej unazës ‘for’, pointeri ‘temp’ pointon në nyjen ecila ka vlerën (të dhënën) që e kthen funksioni peek(). Pastaj pointoni në vlerëne nyjes në urdhërin return, për të kthyer vlerën (të dhënën) tek urdhëri i cili e kathirrur 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;}

Page 258: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 258/699

Avni Rexhepi

258

Funksioni getSize()

Funksioni getSize() e merr vlerën e anëtarit ‘size’ të klasës LinkedList. Do tëvëreni se ky funksion ka vetëm një urdhër, i cili thjeshtë e kthen vlerën e anëtarit

‘size’. Shtrohet pyetja, atëherë përse duhet funksioni getSize(), pasi që do të kishtemundësi që anëtari ‘size’ të ketë qasje publike, duke u vendosur në pjesën publike ët klasës dhe do të lexohej direkt nga aplikacioni! Kjo për arsye seanëtarët e klasës duhet të kenë qasje vetëm nga funksionet anëtare përbrendaklasës ose nga klasët e derivuara (trashëguese). Në këtë mënyrë, ju gjithmonëkontrolloni qasjen në të dhëna dhe i mbroni ato edhe nga ndryshimet e paqëllimshme. Lejimi i ndryshimit nga jashtë, nga shfrytëzuesit e klasës, do tëmund të dërgonte në gabime.

int getSize(){

return size;}

Klasa e zgjeruar LinkedList

Pasi u pa se si funksionojnë zgjerimet individuale të klasës LinkedList, tashohim në vazhdim aplikacionin në tërësi. Aplikacioni është ndarë në tre fajlla:

demo.cpp, LinkedList.h dhe LinkedList.cpp. të tre fajllat janë paraqitur në kodinnë vijim. Fajllat LinkedList.h dhe LinkedList.cpp mund të përdoren me fajllaspecifik për stek dhe queue, që janë parë më herët.

Fajlli demo.cpp përmbanë aplikacionin në C++ i cili përdorë klasën e zgjeruar(plotësuar) LinkedList, për të krijuar dhe manipuluar listën e lidhur. FajlliLinkedList.h përmbanë definicionet e nyjes dhe klasës LinkedList. FajlliLinkedList.cpp përmbanë definicionet e funksioneve anëtare të klasësLinkedList.

 Në fajllin demo.cpp zhvillohet “aksioni”. Aplikacioni fillon me deklarimin einstancës së klasës LinkedList dhe pastaj ndarjen e instancës pointerit të quajtur‘list’. 

Pastaj, thirret pesë herë funksioni appendNode(), i cili është funksion origjinal iklasës LinkedList dhe shton nyje të re në listën e lidhur. Lista e lidhur e krijuar pas thirrjes së fundit të funksionit appendNode() duket si ajo në krye të figurës4.19

Page 259: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 259/699

Algoritmet dhe strukturat e të dhënave

259

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

 Në mes ëshët lista pas thirrjes së funksionit removeNodeAt(3).

 Në fund, lista pas thirrjes së funksionit deleteNode(20).

Kur të jetë krijuar lista e lidhur, aplikacioni thërret funksionin removeNodeAt(3) për të larguar nyjen e lokalizuar në indeksin 3.

Pastaj, aplikacioni e thërret funksionin findNode(20), për të lokalizuar indeksine nyjës që përmbanë vlerën 20 (e dhëna e saj, është 20). Bazuar në listën e paraqitur në figurën 4.19, funksioni findNode(20) kthen vlerën e indeksit 1.

Pastaj thirret funksioni deleteNode(20) thirret dhe largon nga lista e lidhur nyjëne cila ka vlerën 20, si element i të dhënës së saj. Lista e lidhur e paraqitur nëfund të figurës 4.19 ilustron gjendjen pas thirrjes së funksionit deleteNode(20).

Pastaj, në listën e lidhur insertohet një nyje e re, duke thirrut funksionininsertNodeAt(1,35). Ky funksion inserton një nyje të re në indeksin 1 në listën elidhur dhe ia ndanë vlerën 35. Figura 4.20 paraqet listën e lidhur pas thirrjes sëfunksionit insertNodeAt(1,35).

 Në vazhdim, thirret funksioni peek(3) për të nxjerrë (lexuar, shikuar) vlewrn enyjës në pozitën me indeks 3, në listën e lidhur. Bazuar në listën e paraqitur nëfigurën 4.20, funksioni peek(3) do të kthejë ‘50’ si vlerë (e dhënë) e nyjës në pozitën me indeks 3.

Page 260: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 260/699

Avni Rexhepi

260

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

Funksioni i fundit që thirret është funksioni getSize(), i cili kthen madhësinë elistës së lidhur. Si shihet në fig. 4.20, lista e lidhur ka katër nyje, prandajfunksioni getSize() kthen vlerën 4.

Urdhëri i fundit në aplikacionin ‘demo’ përdorë operatorin ‘delete’ për të larguar

instancën e klasës LinkedList nga memoria.//demo.cpp#include <iostream>using namespace std;

//LinkedList.h// Node=Nyje; data=vlera;// previous=ePerparshme; next=eArdhshmestruct 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);

Page 261: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 261/699

Algoritmet dhe strukturat e të dhënave

261

int peek(int);int getSize();

};//LinkedList.cpp

//#include "LinkedList.h" (Nese i bartim ne header fajll te veçant)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;

}

Page 262: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 262/699

Avni Rexhepi

262

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;

Page 263: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 263/699

Algoritmet dhe strukturat e të dhënave

263

}

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 nyjesreturn index;

}else{

temp_node = temp_node->next;index++;}

}

return -1;}

void LinkedList::deleteNode(int data){

Node* temp_node = front;

Page 264: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 264/699

Avni Rexhepi

264

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

Page 265: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 265/699

Algoritmet dhe strukturat e të dhënave

265

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

Page 266: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 266/699

Avni Rexhepi

266

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 për stek dhe queue në mënyrë efikase. Mirëpo, vargjet janëmë efikase në aspekt të keshit (angl. cash), ndërsa listat ofrojnë garanca për performansën “e rastit më të keq”. 

Lista e lidhur njëfish mund të garojë me listën e lidhur dyfish në shumicën easpekteve. Avantazhi i vetëm i vargjeve ciklike ndaj vargjeve të pakufizuaraështë  se ato mund të implementojnë operacionet  pushFront dhe  popFront nëmënyrë efikase.

Efikasiteti hapsinor është poashtu çështje jotriviale. Lista e lidhura janë shumëkompakte, nëse elementet janë shumë më të mëdhaja sesa pointerët. Për tipe tëelementeve të vogla, vargjet janë zakonisht më kompakte, sepse nuk kambingarkim për pointerët. Kjo është sigurisht e vërtetë nëse madhësitë evargjeve janë të njohura paraprakisht ashtu që të mund të përdoren vargjet ekufizuara. Vargjet e pakufizuara, kanë një “pazar" ndërmjet efikasitetithapësinor dhe kopjimit të mbingarkimit gjatë realokimit.

Radha me prioritet - Priority queue ADT

 Në praktikë shpesh kemi të bëjmë me prioritete. Për shembull, në listën eobligimeve ditore, secila detyrë ka një rëndësi (peshë) të caktuar. Është më merëndësi të dërgohet në servis vetura që ka nevojë për riparim, sesa të shikohetnjë film i ri. Përveq shembujve nga jeta e përditshme, edhe detyrat e ndryshmenë kompjuter, punojnë sipas prioritetit. Një shembull i përmendur shpesh ështëalgoritmi i Dijkstra-s për shtegun më të shkurtër (Dijkstra’s Shortest PathAlgorithm). ADT e radhës së pritjes na mundëson të punojmë ne objektet të cilatkanë të shoqëruar një prioritet të caktuar.

 Në aplikacione kemi çiftin (priority, item) (angl. priority-përparësia, prioritet;

angl. item-send, artikull, element), ku elementi është një e dhënë ndihmëse me

Page 267: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 267/699

Algoritmet dhe strukturat e të dhënave

267

të cilën është shoqëruar prioriteti. Për të ruajtur thjeshtësinë, lëmë anash prioritetet dhe marrim në konsiderim që për elementet e1, e2: e1 < e2 do të

thotë që e1 ka prioritet më të lartë se e2. 

Operacionet

  PriorityQueue create() krijon radhën e zbrazët të prioritetit

  boolean isEmpty(PriorityQueue pq) tregon nëse radha e prioritetit pq është e zbrazët 

  insert(PriorityQueue pq, Item e) inserton elementin e në radhën e prioritetit pq 

  Item minimum(PriorityQueue pq) tregon elementin minimal në radhën e prioritetit pq  Parakusht: pq nuk është zbrazët

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

  destroy(PriorityQueue pq) asgjëson radhën e prioritetit pq 

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

Page 268: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 268/699

Avni Rexhepi

268

5. Rekursioni

Rekursion është teknikë e ndarjes së problemit në nënprobleme të tipit të njëjtë.Shembull i përshtatshëm i sqarimit është llogaritja e faktorielit.

Llogaritja e faktorielit

Faktorieli i n, që shënohet si n!, është produkti i numrave të plotë: n*(n-1)*(n-2)...3*2*1, apo e paraqitur në renditjen e kundert, produkti i numrave prej 1 derinë n. Për shembull, 5! = 5*4*3*2*1=120, ose në renditjen e kundërt,5!=1*2*3*4*5=120

Rekursioni është një prej teknikave të llogaritjes së faktorielit. Në fakt, 5!=5*4!.

Pra, për të llogaritur faktorielin e n, duhet të llogarisim faktorielin e (n-1). Për tëllogaritur faktorielin e (n-1), algoritmi duhet të gjejë (n-2)!, e kështu me radhë.Procesi i përshkruar do të zgjaste pafundësisht, sepse nuk është definuar akomarasti themelor  (rasti bazë). Rasti bazë është kushti kur duhet të ndaletrekursioni. Në rastin e faktorielit, rasti bazë është n=1, për 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)

if (n<=1)

return 1;

  else

return n * Faktoriel(n-1);

int Faktoriel(int n)

if (n<=1)

return 1;

  else

return n * Faktoriel(n-1);

int Faktoriel(int n)

if (n<=1)

return 1;

  else

return n * Faktoriel(n-1);

n=3

n=2

n=1

3!=6

return 1;

return 2*1;

return 3*2;

 

Page 269: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 269/699

Algoritmet dhe strukturat e të dhënave

269

Përparësitë dhe të metat e rekursionit

Përparësia kryesore e rekursionit është thjeshtësia e programimit. Gjatë përdorimit të rekursionit, programeri mundet të lërë anash për një kohë

 problemin e tërë dhe të koncentrohet në zgjidhjen e rastit aktual, rastit bazë.Pastaj, duke u kthyer prapa tek problemi i tërë, zvhillohen rastet bazë (sepseështë e mundur të ketë më shumë se një rast bazë) dhe pika hyrëse për rekursion.

 Në anën tjetër, rekursioni ka disavantazhin serioz të përdorimit të sasisë sëmadhe të memories. Për më tepër, për shumicën e gjuhëve programuese,rekursioni përdorë stekun për ruajtjen e gjendjeve të të gjitha thirrjeve rekurziveaktuale. Madhësia e stekut mund të jetë shumë e madhe, por megjithatë ekufizuar. Prandaj, rekursioni shumë i thellë mund të rezultojë me tejngarkim tëstekut (Stack Overfloë). Për të zgjidhur këtë problem rekursioni mund të

simulohet, duke përdorur unazat dhe stekun.

Rekurzioni është koncept themelor në matematikë dhe në shkencatkompjuterike. Definicioni i thjeshtë është se programi rekurziv në gjuhë programuese është ai i cili e thërret vetveten (njësoj si funksioni rekurziv nëmatematikë që është ai funksion i cili është i definuar në terma të vetvetes).Programi rekurziv nuk mund ta thërrasë vetveten përgjithmonë ose përndryshenuk do të ndalonte kurrë (njësoj si funksioni rekurziv që nuk mund të definohetnë terma të vetvetës përgjithmonë, sepse do të bëhej qarkor, cirkularë), kështuqë elementi tjetër përbërës 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 ndërlidhur me studimin e strukturave rekurzive tënjohura si pemë (angl. tree). Pemët do të përdoren edhe për të ndihmuar nëkuptimin e analizimin e programeve rekurzive edhe si struktura eksplicite të tëdhënave. Lidhja ndërmjet programeve rekurzive dhe pemëve përdoret ashtu që pemët përdoren për të kuptuar programet rekurzive dhe programet rekurzive përdoren për të ndërtuar pemët. Gjithashtu ndërlidhja fundamentale mes pemëvedhe rekurzionit përdoret për analizë të algoritmeve. Rekurzioni ndihmon nëzhvillimin e algoritmeve dhs strukturave të të dhënave elegante dhe efikase përtë gjitha llojet e aplikacioneve.

 Në këtë pjesë do të analizojmë programet rekurzive dhe strukturat e të dhënave,si pajisje praktike. Do të diskutohet relacioni ndërmjet rekurrencës matematikedhe programeve të thjeshta rekurzive, skemën themelore rekurzive të njohur si“divide and coquer” (përçaj dhe sundo), e cila përdoret për zgjidhjen e shumë problemeve. Pastaj do të shqyrtohet qasja e përgjithshme e implementimit të programeve rekurzive e njohur si programimi dinakim, e cila ofron zgjidhje

Page 270: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 270/699

Avni Rexhepi

270

efektive për një klasë të gjerë të problemeve. Në fund, do të shqyrtohen pemët,tiparet e tyre matematike dhe algoritmet e shoqëruara me pemët, si përshkimi i pemës, të cilat qëndrojnë nën programet rekurzive për procesim të pemëve, e poashtu edhe algoritmet rekurzive për procesim të grafeve, në veçanti “depth-

first search” (kërkimi thellësia-së pari), që shërben si bazë për shumë algoritmetë procesimit të grafeve.

Shumë algoritme interesante thjeshtë shprehen me metoda rekurzive dhe shumëdizajnerë të algoritmeve preferojnë shprehjen e metodave në mënyrë rekurzive,edhe pse për shumë raste mund të krijohen edhe alternativat jorekurzive, të cilatarrijnë të njëjtin rezultat përmes një sekuence të llogaritjeve.

Algoritmet rekurziveAlgoritëm rekurziv është ai që e zgjidhë problemin duke zgjidhur një ose më

shumë instanca më të vogla të të njëjtit problem. Për të implementuaralgoritmeve rekurzive në C++, përdoren funksionet rekuzive (funksionet të cillate therrasin vetveten), të cilat i korrespondojnë definicioneve rekurzive tëfunksioneve matematike. Në fillim do të shohim shembuj të rekurzionet dukeanalizuar programet të cilat në mënyrë direkte vlerësojnë funksionetmatematike, e pastaj mekanzimat themelorë zgjerohen për të ofruar një paradigmë të përgjishme programimi.

Funksioni rekurziv për llogaritje të funksionit N!,që përdorë definicioninstandard rekurziv është si në vijim. (Kthen vlerë korrekte kur thirret për N

 jonegativ dhe mjaft të vogël, që mund të shprehet si int).

Programi 5.1. Funksioni i faktorielit – implementimi rekurzivint Faktoriel(int N){if (N == 0) return 1;return N* Faktoriel (N-1);

}

Relacionet e rekurrencës janë funksione të definuara në mënyrë rekurzive

(Shiko shtojcën për rekurrencën, në fund të librit). Relacioni i rekurrencësdefinon 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 definohetsi relacion i rekurrencës:

N!=N *(N – 1)!, për N ≥ 1 me 0! = 1.

Ky definicion i përgjigjet direkt funksionit rekurziv në C++, që është një unazë ethjeshtë, që kruen llogaritjen e njëjtë:

for (t = 1, i = 1; i <= N; i++)

Page 271: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 271/699

Algoritmet dhe strukturat e të dhënave

271

t *= i;

Si do të shihet, gjithmon është e mundshme që programi rekurziv tëtransformohet në program jorekurzivm, që kryen llogaritjen e njëjtë. Në anën

tjetër, pashtu mund të shprehim pa unazë seciliën llogaritje që përfshinë përdorimin e unazave, duke përdorur rekurzionin.

Rekurzioni përdoret pasi që shpesh mundëson shprehjen e algoritmevekomplekse në formë kompakte, pa sakrifikuar efikasitetin. Për shembull,implementimi rekurziv i funksionit të faktorielit shmanjgë nevojën për variablalokale. Kostoja e implementimit rekurziv mbahet nga mekanizmat e programit tëcilët përkrahin thirrjet e funksioneve, të cilat janë ekuivalente me stekun e brendshëm. Shumë gjuhë programuese moderne kanë krijuar me kujdesmekanizmat për këtë detyrë. Përkundër avantazheve, shumë lehtë mund të

ndodhë që shkruhet funksioni rekurziv i cili është jashtëzakonisht joefikas, prandaj duhet kujdes i madh dhe ushtrime të shumta për të evituarimplementimet e vështira.

 Nëse argumenti N është tek, funksioni e thërret vetventë me argumentin 3N+1;nëse N është çift me argumentin N/2. Me metodën e induksionit nuk mund tëvërtetojmë se ky program ngalet, sepse jo të gjitha thirrjet rekurzive thërrasin përdorimin me argument më të vogël se ai i dhënë.

 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 karakteristikën themelore të programeve rekurzive: thirrja e vetvetes, me vlerë më të vogël të argumentit dheka kushtin e ndërprerjes (ndalimit) në të cilin rezultati llogaritet direkt. Për tëqenë të bindur në saktësi, mund të përdorim metodën e induksionit, për vërtetuarse programi funksionon ashtu si synohet:

  Ai e llogaritë 0! (bazën).   Nën supozimin se llogaritë k! për k < N (hipoteza induktive), ai e

llogaritë N!.

Duke arsyetuar në këtë mënyrë, mund të ofrojmë një shteg të shpejtë të

zhvillimit të algoritmeve të cilat zgjidhin probleme komplekse.

Page 272: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 272/699

Avni Rexhepi

272

 Në gjuhët programuese si C++, ka disa kufizime në llojet e programev qëshkruhen, por përpiqemi të kufizojmë veten në përdorimin e funksioneverekurzive në ato të cilat mishërojnë provën induktive të korrektësisë. Jemi tëinteresuar që të krijojmë programe komplekse për probleme të vështira dheduhet të kemi siguri që detyra do të zgjidhet si duhet. Mekanizmat si funksionetrekurzive mund të ofrojnë siguri të tillë duke dhënë implementime kompakte.Thënë ndryshe, lidhja me induksionin matematik na tregon se duhet tësigurohemi që funksionet rekurzive kënaqin (plotësojnë) dy kushte themelore:

  Duhet të zgjidhin rastin bazë në mënyrë eksplicite.  Secila thirrje rekurzive duhet të përfshijë vlera më të vogla të

argumeteve.

Këto janë kushte të nënkuptojnë që duhet të kemi prova valide induktive përsecilin funksion rekurziv që shkruhet dhe shërbejnë si udhëzues i dobishëm nëzhvillimin e zgjidhjeve të problemeve.

 Një prej algoritmeve më të vjetra të njohura, që daton para më shumë se 2000vitesh, është metoda rekurzive e gjetjes së plotpjestuesit më të madh të përbashkët për 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 //perbashketint 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;

Page 273: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 273/699

Algoritmet dhe strukturat e të dhënave

273

Duke pasur parasysh kushtet e përmendura, analizojmë programet 5.2 dhe 5.3.

Programi 5.2 është një shembull interesant që ilustron nevojën për një argumentinduktiv. Është funksion rekurziv që then rregullën e thirrjes rekurzive me

 përfshirje të vlerës më të vogël të argumentit, kështu që nuk mund të përdoriminduksionin matematik për ta kuptuar atë. Në të vërtetë, nuk është e ditur nësellogaritja e tij përfundon për çdo N, nëse nuk ka kufizime për madhësinë e N-it.Për numra të plotë të vegjël, që mund të shprehen si int, mund të verifikojmë që programi përfundon (si në Fig. 5.1), mirëpo për numra të mëdhënj (p.sh., 64 bitësh), nuk e dijmë nëse ky program shkon në unazë të pafund.

Kjo sekuencë e ndërthurur e thirrjes së funksioneve në fund ndalet, por nukmund ta provojmë (vërtetojmë) se funksioni rekurziv në programin 5.2 nuk kandërthurje të thellë arbitrare për disa argumente. Preferohen programet rekurzive

të cilat gjithmonë thërrasin 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, për gjetjen e plotpjestuesit më të madh të përbashkët të dy numrave të plotë. Është i bazuar nëvrojtimin që pjestuesi më i madh i përbashkët për dy numra të plotë x dhe y, kux>y, është i njëjtë me pjestuesin më të madh të përbashkët të y dhe x%y (% -moduli; mbetja nga plotëpjestimi i x me y). Një numër t, i pjeston dy numrat xdhe y nëse dhe vetëm nëse t pjeston të dy, y dhe x%y, sepse x është i barabartëme x%y plus shumëfishi i y. thirrjet rekurzive të bëra për një shembull të këtij programi, janë paraqitur në figurën 5.2. Për algoritmin e Euklidit, thellësia erekurzionit varet nga tiparet aritmetike të argumenteve të tij (është e njohur të jetë logaritmik).

Kjo sekuencë e ndërthurrur e thirrjeve të funksionit ilustron operacioniet ealgoritmit të Euklidit që zbulon se 314159 dhe 271828 janë relativisht numra primar.

Page 274: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 274/699

Avni Rexhepi

274

Figura 5.2. Shembull i algoritmtit të Euclid-it

Programi 5.4 është një shembull me thirrje të shumëfishta rekurzive. Është një

vlerësues tjetër 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 ndërmjet disa programeve të tilla.

Për të vlerësuar shprehjet me prefiks, ose i konvertojmë numrat nga ASCII në binar (në unaën while në fund) ose kryejmë operacionin e treguar nga karakterii parë në shprehje në të dy operandet, të vlerësuar rekurzivisht. Funksioni ështërekurziv, por e përdorë vargun global që përmbanë shprehjen dhe një indeks tëkarakterit aktual në shprehje. Indeksi avanson pas secilës nënshprehje tëvlerësuar.

 Programi 5.4. Programi rekurziv për vlerësim 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ë figurën 5.3 është paraqitur operacioni i programit 5.4 në një shembull tëshprehjes me prefiks. Thirrjet e shumëfishta rekurzive maskojnë seritëkomplekse të llogaritjeve. Sikur shumica e programeve rekurzive, ky program

kuptohet më së miri induktivisht: duke supozuar se punon si duhet për shprehje

Page 275: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 275/699

Algoritmet dhe strukturat e të dhënave

275

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

Figura 5.3. Shembull i vlerësimit të shprehjes me prefiks

Kjo sekuencë e ndërthurrur e thirrjeve të funksioneve ilustron operacionin evlerësimit të algoritmit rekurziv të shprehjeve prefiks, në një shembull të

shprehjes. Për thjeshtësi, janë paraqitur argumentet e shprehjes. Vetë algoritmiasnjëherë nuk e vendosë shtrirjen (gjatësinë) e argumentit string të tij, por aimerr atë që i nevojitet nga pjesa e përparme e stringut.

 Një provë induktive precize se programi 5.4 e vlerëson shprehjen si duhet ështëshumë më e vështirë për t’u shkruar sesa provat për funksionet me argumenteintegjer, që janë diskutuar më parë, e do të hasen edhe programe rekurzive dhestruktura të të dhënave të cilat janë edhe më të komplikuara se kjo. Prandaj, në përputhje me rrethanat, nuk synohet qëllimi idealistik i orfrimit të provavekomplete induktive ose korrektësi për secilin program rekurziv. Në këtë rast,

aftësia e programit për të “ditur” se si të ndajë operandët që i përgjigjenoperatorëve të dhënë duke fillimisht misterioze (ndoshta pasi që nuk mund tëshohim menjëherë se si të bëhet kjo ndarje në nivelin më të lartë), por në faktështë llogaritje e drejtpërdrejtë (sepse rruga që duhet ndjekur në secilën thirrje tëfunksionit është padyshim e përcaktuar nga karakteri i parë në shprehje).

 Në parim, mund të zëvendësomë unazën for me një program rekurzivekuivalent. Shpeshherë, programi rekurziv është mënyrë më natyrale për tëshprehur llogaritjen sesa unaza for, kështu që mund të përfitojmë ngamekanizmi i siguruar nga gjuha programuese që përkrahë rekursionin. Mirëpo,

ka një kostro të fshehur, të cilën duhet ta kemi parasysh. Si u tha më parë, kur

Page 276: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 276/699

Avni Rexhepi

276

ekzekutohet programi rekurziv, bëhet ndërthurrja e thirrjes së funksionit(funksion brenda funksionit), gjersa të arrihet në pikën kur nuk ka më thirrjerekurzive dhe kthehemi te prapa. Në shumicën e ambienteve programuese,funksionet e tilla implementohen përmes përdorimit të stekut. Në këtë pjeseanalizohen implementimet e këtilla. Thellësia e rekurzionit është shkallamaksimale e ndërthurrjes së thirrjeve të funksioneve përgjatë llogaritjes. Në përgjithsi, thellësia varet nga hyrja. Për shembull, thellësia e rekurzionit përshembujt e paraqitur në figurën 5.2 dhe 5.3 është 9 dhe 4, repsektifisht. Kur përdoren programet rekurzive, duhet marrë parasysh se ambienti programuesduhet të mirëmbajë edhe stekun me madhësi proporcionale me thellësinë erekurzionit. Për probleme të mëdha, hapësira e nevojshme për stek mund të pengojë përdorimin e zgjidhjes rekurzive.

Strukturat e të dhënave të ndërtuara prej nyjeve me pointerë, janë qenësishtrekurzive. Për shembull, definicioni i listave të lidhura është rekurziv. Prandaj, programet rekurzive ofrojnë implementime natyrale në shumë funksione tëzakonshme, për manipulimin e strukturave të tilla të të dhënave. Programi 5.5, i paraqitur në vazhdim, përmbanë katër shembuj. Implementimet e tilla janëshumë më të lehta për t’u kuptuar, sesa homologët e tyre jorekurziv. Sidoqoftë,gjatë përdorimit të programeve të tilla në rastin e procesimit të listave, duhet pasur kujdes sepse thellësia e rekurzionit për këto funksione mund të jetë proporcionale me gjatësinë e listës, kështu që hapësira e kërkuar për stekunrekurzive mund të bëhet pengesë.

Këto funksione rekurzive për procesim të thjeshtë të listave janë të thjeshta përt’u shprehur, por mund të mos jenë të dobishme për lishta shumë të mëdhasepse thellësia e rekurzionit mund të jetë proporcionale me gjatësinë e listës.

Funisioni i parë, count, numëron numrin e nyjeve të listës. I dytir, traverse,thërret funksionin visit për secilën nyje në listë, prej fillimit deri në fund. Këtody funksione janë të dyja të lehta për t’u implementuar me unaza. Funksioni itretë, traverseR, nuk ka homologun e tij të thjeshtë iterativ. Ai e thërretfunksionin visit për secilën nyje në listë, por në renditje të kundërt.

Funksioni i katërt, remove, i largon prej listës të gjitha nyjet që kanë elementine dhënë. Çelësi i implementimi është ndryshimi i lidhjes x=x->next në paraardhësin e secilës nyje që fshihet, që është bërë e mundur me përdorimin e parametrit referencë. Ndryshimet strukturore të secilës përsëritje të unazëswhile janë të njëjtëa si ato të paraqitura në figurën 3.3., por x dhe t, të dyja ireferohen nyjës së njëjtë.

Page 277: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 277/699

Algoritmet dhe strukturat e të dhënave

277

 Programi 5.5. Shembuj të funksioneve rekurzive për listat e lidhura

//count=numëro, link=lidhje, traverse=pershko, remove=largoint 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ë mënyrë automatike e detektojnë dhe eliminojnërekurzionin e fundit, kur veprimi i fundit është thirrje rekurzive e funksionit,sepse ai nuk është domosdoshmërisht i nevojshëm në mënyrë strikte për tështuar thellësinë e rekurzionit, në raste të tilla. Ky përmirësim do tëtransformonte në mënyrë efektive funksinet për numërim, përshkim dhe largimnë programin 5.5, në unaza, por nuk aplikohet në funksionin për përshkimin nëdrejtim të kundërt.

 Në vazhdim do të algoritmet rekurzive “Përçaj e sundo”, të cilat prezentojnë paradigma themelore të llogaritjeve, si dhe do të shqyrtohen strukturat e tëdhënave të cilat shërbejnë si bazë për një numër të madh të algoritmeve.

Page 278: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 278/699

Avni Rexhepi

278

5.2. Algoritmet përçaj-e-sundo

Shumë algoritme rekurzive përdorin dy thirrje rekurzive, secila prej të cilaveoperon në afërsisht gjysmën e hyrjes (vlerave hyrëse). Kjo skemë rekurzive

ndoshta është instanca më e rendësishme e paradigmës së mirnjohurës “divideand conquer” (përçaj e sundo), për dizajnin e algoritmeve, e cila shërben edhe si bazë për shumë algoritme të rëndësishme.

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ë nëpër tërë vargun, si vijon:

for (t = a[0], i = 1; i < N; i++)

if (a[i] > t) t = a[i];

Zgjidhja rekurzive përçaj-e-sundo e dhënë në vazhdim (Programi 5.6) është poashtu një algoritëm i thjeshtë (tërësisht i ndryshëm) për të njëjtin problem dhezakonisht përdoret për të ilustruar konceptin përçaj-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 pjesët (rekurzivisht) dhe kthen elementinmë të madh prej tyre si maksimumi i tërë vargut. Nëse madhësia e vargut ështëçifte, të dy pjesët janë të ndara në pjesë të barabarta, përndryshe njëra pjesë kanjë element më shumë.

 Programi 5.6. Divide-and-conquer për 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 përçaj-e-sundo më së shpeshti përdoret pasi që ofron zgjidhje më tëshpejtë sesa algoritmet e thjeshta iterative, por është poashtu edhe e rëndësishme për ekzaminim të afërt të mënyrës së kuptimit të natyrës së disa llogaritjevefundamentale.

Figura 5.4 paraqet thirrjet rekurzive të bëra në programin 5.6. Edhe pse strukturaduket e komplikuar, s’ka arsye për shqetësim, pasi që për të vërtetuar se programi funksionon bazohemi në induksion, kurse për analizë të performansës përdoret relacioni i rekurrencës.

Page 279: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 279/699

Algoritmet dhe strukturat e të dhënave

279

Kjo sekuencë e thirrjeve të funksionit ilustron dinamikën e gjetjes sëmaksimumit me algoritëm rekurziv.

Figura 5.4. Qasja rekurzive për gjetjen e maksimumit

Si zakonisht, vetë kodi sygjeron vërtetimin me induksion, se ai kryen llogaritjene duhur:

  Ai e gjenë maksimumin e vargut me gjatësi 1 në mënyrë eksplicite dhemenhëherë.

  Për N>1, e ndanë vargun në dy vargje me gjatësi më të vogël se N, egjenë maksimumin e dy pjesëve sipas hipotezës induktive dhe kthenvlerën më të madhe prej këtyre dy vlerave, e cila duhet të jetë vleramaksimale e tërë vargut.

Për më tepër, mund të përdoret struktura rekurzive e programit, për të kuptuarkarakteristikën e performansës së tij.

Page 280: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 280/699

Avni Rexhepi

280

Vetia 5.1. Funksioni rekurziv i cili e ndanë problemin e madhësisë N në dy

pjesë të pavarura (jo të zbrazëta) të cilat i zgjidhë në mënyrë rekurzive, e

thërret vetvetën më pak se N herë.

 Nëse pjesët janë njëra me madhësi k dhe tjetra me madhësinë N-k, atëherënumri total i thirrjeve rekurzive është:

TN = Tk + TN–k + 1, për N ≥ 1 me T1 = 0.

Zgjidhja T N = N –  1 është e drejtpërdrejtë, përmes induksionit. Nëse madhësitëmbledhen deri në vlerën më të vogël se N, prova se numri është më i vogël se N-1 e përcjellë të njëjtin argument induktiv.

Programi 5.6 është reprezentues për shumë algoritme përçaj-e-sundo me

strukturë saktësisht të njëjtë, por rastet tjera mund të dallojnë në dy aspekte primare. Së pari, programi 5.6 kryen sasi konstante të punës në secilën thirrje tëfunksionit, kështu që koha e tij totale e ekzekutimit është lineare. Agloritmettjera përçaj-e-sundo mund të kryejnë më shumë punë në secilën thirrje tëfunksionit (siç do të shihet në raste të tjera), kështu që përcaktimi i kohës totaletë ekzekutimit kërkon analizë më të ndërlikuar. Koha e ekzekutimit tëalgoritmeve të tilla varet nga mënyra precize e ndarjes në pjesë. Së dyti, programi 5.6 është reprezentues i algoritmeve përçaj-e-sundo për të cilat pjesëtmblidhen për të formuar tërësinë. Algoritmet tjera përçaj-e-sundo mund tëndahen në pjesë më të vogla të cilat përbëjnë më pak se problemi i tërë ose pjesë

të cilat mbulojnë njëra tjetrën, për të përbërë më shumë se problemi i tërë. Këtoalgoritme prap se prap janë algoritme të rregullta rekurzive sepse secila pjesëështë më e vogël se tërësia, por analizimi i tyre është më i vështirë.

Shembulli i algoritmit të kërkimit binar është algoritëm përçaj-e-sundo, që endanë problemin në dy pjesë dhe pastaj punon vetëm me njërën pjesë.

 Në figurën 5.5 paraqitet përmbajtja e brendshme e stekut të mirëmbajtur ngaambienti programues, për të përkrahur llogaritjen në figurën 5.4. Modeli i parqitur është idealistik, por jep pasqyrë të dobishme të strukturës së llogaritjes

 përçaj-e-sundo. Nëse programi ka dy thirrje rekurzive, steku aktual intern përmbanë një vlerë që i korrespondon thirrjes së parë gjersa ai funksion ështëduke u ekzekutuar (i cili përmbanë vlerat e argumenteve, variablave lokale dheadresën për kthim), pastaj një vlerë të ngjashme që i korrespondon thirrjes sëdytë të funksionit gjersa ai është duke u ekzekutuar. Alternativa e paraqitur nëfigurën 5.5 është që të vendosen të dy vlerat në stek përnjëherë, duke mbajtur tëgjitha nën-detyrat e mbetura që të bëhen në mënyrë eksplicite në stek. Kyaranzhim përvijon qartazi llogaritjen dhe vendosë bazat për skemat e përgjithshme llogaritëse, si ato që do të analizohen në pjesët 5.6 dhe 5.8.

Page 281: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 281/699

Algoritmet dhe strukturat e të dhënave

281

Kjo sekuencë shtë një reprezentim idealistik i përmbajtjes së stekut intern gjatëllgoaritjes së njëjtë për figurën 5.4. Fillohet me indeksat e majtë dhe të djathtë tëtërë nënvargut në stek. Secili rresht paraqet rezultatet e tërheqjes (pop) së dyindeksave dhe nëse ata nuk janë të barabartë, duke shtyer (push) katër indeksa,të cilët ndajnë nënvargun e majtë dhe të djathtë pasi nënvargu i tërhequr tëndahet në dy pjesë. Në praktikë, sistemi mbanë adresat e kthimit (return) dhevariablat lokale në stek, në vend se të bëhet ky reprezentim specifik i punës qëduhet bërë, por ky model mjafton për të përshkruar llogaritjen.

Figura 5.5. Shembull i dinamikës së stekut intern

Figura 5.6 paraqet strukturën e algoritmit përçaj-e-sundo për gjetje tëmaksimumit. Është strukturë rekurzive: nyja në krye përmbanë madhësinë evargut në hyrje, struktura për nënvargun e majtë është vizatuar në anën e majtëdhe ajo për nënvargun e djathtë në anën e djathtë. Kjo është strukturë e pemësdhe do të diskutohet në detaje në vazdhim. Pemët janë të dobishme për të

kuptuar strukturat e programve që përfshijnë thirrjet funksioneve tëndërthurrura, në veçanti të programeve rekurzive. Poashtu në figurën 5.6 është paraqitur pema e njëjtë, por me secilën nyje të shënuar me vlerën kthyese përthirrjen përkatëse të funksionit.

Page 282: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 282/699

Avni Rexhepi

282

Figura 5.6. Struktura rekurzive për alroritmin “gjeje maksimumin” (  find-the-

maximum)

Algoritmi përçaj-e-sundo e ndanë problemin e madhësisë 11 në nëj të madhësisë6 dhe një të madhësisë 5, pastaj problemin e madhësisë 6 në dy probleme tëmadhësisë 3, e kështu me radhë, deri sa të arrijë në problemet e madhësisë 1.Secili rreth në këto diagrame reprezenton një thirrje të funksionit rekurziv, për

nyjet e lidhura me to, ndërsa katrorët paraqesin thirrjet për të cilat përfundonrekurzioni.

Asnjë diskutim për rekurzionin nuk do të ishte i kompletuar pa përmendur problemin antik të kullave të Hanoit. Kemi tre kunja (tri shtylla) dhe N disqe, tëcilat vendosen në shtylla. Disqet janë me madhësi të ndryshme dhe fillimisht janë të vendosur në njërën shtyllë, sipas radhitjes prej më të madhit (disku N) nëfund deri tek më i vogli (disku 1) në krye. Detyra është që të lëvizet steku idisqeve djathtas, një nga një, duke ju përmbajtur rregullave vijuese: 1-vetëm njëdisk mund të lëvizet në një moment kohor; dhe 2-disku i madh nuk mund të

shkojë mbi të voglin.Legjenda thotë se kur monarkët e një tempulli në Hanoi të përfundojnë lëvizjen e 64disqeve të arta në tri shtulla diamanti, do të vije fundi i botës.

Duke pasur parasysh se problemi mund të paraqitet me çfarëdo numri të disqeve,numri i lëvizjeve të nevojshme për zgjidhje të problemit të kullave të Hanoit është 2

n-1,

ku n është numri i disqeve.

Sikur legjenda të ishte e vërtetë dhe sikur të mund të bëhej një lëvizje e diskut përsekond, atëherë numri më i vogël i lëvizjeve do të kërkonte 2

64-1 sekonda, që i bie

përafërsisht 585 miliardë vite ose 18,446,744,073,709,551,615 lëvizje për të përfunduar

(s’ka arsye për panikë :) ).

Page 283: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 283/699

Algoritmet dhe strukturat e të dhënave

283

Programi 5.7 jep zgjidhjen rekurzive të problemit. Ai specifikon se cili diskduhet të lëvizet në seclin hap dhe në cilin drejtim (‘+’ do të thotë lëviz njështyllë djathtas, duke vazhduar ciklin në skajin e majtë, nëse lëvizet nga shtyllae skajit të djathtë; ndërsa ‘-‘ do të thotë lëviz një shtyllë më majtas, dukevazhduar ciklin në shtyllën më të djathtë kur kemi arritur skajin e majtë).Rekurzioni është i bazuar në idenë vijuese: për të lëvizur N disqet një shtyllë nëtë djathtë, së pari lëvizen N-1 disqet në krye një shtyllë në të majtë, e pastajzhvendosim diskun N një shtyllë në të djathtë, e pastaj lëvizen N-1 disqet njështyllë në të majtë (në diskun N). Vërtetimi i zgjidhjes mund të bëhet përmesinduksionit. Në figurën 5.7 janë paraqitur lëvizjet për N=3 dhe thirrjet rekurzive për 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 katër disqet e epërme, e pastaj lëvizet disku i5, pastaj zhvendosen katër disqet e epërme për një pozitë majtas , e kështu meradhë. Sekuenca e thirrjeve të funksionit paraqet rastin për tri disqe. Sekuenca elëvizjeve është: +1 -2 +1 +3 +1 -2 +1, e cila paraqitet katër herë në zgjidhje(p.sh, 7 lëvizjet e para)

Page 284: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 284/699

Avni Rexhepi

284

 Figura 5.7. Kullat e Hanoit, rasti me 5 disqe

Sekuenca e thirrjeve të funksionit

Struktura rekurzive e zgjidhjes tregon numrin e lëvizjeve të kërkuara.

Vetia 5.2. Algoritmi rekurziv përçaj-e-sundo për problemin e kullave të

Hanoit, prodhon zgjidhjen që ka 2N – 1 lëvizje

Si zakonisht, është e drejtpërdrejtë nga kodi se numri i lëvizjeve plotëson

rekurrencën. Në këtë rast, rekurrenca që potësohet nga numri i lëvizjeve të

Page 285: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 285/699

Algoritmet dhe strukturat e të dhënave

285

diskut është e ngjashme me formulën 2.5:

TN = 2TN – 1 + 1, për N ≥ 2 me T1 = 1.

Mund të verifikojmë rezultatin e dhënë drejtpërdrejt me inuksion: kemi T(1)=21  –  1=1; dhe nëse T(k)=2k  –  1 për k < N, atëherë T(N) = 2(2N – 1 – 1) + 1 = 2N  –  1.

Metoda e thjeshtë, jorekurzive, e cila do të jepte zgjidhjen e njëjtë me lehtësi.Zgjidhja është relevante për shumë probleme praktike.

I zhvendosim disqet në të djathtë (rekurzivisht) duke zhvendosur të gjitha disqet,(përveq atij të fundëm) në të majtë, e pastaj e lëvizim diskun e fundëm në të

djathtë. Pastaj (rekurzivisht) zhvendosim kullën prapa në diskun e fundëm. 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);

Për të kuptuar zgjidhjen e problemit të kullave të Hanoit, mund të shqyrtohetedhe detyra më e thjeshtë e vizatimit të vizave ndarëse të vizores. Në secilininch të gjatësisë, vizorja ka një shenjë në 1/2 inch, një pak më të shkurtër në 1/4inch, edhe më të shkurtër në 1/8 inch, e kështu me radhë. Detyra është tëshkruhet programi i cili vizaton këto shenja në çfarëdo rezolucioni të dhënë,duke supozuar se kemi në dispozicion funksionin mark(x,h), për të bërë shenjën‘h’ njësi të gjatë në pozitën ‘x’. 

 Nëse do të duhej rezolucioni 1/2 inch, reshkallëzojmë ashtu që detyra do të ishte

të vendoset shenja në çdo pikë ndërmjet 0 dhe 2n

  (pa përfshirë pikat skajore).Prandaj, shenja në mes duhet të jetë ’n’ njësi e lartë, shenjat në mes të gjysmëssë majtë dhe të djathtë duhet të jenë (n-1) njësi të larta, e kështu me radhë.Programi 5.8 është algoritëm i drejtpërdrejtë përçaj-e-sundo për të realizuar këtëobjektiv, ndërsa figura 5.8 paraqet atë gjatë operimit në një shembull të vogël.Thënë rekurzivisht, idea e këtij funksioni është si vijon. Për të vendosur shenjatnë një interval, së pari e ndajmë atë në dy pjesë të barabarta. Pastaj, bëjmëshenjat (e shkurtëra) në gjysmën e majtë (rekurzivisht), shenjën e lartë në mesdhe pastaj shenjat (e shkurtëra) në gjysmën e djathtë (rekurzivisht). Thënë

iterativisht, figura 5.8 ilustron që funksioni bën shenjat më radhë, nga e majta në

Page 286: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 286/699

Avni Rexhepi

286

të djathtë  –   triku është në llogaritjen e gjatësive. Pema e rekurzionit në figurë,ndihmon për të kutpuar llogaritjen: duke lexuar teposhtë, shohim se gjatësia eshenjave zvogëlohet për 1 për secilën thirrje rekurzive të funksionit. Dukelexuar përgjatë, fitojmë shenjat në renditjën në të cilën vizatohen, sepse, përcilëndo nyje të dhënë, së pari vizatojmë shenjat e shoqëruara me thirjjen efunksionit në të majtë, pastaj shenjën e shoqëruar me nyjen dhe shenjat eshoqëruara me thirrjen e funksionit në të djathtë.

Figura 5.8. Thirrjet e funksionit për vizatim të vizores

Kjo sekuencë e thirrjeve të funksionit formon llogaritjen për vizatimin e vizoresme gjatësi 8, duke rezultuar me shenjat në gjatësitë: 1, 2, 1, 3, 1, 2, dhe 1.

Për të vizatuar shenjat në vizore, vizatojmë shenjat në gjysmën e majtë, pastajshenjën më të gjatë në mes, pastaj shenjat në gjysmën e djathtë. Programi është i paraparë për përdorim me r-1 të barabartë me fuqi të 2-shit, veti të cilën e ruannë thirjet e tij rekurzive.

 Programi 5.8. Algoritmi përçaj-e-sundo për vizatim të vizores

//rule=vendos, mark=shenjovoid rule(int l, int r, int h){ int m = (l+r)/2;if (h > 0)

Page 287: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 287/699

Algoritmet dhe strukturat e të dhënave

287

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

}} 

Shihet se sekuenca e gjatësive është saktësishtë e njëjtë si sekuenca e lëvizjevetë disqeve në problemin e kullave të Hanoit. Provë e thjeshtë se janë identikeështë fakti se programet rekurzive të tyre janë të njëjta. Thënë ndryshe, për tëvendosur se cili disk duhet të lëvizet, mund të përdoren shenjat në vizore.

Për më tepër, të dy zgjidhjet janë varianta të skemës përçaj-e-sundo tëshembullit në programin 5.6. Të tri rastet zgjidhin problemin e madhësisë 2n 

duke e ndarë në dy probleme me madhësi 2n-1

. Për gjetjen e maksimumit, kemizgjidhje me kohë lineare me madhësinë e hyrjes; për vizoren dhe kullat eHanoit, kemi zgjidhje me kohë lineare me madhësinë e daljes. Për kullat eHanoit, normalisht mendojmë për zgjidhjen si të jetë e kohës eksponenciale,sepse e masim madhësinë e problemit në terma të numrit të disqeve, n.

Vizatimi i shenjave në vizore përmes programit rekurzive është i lehtë, por a kandonjë mënyrë edhe më të thjeshtë për të llogaritur gjatësinë e shenjës së i-të, për një ‘i’ të dhënë? Figura 5.9 paraqet një proces të thjeshtë llogaritës që jep përgjigje në këtë pyetje. Numri i i-të i shtypur, në të dy problemet, atë të vizores

dhe atë të kullave të Hanoit, nuk është asgjë tjetër përveq numrit të bitave 0 tënjëpasnjëshëm, që e  pasojnë reprezentimin binar të ‘i’-së. Kjo veti mund të provohet përmes induksionit me antë të korrespondencës me formulimin përçaj-e-sundo të procesit të shtypjes së tabelës së numrave n-bitësh. Shtypni tableën enumrave (n-1) bitësh, secili i paraprairë me bitin 0, e pastaj tabelën e numrave(n-1) bitësh të paraprirë me bitin 1.

Page 288: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 288/699

Avni Rexhepi

288

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-bitësh.

Për problemin e kullave të Hanoit, implikimi i korrespondencës me numrat n- bitësh është algoritëm i thjeshtë për detyrën. Një grumbull mund të lëvizet njështyllë djathtas duke përsëritur dy hapat vijues (deri në përfundim):

1.  Lëvize diskun e vogël në të djathtë nëse ‘n’ është tek (në të majtënëse n është çift).

2.  Bëje lëvizjen e vetme të lejuar që nuk e përfshinë diskun e vogël.

Kjo i bie që, pasi të lëvizet disku i vogël, dy shtyllat tjera përmbajnë dy disqe,njëri me i vogël se tjetri. Lëvizja e vetme e lejuar që nuk përfshinë diskun e

Page 289: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 289/699

Algoritmet dhe strukturat e të dhënave

289

voëgl është që të lëvizet disku më i vogël në atë më të madh. Secila lëvizje tjetër përfshinë diskun më të vogël për të njëtjën arsye se secili numër tjetër është tekdhe secila shenjë tjetër në vizore është më e shkurtëra.

Provë formale përmes induksionit, se secila lëvizje tjetër në zgjidhjen për kullate Hanoit përfshinë diskun e vogël (duke filluar dhe mbaruar me lëvizje të tilla)është instruktive: Për n=1, ka vetëm një lëvizje, e cila përfshinë diskun e vogël,kështu që rregulla vlenë. Për n>1, supozimi se rregulla vlenë për n-1 implikonfaktin se ajo vlenë për n sipas konstruksionit rekurziv: zgjidhja e parë për n-1fillon me lëvizje të diskut të vogël dhe zgjidhja e dytë për n-1 mbaron me lëvizjetë diskut të vogël, kështu që zgjidhja për ‘n’ fillon dhe mbaron me lëvizje tëdiskut të vogël. E vendosim një lëvizje që nuk e përfshinë diskun e vogëlndërmjet dy lëvizjeve të cilat e përfshijnë diskun e vogël (lëvizja që e përfundonzgjidhjen e parë për n-1 dhe lëvizja që e fillon zgjidhjen e dytë për n-1), ashtu qërregulla që secila lëvizje tjetër përfshinë diskun e vogël, ruhet.

Për dallim nga programi 5.8, vizoren mund ta vizatojmë edhe duke vizatuar së pari të gjitha shenjat në gjatësinë 1, apstaj në gjatësinë 2, e kështu me radhë.Variabla ‘t’ bartë gjatësinë e sjenjave dhe variabla ‘j’ bartë numrin e shenjavendërmjet dy thirrjeve sukcesive të gjatësisë ‘t’. Unaza a jashtme ‘for’inkrementon ‘t’-në dhe ruan vetinë j=2t-1. Unaza e brendshme ‘for’ vizaton tëgjitha shenjat në gjatësinë ‘t’.

 Programi 5.9. Programi jorekurziv për vizatim të vizores//rule=vendos, mark=shenjovoid 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ë mënyrë alternative e vizatimit të vizores që është e

inspiruar nga korrespondenca (ndërlidhja) me numrat binar (shiko figurën 5.10).Këtij versioni të algoritmit i referohemi se implementimi “bottom-up” ( prej poshtë, te lartë). Nuk është rekurziv, por sigurisht është i sygjeruar nga algoritmirekurziv. Ndërlidhja mes algoritmeve përçaj-e-sundo dhe reprezentimit binar tënumrave shpeshherë jep ‘mendjeprehtësi” për analziën dhe zhvillimin eversioneve të për mirësuara, siç janë qasjet “prej poshtë, te lartë”. Kjo qasjemund të shqyrtohet për të kuptuar dhe mundësisht për të përmirësuar secilinalgoritëm përçaj-e-sundo.

Page 290: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 290/699

Avni Rexhepi

290

Figura 5.10. Vizatimi i vizores në renditjen “poshtë-lart”. 

Për të vizatuar vizoren në mënyrë jorekurzive, i alternojmë vizatimin e shenjaveme gjatësi 1 dhe kalimin e pozicioneve, pastaj alternojmë vizatimet e shenjave të

gjatësisë 2 dhe kalimet e pozicioneve të mbetura, pastaj alternojmë vizatimet eshenjave me gjatësi 3 dhe kalimet e pozitave të mbetura, e kështu me radhë.

Qasja “prej poshtë-te lartë” përfshinë riaranzhimin e radhës së llogaritjes kur jemi duke vizatuar vizoren. Figura 5.11 paraqet një shembull tjetër, kurirregullohet renditja e tri thirrjeve të funksioneve në implementimin rekurziv.Kjo reflekton llogaritjen rekurzive në mënyrën e përshkruar herën e parë: Vizatoshenjat e mesit, pastaj gjysmën e majtë, pastaj gjysmën e djathtë. Modeli ivizatimit të shenjave është kompleks, por është rezultat i thjeshtë ndërrimit të dyurdhërave në programin 5.8 dhe relacioni mes figurave 5.8 dhe 5.11 është i

ngjashëm me atë që kanë mes veti shprehjet aritmetike me prefiks dhe postfiks.Kjo është sekuenca e cila tregon rezultatin e vizatimit të shenjave para thirrjeverekurzive, në vend se ndërmjet tyre:

Page 291: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 291/699

Algoritmet dhe strukturat e të dhënave

291

Figure 5.11. Funksioni për vizatim të vizores ( versioni ‘  preorder ’  )

Vizatimi i shenjave në radhën si në firgurën 5.8 mund të jetë më i preferuar ndajriaranzhimit të llogaritjeve të bëra në programin 5.9 si është treguar në figurën5.11, sepse mund të vizatojmë një vizore arbitrarisht të gjatë, nëse imagjinojmë pajisjen për vizatim e cila thjeshtë lëvizë në shenjën e ardhshme në rrotullim tëvazhdueshëm. Ngjashëm, për të zgjidhur problemin e kullave të Hanoit, jemi tëkufizuar në prodhimin e sekuencës së lëvizjes së disqeve në renditjen në të cilënato kryhen. Në përgjithsi, programet rekurzive varen nga nënproblemet qëzgjidhen në renditje të veçantë (të caktuar). Për llogaritjet tjera, si p.sh., programi 5.6, renditja në të cilën zgjidhet problemi është e parëndësishme. Përllogaritje të tilla, kufizimi i vetëm është se duhet të zgjidhen nënproblemet para

se të mund të zgjidhet problemi kryesor. Të kuptuarit se kur e kemifleksibilitetin e rirenditjes së llogaritjeve, jo vetëm që është sekreti i suksesit nëdizajnim të algoritmeve, por ka edhe efekte direkte praktike në shumë kontekste.Për shembull, jo çështje është shumë kritike kur shqyrtohet implementimi ialgoritmeve në procesorë paralel.

Qasja “prej poshtë-te lartë” i korresondon metodës së përgjithshme të dizajnit tëalgoritmeve ku kemi për të zgjidhur problemin së pari duke zgjidhurnënproblemet e thjeshta, e pastaj duke i kombinuar këto zgjidhje për të zgjidhur

Page 292: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 292/699

Avni Rexhepi

292

nënproblemet pak më të mëdha, e kështu me radhë, deri sa të zgjidhet i tërë 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ëfigurën 5.12, është një hap i vogël. Kjo figurë ilustron se si një përshkrim ithjeshtë rekurziv, mund të dërgojë në një llogaritje që duket të jetë komplekse.

Fraktali në vijim është versioni dy-dimensional i figurës 5.10. në rastin e fundit,katrorët e kufizuar thkesojnë strukturën rekurzive të llogaritjes.

Figura 5.12. Fraktal dy dimensional  

Modelet gjeometrike të definuara në mënyrë rekurzive, si këto në figurën 5.12quhen fraktale. Nëse përdoret ndonjë figurë fillestare më e komplikuar dhe përfshihen thirrje rekurziva me të komplikuara (posaqërisht dukë përfshirëfunksione të definuara rekurzivisht në rrafshin real dhe kompleks), mund tëzhvillohen modele shumë komplekse. Një shembull tjetër është ylli i Koch-ut idemonstruar në figurën 5.13, i cili është i definuar rekurzivisht, si vijon: YlliKoch i rendit 0 është thjeshtë një “kodër”, e cila përsëritet pastaj me zevendësimnë secilin segment. Pra, ylli i rendit ‘n’ është yll i rendit ‘n -1’, ku secili segmentështë i zëvendësuar me yllin e rendit 0, me shkallëzim të duhur.

 Figure 5.13. Fraktali rekurziv i Koch-ut  

Sikur vizatimi i vizores dhe zgjidhja e kullave të Hanoit, këto algoritme janëlinearë në numrin e hapava, por ai numër është eksponencial në thellësinëmaksimale të rekurziont. Ato mund të ndërlidhen drejtpërdrejt edhe menumërimin në sistemin numerik të caktuar.

Këto probleme janë mjaft interesante dhe ndërlidhja e tyre me numrat binarështë befasuese, mirëpo interesi kryesor është se ofrojnë një kuptim të paradigmës themelore të dizajnit të algoritmeve, të ndarjes përgjysmë dhezgjidhjes së dy gjysmave në mënyrë të pavarur, që është një prej teknikave

kryesore.

Page 293: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 293/699

Algoritmet dhe strukturat e të dhënave

293

Algoritmet themelore “përçaj-e-sundo” 

Kërkimi binar dhe sorti “merge” (shpjegohet në pjesën e sortimeve) janëalgoritme prototipik e “përçaj-e-sundo”, të cilat ofrojnë performansë optimale tëgarantuar për kërkim dhe sortim. Rekurrenca tregon natyrën e llogaritjeve“përçaj-e-sundo” për secilin algoritëm. Kërkimi binar e ndanë problemin përgjysmë, e bën një krahasim dhe pastaj bën thirrjen rekurzive për njërën prejgjysmave. Sorti ‘merge’ e ndanë problemin në gjysmë, pastaj punon në të dygjysmat rekurzivisht, e pastaj bën N krahasime. Shumë algoritme të tjerazhvillohen me këto skema rekurzive.

Rekurrenca Zgjidhja e përafërtKërkimi binar

Krahasime C N = C N/2 + 1 lg N

‘mergesort’ 

Thirrje rekurzive

Krahasime

A N = 2A N/2 + 1

C N = 2C N/2 + N

 N

 N lg N

‘Quicksort’ (që shpjegohet në pjesën e sortimeve) dhe pema e kërkimit binar(poashtu shpjegohet më vonë) paraqesin variacione të rëndësishme të skemësthemelorë ‘përçaj-e-sundo’, ku problemi ndahet në nënprobleme të madhësisë‘k -1’ dhe ‘N-k’, për një vlerë  të ‘k’, e cila përcaktohet nga hyrja. Për vlerëhyrëse të rastit, këto algoritme e ndajnë problemin në nënprobleme që janëmesatarisht sa gjysma e madhësisë (si në rastin e sortit ‘merge’ ose kërkimit binar).

Variacionet tjera në skemën themelore e që ia vlenë të konsiderohen janë: ndarjanë pjesë me madhësi të ndryshme, ndarja në më shumë se dy pjesë, ndarja në pjesë që mbulojnë njëra tjetrën. Këto bëjnë sasi të ndryshme të punës në pjesën jorekurzive të algoritmit. Në përgjithësi, algoritmet ‘përçaj-e-sundo’ përfshijnë punën për ndarjen e hyrjes në pjesë ose bashkimin e rezultatit të procesimit të dy pjesëve të pavarura të zgjidhura të hyrjes, ose për të ndihmuar gjërat pasi të jetë procesuar gjysma e hyrjes. Do të thotë, mund të ketë kod para, pas apo ndërmjetthirrjeve rekurzive. Natyrisht, variacionet e tilla dërgojnë në algoritme mëkomplekse sesa kërkimi binar dhe sorti ‘merge’ dhe janë më të vështira për t’u

analizuar.

Page 294: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 294/699

Avni Rexhepi

294

Algoritmet dinamike

 Një karakteristikë themelore e algoritmeve përçaj-e-sundo është se ato e ndajnë

 problemin në nënprobleme të pavarura. Kur nënproblemet nuk janë të pavarura,situata është më e komplikuar, pikë së pari pasi që implementimet direkterekurzive ose edhe algoritmet më të thjeshta të këtij lloji mund të kërkojnë sasitë paimagjinueshme të kohës. Në këtë pjesë do të shqyrtohet teknika sistematikee evitimit të kësaj gracke në disa raste.

Për shembull, programi 5.10, është implementim rekurziv direkt i rekurrencës qëdefinon numrat ‘Fibonacci’ (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... ku, përveq fillimit, 0dhe 1, numrat pasardhës, janë shuma e dy numrave paraprak). Normalisht, mose përdorni këtë program pasi që është tërësisht joefikas. Vërtetë, numri ithirrjeve rekurzive për të llogaritur F N është saktësisht F N+1. Mirëpo, F N është ø N,ku ø 1:618 është “raporti i artë”. E vërteta e pakëndshme është se programi5.10 është algoritëm i kohës eksponenciale, për këtë llogaritje triviale. Figura5.14, e cila paraqet thirrjet rekurzive për një numër të vogël (p.sh., deri në 8), e bën të qartë sasinë e rillogaritjeve që përfshihen.

Pamja e thirrjeve rekurzive të nevojshme për të llogaritjen e F8 sipas algoritmitstandard rekurziv ilustron se si rekurzioni me nënprobleme pjesërisht të përputhura (mbuluara) mund të dërgojë në kosto eksponenciale. Në këtë rast,thirrja e dytë rekurzive injoron llogaritjet e bëra gjatë thirrjes së parë, gjë qërezulton me rillogaritje masive pasi që efekti shumëfishohet rekurzivisht.Thirrjet rekurzive për të llogaritur F6 = 8 (të cilat janë reflektuar në nënpemën edjathtë të rrënjës dhe nënpemën e majtë të nënpemës së majtë të rrënjës) janëlistuar në vijim.

Page 295: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 295/699

Algoritmet dhe strukturat e të dhënave

295

Figura 5.14. Struktura e algoritmit rekurziv për numrat ‘Fibonacci’

 Në anën tjetër, është e lehtë të llogariten N numrat e parë Fibonacci, në një kohë

 proporcionale me N, duke përdorur vargun:F[0] = 0; F[1] = 1;for (i = 2; i <= N; i++)F[i] = F[i-1] + F[i-2];

 Numrat rriten eksponencialisht, kështu që vargu është i vogël  –  për shembull,F45=1836311903 është numri më i madh Fibonacci që mund të reprezentohet siinteger 32 bitësh, kështu që vargu me madhësi 46 kryen punë.

Kjo teknikë jep një mënyrë të drejtpërdrejt për të fituar zgjidhjet numerike përçfarëdo relacioni të rekurrencës. Në rastin e numrave Fibonacci, mund të lëmëanash fare vargun dhe të përcjellim vetëm dy vlerat e përparshme. Për shumërekurrenca të tjera të hasura zakonisht, duhet të mirëmbajmë vargun me të gjithavlerat e njohura.

Rekurrenca është funksion rekurziv me vlera integer. Diskutimi paraprak i paragrafit të mëparshëm dërgon në konkluzionin se mund të vlerësojmë çfarëdofunksionin të tillë duke llogaritur të gjitha vlerat e funksionit në renditjen qëfillon prej më të voglit, duke përdorur në secilin hap vlerat e llogaritura paraprakisht, për të llogaritur vlerën aktuale. Kësaj teknike i referohemi siteknika “bottom-up dynamic programming” (programimi dinamik prej poshtë –  

Page 296: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 296/699

Avni Rexhepi

296

te lartë). Kjo teknikë aplikohet në çdo llogaritje rekurzive, po qe se mund t’ialejojmë vetes (nëse ka llogari) që të ruhen të gjitha vlerat e llogaritura më parë.Kjo është një teknikë e dizajnimit të algoritmeve që është përdorur me sukses për një rang të gjerë të problemeve. Duhet pasur kujdes për teknikën e thjeshtë ecila mund të përmirësojë kohën e ekzekutimit të një algoritmi, prej asajeksponenciale në atë lineare!

Programimi dinamik “top-down” (nga lartë –  te poshtë) është pamje edhe më ethjeshtë e teknikës që lejon të ekzekutojmë funksionet rekurzive, me kosto tonjëjtë (ose edhe më të ulët) sikur programimi dinamik “bottom-up” (prej poshtë-te lartë), në mënyrë automatike. E instrumentalizojmë programin rekurziv për tëruajtur secilën vlerë të cilën e ruan (si veprim të fundit) dhe për të verifikuarvlerat e ruajtura për të evituar rillogaritjën e cilësdo prej tyre (si veprim të parëtë saj). Programi 5.11 është transformim mekanik i programit 5.10, që ezvogëlon kohën e ekzekutimit që të jetë lineare, përmes programimit dinamik“top-down”. Figura 5.15, paraqet zvogëlimin drastik të numrit të thirrjeverekurzive, të arritur përmës këtij ndryshimi të thjeshtë automatik. Programimidinamik “top-down”, ndonjëherë thirret edhe memorizim. 

Figura 5.15. Programimi dinamik “top-down”, për llogaritje të numrave

Fibonacci

Kjo figurë e thirrjeve rekurzive të përdorura për të llogaritur F8  përmes

implementimit të programimit dinamik “top-down” të algoritmit rekurziv,ilustron se si ruajtja e vlerave të llogaritura zvogëlon koston prej asajeksponenciale (fig. 5.14) në atë lineare.

Programi 5.10. Numrat Fibonacci (implementimi rekurziv)

Ky program, edhe pse kompakt dhe elegant, nuk është i përdorshëm pasiq qëmerr kohë eksponenciale për të llogaritur F N. Koha e ekzekutimit për tëllogaritur F N+1, është e gjatë ø 1.6 herë sa koha e ekzekutimit për të llogariturF N. Për shembull, pasi që ø9  > 60, nëse vërejmë se kompjuteri po merr

Page 297: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 297/699

Algoritmet dhe strukturat e të dhënave

297

 përafërsisht një sekond për të llogaritur F N, e dijmë se do të merr më shumë senjë minut për të llogaritur F N+9, dhe më shumë se një orë për të llogaritur F N+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 çantës së shpinës).

Problemi “Knapsack” (angl. knapsack –  çantë

e shpinës), është problem i optimizimitkombinatorik. Për një bashkësi të dhënë tëelementeve, secili me një peshë dhe vlerë tëcaktuar, përcaktoni numrin e elementeve tëcilat do të përfshihen në koleksion (do të futennë çantë), ashtu që pesha totale është më evogël 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 kagjetur një thesar në një sef, por në çantën e tij,ka vend për një pjesë të vogël të elementeve,

kështu që ai duhet të mbushë çantën, meelementet që kanë vlerën më të madhe. 

Pra, duhet gjetur kombinimi i elementeve, për të maksimizuar vlerën totale tëelementeve. Për shembull, me elementet e paraqitura në figurën 5.16, çantaështë me madhësi 17, mund të mirren 5 A-ja (por jo gjashtë), për një total tëvlerës prej 20, ose një D dhe jë E, për një total të vlerës prej 24, ose ndonjëkombinim tjetër. Qëllimi është që të gjindet një algoritëm efikas i cili disi do tëgjejë maksimumin në mesin e të gjitha opsioneve të mundura, për çfarëdo bashkësie të elementeve dhe kapaciteti të çantës.

Page 298: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 298/699

Avni Rexhepi

298

Figur a 5.16. Shembull i “Knapsack”

 Një instancë e problemit “knapsack” konsiston në kapacitetin e çantës dhe bashkësinë e elementeve me madhësi të ndryshme (dimensioni horizontal) dhe

vlerave (dimensioni vertikal). Këto figura paraqesin katër mënyra të ndryshme për të mbyshur çantën me madhësi/kapacitet 17, dy prej të cilave dërgojnë nëvlerën më të lartë.

Page 299: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 299/699

Algoritmet dhe strukturat e të dhënave

299

Fig. 5.17

Ka shumë aplikacione, për të cilat zgjidhja e problemit “knapsack” është erëndësishme. Për shembull, një kompani transporti dëshiron të dijë mënyrën mëtë mirë për 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 për shembull: mund të ketë numër të kufizuar të elementeve nëdispozicion, ose mund të jenë dy kamionë. Shumë variante të tilla mund tëtrajtohen me qasjen e njëjtë sikur në rastin e zgjidhjes së problemit themelor, të paraqitur paraprakisht (disa dalin të jenë shumë më të vështira). Ka një vijëshumë të hollë ndërmjet problemeve të zgjidhshme dhe të pazgjidhshme të këtijlloji.

 Në zgjidhjen rekurzive të problemit “knapsack”, secilën herë që zgjedhet njëelement, supozojmë se mund të gjejmë (rekurzivisht) një mënyrë optimale për

 paketuar pjesën tjetër të çantës. Për çantën me madhësi “cap”, përcaktojmë përsecilin 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 përreth. Ky paketim optimal është thjeshtë njëri që e kemizbuluar (ose do ta zbulojmë) për madhësi më të vogël të çantës ‘cap-items[i].size’. Kjo zgjidhje eksploaton principin se vendimet optimale, të bëra një herë, nuk duhet të ndryshohen. Kur një herë të dijmë se si të paketohetçanta me kapacitet më të vogël me një bashkësi optimale të elementeve, nukkemi nevojë të ri-ekzaminojmë këto probleme, pamarrë parasyshë çfare janë

elementet tjera në vazhdim.Programi 5.12 është zgjidhja direkte rekurzive e bazuar në këtë diskutim.Përsëri, ky program nuk është i realizuesëm për përdorim në zgjidhjen e problemeve aktuale, sepse merr kohë eksponenciale për shkak të rillogaritjevemasive (shiko figurën 5.17), mirëpo, mund të aplikojmë programimin dinamik“top-down” për të eliminuar këtë problem, ashtu si është paraqitur në programin5.13. Si edhe më parë, kjo teknikë eliminon rillogaritjen, si është paraqitur nëfigurën 5.18.

Page 300: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 300/699

Avni Rexhepi

300

Figura 5.18a. Struktura rekurzive e algoritmit “knapsack”

Kjo pemë paraqet strukturën e thirrjeve rekurzive të algoritmit të thjeshtërekurziv “knapsack”, në programin 5.12. Numri në secilën nyje reprezentonkapacitetin e mbetur në çantë. Algoritmi vuan problemin e njëjtë themelor të performansës eksponenciale për shkak të rillogaritjes masive për nënproblemetme përputhje, të cilat u shqyrtuan në llogaritjen e numrave Fibonacci (Fig. 5.14).

Ashtu si në bëri në llogritjen e numrave Fibonacci, teknika e ruajtjes së vleravetë njohura e redukon koston e algoritmit knapsack, nga eksponenticale në lineare

(Figura 5.18).

Figura 5.18b. Programimi dinamik “top-down” për algoritmin knapsack

Sipas dizajnit, programimi dinamik eliminon të gjitha rillogaritjet në cilindo program rekurziv, subjekt vetëm të kushtit që mund t’ia lejojmë vetes që tëruajmë vlerat e funksionit për argumentet më të vogla sesa thirrja aktuale (në pyetje).

Duke ruajtur vlerat që llogariten në një varg statik (vlerat e të cilit inicializohennë 0, në C++), evitojmë rillogaritjen në mënyrë eksplicite. Ky program llogaritëF N  në kohë proporcionale me N, në kontrast të zymtë me kohën O(ø N) të përdorur nga programi 5.10.

 Programi 5.11. Numrat Fibonacci (Programimi dinamik)

//known=i/e njohurint F(int i){ static int knownF[maxN]; //knownF = F e diturif (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;

Page 301: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 301/699

Algoritmet dhe strukturat e të dhënave

301

Vetia 5.3. Programimi dinamik redukton kohën e ekzekutimit të

funksioneve rekurzive, që të jetë së shumti sa koha e kërkuar për të

vlerësuar funksionin për të gjitha argumentet më të vogla ose barazi me

argumentin e dhënë, duke trajtuar koston e thirrjes rekurzive si konstantë.

Për 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; për kapacitete shumë të mëdha, kërkesatkohore dhe hapsinore mund të jenë jashtëzakonisht të mëdha.

Edhe programimi dinamik “bottom-up” (prej poshtë-te lartë), mund të aplikohet për problemin ‘knapsack’. Në të vërtetë, mund të përdorim qasjen “bottom-up”secilën herë që përdorim 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. Për funksionet me argumentetë vetme integer, si këto të dyja që u shqyrtuan, thjeshtë vazhdojmë në renditjerritëse të argumenteve; për funksione më të komplikuara rekurzive, përcaktimi irenditjes së duhur mund të jetë sfidë.

Për shembull, nuk duhet të kufizohemi në funksione rekurzive me nga njëargument të vetëm integer. Kur kemi funksione me argumente të shumëfishtainteger, mund të ruajmë zgjidhjet e nënproblemeve të vogla në vargjeshumëdimensionale, nga një për secilin agrument. Situatat tjera nuk përfshijnëfare argumente integer, por më parë përdorin formulim diskret abstrakt të

 problemit, që lejon dekompozimin e problemeve në probleme më të vogla.

Si edhe në rastin e numrave Fibonacci, mos e përdorni këtë zgjidhje të problemit (këtë program), sepse do të merr kohë eksponenciale dhe prandaj nukdo të kompletojë ekzekutimin edhe për probleme të vogla. Mirëpo, sidoqoftë, paraqet zgjidhje kompakte e cila mund të përmirësohet lehtë (shiko programin5.13). Ky kod supozon se elementet janë struktura me madhësi dhe vlerë, tëdefinuar përmes:

typedef struct { int size; int val; } Item;

dhe kemi një varg me N elemente, të tipit ‘Item’. Për secilin element tëmundshëm, llogarisim (rekurzivisht) vlerën maksimale që do të mund të arrihejduke përfshirë atë element, e pastaj duke marrë maksimumin e të gjitha atyrevlerave.

 Programi 5.12. Problemi ‘Knapsack’ (implementimi rekurziv) 

//knap=knapsack, space=hapesira, cap=kapacitetiint knap(int cap){ int i, space, max, t;

Page 302: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 302/699

Avni Rexhepi

302

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ë përgjithësi preferojmë programimin dinamik “top-down” ndaj atij “bottom-up”, sepse 

  Është transformim mekanik i zgjidhjes natyrale të problemit.  Radha e llogaritjes së nënproblemeve kujdeset për vetveten.

  Mund të mos kemi nevojë që të llogarisim përgjigjet për të gjithanënproblemet.

Aplikacionet e programimit dinamik dallojnë në natyrën e nënproblemeve dhenë a sasinë e informacioneve të nevojshme për t’u ruajtur lidhur menënproblemet.

Pikë kritike e cila nuk mund të lihet anash është se programimi dinamik bëhet joefektif kur numri i vlerave të mundshme të funksioneve të cilat mund tënevojiten është aq i madh sa që nuk mund t’i lejo jmë vetes (të kemikomoditetin) e ruajtjes (top-down) ose parallogaritjes (bottom-up) së të gjithavlerave. Për shembull, nëse M dhe madhësitë e elementeve janë madhësi 64- bitëshe ose numra “floating- point” (me presje të lëvizshme) në probleminknapsack, ne nuk do të jemi në gjendje t’i ruajmë vlerat duke i indeksuar në njëvarg. Ky dallim shkakton më shumë sesa bëzdi të vogël  –   paraqet vështirësifundamentale. Nuk ka zgjidhje të mirë të njohur për probleme të tilla ka arsye të besohet se zgjidhjet e tilla nuk ekzistojnë.

Programimi dinamik është teknikë e dizajnit të algoritmeve që është e

 përshtatshme për problemet e avansuara. Programimi dinamik ‘top-down’ ështëteknikë bazë për zhvillimin e implementimeve efikase të algoritmeve rekurzivedhe është vegël për këdo që mirret me dizajnin dhe implementimin ealgoritmeve.

Ky modifikim mekanik i kodit të programit 5.12 redukon kohën e ekzekutimit prej asaj eksponenciale në atë lineare. Thjeshtë i ruajmë vlerat e funksionieve tëcillat llogariten, pastaj i “thërrasim” vlerat e ruajtura kurdo që na duhen (duke përdorur vlerat “rezervë” për të reprezentuar vlerat e panjohura), në vend se të bëhen thirrjet rekurzive. Ruajmë indeksin e elementit, ashtu që të mund të

Page 303: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 303/699

Algoritmet dhe strukturat e të dhënave

303

rikonstruktojmë përmbajtjen e çantës pas llogaritjes, nëse dëshirojmë që:itemKnoën[M]  të jetë në çantë, përmbajtja e mbetur është e njëjtë sikur përknapsack-un optimal të madhësisë M-itemKnoën[M].size  ashtu që

itemKnoën[M-items[M].size]  është në çantë, e kështu me radhë. Programi 5.13. Poblemi Knapsack (programimi dinamik)

// space=hapsira; maxKnown=max_i_njohur;// unknown=i_panjohur; itemKnown=elementi_i_njohurint 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;

Page 304: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 304/699

Avni Rexhepi

304

6. Pemët

Pema (angl. Tree) si strukturë e të dhënave është e ngjashme me rrugën e përshkuar për të arritur në një pikë të caktuar, ku gjatë rrugës, në shumë vende

kemi degëzime dhe duhet të vendosim a të shkojmë majtas apo djathta. Pra,edhe pse në idenë e parë të pemës, menjëherë na parafytyrohet një pemë (tëcilën e pëlqejmë më së shumti), me trungun, degët, gjethet dhe frutat e saj, nëfakt duhet ta imagjinojmë si pemë të përmbysur, me strukturë të kontrolluar.

Pemët janë abstraksion matematik që luan rol qëndror në dizajnin dhe analizëne algoritmeve, sepse

  Pemët përdoren për të përshkruar tiparet dinamike të algoritmeve.   Ndërtojmë dhe përdorim struktura eksplicite të të dhënave të cilat janë

realizim konkret i pemëve.

Veq kemi parë shembuj të këtyre përdorimeve. Ne dizajnojmë algoritmet për problemet e lidhjes të cilat janë të bazuara në strukturën e pemës dheshpjegojmë strukturën e thirrjeve të algoritmeev rekurzive përmes strukturës së pemës.

Pemët i hasim shpesh në jetën e përditshme dhe koncepti themelor është injohur. Për shembull, njerëzit ruajnë të dhënat për paraardhërsit dhe pasardhësit përmes pemës familjare (trungut familjar) dhe terminologjia e përgjithshme vjen pikërisht prej këtij përdorimi. Shembull tjetër është skema e organizimit tëgarave sportive, ose skema organizative e ndonjë organizate të madhe. Ky përdorim përkujton dekompozimin hierarkik që i karakterizon algoritmet përçaj-e-sundo. Shembull tjetër është pema e analizës gramatikore të fjalisë, në pjesët përbërëse të saj; pemët e tilla janë të lidhura ngushtë me procesimin e gjuhëve programuese. Figura 6.1 paraqet një shembull tipik të pemës  –   një që e përshkruan strukturën e librit.

 Figura 6.1 - Pema

Për secilin ‘entitet’ ka një nyje. secila nyje është e lidhur me nyjet pasuese përmes lidhjeve dhe ato paraardhëse.

 Në aplikacionet kompjuterike, një prej përdorimeve më familjare të strukturës së pemës është organizimi i sistemit të fajllave. Fajllat i ruajmë nëpër “folder”-a tëcilët janë të definuar rekurzivisht si sekuencë e folderave dhe fajllave.

Page 305: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 305/699

Algoritmet dhe strukturat e të dhënave

305

Definicioni rekurziv përseri reflekton natyrën rekurzive të dekompozimit dheështë identik me definicionin e një tipi të zakonshëm të pemës.

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

kllapa tw ndwrthurrura, zhvendosje e shkallwzuar dhe si pemw.

-

da

b   c

+

*

f e

*

 /

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

Page 306: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 306/699

Avni Rexhepi

306

Ka shumë lloje të ndryshme të pemëve dhe është me rëndësi që të kuptohetdallimi ndërmjet abrstaksionit dhe reprezentimit konkret me të cilin punojmë përnjë aplikacion të dhënë. Varësisht prej rastit do të shqyrtojmë lloje të ndryshmetë pemëve dhe reprezentimet e detajuara të tyre. Në fillim do të definohen pemëtsi objekte abstrakte dhe do të prezentohet terminologjia themelore e shoqëruarme to. Në mënyrë joformale do të diskutohen llojet e ndryshme të pemëve tëcilat duhet të shqyrtohen sipas radhës së përgjithësisë:

  Pemët  Pemët e rrënjëzuara  Pemët e renditura  Pemët binare dhe M-are

Figura 6.4 - Llojet e pemëve

 Në figurën 6.4 janë paraqitur disa shembuj të pemëve: pema binare, pematernare, me rrënjë dhe e lirë. Kjo figurë ilustron shumë koncepte themelore të

cilat do të diskutohen dhe definohen.

Pema është bashkësi (koleksion) jo i zbrazët i nyjeve dhe degëve, që i plotësondisa kushte të caktuara. Nyja është objekt i thjeshtë që mund të ketë emër dhemund të bartë edhe informata të tjera të shoqëruara. Dega është një lidhjendërmjet dy nyjeve. Shtegu në pemë është një listë e degëve të ndryshme në tëcilin nyjet e njëpasnjëshme lidhen përmes degëve në pemë. Një tipar definues i pemës është se ekziston saktësisht një shteg që i lidhë cilatdo dy nyje. Nëse kamë shumë se një shteg ndërmjet ndonjë çifti të nyjeve ose nëse nuk ka shtegndërmjet ndonjë çifti të nyjeve, atëherë kemi të bëjmë me graf, jo me pemë. Në

Page 307: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 307/699

Algoritmet dhe strukturat e të dhënave

307

anën tjetër, mund të thuhet se pema është rast specifik i grafit. Një bashkësi e pemëve të ndara, quhet pyll (angl. forest).

Pema e rrënjëzuar (ose pema me rrënjë) është ajo pemë në të cilën e

 përcaktojmë një nyje si rrëntë të pemës. Në shkencat kompjuterike, normalishttermi pemë i referohet pemës me rrënjë dhe përdoret termi pemë e lirë për të jureferuar strukturës së përgjithshme të pemës. Në pemën me rrënjë, cilado nyjeështë rrënjë e nënpemës që përbëhet prej saj dhe prej nyjeve nën të.

Ekziston saktësisht një shteg ndërmjet rrënjës dhe secilës nyje tjetër në pemë.Definicioni nuk përfshinë drejtimin (kahjen) në degë. Normalisht, i mendojmëdegët si të jenë të gjitha të drejtuara prej rrënjës teposhtë ose të gjitha tëdrejtuara kah rrënja, varësisht prej aplikacionit. Zakonisht, pema me rrënjëvizatohet si pemë e përmbysur, me rrënjën në maje (edhe pse fillimisht duket si

 jo e natyrshme) dhe flasim për nyjen ‘y’ si nyje që ndodhet nën nyjen ‘x’ (dhe‘x’ mbi ‘y’) nëse nyja ‘x’ është në shtegun prej ‘y’ kah rrënja (do të thotë, yështë lidhur me x përmes degës që nuk kalon nëpër rrënjë). Secila nyje (përveqrrënjës) ka saktësisht një nyje përmbi vetën, e cila quhet prind. Nyjetdrejtpërdrejt një nyje quhen fëmijët e saj. Terminologjia familjare përdoret edhe për nivelet si “gjyshërit” ose “vëllezërti/motrat” për nyjet e nivelit të njëjtë, me prind të njëjtë.

 Nyjet të cilat nuk kanë fëmijë, quhen “gjethe” ose “nyje fundore”. Nyjet të cilatkanë së paku një fëmi jë, ndonjëherë quhen edhe “nyje jo-fundore”. Në pemët të

cilat përdoren për të prezentuar strukturën e thirrjeve të algoritmeve rekurzive,nyjet jofundore (rrathët) paraqesin thirrjet e funksioneve me thirrje rekurzive,ndërsa nyjet fundore (katrorët) paraqesin thirrjet e funksioneve pa thirrjerekurzive.

 Në disa aplikacione, është shumë me rëndësi mënyra në të cilën janë renditurfëmijët e secilës nyje, kurse në të tjerat nuk është me rëndësi. Një pemë erenditur është pema me rrënjë në të cilën renditja e fëmijëve në secilën nyjeështë e përcaktuar (specifikuar). Pemët e renditura janë reprezentim natyral: p.sh., fëmijët i vendosim sipas një radhe të caktuar kur e vizatojmë pemën. Në të

vëertetë në shumë reprezentime konkrete natyrale ka renditje të nënkuptuar dheky përcaktim është shumë i rëndësishëm kur shqyrtohet përfaqësimi i pemëve nëkompjuter.

 Nëse secila nyje duhet të ketë një numër të caktuar të fëmijëve, që paraqiten nënjë renditje specifike, atëherë kemi të bëjmë me pemën M-are. Në pemën e tillë,është e zakonshme të definohen nyjet eksterne speciale të cilat nuk kanë fëmijë.Pastaj, nyjet eksternale mund të sillen (veprojnë) si nyje “të rrejshme” përreferencë nga nyjet të cilat nuk kanë numrin e specifikuar të fëmijëve. Nëveçanti, pema më e thjeshtë M-are është pema binare. Pema binare është pemë e

renditur e cila përbëhet nga dy lloje të nyjeve: nyjet eksterne që nuk kanë fëmijë

Page 308: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 308/699

Avni Rexhepi

308

dhe nyjet interne, të cilat kanë saktësisht nga dy fëmijë. Pasi që dy fëmijët esecilës nyje interne janë të renditur, atyre ju referohemi si fëmija i majtë dhefëmija i dajthtë. Secila nyje interne duhet të ketë të dy fëmijët, të majtin dhe tëdjathtin, edhe pse njëra ose të dyja mund të jenë nyje eksterne. Në pemën M-are,gjethja është nyja interne fëmijët e së cilës janë të gjithë eksternal.

Kjo është terminologjia themelore, ndërsa në vazhdim do të shqyrtohendefinicionet formale, reprezentimet dhe aplikacionet, në renditje rritëse të përgjithësimit.

  Pemët binare dhe M-are  Pemët e renditura  Pemët me rrënjë  Pemët e lira

Definicion 6.1. Pema binare është ose nyje eksterne ose nyje interne e lidhur menjë çift të pemëve binare, të cilat quhen nënpema e majtë dhe nëpema e djathtë easaj nyjeje.

Ky definicion e bën të qartë se vetë pema binare është një koncept abstraktmatematik. Kur punojmë me reprezentimin kompjuterik, jemi duke punuar mevetëm një realizim konkret të këtij abstraksioni. Situata nuk është e ndryshmenga reprezentimi i numrave real, me float, numrave të plotë me int, etj. Kur

e vizatojmë pemën me një nyje në rrënjë, të lidhur me degë për në nënpemën emajtë në të majtë dhe nënpemën e djathtë në të djathtë, jemi duke zgjedhur njëreprezentim të përshtatshëm. Ka shumë mënyra të ndryshme për të reprezentuar pemët binare, të cilat fillimisht mund të duken befasuese.

Reprezentimi konkret që përdoret më së shpeshti kur implementohen programettë cilat përdoren për manipulim të pemëve binare, është struktura me dy lidhje(të majtën dhe të djathtën, që janë pointerë) për nyjet interne (shih figurën 5.21).Kjo strukturë është e ngjashme me atë të listës së lidhur. Lidhjet “Null” i përgjigjen nyjeve eksterne. Në mënyrë specifike, në repezentimin standard të

nyjes, kemi:struct node { Item item; node *l, *r; };

typedef node *link;

që në fakt është kodi në C++ për definicionin 5.1. Nyjet përbëhen prejelementeve dhe çifteve të pointerëve për në nyje, të cilëve ju referohemi silidhje. Kështu p.sh., operacionin abstrakt të lëvizjes në nënpemën e majtë eimplementojmë me referencën me pointer si: x = x->l. (l  –  për left, angl. left-majtas).

Page 309: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 309/699

Algoritmet dhe strukturat e të dhënave

309

Figura 6.5 - Reprezentimi i pemës binare

Ky reprezentim standard mundëson implementim efikas të operacioneve të cilat përdoren për të lëvizur nëpër pemë, nga rrënja e teposhtë, por jo për operacionettë cilat përdoren për të lëvizur te lartë pemës, nga fëmija tek prindi. Përalgoritmet të cilat kërkojnë operacione të tilla, mund të shtojmë një lidhje tëtretë në secilën nyje, e cila do të pointojë në prindin. Kjo alternativë është engjashme me atë të listës së lidhur dyfish. Sikur në rastine listave të lidhuradyfish, nyjet e pemës i mbajmë në një varg dhe i përdorim indeksat në vend të pointerëve, si lidhje për situatat e caktuara.

Për shkak të mundësive të ndryshme të reprezentimit, do të mund të zhvillonimtipin abstrakt të të dhënave (ADT) të pemës binare, i cili do të enkapsulojëoperacionet e rëndësishme të cilat do të dëshirojmë t’i kryejmë dhe që ndanë përdorimin dhe implementimin e këtyre operacioneve. Megjithatë, nuk do ta përdorim këtë qasje, sepse

  Më së shpeshti përdoret reprezentimi me dy lidhje (dy pointera)  Pemët përdoren për të implementuar ADT të niveleve më të larta dhe

fokusi mbetet tek to  Përdorim algoritme efikasiteti i të cilave varet nga reprezentimi i veçantë

 –  fakt që mund të humbet në një ADT.

Kështu, reprezentimi binar i paraqitur në figurën 6.5 është fundamental. Përlistat e lidhura, operacionet elementare ishin insertimi dhe largimi i nyjeve. Perreprezentimin standard të pemëve binare, operacionet e tilla nuk janë

elementare, për shkak të lidhjes së dytë. Nëse dëshirojmë të largojmë një nyjenga pema binare, duhet të bashkrendojmë problemin themelor se do të duhet tëtrajtojmë dy fëmijë pasi të jetë larguar nyja, por vetëm një prind. Këto janë trioperacione natyrale të cilat nuk e kanë këtë vështirësi: insertimi i nyjes në fund(zëvendëso lidhjen Null me lidhjen për nyjen e re), largimi i gjethes (zëvendësolidhjen e saj me Null) dhe kombinimi i dy pemëve duke krijuar rrënjë të re melidhje të majtë në njërën pemë dhe me lidhje të djathtë në pemën tjetër. Këtooperacione përdoren gjerësisht gjatë manipulimit të pemëve binare.

Page 310: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 310/699

Avni Rexhepi

310

Definicion 6.2. Një pemë M-are është ose nyje eksterne ose nyje interne e lidhurme një sekuencë të renditur të M pemëvë, të cilat poashtu janë pemë M-are.

 Normalisht, nyjet e pemës M-are reprezentohen si strukturë me M lidhje të

emërtuara (si në pemën binare, pointerë) ose si vargje me M lidhje. Përdorimi ivargjeve për të mbajtur lidhjet është i duhur sepse vlera e M është fikse, edhe pse, si do të shohim, duhet kushtuar kujdes përdorimit të tepërt të hapësirës kur përdoret reprezentimi i tillë.

Definicion 6.3. Pema e renditur është nyja (e quajtur rrënjë) e lidhur me njësekuencë të pemëve të ndara. Sekuenca e tillë, quhet pyll.

Dallimi ndërmjet pemës së renditur dhe pemës M-are është se nyjet e pemës sërenditur kanë numër të çfarëdoshëm të fëmijëve, ndërsa në pemën M-are, duhettë ketë saktësisht M fëmijë. Ndonjëherë përdoret termi pemë e përgjithshme, për

të dalluar pemën e renditur nga pema M-are.

Pasi që nyjet e pemës së renditur mund të kenë çfarëdo numri të lidhjeve, është enatyrshme të shqyrtohet përdorimi i listës së lidhur, përpara vargjeve, për tëmbajtur lidhjet për fëmijët e nyjes. Figura 6.6 është një shembull i reprezentimittë tillë. Prej këtij shembulli, është e qartë se secila nyje pastaj përmbanë dylidh je, një për listën e lidhur për t’a lidhur atë me moshatarët (vëllezërit/motrat)e vet dhe tjetra për listën e lidhur të fëmijëve të saj.

Figura 6.6. Reprezentimi i pemës

Reprezentimi i pemës duke mbajtur listën e lidhur të fëmijëve të secilës nyjeështë ekuivalent me reprezentimin e saj si pemë binare. Diagrami lartë, në tëdjahtë, paraqet reprezentimin e pemës majtas lartë, përmes listës së lidhur tëfëmijëve, me listën e implementuar në lidhjet e djathta të nyjeve dhe me secilënlidhje të majtë të nyjes duke pointuar në nyjen e parë në listën e lidhur të

Page 311: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 311/699

Algoritmet dhe strukturat e të dhënave

311

fëmijëve të saj. Diagrami në të djathtë poshtë, paraqet një version të riaranzhuartë diagramit lartë dhe qartazi reprezenton pemën binare në skajin e majtë poshtë.Pra, mund të shqyrtojmë pemën binare si përfaqësues të pemës.

Vetia 6.1. Ekziston një lidhje një-me-një ndërmjet pemëve binare dhe pyjeve tërenditura.

Kjo lidhje është paraqitur në figurën 6.6. Mund të paraqesim çfarëdo pylli si pemë binare, duke bërë lidhjen e majtë të secilës nyje që të pointojë në fëmijënmë të majtë të saj dhe lidhjen e djathtë të secilës nyje që të pointojë nëvëllaun/motrën në të djathtë.

Definicion 6.4. Pema me rrënjë (ose pema e parenditur) është një nyje (e quajturrrënjë) e lidhur me një “multiset” të pemëve me rrënjë (multiseti i tillë quhet një pyll i parenditur).

Pema ku renditja në të cilën shqyrtohen fëmijët e nyjes nuk është e rëndësishmeështë pemë e parenditur. Pema e parenditur mund të definohet edhe si pemë të parenditura të përbëra nga një bashkësi e relacioneve prind-fëmijë ndërmjetnyjeve. Kjo zgjidhje duket të ketë një lidhje të vogël me strukturat rekurzive që janë duke u shqyrtuar, por ndoshta është reprezentim konkret që është më i sakti për nocionin abstrakt.

Mund të zgjedhim që pemën e parenditur, në kompjuter ta reprezentojmë përmes një peme të renditur, duke ditur se shumë pemë të ndryshme të renditura

mund të reprezentojnë të njëjtën pemë të parenditur. Me të vërtetë, problemi ikundërt i përcaktimit nëse dy pemë të ndryshme të renditura reprezentojnë ose jo të njëjtën pemë të parenditur (problemi i izomorfizmit të pemës) është ivështirë për t’u zgjidhur. 

Tipi më i përgjithshëm i pemës është ai ku nuk ka nyje rrënjë të dalluar. Për tëdefinuar si duhet pemën e parrënjë, të parenditur ose të lirë, duhet të jepetdefinicioni i grafit.

Definicion 6.5. Grafi është një bashkësi e nyjeve së bashku me bashkësinë edegëve të cilat i lidhin çiftet e nyjeve të ndryshme (me më së shumti një degë qëlidhe cilindo çift të nyjeve).

Mund të përfytyrojmë fillimin nga një nyje dhe vazhdimin nëpër një degë deritek nyja e lidhur në të, e pastaj duke vazhduar nëpër një rrugë prej asaj nyje, nënjë nyje tjetër, e kështu me radhë. Sekuenca e degëve të cilat dërgojnë prej njënyje në një nyje tjetër në këtë mënyrë, pa asnjë nyje që paraqitet dy herë, quhetshteg i thjeshtë (angl. simple path). Grafi është i lidhur nëse ka një shteg tëthjeshtë që lidhë cilindo çift të nyjeve. Shtegu që është i thjeshtë, por në të cilinnyja e parë dhe e fundit janë e njëjta nyje quhet cikël (fillimi dhe mbarimi në tënjëjtën nyje).

Page 312: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 312/699

Avni Rexhepi

312

Secila pemë është graf, mirëpo cilat grafe janë pemë? Konsiderojmë se grafi përtë qenë pemë duhet t’i përmbushë cilindo prej kushteve vijuese: 

  G ka N –  1 degë dhe asnjë cikël.  G ka N –  1 degë dhe është i lidhur.  Saktësisht një shteg i thjeshtë lidhe secilin çift të nyjeve në graf.  G është i lidhur, por nuk mbetet i lidhur nëse largohet cilado nyje.

Cilido prej këtyre kushteve është i nevojshëm dhe i mjaftueshëm për të provuar(vërtetuar) tri të tjerat. Formalisht, duhet të zgjedhim njërin prej tyre që tëshërbejë si definicion i pemës së lirë, mirëpo joformalisht lejojmë që të gjitha së bashku të shërbejnë si definicion.

 Ne reprezentojmë pemën e lirë thjeshtë si koleksion të degëve. Nëse zgjedhimqë të reprezentojmë pemën e lirë si një pemë të parenditur, të renditur soe edhe binare, duhet të pranojmë se, në përgjithësi, ka shumë mënyra të ndryshme përtë reprezentuar secilën pemë të lirë.

Abstraksioni i pemës paraqitet si nevojë shpeshherë dhe dallimet e diskutuara nëkëtë pjesë janë të rëndësishme, sepse njohja e abstraksioneve të ndryshme të pemëve është shpeshherë esenciale në gjetjen e algoritmeve efikase dhestrukturave gjegjëse të të dhënave, për problemin e dhënë. Shpesh punojmëdrejtpërdrejt me reprezentimin konkret të pemëve pa pasur kujdes për

abstraksionin e veçantë, por shumë herë përfitojmë nga puna me abstraksionin eduhur të pemës, e pastaj duke shqyrtuar reprezentimet konkrete. Ka shumëshembuj të ndryshëm.

 Në vazhdim do të paraqesim një numër të tipareve themelore matematike të pemëve, të cilat do të jenë të dobishme në dizajnin dhe analizën e algoritmeve të pemëve.

Tiparet matematike të pemëve binare

Pasi që më së shpeshti përdoren pemët binare, do të fokusohemi në tiparetmatematike të tyre. Të kuptuarit e tipareve themelore është bazë për të kuptuarite karakteristikave të performansës së algoritmeve të ndryshme të cilat hasen jovetëm në përdorimin e pemëve binare si strukturë eksplicite e të dhënave, porgjithashtu edhe për algoritmet rekurzive përçaj-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 përmes induksionit: pema binare pa asnjë nyje interne ka një

nyje eksterne, kështu që vetia vlenë për N=0. Për N>0, cilado pemë binare me N

Page 313: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 313/699

Algoritmet dhe strukturat e të dhënave

313

nyje interne ka k nyje interne në nënpemën e saj të majtë dhe N-1-k nyje internenë nënpemën e saj të djathtë, për ndonjë k ndërmjet 0 dhe N-1, pasi që rrënjaështë një nyje interne. Sipas hipotezës induktive, nënpema e majtë ka k+1 nyjeeksterne dhe nënpema e djathë ka N-k nyje eksterne, për një total prej N+1nyjesh.

Vetia 6.3. Pema binaer me N nyje interne ka 2N lidhje/degë: N-1 lidhje për nënyjet interne dhe N+1 nyje për në nyjet eksterne.

 Në cilëndo pemë me rrënjë, secila nyje, përveq rrënjës, ka një prind unik, dhesecila degë lidhë një nyje me prindin e saj, kështu që janë N-1 lidhje që lidhinnyjet interne. Ngajshëm, secila prej N+1 nyjeve eksterne ka një lidhje, për në prindin e saj unik.

Karakteristika e performansës së shumë algoritmeve varet jo vetëm në numrin e

nyjeve në pemën e shoqëruar, por edhe në tiparet e ndryshme strukturale.

Definicion 5.6. Niveli i nyjes në pemë është për një më i lartë se niveli i prindittë saj (me rrënjën në nivelin 0). Lartësia e pemës është maksimumi i niveleve tënyjeve të pemës. Gjatësia e shtegut të pemës është shuma e të gjitha niveleve tëtë gjitha nyjeve të pemës. Gjatësia e shtegut intern të pemës binare është sashuma e niveleve të të gjitha nyjeve interne të pemës. Gjatësia e shteguteksternal të pemës binare është sa shuma e niveleve të të gjitha nyjeve eksternetë pemës.

Mënyrë e përshtatshme për të llogaritur gjatësinë e shtegut të pemës është që tëmblidhen, për çdo ‘k’, produkti i ‘k’ dhe numrit të nyjeve në nivelin ‘k’. 

Këto madhësi gjithashtu kanë definicione të thjeshta rekurzive të cilat rrjedhindrejtpërdrejt prej definicioneve rekurzive të pemëve dhe pemëve binare. Përshembull, lartësia e pemës është 1 më e madhe sesa maksimumi i lartësisë sënënpemëve të rrënjës së saj dhe gjatësia e shtegut të pemës me N nyje ështëshuma e gjatësive të shtigjeve të nënpemëve të rrënjës së saj plus N-1.Madhësitë gjithashtu ndërlidhen drejtpërdrejtë me analizën e algoritmeverekurzive. Për shembull, për shumë llogaritje rekurzive, lartësia e pemës

korresponduese është saktësisht thelëësia maksimale e rekurzionit ose madhësiae stekut të nevojshëm për të përkrahur llogaritjen.

Vetia 6.4. Gjatësia e shtegut eksternal të cilësdo pemë binare me N nyje interneështë 2N më e madhe sesa gjatësia e shtegut intern.

Kjo mund të provohet (vërtetohet) me induksion, por një provë alternative (që poashtu funksionon edhe për vetinë 5.6) është instruktive. Vëreni se cilado pemë binare mund të konstruktohet përmes procesit vijues: filloni me pemën binare që përbehet prej një nyjeje eksterne. Pastaj, përsëritni N herë si në vijim: zgjedhninjë nyje eksterne dhe zëvendësojeni me një nyje të re interne me dy nyje

Page 314: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 314/699

Avni Rexhepi

314

eksterne si fëmijë. Nëse nyja eksterne e zgjedhur është në nivelin k, gjatësia eshtegut intern është rritur për k, por gjatësia e shtegut ekstern është rritur përk+2 (një nyje eksterne në nivelin k është larguar, por janë shtuar dy në nivelink+1). Procesi fillon me pemën me gjatësi interne dhe eksterne të shtegut të barabarta me zero dhe secili prej N hapave, e rritë gjatësinë e shtegut ekstern për2 më shumë sesa gjatësia e shtegut intern.

Vetia 6.5. Lartësia e pemës binare me N nyje interne është më së paku lg N dhemë së shumti N-1.

Rasti më i keq është pema e degjeneruar me vetëm një gjete, me N-1 lidhje prejrrënjës deri tek gjethja (shih figurën 6.7). rasti më i mirë është pema e balansuarme 2i  nyje interne në secilin nivel ‘i’ përveq nivelit të fundit (si në Fig. 6.7). Nëse lartësia është ‘h’, atëherë duhet të kemi:

2h – 1 < N + 1 ≤ 2h,

 pasi që janë N+1 nyje eksterne. Ky inekuacion implikon vetinë e shpallur:gjatësia e rastit më të mirë (angl. best-case) është saktësishtë e barabartë me lg N, e rrumbullaksuar në numrin e plotë më të afërm.

Figura 6.7. Tri pemë binare me nga 10 nyje interne

Pema binare majtas, ka lartësinë 7, gjatësinë e shtegut intern 31 dhe gjatësinë eshtegut ekstern 51. Pema tërësisht e balansuar (në mes) me 10 nyje interne kalartësinë 4, gjatësinë e shtegut intern 19 dhe gjatësinë e shtegut ekstern 39 (asnjë

 pemë binare me 10 nyje nuk ka vlera më të vogla për këto madhësi). Pema edegjeneruar (në skajin e djathtë) me 10 nyje interne, ka lartësinë 10, gjatësinë eshtegut intern 45 dhe gjatësinë e shtegut ekstern 65 (asnjë pemë binare me 10nyje nuk ka vlera më të mëdhaja për këto madhësi).

Vetia 6.6. Gjatësia e shtegut intern të pemës 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 përtë njejtat pemë si ato të referuara në diskutimin e vetisë 5.8 dhe të paraqitur nëfigurën 6.7. Gjatësia e shtegut intern të rastit më të keq është: 0+1+2+...+(N–

Page 315: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 315/699

Algoritmet dhe strukturat e të dhënave

315

1)=N(N–1)/2. Pema e rastit më të mirë ka (N+1) nyje eksterne në lartësinë jomë shumë se [lg N]. Duke shuëmzuar këto dhe duke aplikuar vetinë 5.7, fitojmëkufirin (N+1)[lgN]–2N<Nlg(N/4).

Si do të shihet, pemët binare paraqiten gjerësisht në aplikacionet kompjuterikedhe performansa është më e mira kur pema binare është tërësisht e balansuar(ose afërsisht ashtu). Për shembull, pemët që përdoren për të përshkruaralgoritmet përçaj-e-sundo, siq janë kërkimi binar dhe ‘meregesort’ janë tërësishttë balansuara. Do të shqyrtohen edhe strukturat eksplicite të të dhënave të cilat janë të bazuara në pemët e balansuara.

Këto tipare themelore të pemëve sigurojnë informatën që nevojitet për tëzhvilluar algoritme efikase për një numër të problemeve praktike. Analiza më edetajuar e disa algoritmeve specifike kërkon analizë të sofistikuar matematike,

edhe pse mund të marrim përafrime të dobishme me argumente të drejtpërdrejtainduktive si ato që u përdorën në këtë pjesë. Tiparet tjera matematike të pemëvedo të diskutohen sipas nevojës.

Përshkimi i pemës

Para se të shqyrtohen algoritmet të cilat konstruktojnë pemët dhe pemët binare,do të shqyrtojmë algoritmet për funksionin themelor të procesimit të pemëve,atë të përshkimit të pemës (kalimit me radhë nëpër anëtarët e pemës - angl. tree

traversal). Me pointerin e dhënë për pemën, dëshirojmë të procesojmë secilënnyje në pemë në mënyrë sistematike. Në listën e lidhur, lëvizim prej nyjes nënyje duke përcjellur një lidhje (për listën e lidhur njëfish). Për pemët, sidoqoftë,duhet të marrim një vendim, sepse mund të ketë lidhje të shumëfishta, për t’u përcjellur.

Ka shumë veprime që mund të kryhen në strukturën e pemës. Një i zakonshëmdo të ishte kryerja e një operacioni të caktuar P në seclin element të pemës.Atëherë, nënkuptohet që P duhet të kalojë nëpër secilën nyje të pemës, përmes përshkimit të pemës. Nëse e konsiderojmë operacionin si proces i njëshëmsekuencial, atëherë nyjet individuale vizitohen në një renditje të caktuar dhemund të konsiderohen sikur të ishin shtrirë në renditje lineare. Në fakt, përshkrimi i shumë algoritmeve lehtësohet shumë nëse flasimpër procesimin eelementit të ardhshëm në pemë bazuar në renditjen e caktuar. Janë tri renditje parimore të cilat dalin natyrshëm nga struktura e pemës. Sikur vetë struktura e pemës, ato janë të përshtatshme që të përshkruhen në terma rekurziv. Duke jureferuar figurës në vijim, në të cilën R është rrënja, kurse A dhe B paraqesinnëndegën (nënpemën) e majtë dhe të djathtë, tri renditjet e mundëshme janë:

Page 316: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 316/699

Avni Rexhepi

316

1.  Preorder: R, A, B (vizito rrënjën, para nëndegëve)

2.  Inorder: A, R, B

3.  Postorder: A, B, R (vizito rrënjën, pas nëndegëve)

 Fig. 6.8 –  Pema binare

 Nëse përcillet rruga e kaluar atëherë mund të shihet se secilën nyje e vizitojmënga tri herë (në secilën nyje “hyjmë” nga tri her ë): në rrugë e sipër, kurvizitojmë nyjen herën e parë (Preorder), në kthim nga fëmija i majtë (Inorder)dhe në kthim nga fëmija i djathtë (Postorder).

Inorder

Preorder

Postorder

  Fig. 6.9 –  Përshkimi i nyjeve të pemës 

Algoritmet e ndryshme, bëjnë kryerjen e operacioneve në momente të ndyshme,duke zgjedhur kryerjen e operacioneve në secilën nyje, në ndonjërën prejvizitave të nyjes. Kjo zgjedhje ka ndikim në mënyrën e kryerjes sëoperacioneve, paraqitjen dhe rezultatet. P.sh., përshkimi i pemës nga figura 5.xdhe regjistrimi i karaktereve në secilën nyje në renditjen me të cilën hasen,fitojmë renditjet vijuese:

Page 317: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 317/699

Algoritmet dhe strukturat e të dhënave

317

-

da

b   c

+

*

f e

*

 /

 

1. Preorder: * + a / b c - d * e f2. Inorder: a + b / c * d - e * f3. Postorder: a b c / + d e f * - *

 Fig. 6.10 –  Përshkimi i pemës –  shprehjet

Këto tri forma të shprehjes rezultojnë me mënyrën e paraqitjes së tyre:

-   përshkimi ‘preorder’ rezulton me notacionin ‘ prefix’ ;-   përshkimi ‘postrorder’ gjeneron notacionin ‘ postifx’ , dhe-   përshkimi ‘inorder’, rezulton me notacionin konvencional, ‘infix’  (edhe

 pse pa kllapat e nevoshme për të qartësuar prioritetet e operatorëve)

Përshkimi rekurziv i pemës 

Funksioni rekurziv në vijim e merr si argument një lidhje për në pemë dhe ethërret funksionin visit me secilën prej nyjeve të pemës si argument. Kështu siështë, funksioni implementon përshkimin ‘preorder’ (rendi paraprak, ose

 pararendja); nëse e lëvizim thirrjen e funksionit visit  ndërmjet thirrjeverekurzive, do të kemi përshkimin ‘inorder’ (në rend); dhe nëse e lëvizim thirrjene funksionit visit pas thirrjeve rekurzive, do të kemi përshkimin ‘postorder’(rendi pasues, pasrendja).

Programi 6.1- Përshkimi rekurziv i pemës// traverse=përshko,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 për pemët binare. Për listat e lidhura, kishimdy opcione themelore: proceso nyjen dhe pastaj përcjelle lidhjen (në të cilin rastmund t’i vizitonim nyjet me radhë) ose përcille lidhjen e pastaj proceso nyjen(në të cilin rast do të mund të vizitonim nyjet në renditjen e kundërt). Për pemët

Page 318: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 318/699

Avni Rexhepi

318

 binare, si u pa edhe më herët, kemi dy lidhje dhe prandaj kemi tri renditjethemelore në të cilat mund të vizitojmë nyjet:

  Preorder, ku vizitojmë nyjen, pastaj vizitojmë nënpemën e majtë dhe atëdjathtë

  Inorder, ku vizitojmë nëndegën e majtë, pastaj vizitojmë nyjen, pastajvizitojmë nëndegën e djathë

  Postorder, ku vizitojmë nëndegën e majtë dhe nëndegën e djathë, e pastajvizitojmë nyjen

Këto funksione mund t’i implementojmë me lehtësi me një program rekurziv,siç është paraqitur në programin 6.1, i cili është përgjithësim direkt i programit5.5 për përshkimin e listës së lidhur. Për të implementuar përshkimin në

renditjet tjera, bëjmë permutacion të thirrjeve të funksionit në programin 6.1, nëmënyrën e duhur. Figura 6.12 paraqet renditjen në të cilën vizitohen nyjet në një pemë të marrur si shembull, për secilën renditje. Figura 6.11 paraqet sekuencëne thirrjeve të funksionit që ekzekutohet kur thirret programi 6.1, në pemënshembull të figurës 6.12.

Kjo sekuencë e thirrjeve të funksionit konstituon përshkimin ‘preorder’ për pemën në figurërn 6.12.

Page 319: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 319/699

Algoritmet dhe strukturat e të dhënave

319

Figura 6.11 - Thirrjet e funksionit në përshkimin ‘Preorder’  

Page 320: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 320/699

Avni Rexhepi

320

E

H

F

D

B

A   C   G

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   C   G

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   C   G

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

 

Figura 6.12. Renditjet e përshkimit të pemës

Page 321: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 321/699

Algoritmet dhe strukturat e të dhënave

321

Këto sekuenca paraqesin rendin në të cilin vizitohen nyjet me rastin e përshkimittë pemës, ‘pre-order’ (majtas), inorder (në mes) dhe ‘post-order’ (djathtas). 

Procesi i njëjtë rekurziv në të cilin janë të bazuara metodat e ndryshme të

 përshkimit të pemës veç është hasur më parë, në programet rekurzive përçaj-e-sundo (shih figurën 5.8 dhe atë 5.11) dhe në shprehjet aritmetike. Për shembull,kryerja e përshkimit ‘preorder’ i përgjigjet vizatimit të shenjave në vizore së pari, e pastaj thirrjeve rekurzive (shih figurën 5.11); kryerja e përshkimit‘inorder’ i përgjigjet lëvizjes së disqeve të mëdha në zgjidhjen e problemit tëkullave të Hanoit në mes të thirrjeve rekurzive të cilat i lëvizin të gjitha të tjerat;kryerja e përshkimit ‘postorder’ i përgjigjet vlerësimit të shprehjeve postfiks, ekështu me radhë. Këto korrespondenca na japim pamje të drejtpërdrejt nëmekanizmat prapa përshkimit të pemës. Për shembull, ne e dijmë se secila nyjetjetër në përshkimin inorder është një nyje eksterne, për të njëjtën arsye siçsecila lëvizje tjetër në problemin e kullave të Hanoit përfshinë diskun e vogël.

Është e dobishme të shqyrtohet edhe implementimi jorekurziv i cili e përdorënjë stek eksplicit. Për thjeshtësi, fillojmë duke shqyrtuar një stek abstrakt i cilimund të mbajë elementet ose pemët, të inicializuar me pemën që duhet përshkuar. Pastaj, hyjmë në unazë, ku tërheqim (pop) dhe procesojmë vlerën ekreut (top) në stek, duke vazhduar gjersa steku të jetë i zbrazët. Nëse entiteti itërhequr është një element, e vizitojmë atë; nëse entiteti i tërhequr është pemë,atëherë kryejmë sekuencën e veprimeve “push” që varet nga renditja edëshiruar:

  Për preorder, e shtyejmë (push) nëndegën e djathtë, pastaj të majtën dhe pastaj nyjen.

  Për inorder, e shtyejmë së pari nëndegën e djathtë, pastaj nyjen dhe pastaj nëndegën e majtë.

  Për postorder, e shtyejmë në stek nyjen, pastaj nëndegën e djathtë dhe pastaj nëndegën e majtë.

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

homologun e tij rekurziv (programin 6.1).

Programi 6.2 - Përshkimi preorder (jorekurziv)//traverse=pershko, link=lidhje, s-stekuvoid 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);

Page 322: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 322/699

Avni Rexhepi

322

if (h->l != 0) s.push(h->l);}

}

Degët Null nuk i shtyjmë në stek. Figura 6.13 apraqet përmbajtjen e stekutgjersa përdorim secilën prej metodave të përshkimit të pemës, për pemën eshembullit nga figura 6.12. Përmes induksionit mund të vërtetojmë se kjometodë prodhon të njëjtin rezultat sikur ajo rekurzive, për çfarëdo peme binare.

Figura 6.13 - Përmbajtja e stekut për algoritmet e përshkimit të pemës

Këto sekuenca tregojnë se përmbajtja e stekut për përshkimin e pemës preorder(majtas), inorder (qendër) dhe postorder (djathtas), për një model të idealizuar tëllogaritjes, të ngjashëm me atë që u përdor në figurën 5.5, ku vendosim njëelement dhe dy nëndegët e tij në stek, në renditjen e treguar.

Skema e përshkruar është konceptuale dhe përmbledhë të tri metodat e përshkimit, por implementimet që përdoren në praktikë janë pak më të thjeshta.Për shembull, për preorder, nuk kemi nevojë që të shtyejmë nyjet në stek (nevizitojmë rrënjën e secilës pemë që e tërheqim (pop)) dhe prandaj ne mund të përdorim një stek të thjeshtë i cili përmbanë vetëm një tip të elementeve (degë të pemës) siur në implementimin jorekurziv në programin 6.2. Steku i sistemit që përkrahë programin rekurziv përmbanë adesat e kthimit dhe vlerat eargumenteve, më parë sesa elementet ose nyjet, por sekuenca aktuale në të cilëni bëjmë llogaritjet (vizitojmë nyjet) ëhstë e njëjtë për metodat rekurzive dhe atotë bazuara në stek.

Page 323: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 323/699

Algoritmet dhe strukturat e të dhënave

323

Përshkimi me renditje të nivelit (angl. Level-order traversal)

Shkëmbimi i strukturës themelore të të dhënave në përshkimin preorder(programi 6.2) nga steku në queue (radhë), transformon përshkimin në ‘level-

order’ (përshkim me renditje të nivelit). Programi 6.3. Përshkimi ‘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 katërt natyrale e përshkimit është thjeshtë të vizitohen nyjet në pemëashtu si paraqiten në faqe, duke lexuar prej lartë te poshtë dhe prej të majtës kahe djathta. Kjo metodë quhet përshkimi ‘level order’(angl. level-nivel) sepsenyjet në secilin nivel, paraqiten sëbashku, me rend. Figura 6.14 paraqet mënyrën

se si vizitohen nyjet e pemës (nga figura 6.12). në renditjen sipas nivelit (level-order).

Page 324: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 324/699

Avni Rexhepi

324

E

H

F

D

B

A   C   G

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A   GC

E

H

F

D

B

A  GC

E

H

F

D

B

A   GC

 Figura 6.14 - Përshkimi ‘level -order’  

Kjo sekuencë ilustron rezultatin e vizitimit të nyjeve në pemë në renditjen prej

lartë te poshtë dhe prej të majtës kah e djathta.

Page 325: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 325/699

Algoritmet dhe strukturat e të dhënave

325

Mrrekullisht, mund të përfitojmë përshkimin level-order duke zëvendësuarqueue-n për stekun në programin 6.2, siq është paraqitur në programin 6.3. Për përshkimin preorder, përdorim strukturë të të dhënave LIFO (Last In Firs Out –  stek), ndërsa për renditje ‘level-order’ përdorim strukturë të të dhënave FIFO(Firs In First Out  –   queue). Këto programe meritojnë studim të kujdesshëm,sepse reprezentojnë qasjet e organimzimit të punës që mbetet për t’u bërë qëdallojnë në një mënyrë esenciale. Në veçanti, level-order nuk i korrespondonimplementimit rekurziv që ndërlidhet me strukturën rekurzive të pemës.

Preorder, postorder dhe level-order janë të definuara mirë edhe për rastin e pyjeve. Për t’i bërë definicionet konsistente, mendoni për pyllin si një pemë merrënjë imagjinare. Pastaj, rregulla preorder është “vizito rrënjën, pastaj secilënnënpemë”, rregulla postrorder është “vizito secilën nënpemë, pastaj rrënjën”.Rregulla level-order është e njëjtë si për pemët binare. Implementimet direkte tëkëtyre metodave janë përgjithsime të drejtpërdrejta të programeve të përshkimit preorder të bazuara në stek (programet 6.1 dhe 6.2), për pemët binare që sapo ushqyrtuarn. Shqyrtimi i procedurës më të përgjithsuar të implementimeve bëhetnë pjesën e përshkimit të grafit.

Algoritmet rekurzive të pemës binare

Algoritmet e përshkimit të pemës ilustrojnë faktin themelor se jemi të drejtuar

në shqyrtimin e algoritmeve rekurzive për pemët binare, për shkak të vetënatyrës së këtyre pemëve si struktura rekurzive. Shumë detyra pranojnëalgoritmet direkte rekurzive përçaj-e-sundo, të cilat në esencë i përgjithsojnëalgoritmet e përshkimit. Pema procesohet duke procesuar rrënjën dhe(rekurzivisht) nëndegët e saj; llogaritjen mund ta bëjmë para, ndërmjet apo pasthirrjeve rekurzive (ose ndoshta në të trijat).

Shpeshhere na duhet të gjejmë vlerat e parametrave të ndryshëm struktural për pemën, kur na është dhënë vetëm një lidhje për në pemë. Për shembull, programi 6.4 përmban funksionet rekurzive për llogaritjen e numrit të nyjeve në

 pemë dhe lartësinë e pemës së dhënë. Funksionet pasojnë drejtpërdrejt ngavetia6.6. Asnjëri prej këtyre funksioneve nuk varet prej radhës në të cilën procesohenthirrjet rekurzive: ata procesojnë të gjitha nyjet në pemë dhe kthejnë përgjigjen enjëjtë, nëse për shembull, i ndërrojmë renditjet e thurrjeve rekurzive. Nukllogariten aq lehtë të gjithë parametrat. Për shembull, programi për llogaritjeefikase të gjatësisë së shtegut intern të pemës binare është më sfidues.

 Ne mund të përdorim procecura të thjeshta rekurzive siç janë këto për të mësuartiparet themelore strukturale të pemëve.

Page 326: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 326/699

Avni Rexhepi

326

Programi 6.4. Llogaritja e parametrave të pemës.//count=numri,numëro; 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 tjetër që është shumë i dobishëm kurdo që shkruajmë programe tëcilat procesojnë pemët është ai që shtypë ose vizaton pemën. Për shembull, programi 6.5 është procedurë rekurzive që shtypë pemën (nyjet e pemës) nëformatin e paraqitur në figurën 6.15. Mund të përdorim skemën e njëjtërekurzive për të vizatuar reprezentime më të hollësishme të pemëve.

Figura 6.15. Shtypja e pemës (inorder dhe preorder)

Rezultati majtas rezulton nga përdorimi i programit 6.5 në pemën e shembullitnga figura 6.12 dhe shfaqë strukturën e pemës në mënyrë të ngjashme mereprezentimin grafik të cilin jemi duke e shfrytëzuar, të rrotulluar për 90 shkallë.Rezultati djathtas është prej programit të njëjtë por me urdhërin e shtypjes tëzhvendosur në fillim; shfaqë strukturën e pemës në formatin e zakonshëm të përvijimit.

Page 327: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 327/699

Algoritmet dhe strukturat e të dhënave

327

Programi 6.5 është një përshkim inorder –  nëse shtypim elementin para thirrjeverekurzive, fitojmë përshkimin preorder, i cili është ilustruar në figurën 6.15. Kyformat është i afërm me atë që mund ta përdorim për shembull për shtypjen etrungut familjar ose për të listuar fajllat e sistemit të fajllave të bazuar në pemëose për të bërë një përmbledhje (përvijim) të dokumentit të shtypur. Përshembull, bërja e përshkimit preorder në pemën në figurën 6.1 jep versionin etabelës së përmbajtjes së librit.

Shembulli i parë i programit që ndërton një strukturë eksplicite të pemës binareështë i shoqëruar me aplikacionin për gjetje të maksimumit. Qëllimi i jonë ështëqë të bëjmë një garë (turne, dyluftim): pema binare në të cilën secili element nënyjen interne është kopje e elementit më të madh prej dy fëmijëve. Në veçanti,elementi në rrënjë është kopja e elementit më të madhë në garë. Elementet nëgjethe (nyjet që nuk kanë fëmijë) përbëjnë të dhëna me interes dhe pjesa tjetër e pemës është strukturë e të dhënave që na mundëson gjetjen e elementit më tëmadh, në mënyrë efikase.

Programi 6.5. Funksioni për shtyjen e shpejtë të pemës//printnode=shtype nyjen, item=elementi, show=paraqitevoid 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 përcjellë lartësinë e pemës dhe përdorë këtë informatë përzhvendosje (dhëmbëzim) të shtypjes së reprezentimit të pemës, që mund të përdoret për të “debug”-uar programet për procesim të pemëve (shih figurën

6.1). Supozohet se elementet në nyje janë të tipit Item, për të cilin operatori‘<<’ është definuar përmes mbingarkimit (angl. overloading). 

Programi 5.19 është program rekurziv i cili ndërton turneun prej elementeve nënjë varg. Është një zgjedrim i programit 5.6, përdorë strategjinë përçaj-e-sundo: për të ndërtuar turneun për një element të vetëm, ne krijojmë (dhe kthejmë)gjethen që përmbanë një element. Për të ndërtuar turneun për N>1 elemente, përdorim strategjinë përqaj e sundo: ndaji elementet në gjysmë, ndërto turnetë për secilën gjysmë dhe krijo nyje të er me lidhje për në të dy turnetë dhe menjëelement që është kopja e elementit më të madh në rrënjët e të dy turneve.

Page 328: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 328/699

Avni Rexhepi

328

Figura 5.30 është shembull i një strukture eksplicite të pemës të ndërtuar përmes programit 5.19. Ndërtimi i strukturës rekurzive të të dhënave si kjo ndoshtaështë më e preferuar se gjetja e maksimumit duke skenuar të dhënat, siç ështëvepruar në programin 5.6, sepse struktura e pemës na jep fleksibilitietin për tëkryer operacione të tjera. Vetë operacioni që përdoret për të krijuar turneunështë një shembull i rëndësishëm: nëse janë dhënë dy turne, mund t’ikombinojmë ato për të krijuar një turne të vetëm në kohë konstante, duke krijuarnjë nyje të re dhe duke bërë që lidhja e saj e majtë (pointeri) të pointojë njënjërin prej turneve dhe lidhja e saj e djathtë (pointeri) të pointrojë në tjetrin dheduke marrë elementin më të madh prej ty dyve (që ndodhet në rrënjët e dyturneve të dhëna) si elementin më të madh në turneun e kombinuar. Gjithashtumund të shqyrtojmë algoritmet për shtimin e elementeve, largimin e elementevedhe për kryerjen e operacioneve të tjera.

Figura 6.16. Pema eksplicite për gjetjen e maksimumit (turneut)

Kjo figurë paraqet strukturën eksplicite të pemës që konstruktohet nga programi

6.6, prej hyrjes: A M P L E. Elementet e të dhënave ndodhen në gjethe. Secilanyje interne ka kopjen e elementit më të madh nga dy fëmijët e saj, kështu që përmes induksionit, elementi më i madh ndodhet në rrënjë.

Programi 6.6. Konstruktimi i turneut.//item=elementi/nyja; l-left, r-rightstruct 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;

Page 329: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 329/699

Algoritmet dhe strukturat e të dhënave

329

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], ndërton turneun për të dy pjesët(rekurzivisht) dhe bën turneun për vargu e ploët duke përcaktuar lidhjet në nyjene re për në turnetë e ndërtuara rekurzivisht dhe duke caktuar elementin e saj nëmë të madhin nga rrënjët e dy turneve të ndërtuara rekurzivisht.

Implementimet e bazuara në pemë për shumë ADT të queue-ve janë tërëndësishme. Poashtu shumë algoritme janë të bazuara në pemët binare tëkërkimit dhe sfitë për implementimin dhe përdorimin 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ë ndërton pemën binare është modifikimi i programit për vlerësimin e shprehjeve me prefiks (Programi 5.4), për tëkonstruktuar pemën që reprezenton shprehjen me prefiks, në vend se vetëm tëvlerësohet ajo (shih figurën 6.17). Programi 6.7 përdorë skemën e njëjtërekurzive sikur programi 5.4, por funksioni rekurziv kthen lidhjen për në pemë,e jo vlerën. Për secilin karakter në shprehje krijojmë një nyje të re të pemës:nyjet që i korrespondojnë operatorëve kanë lidhjet (pointerët) tek operandët etyre dhe gjethet përmbajnë variablat (ose konstantet) të cilat janë hyrjet përshprehjen.

Figura 6.17. Pema e analizës

Pema është konstruktuar nga programi 6.7 për shprehjen prefiks: * + a * * b c +d e f. Kjo është një mënyrë natyrale për të reprezentuar shprehjen: secili operandnë gjethe (të cilën 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ë përmbanë operatorin.

Programet përkthyese siç janë kompajlerët shpesh përdorin reprezentim të tillëintern të pemëve për programet, sepse pemët janë të dobishme për shumëqëllime. Për shembull, mund të imagjinojmë operandë që i korrespondojnë

variablave të cilat marrin vlerat dhe mund të gjenerojmë kod të makinës për të

Page 330: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 330/699

Avni Rexhepi

330

vlerësuar shprehjet e reprezentuara përmes pemës me një përshkim postorder.Ose, mund të përdorim pemën për të shtypur shprehjen në infix, përmes përshkimit inorder ose në postfix me një përshkim postorder.

Do të shqyrtojmë disa shembuj për të prezentuar konceptin me të cilin mund tëkrijohet dhe procesohet strukturat e pemës së lidhura në mënyrë eksplicite meanë të programeve rekurzive. Për të bërë këtë në mënyrë efektive, duhet tëshqyrtojmë performansat e algoritmeve të ndryshme, reprezentimeve alternative,alternativave jo-rekurzive dhe shumë detaje të tjera, të cilat do të bëhen tekalgoritmet për kërkim dhe për hash tabelat.

Programi 6.7. Konstruktimi i pemës së analizës//parse=analizochar *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 përdorur strategjinë e njëjtë që u përdor për të vlerësuar shprehjet prefix(programi 5.4), ky program krijon pemën e analizës prej shprehjes prefix. Përthjeshtësi, supozojmë se operandët janë karaktere të vetme. Secila thirrje efunksionit rekurziv krijon një nyje të re me karakterin e ardhshëm prej hyrjes, sishenjë (angl. token). Nëse tokeni është operand, kthejmë nyjen e re; nëse ështëoperator, caktojmë pointerët e majtë dhe të djathtë për në pemën e ndërtuar(rekurzivisht) për të dy argumentet.

Përshkimi i grafit

Si shembull të programit rekurziv, shqyrtojmë një prej alroritmeve më tërëndësishme rekurzive: përshkimin rekurziv të grafit ose kërkimin thellësia-së- pari (angl. depth-first search). Ky funksion për vizitimin sistematik të të gjithanyjeve në graf, është përgjithsim direkt i funksioneve për përshkimin e pemëvedhe shërben si bazë për shumë algoritme të tjera themelore për procesimin egrafeve. Është një algoritëm rekurziv i thjeshtë.

Duke filluar nga cilado nyje v:

Page 331: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 331/699

Algoritmet dhe strukturat e të dhënave

331

  Vizito v.  (Rekurzivisht) vizito secilën nyje (të pavizituar) të lidhur me v.

 Nëse grafi është i lidhur, në fund arrihen të gjitha nyjet. Programi 6.8 është njëimplementim i kësaj procedure rekurzive.

Për shembull, supozojmë se përdorim reprezentimin e grafit përmes listës sëfqinjësisë. Figura 6.18  paraqet thirrjet rekurzive të bëra gjatë kërkimit ‘depth-first’ të këtij grafi dhe sekuenca në të majtë në figurën 6.19 paraqet mënyrën nëtë cilën përcillen degët e grafit. Përcjellim secilën degë në graf, me një prej dyrezultateve të mundshme: nëse dega na dërgon në një nyje të cilën veq e kemivizituar, e injorojmë atë; nëse na dërgon tek një nyje të cilën akoma nuk e kemivizituar, e përcjelli atë atje përmes thirrjes rekurzive. Bashkësia e të gjithadegëve të cilat i përcjellim në këtë mënyrë formon pemën e shtrirjes për grafin(angl. graph spanning tree).

Figure 6.18. Thirrjet e funksionit tek ‘Depth-first –search’  

Kjo sekuencë e thirrjeve të funksioneve përbën kërkimin thellësia-së-pari përgrafin në figurë. Pema e cila ilustron strukturën e thirrjeve rekurzive, quhet pema e kërkimit thellësia-së-pari.

Page 332: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 332/699

Avni Rexhepi

332

Figura 6.19. Kërkimi ‘Depth- first’ dhe kërkimi ‘breadth- first’

Page 333: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 333/699

Algoritmet dhe strukturat e të dhënave

333

Kërkimi thellësia-së-pari (majtas) lëvizë prej nyjes në nyje, duke u kthyer prapanë nyjen e përparshme për të tentuar mundësinë e ardhshme sa herë që ka provuar secilën mundësi në nyjen aktuale. Kërkimi “Gjerësia-së- pari” (angl.Breadth-first search), shterron të gjitha mundësitë në një nyje, para se të lëvizënë tjetrën.

Dallimi ndërmjet kërkimit thellësia-së-pari dhe përshkimit të përgjithshëm të pemës (programi 6.1) është se duhet të kujdesemi në mënyrë ekslicite që të mosvizitojmë nyjet të cilat veç janë vizituar. Në pemë, asnjëherë nuk hasim në nyjetë tilla. Vërtetë, nëse grafi është pemë, kërkimi rekurziv thellësia-së-pari dukefilluar nga rrënja është ekuivalent me përshkimin ‘preorder’. 

Vetia 6.7. Kërkimi “Depth-first” kërkon kohë proporcionale me V + E në

grafin me V nyje dhe E degë, duke përdor reprezentimin me listë të

fqinjësisë.

 Në reprezentimin me listë të fqinjësisë, ka një nyje të listës që i korrespondonsecilës degë në graf dhe një pointer të kokës së listës, që i përgjigjet secilës nyjenë graf. Kërkimi thellësia së pari, i prektë të gjitha, më së shumti nga një herë.

 Fig. 6.20 - Lista e fqinjësisë për grafin nga figura 6.19.

Kërkimi “thellësia së pari” 

Programi 6.8. Kërkimi thellësia-së-pari (Depth-first search).//visit=vizito; visited=vizituarvoid 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);

}

Page 334: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 334/699

Avni Rexhepi

334

Për të vizituar të gjitha nyjet e lidhura me nyjen k në graf, i shënojmë ato si tëvizituara dhe pastaj (rekurzivisht) i vizitojmë të gjitha nyjet e pavizituara nëlistën e fqinjësisë së nyjes k.

Pasi që poashtu për të ndërtuar listën e fqinjësisë nga një sekuencë hyrëse enyjeve merr kohë proporcionale me V+E, kërkimi thellësia-së-pari na jep njëzgjidhje të kohës lineare për problemin e konektivitetit (lidhjes së një pike me pikat tjera). Për grafet shumë të mëdhaja, sidoqoftë, më të preferuara do të ishinzgjidhjet me union, sepse reprezentimi i tërë grafit kërkon hapësirë proporcionale me E (numrin e degëve), gjersa zgjidhja me union, merr hapësirë proporcionale vetëm me V (numrin e nyjeve).

Ashtu si vepruam me përshkimin e pemës, mund të definojmë funksionin për përshkimin e grafit i cili përdorë stekun eksplicit, siç është ilustruar në figurën

6.21.Mund të mendojmë për një stek abstrakt i cili mbanë ‘hyrje’ të dyfishta: nyjendhe pointerin në listën e fqinjësisë së nyjes. Me stekun e inicializuar në nyjenfillestare dhe pointerin e inicializuar për në nyjen e parë në listën e fqinjësisë sëasaj nyjeje, algoritmi thellësia-së-pari është ekuivalent me hyrjen në unazë, kuvizitojmë nyjen në krye (top) të stekut (nëse nuk ka qenë i vizituar); ruajmënyjen e referencuar nga pointeri i listës aktuale të fqinjësisë; azhurojmëreferencën e listës së fqinjësisë në nyjen e ardhshme (duke tërhequr (pop) atëvlerë nëse është në fund të listës së fqinjësisë); dhe duke shtyrë (push) në stek

vlerën për nyjen e ruajtur, duke ju referuar nyjes së parë në listën e saj tëfqinjësisë.

Page 335: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 335/699

Algoritmet dhe strukturat e të dhënave

335

Figura 6.21. Dinamika e stekut për kërkimin thellësia së pari.

Mund të mendojmë për stekun që përkrahë kërkimin thellësia-së-pari sikur përmbanë nyjen dhe referencën (pointerin) në listën e saj të fqinjësisë (të treguarme nyjen e rrethuar, - si në anën e majtë). Prandaj, fillojmë me nyjen 0 në stek,me referimin (me pointer) në nyjën e parë në listën e saj, nyja 7. Secili rresht

tregon rezultatin e tërheqjes (pop) nga steku, shtyrjes (push) së pointerit për nënyjën e ardhshme në listë për nyjet që janë vizituar dhe shtyrjes (push) së njëvlere në stek për nyjet që nuk janë vizituar. Alternativisht, mund të mendojmë për procesin sikur thjeshtë shtyhen (push) në stek të gjitha nyjet fqinje të cilësdonyje të pavizituar (ana e djathtë).

Përndryshe, ashtu si vepruam për përshkimin e pemës, mund të konsiderojmë sesteku përmbanë vetëm lidhjet për në nyje. Me stekun e inicializuar në nyjenfillestare, hyjmë në unazë ku vizitojmë nyjen në krye (top) të stekut (nëse nukështë vizituar), pastaj shtyejmë (push) në stek të gjitha nyjet fqinje me të. Figura

Page 336: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 336/699

Avni Rexhepi

336

6.21 ilustron se të dy këto metoda janë ekuivalente për kërkimin thellësia-së-pari për grafin e shembullit dhe kjo ekuivalencë vlenë në përgjithësi.

Algoritmi “vizito kreun (top) dhe shtyej në stek të gjithë fqinjët” është një

formulim i thjeshtë i kërkimit thellësia-së-pari, mirëpo nga figura 6.21 është eqartë se vuan nga disavantazhi i mundësisë së lënies në stek të kopjeve tëshumëfishta të secilës nyje. Ky bën kështu edhe nëse testojmë nëse ështëvizituar secila nyje që është duke u përgatitur për të shkuar në stek dhe përmbahemi nga vendosja e nyjes në stek, nëse ajo ka qenë e vizituar. Për tëevituar këtë problem, mund të përdorim implementimin e stekut që pamundësonduplikatet, duke përdorur rregullën “harro-elementet-e-vjetra”, sepse kopja më eafërt me kreun e stekut është gjithmonë e para që vizitohet, kështu që të tjeratthjeshtë tërhiqen (pop).

Dinamikat e stekut për “depth-first search” që janë ilustruar në figurën 6.21varen nga ajo që nyjet në secilën listë të fqinjësisë përfundojnë në stek nërenditjen e njëjtë në të cilën paraqiten në listë. Për të fituar këtë renditje përlistën e dhënë të fqinjësisë në rastet kur shtyhet (push) nga një listë në kohë, dotë duhej të shtyejmë në stek së pari nyjen e fundit, pastaj atë të parafundit, ekështu me radhë. Për më tepër, për të limituar madhësinë e stekut në numrin enyjeve, gjersa në të njëjtën kohë vizitohen nyjet në renditjen e njëjtë si në“depth-first search”, duhet të përdorim disciplinën e stekut me rregullën “harro-elementin-e-vjetër”. Nëse vizitimi i nyjeve në renditje të njëjtë sikur në rastin ekërkimit thellësia-së-pari (depth-first search) nuk është me rëndësi për ne, mundtë evitojmë të dy këto komplikime dhe të formulojmë direkt metodën jorekurzive të përshkimit të grafit të bazuar në stek. Me stekun e inicializuar nënyjen fillestare, hyjmë në unazë ku vizitojmë nyjen në krye të stekut (top), pastajvazhdojmë nëpër listën e saj të fqinjësisë, duke shtyer (push) në stek secilënnyje (nëse nyja nuk ka qenë e vizituar), duke përdorur implementimin e stekutqë pamundëson duplikatet me rregullën “injoro-elementin-e-ri”. Ky algoritëmviziton të gjitha nyjet në graf, në mënyrën e ngjashme me “depth-first search”, por nuk është rekurziv.

Kërkimi “gjerësia së pari” 

Për 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 nyjene ardhshme nga queue dhe nëse ajo nuk është vizituar, e vizitojmë dhe shtyjmëtë gjitha nyjet e pavizituar në listën e saj të fqinjësisë, duke vazhduar gjersaqueue të jetë i zbrazët.

Page 337: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 337/699

Algoritmet dhe strukturat e të dhënave

337

Programi 6.9. Kërkimi gjerësia-së-pari (Breadth-first search)//traverse=përshko,kalo nëpër; visited=vizituarvoid 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 rëndësishëm sepse do të mund të përdornim çfarëdo ADT të queue të përgjithsuar (gjeneralizuar) dhe akoma tëvizitojmë secilën nyje në graf (dhe të gjenerojmë pemën e shtrirjes). Përshembull, nëse përdorim queue në vend të stekut, atëherë kemi kërkiminthellësia-së-pari (angl. Breadth-first search), i cili është i ngjashëm me përshkimin “level-order” (renditja në nivel) në pemë. Programi 6.9 është njëimplementim i kësaj metode (funksioni); një shembull i algoritmit në veprimështë ilustruar në figurën 6.22.

Figura 6.22. Dinamikat e queue-s “Breadth-first –search”  

Fillojmë me nyjen 0 në queue, pastaj e marrim 0, e vizitojmë dhe vendosimnyjet në listën e saj të fqinjësisë 7 5 2 1 6, në atë renditje në queue. Pastaj

marrim 7, e vizitojmë dhe vendosim nyjet në listën e saj të fqinjësisë, e kështu

Page 338: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 338/699

Avni Rexhepi

338

me radhë. Duke pamundësuar duplikatet me një rregull “injoro-elementin-e-ri”(djathtas), fitojmë rezultatin e njëjtë, pa vlera të tepërta në queue.

Të dy mënyrat e përshkimit të grafit, gjerësia-së-pari dhe thellësia-së-pari i

vizitojnë të gjitha nyjet në graf, por mënyra se si e bëjnë këtë është dramatikishte ndryshme. Kërkimi gjerësia së pari përmblidhet në rastin e një morie tëkërkuesve që shpërndahen për të mbuluar territorin, gjersa kërkimi thellësia-së- pari i korrespondon një kërkuesi të vetëm, i cili gjurmon territorin e panjohur same thellë që të jetë e mundur, duke u kthyer prapa vetëm kur arrinë në rrugë padalje. Këto paradigma themelorë të zgjidhjes së problemeve janë me rëndësi nëshumë sfera të shkencave kompjuterike, përtej kërkimit të grafeve.

Figura 6.23. Pemët e përshkimit të grafeve

 Në figurën 6.23 janë paraqitur rrugëtimet e pjesërishme gjatë kërkimit nëpër njëgraf shumë të madh e kompleks (majtas), të kërkimit thellësia-së-pari (depth-first search; në qendër) dhe atij gjerësia-së-pari (breadth-first search; djathtas).Thellësia së pari gjarpëron nga nyja në nyje, kështu që shumica e nyjeve janë tëlidhura vetëm me dy të tjera (të përparshmen dhe të ardhshmen). Në anën tjetër,kërkimi gjerësia-së- pari “përfshinë, pushton” nëpër graf, duke vizituar nyjet elidhura me nyjen e dhënë, para se të shkojë tutje, kështu që disa nyje janë tëlidhura me shumë të tjera.

Page 339: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 339/699

Algoritmet dhe strukturat e të dhënave

339

Pema binare

Pema, në të cilën secila degë në vazhdim ndahet në dy degë të tjera apo thënëndryshe, çdo nyje (prind) ka dy nyje pasardhëse (fëmijë), e paraqet pemën

 binare (angl. Binary tree).Binare, nënkupton po atë që vlenë për sistemin binar të numrave, ku kemi vetëmdy shifra: zero dhe një. Edhe në pemën binare, secili ‘k ërcell’ ka më së shumtidy degë, por ka raste kur ndonjëri ka vetëm një degë ose thjeshtë mbaron, dukerezultuar me përfundim të degëzimit në atë degë, si në figurën 6.24.

 Figura 6.24 - Pema binare

 Në programim, pema binare përdoret si model për krijimin e strukturës së tëdhënave për të koduar logjikën e marrjes së vendimeve komplekse. Le të themi

se një degë është një bashkësi e urdhërave të programit. Në fund, programivlerëson një shprehje binare. Dihet se shprehja binare jep vlerën bool-eane ‘true’ose ‘false’. Bazuar në vlerësim, programi vazhdon tutje nëpër njërën degë.Secila degë ka bashkësinë ëe vet të urdhërave.

Koncepti i pemës binare përdoreë logjikën Bool-eane, të implementuar nëurdhërin ‘if’. Mir ëpo, pema binare është shumë më shumë sesa urdhëri ‘if’.

Elementet e pemës binare

 Në programim janë përcaktuar terma të ndryshme ndaj atyre të cilët përdoren

zakonisht kur flitet për pemën.

Page 340: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 340/699

Avni Rexhepi

340

Pikat ku ka ndërprerje ose mbaresa dhe pastaj degëzime quhet ‘Nyje’ (angl. Node). Në pemën binare ka tri lloje të mbaresave (Fig. 6.25): nyja fillestare,nyja fundore (ose nyja përfundimtare) dhe nyja degë. Nyja fillestare quhet“Rr ënjë” (angl. root node  –  nyje rrënjë) dhe ndodhet në nivelin më të lartë të pemës. Nëse përdoret terminologjia e trungu familjar, quhet ‘Prind’. Degët ngarrënja dërgojnë në nyjet e degëve. Nyja degë është ‘bigëzim’ (degëzim nëdyshe) në rrugën që lidhë rrënjën me dy degë të tjera. Secila degë përfundon mënyje fundore ose të quajtura edhe nyje fëmijë, nyje gjethe, etj. Degët e dala nganyja, quhen dega e majtë dhe dega e djathtë.

 Figura 6.25: Pema binare përbëhet prej disa nyjeve, secila e ndërlidhur me

nyjet tejra të pemës.

Pra, mund të shihet, se pema binare definon lidhje të fortë prind-fëmijë,ndërmjet nyjeve. Relacioni prind-fëmijë është relativ, varësisht prej nivelit kundodhemi. Të gjitha nyjet, përveq nyjes rrënjë, kanë nyje prind. Mirëpo, disanyje nuk kanë fëmijë, gjersa disa kanë një ose dy fëmijë. Relacioni prind-fëmijënë programim përcaktohet duke zgjedhur një nyje, e cila quhet nyje aktuale(angl. current node). Nyja e cila e ka “lindur” nyjen aktuale, quhet nyje prind enyjes aktuale. Nyja ose nyjet e dala nga nyja aktuale quhen nyje fëmijë.

Page 341: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 341/699

Algoritmet dhe strukturat e të dhënave

341

 Nyja fëmijë gjithashtu referohet edhe si nyje e majtë ose nyje e djathtë, varësisht prej anës në raport me nyjen aktuale. Nëse nyja aktuale nuk ka ndonjë nyjefëmijë, atëherë nyja aktuale referohet si nyje gjethe. Nyja gjethe është elokalizuar në fund të pemës, njësj si gjethet e pemës, që ndodhen në pikënfundore të pemës.

Thellësia dhe madhësia

Pema binare përshkruhet duke përdorur dy parametra matës: thellësia (angl.depth) dhe madhësia (angl. size), si në figurën 6.26. Thellësia e pemës paraqetnumrin e niveleve në pemë. Niveli i ri krijohet secilën herë që nyja aktualedegëzohet në nyje fëmijë. Për shembull, një nivel krijohet, kur rrënja degëzohetnë nyjet fëmijë.

 Figura 6.26 - Numri i niveleve të pemës definon thellësinë, ndërsa numri i

nyjeve definon madhësinë e pëmës.

Madhësia e pemës është numri i nyjeve në pemë. Për shembull, në figurën 6.26,niveli i parë ka vetëm një nyje, që është nyja rrënjë. Niveli i dytë ka më sëshumti dy nyje, të cilat janë nyjet fëmijë të rrënjës. Niveli i tretë mund të ketëderi në katër nyje. Madhësia e pemës binare llogaritet duke përdoru formulën:

madhesia  2 thellesia (size  2 depth )

Page 342: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 342/699

Avni Rexhepi

342

Le të thëmi se pema binare ka pesë nivele, që do të thotë se thellësia është 5.Atëherë, madhësia e pemës llogaritet si:

madhsia  2 5   

Pra, madhësia është vlera e përafërt, sepse pema binare mund të mos jetë e balansuar. Pemë e balansuar është pema binare në të cilën secila nyje ka dyfëmijë. Pema binare e pabalansuar është ajo pemë ku një ose më shumë nyjekanë më pakë se dy fëmijë. Formula jep idenë e vrazhdë se sa është e balansuar pema binare. Zakonisht pema binare përdoret për bashkësi shumë të mëdha të tëdhënave.

Përse përdoret pema binare?Pema binare përdoret në programim për të gjetur shpejtë të dhënat e ruajtura nësecilën nyje të pemës binare. Le të themi se duhet të gjeni ID-në e studentit nëlistën me një milion studentë. Sa është numri maksimal i krahasimeve tënevojshme për të gjetur ID-në e studentit?

 Nëse kërkimi bëhet në mënyrë sekuenciale në listën prej një milion studentëve,do të mund të bëhen maksimalisht një milion krahasime. Nëse kërkimi bëhet nëmënyrë të rastit, duke zgjedhur një ID nga lista dhe pastaj duke e kthyer përsëri

në listë, nëse nuk është ajo që kërkohet.Mirëpo, nëse të dhënat ruhen në strukturën e pemës binare, atëherë do tënevojiten vetëm 20 krahasime për të gjetur ID-në. Kjo për arsye të mënyrës sëorganizimit të të dhënave në pemën binare. E dhëna (vlera) e ruajtur në nyjen emajtë është më e vogël sesa vlerat në të gjitha nyjet e anës së djathtë, për secilënnyje aktuale.

Kjo mund të tingëlloj si konfuze, mirëpo përmës ilustrimit, koncepti do të jetë iqartë. Supozojmë se kemi një listë prej pesë ID-ve të studentëve: : 101, 102,103, 104 dhe 105. Këto ID të studentëve janë ruajtur në një pemë binare, kështu

që ID-ja në qendër është rrënja, ID-të më të vogla se nyja aktuale (rrënja)vendosen në nyjen e fëmijës së majtë dhe ato më të mëdha në anën e djathtë, sinë figurën 6.27.

Page 343: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 343/699

Algoritmet dhe strukturat e të dhënave

343

 Figura 6.27 - Nyja e majtë është gjithmonë më e vogël sesa nyja prind dhe nyjae djathtë është gjithmonë më e madhe sesa nyja prind.

Modeli i njëjtë aplikohet në secilën nyje fëmijë. Prandaj, ID 101 është fëmijë imajtë i nyjës që përmbanë ID 102. Ngashëm, ID 105 është nyje e djathtë e ID-së104 sepse është më madhe (për nga vlera).

Le të themi se kërkohet të lokalizohet ID 101 në pemën binare. Së pari, vlera ekërkuar krahasohet më nyjen rrënjë. Nuk ka përputhje (angl. match), prandaj ajonuk është vlera e kërkuar. Pasi ID 101 është më e vogël se ID 103 (e rrënjës),krahasimi i ardhshëm përdorë nyjen e majtë. Kjo do të eliminojë nevojën e

krahasimit me të gjitha nyjet në anën e djathtë të rrënjës (që përmbanë ID 103).Pra, mund të injorohen gjysma e ID-ve sepse e dijmë se ID 101 nuk ndodhet nënyjën e djathtë ose në fëmijët e saj.

Pas krahasimit të ID 101 me ID 102, vërehen dy gjëra. Së pari, ato nuk përputhen. Së dyti, ID 102 është më e madhe sesa ID 101. Kjo do të thotë që nëvazhdim ID 101 karahasohet me nyjën fëmijë të majtë. Injorohet nyja e djathtëdhe të gjitha nyjet pasuese të saj, sepse ato do të jenë më të mëdha se ID 101. Nuk këtë rast, nuk ka fëmijë të djathtë të nyjës me ID 102. Krahasimi iardhshëm do të rezultojë me përshtatje, që do të thotë se u gjet vlera e kërkuar.Kështu, në një pemë të madhe binare, secili krahasim eliminon nga kërkimigjysmën tjetër të mbetur të nyjeve. Nëse do të kishim 1 milion nyje në pemë,atëherë do të kishim pjestim me dy (ndarje në dy, përgjysme), për afër 20 herë, për të zvogëluar numrin deri në një nyje (pasi që 220 është përafërsisht njëmilion). Në këtë mënyrë, mund të gjeni nyjen e kërkuar duke bërë afër 20krahasime.

Programerët i shohin të gjitha nyjet si nyje rrënjë dhe të gjitha nyjet në vazhdimsi “nënpemë” e tyre. Aplikohet kjo mënyrë e qasjes, sepse funksionet të cilat punojnë me pemë janë rekurzive. Funksioni punon me nyjën fëmijë dhe kryen

Page 344: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 344/699

Avni Rexhepi

344

funksionalitetin e njëjtë sikur ajo nyje fëmijë të ishte nyje rrënjë e një peme tëtërë. Kjo do të thotë që, vlera e nyjes fëmijë krahasohet me vlerën e nyjes së sajtë majtë dhe të djathtë, për të përcaktuar se nëpër cilën degë të pemës tëvazhdojë tutje.

Vlera çelës

Secila nyje e pemës përmbanë një çelës (angl. key) që i shoqërohet vlerës në njërelacion të ngjashëm me atë ndërmjet çelësit primar në bazën e të dhënave dherreshtit në tabelë të bazës së të dhënave. Çelësi është vlera që krahasohet nëkriteret e kërkimit. Nëse indeksi dhe kriteri i kërkimit përshtaten (përputhen),atëherë aplikacioni nxjerrë të dhënat e rreshtit të cilat i përgjigjen atij çelësi. Edhëna i referohet vlerës së nyjës, si në figurën 6.28.

 Figura 6.28 - Secila nyje ka një indeks (çelës) dhe vlerën: indeksi identifikon

nyjen në mënyrë unike dhe “nxjerr ë ” vler ën e nyjes.

Si çelës mund të përdoret çfarëdo tipi i të dhënave. Në shembujt në këtë pjesë dotë përdoret stringu, edhe pse mund të zgjedhet cilido tip i të dhënave. Për dallimnga çelësi primar i bazës së të dhënave, çelësi i pemës nuk është e nevojshme të jetë në renditje natyrale. D.m.th., çelësi nuk duhet të jetë në renditje alfabetikeose numerike. Në një implementim tipik të pemës, definohet një krahasues përt’i treguar pemës se si t’i renditë nyjet. Në rastin tonë, do të përdorim njësekuencë natyrale renditëse për stringje, ashtu që të mund të mbajmë fokusin në punën me pemë.

Page 345: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 345/699

Algoritmet dhe strukturat e të dhënave

345

Krijimi i pemës binare

Për të krijuar pemën binare së pari definohet struktura e saj. Struktura do tëemërtohet “Metadata” sepse përshkruan të dhënat që përdoren në pemën binare,

e termi metadata i refereohet të dhënave të cilat i përshkruajnë të dhënat, si ajose si ID-ja e studentit e gjenë emrin e studentit (ose të dhënat tjera të studentit). Në mënyrë tipike Metadat i referohet çifteve emër/vlerë ose në rastin tonë,çifteve çelës/vlerë. 

Secila instancë e strukturës së të dhënave është nyje në pemën binar dhe përmbanë katër elemente të dhënash. Dy elementet e para janë çelësi dhe vlera. Në këtë shembull, të dyja janë vargje karakteresh. Madhësia e këtyre vargjeve përcaktohet nga direktiva preprocesuese #define, në fillim të shembullit.Madhësia e vargut caktohet duke përdorur direktivën preprocesorike sepse

 pastaj me lehtësi mund të ndryshohet vlera e saj në një vend të vetëm, pa pasurnevojë të lokalizohet çdo pozitë brenda kodit ku përdoret vlera e madhësisë sëvargut.

Dy elementet tjera të strukturës metadata janë pointerët e quajtur ‘left’ dhe‘right’ (majtë dhe djathtë). Secili prej tyre pointon në metadata strukturë. Mefjalë tjera, ata pointojnë në nyjen e ardhshme në të majtë dhe nyjen e ardhshmenë të djathtë të nyjës aktuale. Kjo i mundëson aplikacionit që të bëjë dy gjëra. Së pari, aplikacioni mund të lëvizë në nivelin tjetër të pemës. Gjithashtu mundet t’içaset edhe çelësit edhe vlerës së nyjes në nivelin e ardhshëm.

 Në shembull, struktura e përdorë vetë definicionin e saj, pra struktura e përdorëvetveten për të inicializuar elementet e strukturës. Çelësi dhe vlera e tij i përcillën strukturës Metadata kur të deklarohet një strukturë metadata, d.m.th.,kur të insertohet nyja e re në pemën binare. Të dy këto vlera përcillen si pointerë“char” sepse janë vargje.

Aplikacioni kopjon çelësin dhe vlerën në elementet e të dhënave të instancës sëstrukturës Metadata duke përdorur funksionin strcpy(), i cili kopjon parametrin edytë në parametrin e parë. Vëreni se në këtë shembull përdoret operatori ‘this’, i

cili i tregon kompajlerit se dëshironi t’i referoheni elementit të të dhënës së kësajinstance të strukturës në vend se parametrit që i është përcjellur.

Dy elementet e fundit të të dhënave për t’u inicializuar janë pointerët ‘left’ (imajtë) dhe ‘right’ (i djathtë). Të dy këta caktohen në NULL sespe nyja e re nukka fëmijë kur të krijohet. Më vonë do të definohen funksionet që shtojnë nyjenfëmijë dhe zëvendësojnë vlerën NULL me pointerin në një nyje aktuale.

#include <string>#define SIZE_KEY 32#define SIZE_VALUE 256

typedef struct Metadata

Page 346: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 346/699

Avni Rexhepi

346

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

Përveq definimit të strukturës do të definohet edhe klasa BinarySearchTree.Klasa BinarySearchTree definon të dhënat dhe funksionet të cilat krijojnë dhemanipulojnë nyjen. Si është ilustruar në kodin vijues, definicioni i klasës është iorganizuar në dy pjesë, atë private dhe atë  publike. Aplikacioni mund t’i qasetvetëm të dhënave dhe funksioneve publike; anëtarët e definuar në pjesën privatemund të qasen vetëm përmes funksioneve të klasës. Në pjesën private të klasësBinarySearchTree janë deklaruar dy anëtarë  privat: ‘size’ dhe ‘root’. Anëtari‘size’ është një integer që ruan numrin e nyjeve në pemë, kurse ‘root’ është pointer në një instancë të strukturës metadata. Me fjalë të tjera, root është nyja e parë në pemë.

Pjesa private përmbanë poashtu edhe nëntë funksione anëtare:

addNode() //shtoNyje()getNode() //merrNyje()removeAllNodes() //largoTeGjithaNyjet()processNodesInOrder() //procesoNyjetMeRadhe()getTreeDepth() //merrThellesineePemes()containsNode() //permbaneNyjen()removeNode() //largoNyje()removeRootNode() //largoNyjenRrenje()

moveLeftMostNode() //levizNyjenMeTeMajte()

Këto funksione përdoren nga funksionet e definuara në pjesën publike të klasës, për të manipuluar nyjet e pemës. Më vonë do të paraqiten të gjitha këtofunksione.

Pjesa publike përmbanë edhe konstruktorin dhe destruktorin dhe disa funksionetë cilat i mundësojnë aplikacionit që të krijojë dhe largojë nyjet dhe tëmanipulojë nyjet e pemës. Funksionet do të paraqiten të kompletuara më vonë.

//BinarySearchTree=Pema e kerkimit Binar

BinarySearchTree();

Page 347: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 347/699

Algoritmet dhe strukturat e të dhënave

347

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

}; 

Page 348: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 348/699

Avni Rexhepi

348

Konstruktori dhe Destruktori

Konstruktori i klasës BinarySearchTree inicializon anëtarin ‘root’ me vler ën NULL. ‘root’ është pointer në një instancë të strukturës metadata dhe pointon në

nyjen rrënjë (root) për pemën binare të kërkimit, kur të jetë shtuar nyja në pemë.Konstruktori poashtu inicializon anëtarin ‘size’ në zero. Kjo do të thotë se nukka nyje në pemë. Anëtari ‘size’ inkrementohet secilën herë që shtohet një nyjenë pemë dhe dekrementohet secilën herë që largohet një nyje nga pema.

Destruktori largon të gjitha nyjet nga pema dhe liron hapësirën e zënë tëmemories. Destruktori nuk i largon nyjet drejtpërdrejtë, por e thërret 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ë klasës BinarySearchTree(siç do të shihet në pjesën e kodit në vijim. Funksioni add() kërkon dy parametra, pointerin për në çelësin e nyjes së re dhe një pointer tjetër për nëvlerën e nyjes. Këta pointerë do të emërtohen ‘key’ (çelësi) dhe ‘value’ (vlera). 

Para se të shtohet nyja në pemë, funksioni add() i verifikon pointerët ‘key’ dhe‘value’ me dy teste. Së pari, sigurohet që ata nuk kanë vlerë NULL. Pastaj,teston për të qenë i sigurtë që asnjëri prej tyre nuk është më imadh sesamadhësia e vargut të alokuar për ‘key’. K ëtë e bën duke krahasuar gjatësinë e

‘key’ dhe gjatësinë e vlerës (value) për në vlerën gjegjëse të definuar nëdirektivën preprocesorike #define. Nëse ndonjëri prej këtyre testeve dështon,atëherë funksioni add() kthen vlerën Bool-eane “False” në urdhërin nëaplikacion i cili e ka thirrur funksionin add().

 Nëse ‘key’ dhe ‘value’ janë vlera valide, atëherë funksioni add() vazhdon mekrijimin e nyjes së re. Së pari, ai deklaron një instancë të strukturës metadatadhe i përcjellë ‘key’ dhe ‘value’ në instancë. Më parë u tha se ‘key’ dhe ‘value’ bëhen vlerat iniciale për elementet gjegjëse të të dhënave (anëtarët) të strukturësmetadata.

Page 349: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 349/699

Algoritmet dhe strukturat e të dhënave

349

Hapi i fundit në procesin e shtimit të nyjes së re në pemë është thirrja efunksionit addNode(). Funksioni addNode() është i definuar në pjesën private tëklasës BinarySearchTree dhe është përgjegjës për 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 kërkon dy argumente.Argumenti i parë është pointer në pointerin i cili pointon në nyjen aktuale.Arguementi tjetër është pointeri në nyjen e re. procesi i shtimit të nyjes së re në pemë fillon me kontrollimin nga funksioni addNode() nëse nyja e re e përcjellurështë  NULL. Kur vlera e ‘current_node’ (nyja aktuale) është NULL, atëherëështë arritur në një gjethe në pemë. Këtu është vendi ku ndodhë shtimi i nyjes sëre. Të gjitha nyjet shtohen si nyje gjethe. Nëse kjo është nyja e parë që shtohetnë pemë, atëherë gjethja është edhe rrënjë. Nyja e re caktohet në pointerin e

‘current_node” dhe anëtari ‘size’ inkrementohet. Kjo e shton nyjen e re në pemë. Funksioni addNode() kthen vlerën Bool-eane “True”, duke treguar seoperacioni ishte i suksesshëm. Duhet të përcillet pointeri për në pointer siargument i parë sepse do të ndryshoni vlerën në atë nyje. Ajo çka përcillni në tëvërtetë është adresa e pointerit në prind. Pointeri në prind ndryshohet që të pointojë në këtë nyje të re që është duke u shtuar në pemë.

 Nëse nyja aktuale (current node) nuk është NULL, atëherë hapi i ardhshëm ështëqë të gjindet vendi se ku do të shtohet nyja e re në pemë. Ky porces është ikomplikuar, pasi që nyja e re duhet të lokalizohet në pozitën ku do të jetë më e

madhe ose më e vogël sesa prindi i saj.Funksioni addNode() krahason çelësin (key) e nyjes aktuale (current node) meçelësin e nyjes së re (neë node) duke përdorur funksionin strcmp(). Nëse vlera ekthyer nga funksioni strcmp() është më e vogël se zero, atëherë çelësi i nyjes sëre është më i vogël sesa ai i nyjes aktuale. Pastaj funksioni addNode() thirret përsëri rekurzivisht, por këtë herë pointeri në nyjen e majtë të nyjes aktuale përcillet si argument i parë në funksionin addNode(). Si ju kujtohet, argumenti i parë konsiderohet nga funksioni addNode() si nyja aktuale. Në këtë rast, nyja emajtë e nyjes aktuale konsiderohet si nyje aktuale. Argumenti i dytë është nyja e

re. Vëreni se funksioni addNode() thirret rekurzivisht deri sa të gjejë vendin për

Page 350: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 350/699

Avni Rexhepi

350

nyjen e re (neë node). Thirrja e parë përcjellë argumentin e parë si rrënjë e pemës. Secila thirrje pasuese përcjellë rrënjën e nënpemës (nëndegës). Rikujtoniqë secila nyje e pemës mund të konsiderohet si rrënjë për të gjitha nyjet nën të.Rregullat e njëjta aplikohen në secilën nyje –  të gjitha nyjet në anën e majtë janëmë të vogla dhe të gjitha nyjet në të djathtë janë më të mëdhaja.

 Nëse çelësi i nyjes së re është i barabartë me nyjen ekzistuese, atëherë nyja e refshihet (largohet) dhe funksioni addNode() kthen vlerën Bool-eane “false”. Kjo për arsye se të gjithë çelësat duhet të jenë unik: në pemë nuk lejohen çelësatduplikat.

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

}elseif(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ë-hapësh që fillon kuraplikacioni e thërret funksionin remove(), si në pjeën vijuese të kodit. Funksioniremove() kërkon çelësin (key) e nyjes që duhet të largohet. Pastaj ai e thërret

Page 351: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 351/699

Algoritmet dhe strukturat e të dhënave

351

funksionin removeNode(), i cili është anëtar privat i klasës BinarySearchTreedhe prandaj nuk mund të thirret drejtpërdrejt nga aplikacioni.

Funksioni removeNode() i kërkon dy parametra. I pari është referenca në nyjen

aktuale që është duke u vlerësuar, që është vendi ku fillon kërkimi. Kërkimifillohet duke përcjellur rrënjën e pemës, pastaj thirrjet pasuese do të përcjellinrrënjët e nënpemëve. Gjithnjë për pemën duhet të mendoni si bashkësi enënpemëve  –  secila nyje është rrënjë për të gjitha nyjet nën të. Parametri i dytëështë çelësi (key) i pranuar nga funksioni remove().

bool remove(char* key){

return removeNode(&root, key);}

Funksioni removeNode(), i paraqitur në pjesën e kodit në vijim, përdorë vlerën e përcjellur nga ana e funksionit remove() për të lokalizuar nyjen e cila do tëfshihet. Para se të fillojë kërkimi, funksioni removeNode() kontrollon(përcakton) nëse nyja rrënjë e përcjellur tek ai nga ana e funksionit remove()është NULL. Kjo mund të jetë rrënja e pemës nëse kjo është thirrja e parë efunksionit ose mund të jetë rrënjë e nënpemës. Nëse është NULL, atëherëkthehet vlera Bool-eane ‘false’ sepse nyja për t’u larguar nuk është gjetur.

 Nëse nyja rrënja nuk është NULL, kërkimi vazhdon. Objektiv i funksionitremoveNode() është që të gjindet çelësi i nyjes në pemë i cili përputhet

(përshtatet) me çelësin e përcjellur nga ana e funksionit remove(). Kur të jetëgjetur njëherë, referenca për në nyjen që përmbanë çelësin i përcillet funksionitremoveRootNode(), i cili në fakt largon nyjen nga pema. FunksioniremoveRootNode(0 mund të largojë rrënjën e pemës ose rrënjën e nënpemës.

Kërkimi fillon duke krahasuar çelësin e nyjes rrënjë të përcjellur nga funksioniremove() me çelësin e përcjellur nga funksioni remove(). Nëse çelësat përputhen, atëherë nyja rrënjë i përcillet funksionit removeRootNode() ku nyjalargohet. Anëtari ‘size’ dekrementohet për të reflektuar largimin e nyjes nga pema. Pastaj nga funksioni removeNode() kthehet vlera Bool-eane ‘true’. 

 Nëse nuk ka përputhje, atëherë funksioni removeNode() përcakton nëse çelësi inyjes rrënjë është më i vogël se çelësi i përcjellur prej funksionit remove(). Nëseështë kështu, atëherë funksioni removeNode() krahason çelësin e nyjes së majtëme atë të çelësit të përcjellur prej funksionit remove(). Funksioni removeNode()thirret rekurzivisht deri sa të gjindet përshtatja, me ç’rast thirret funksioniremoveRootNode(0 dhe i përcillet referenca për në nyjen e përshtatur(përputhur).

 Nëse çelësi është më i madhe se çelësi i nyjes rrënjë, atëherë ai krahasohet me

çelësin e nyjes së djathtë. Përsëri, funksioni removeNode() thirret rekurzivisht

Page 352: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 352/699

Avni Rexhepi

352

deri sa të gjindet përshtatja, me ç’rast thirret funksioni removeRootNode(0 dhe i përcillet referenca për në nyjen e përshtatur (përputhur).

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 rr ënjë) ndonjëherë mund të shkaktojë konfuzionsepse intuitivisht supozohet se është fjala për rrënjën e tërë pemës, mirëpo nërealitet secila nyje mund të jerë rrënjë për të gjitha nyjet nën të. Edhe nëse nyjaështë gjethe, ajo akoma është rrënjë e nënpemës (në këtë rast, të zbrazët). Pra,ndoodhë që ajo është e vetmja nyje e nënpemës. Prandaj, në emrin e këtij

funksioni përdoret fjala “Node” (rr ënja).Funksioni removeRootNode() kërkon një argumen, i cili është pointeri në pointerin për nyjen që largohet. Procesi i largimit fillon me deklarimin e pointerit në strukturën metadata. Në pjesën vijuese të kodit, ky pointeremërtohet ‘temp’. 

Para se të largojë nyjen, funksioni removeRootNode() kontrollon nëse nyja kafëmijë të djahtë dhe fëmijë të majtë. Nëse të dy fëmijët janë NULL, atëherë nukka asnjë fëmijë dhe thirret operatori ‘delete’ për të liruar memorien e shoqëruarme këtë nyje. pastaj pointeri në nyjen prind vendoset në NULL, sepse kjo nyje

fëmijë është larguar. Vëreni se nëse kjo nyje do të ishte nyja e vetme e pemës,

Page 353: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 353/699

Algoritmet dhe strukturat e të dhënave

353

funksioni do të caktonte rrënjën e pemës në NULL, gjë që ka kuptim pasi që pema do të ishte e zbrazët.

 Nëse njëri prej fëmijëve nuk është NULL, atëherë do të fëmija i djathtë do të

krahasohej me NULL. Ky do të ishte rasti nëse nyja që fshihet nuk ka fëmijë tëdjathtë. Në këtë rast, ndryshohet pointeri në nyjen prind në të majtë të nyjes qëështë duke u larguar. Nyja rrënjë caktohet për në  pointerin ‘temp’ për tëmbajtuar mend lokacionin e nyjes që është duke u larguar nga pema. Pastajreferenca për në nyjen e majtë caktohet për në nyjen prind. Operatori ‘delete’ pastaj e largo nyjen që referohet përmes pointerit ‘temp’ për të liruar memoriene shoqëruar me nyjen që është duke u fshirë.

 Nëse nyja e djathtë nuk është NULL, atëherë funksioni removeRootNode()kontrolon nëse nyja e majtë është NULL. Kjo përcjellë logjikën e njëjtë me

rastin e mëparshëm, përveq se nyja që është duke u fshirë nuk ka fëmijë tëmajtë, kështu që pointeri në nyjen prind caktohet për në nyjen në të djathë tëasaj që është duke u fshirë. Referenca për ny nyjen rrënjë caktohet për në pointerin ‘temp’. referenca për në nyjen e djathtë pastaj caktohet për në nyjen prind. Operatori ‘delete’ e liron memorin e shoqëruar me nyjen që është duke ularguar.

Skenari i fundit dhe më i komplikuari është nëse nyja që është duke u larguar katë dy fëmijët, edhe nyjën fëmijë të majtë edhe atë të djathtë. Në këtë rast,funksioni removeRootNode() thërret funksionin moveLeftMostNode() (lëvize

nyjen më të majtë) dhe i përcjellë atij adresën 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);

Page 354: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 354/699

Avni Rexhepi

354

}else{

moveLeftMostNode(&((*root)->right), *root);

}}

Objektiv i funksionit moveLeftMostNode() është të gjejë nyjen e cila do tëzëvendësojë nyjen aktuale të nënpemës. Për të arritur këtë qëllim, duhet tëlëvizni një herë në të djathtë dhe pastaj të shkoni teposhtë pemës sa më larg (samë thellë) që të jetë e mundur në të majtë, deri sa të gjendet nyja më e vogël nëtë djathtë. Lëvizja në të djathtë ndodhë kur funksioni moveLeftMostNode()thirret për herë të parë. Pastaj lëvizni majtas thirrjet pasuese të funksionit. Kur të jetë gjetur vlera më e vogël në anën e djathtë, nyja që përmbanë vlerën më të

vogël bëhet nyja e re rrënjë.Le të shohim se si funksionon kjo duke lëvizuar (ecur) nëpër definicionin vijuestë funksionit moveLeftMostNode(). Ky funksion kërkon dy argumente, nyjenaktuale që është duke u vlerësuar dhe nyjën e cila do të zëvendësohet. Mbanimend, ky funksion do të kopjojë çelësin dhe vlerën (të dhënën) prej nyjes më tëvoglël në nënpemën e djathtë, në nyjën e cila është duke u larguar. Kjozëvendëson nyjen që është duke u larguar me njërën nga nyjet gjete dhe pastajnyja gjethe fshihet.

 Nëse referenca për në nyjen që është duke u lëvizur nuk është NULL dhe pointeri i majtë i nyjes që ësthë duke u lëvizur është NULL, atëherë keni gjeturnyjen e cila do të lëvizet përpjetë deri në pozitën e nyjes që është duke u larguar.Deklarohet një pointer dhe caktohet për në nyjen që lëvizet. Pastaj, çelësi dhevlera e nyjes kopjohen në çelësin dhe vlerën e nyjes rrënjë. Nyja rrënjë në këtërast është nyja që ësthë duke u larguar nga pema. Pasi që jeni duke lëvizurfëmijën më të majtë të nënpemës së djathtë, ky fëmijë më i majtë mund të ketëfëmijë në të djathtë të tij. Vlera e pointerit në prindin e nyjes që ësthë duke ulëvizur caktohet në pointerin e djathtë të asaj që është duke u lëvizur. Kjo imbanë këto nën-nyje të paprekura. Përfundimisht, operatori ‘delete’ e largon

nyjen.

 Nëse nuk është gjetur nyja më e majtë e nënpemës së djathtë, atëherë thirret përsëri funksioni moveLeftMostNode(). Këtë herë, nyja e fëmijës së majtë përcillet si nyje që duhet të lëvizet në pozitën rrënë. Pozita rrënjë në këtë rastështë nyja që është duke u larguar.

void moveLeftMostNode(METADATA** node, METADATA* root){

if(*node != NULL && (*node)->left == NULL){

Page 355: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 355/699

Algoritmet dhe strukturat e të dhënave

355

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 kurdo të nevojitet që të largohen të gjitha nyjet nga pema. Për të bërë këtë, duhet tëthirret funksioni removeAll() (largo të gjitha).

Funksioni removeAll() i paraqitur në pjesën vijuese të kodit, kryen dyoperacione. Së pari, ai thërret funksionin removeAllNodes() (largo të gjithanyjet), i cili është i definuar në pjesën private të klasës BinarySearchTree. Kyështë funksioni i cili në fakt largon të gjitha nyjet nga pema. Operacioni i dytëështë që të resetohen (rivendosen (angl. reset)) anëtarët ‘root’ dhe ‘size’ të

klasës BinarySearchTree. Anëtari ‘root’ caktohet në NULL, për të treguar senuk ka asnjë nyje në pemë. Anëtari ‘size’ caktohet në zero, për të treguar se pema është e zbrazët. Funksioni removeAll() poashtu thirret edhe ngadestruktori.

void removeAll(){

removeAllNodes(root);root = NULL;size = 0;

}

Funksioni removeAllNodes() kërkon një argument, i cili është pointeri për nënyjen rrënjë. Përderi sa nyja rrënjë nuk është NULL, funksioniremoveAllNodes() thërret vetveten secilën herë, duke i përcjellur së pari nyjen efëmijës së majtë dhe pastaj nyjen e fëmijës së djathtë si nyje rrënjë. Renditja ekëtyre thirrjeve është me rëndësi. Rikujtoni që nyje rrënjë është ose nyja rrënjë etërë pemës ose rrënja e nënpemës (nëndegës). Para se të largohet prindi duhet tëlargohen të gjitha nyjet fëmijë. I adresoheni pemës së majtë, pastaj pemës sëdjahtë, e pastaj kur ktheht tek thirrësi, është e sigurtë që të fshihet nyja aktuale(nyja rrënjë) sepse të gjithë fëmijët do të jenë fshirë. Sikur me të gjitha

Page 356: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 356/699

Avni Rexhepi

356

funksionet rekurzive, duhet të definohet pika e ndalimit. Në këtë rast, nëse jeninë nyje gjethe, pointeri i majtë dhe i djathtë do të jenë NULL dhe thirrjet efunksionit removeAllNodes() do të kthenin (return), (ato nuk do të vazhdoninrekurzionin), sepse nyja do të jetë NULL.

 Në ekran do të paraqitet porosia që tregon çelësin dhe vlerën e nyjes që ështëduke u larguar nga pema. Pastaj përdoret operatori ‘delete’ për të larguar nyjen.

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

}}

Funksionet get() dhe getNode()

Funksioni get() (merr) i klasës BinarySearchTree thirret përbrenda aplikacionitsa herë që dëshironi të lexoni/“tërheqni” (angl. retrieve-tërheqë, rikthej) vlerën e

nyjes. Për të tërhequr vlerën, duhet t’i siguroni funksionit get() çelësin përkërkim dhe variablën e cila do të ruaj vlerën, kur të gjindet çelësi. 

Kjo gjë funksionon në mënyrën si është ilustruar në pjesën vijuese të kodit, ku i përcillni funksionit get() dy argumente. Argumenti i parë është pointeri që ireferohet çelësit për kërkim. Në këtë shembull, çelësi është një string/tekst/fjalë.Prandaj, funksionit i përcillet 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 përdorë për të ruajtur vlerën e nyjes që është e shoqëruar

me çelësin e kërkimit.

Le të marrim si shembull që çelësi i kërkimit është ID e studentit “1234” dhevlera e shoqëruar me këtë çelës është “Ben Shpati”. Ju i përcillni funksionit get()“1234” dhe ai kopjon vler ën “Ben Shpati” në vlerën e vargut të karaktereve,nëse çelësi “1234” është gjetur në ndonjë nyje të pemës. Pastaj e përdorni vlerëne vargut të karaktereve nëpër aplikacionin tuaj.

Funksioni get() është i definuar në pjesën publike të klasës BinarySearchTreedhe prandaj është i qasshëm për aplikacionet. Mirëpo, funksioni get() thjeshtë

vetëm e thërret funksionin getNode(), i cili është i definuar në pjesën private të

Page 357: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 357/699

Algoritmet dhe strukturat e të dhënave

357

klasës BinarySearchTree. Funksioni getNode() kthen vlerën Bool-eane ‘true’nëse çelësi i kërkuar gjindet; përndryshe kthehet vlera Bool-eane ‘false’. Vlera ekthyer prej tij poashtu bëhet vlerë e kthyer prej funksionit get().

bool get(char* key, char* value){return getNode(root, key, value);

}

Funksioni getNode() është vendi ku ndodhe i tërë veprimi. Këtu bëhet kërkimidhe vlera e nyjes kopjohet në vlerën e vargut. Siç është ilustruar në vijim,funksioni getNode() kërkon tri argumente. Argumenti i parë është pointeri që ireferohet nyjes rrënjë. Nyja rrënjë është pozita startuese e kërkimit dhezakonisht është nyja më e lartë e pemës, por mund të jetë cilado nyje. Argumenti

i dytë është pointeri që i referohet çelësit të kërkimit, i cili në këtë shembullështë  pointer i tipit ‘char’. Argumenti i tretë është pointeri në variablaën që eruan vlerën e nyjes e cila përmbanë çelësin e kërkimit. Të dyja, çelësi dhe vlerae variablës janë të njëjta me ato që i përcillen funksionit get().

Funksioni getNode() fillon procesimin duke verifikuar nyjen rrënjë. Nëse nyjarrënjë është NULL, atëherë argumenti i vlerës caktohet në string të zbrazët (ecakton karakterin e parë në NULL) dhe nga ana e funksionit getNode() kthehetvlera Bool-eane ‘false’ për të terguar se çelësi nuk është gjetur në pemë.

 Nëse rrënja nuk është NULL, atëherë vazhdon kërkimi. Funksioni getNode()

thirret në mënyrë rekurzive. Secilën herë që thirret, ai krahason çelësin qëkërkohet me çelësin e nyjes rrënjë. Nëse ata përputhen, atëherë vlera e nyjesrrënjë kopjohet në vlerën e variablës dhe nga funksioni kthehet vlera Bool-leane‘true’. 

 Nëse çelësi i kërkimit nuk përshtatet me çelësin e rrënjës, atëherë funksionigetNode() përcakton nëse çelësi është më i vogël ose më i madhe se çelësi inyjes rrënjë. Varësisht prej rezultatit të këtij krahasimi, funksioni getNode() ethërret vetveten dhe përdorë ose fëmijën e majtë ose atë të djathtë të rrënjës siargument për nyje rrënjë të funksionit getNode(). Ky proces vazhdon deri sa ose

çelësi i kërkimit përpythet me çelësin e nyjes rrënjë (aktuale) ose nyja rrënjëështë NULL, duke treguar se çelësi që kërkohet nuk ekziston në pemë. Ky tip ikërkimit është ajo ku vjen në shprehje fuqia e pemëve binare. Vëreni që secilënherë që thirret funksioni, duke bërë një krahasim në çelës, eliminohet ngakërkimi gjysma e nyjeve të mbetura, kështu që do të jeni në gjendje që të gjeniçelësin shumë shpejt, edhe në bashkësi shumë të madhe të të dhënave.

bool getNode(METADATA* node, char* key, char* value){

if(node == NULL)

{

Page 358: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 358/699

Avni Rexhepi

358

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ë herët është theksuar se çelësi në pemë duhet të jetë unik. Nuk mund të kenidy çelësa me vlerë të njëjtë të çelësit. Mos e përzieni vlerën e çelësit me vlerën eruajtur në nyje. Vlera çelës është vlera e vetë çelësit.

Para se të shtohet një nyje e re në pemë, duhet të përcaktoni nëse çelësi i nyjessë re veç ekziston në pemë. Është e mundur që të konstruktohet pema binare ecila lejon çelësa duplikat, mirëpo ky nuk është zbatim (implementim) izakonshëm. Në këtë rast, kemi definuar rregullën që të gjithë çelësat e pemësduhet të jenë unik.

Për të përcaktuar nëse çelësi veq ekziston në pemë, thirret funksioni contains()

(angl. contains-përmbanë) që është anëtarë i klasës BinarySearchTree, i ciliështë paraqitur në vazhdim. Funksioni contains() kërkon një argument, pointerinqë i referohet çelësit. Ai kthen vlerën Bool-eane ‘true’ nëse çelësi ekziston, përndryshme kthen vlerën ‘false’. 

Funksioni contains() është funksion i thjeshtë dhe ka vetëm një urdhër. Kyurdhër thërret funksionin containsNode(), anëtarë i klasës. FunksionicontainsNode() kërkon pemën për çelësin e kërkimit dhe kthen vlerën Bool-eane‘true’ nëse çelësi është gjetur, përndryshe kthen vlerën Bool-eane ‘false’, e cilavlerë pastaj përdoret si vlerë kthyese prej funksionit contains().

Page 359: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 359/699

Algoritmet dhe strukturat e të dhënave

359

Funksioni contains() është definuar në pjesën publike të klasësBinarySearchTree, ndërsa funksioni containsNode() është definuar në pjesën private të klasës së njëjtë.

bool contains(char* key){return containsNode(root, key);

}

Funksioni containsNode(), siç është treguar në vazhdim, kërkon dy argumente(dy pointerë). Argumenti i parë është pointeri që i referohet nyjes rrënjë. Nyjerrënjë mund të jetë cilado nyje, por në mënyrë tipike rrënjë është nyja e parë e pemës sepse dëshironi që të kërkoni çelësin duke filluar nga maja e pemës.Argumenti i dytë është pointeri që i referohet çelësit (key), i cili është çelësi i

njëjtë që i përcillet funksionit contains().Procesi fillon me përcaktimin (kontrollimin) nëse pointeri i rrënjës është NULL. Nëse pointeri është NULL, atëherë çelësi nuk ekziston dhe kthehet vlera Bool-eane ‘false’; përndryshe çelësi krahasohet dhe kërkimi vazhdon.

Së pari, funksioni containsNode() krahason çelësin me çelësin e nyjes rrënjë. Nëse ka përputhje (përshtatje), atëherë kthehet vlera Bool-eane ‘true’ dhekërkimi mbaron. Nëse janë të ndryshëm, atëherë funksioni containsNode() përcakton nëse çelësi (key) është më i vogël se ai i nyjes rrënjë. Nëse po, atëherëfunksioni containsNode() e thërret vetveten dhe përdorë nyjen e fëmijës së majtë

të rrënjës, si nyje e re rrënjë. 

 Nëse çelësi nuk është më i vogël se çelësi i rrënjës, atëherë funksionicontainsNode() e përcakton nëse çelësi është më i madh se ai i rrënjës. Nëse po,atëherë funksioni containsNode() e thërret vetveten duke përdorur fëmijën edjathtë të rrënjës si nyje e re rrënjë.

Funksioni containsNode() thirret rekurzivisht deri sa ose të gjindet çelësi osederi sa vlera e rrënjës është NULL, duke treguar se keni arritur në fund të pemës pa e gjetur çelësin.

bool containsNode(METADATA* node, char* key){

if(node == NULL){

return false;}else{

if(strcmp(key, node->key) == 0){

return true;

Page 360: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 360/699

Avni Rexhepi

360

}else if(strcmp(key, node->key) < 0){

return containsNode(node->left, key);

}else{

return containsNode(node->right, key);}

}}

Funksionet displayInOrder() dhe processNodesInOrder()

Përmbajtja e pemës mund të shfaqet (paraqitet) duke e thirrur funksionindisplayInOrder() (paraqiti me radhë), anëtarë i klasës BinarySearchTree. Siçtregon edhe emri, funksioni displayInOrder() është funksion publik i cili paraqetçelësin dhe vlerën e të gjitha nyjeve të majta, të pasuara nga të gjitha nyjet edjathta, për secilën nyje në pemë.

Siç ësthë treguar në vijim, funksioni displayInOrder() ka një urdhër, i cili thërretfunksionin processNodesInOrder() (proceso nyjet me radhë), funksion anëtarë iklasës BinarySearchTree. Funksioni processNodesInOrder() është i definuar në

 pjesën private të klasës prandaj nuk është në dispozicion për alikacionin(aplikacioni nuk ka qasje në pjesën private!).

Funksionit processNodesInOrder() duhet t’i përcillet një argument, i cili është pointeri që i referohet nyjes rrënjë. Nyje rrënjë në mënyrë tipike është nyja e parë e pemës, mirëpo mund të filloni paraqitjen e përmbajtjes së pemës ngacilado nyje, duke e përcjellur atë si argument të funksionit processNodesInOrder().

void displayInOrder(){

processNodesInOrder(root);}

Definicioni i funksionit processNodesInOrder(0 është paraqitur në vijim. Do tëvëreni se ky është një funksion rekurziv dhe thirret shumë herë në mënyrë që tështypë nyjet që ndodhen në degën e majtë dhe atë të djathtë të pemës.

Procesimi fillon duke përcaktuar (kontrolluar) nëse rrënja është NULL. Nëse po, jeni në fund të pemës. Nëse nuk është NULL, atëherë funksioni processNodesInOrder() thirret përsëri dhe i përcillet fëmija i majtë i nyjes rrënjë.Atëherë paraqitet në ekran çelësi dhe vlera e nyjes.

Page 361: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 361/699

Algoritmet dhe strukturat e të dhënave

361

Kjo vazhdon deri sa të paraqiten në ekran çelësat dhe vlerat e të gjitha nyjevemajtas. Procesi i njëjtë përcillet për të paraqitur fëmijët djathtas të nyjes rrënjë.Për cilëndo nyje të dhënë, së pari do të shtypen të gjitha nyjet majtas, pastajshtypet 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ë herët është thënë se pema matet për nga numri i nyjeve dhe për nga numri iniveleve. Numri i nyjeve në pemë thirret madhësia e pemës (angl. size) dhenumri i niveleve të pemës është thellësia e pemës (angl. depth). Kemi definuarfunksionet anëtare të cilat mund të përdoren për të përcaktuar madhësinë dhethellësinë e pemës.

Funksioni i parë quhet getSize() (merre madhësin), i cili është paraqitur në

vazhdim. Ky funksion thjeshtë kthen vlerën e anëtarit ‘size’ të klasësBinarySearchTree. Funksionet të cilat shtojnë dhe largojnë nyjet e përshtasinvlerën e anëtarit ‘size’ ashtu që anëtari ‘size’ i klasës gjithmonë reflektonnumrin aktual të nyjeve në pemë.

int getSize(){ return size; }

Funksioni getDepth() (merre thellësinë), përcakton numrin e nivele në pemë. Kyfunksion thërret funksionin anëtarë getTreeDepth() (merre thellësinë e pemës)dhe ia përcjellë atij referencën për në nyjen rrënjë e cila përdoret si nivel startues

kur llogaritet thellësia e pemës. Ai kthen një integer që përfaqëson numrin eniveleve në pemë.

Funksioni getDepth() dhe funksioni getSize() janë të dy të definuar në pjesën publike të klasës BinarySearchTree. Funksioni getTreeDepth() është i definuarnë pjesën private.

int getDepth(){

return getTreeDepth(root);}

Page 362: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 362/699

Avni Rexhepi

362

Funksioni getTreeDepth() është paraqitur në vazhdim dhe kryen të gjithallogaritjet për të përcaktuar numrin total të niveleve në pemë. FunksionigetTreeDepth() kërkon një argument, i cili është referencë në nyjen rrënjë. Kjoduhet të jetë nyja e parë në pemë, edhe pse mund të përdoret cilado nyje. Nëse përdorni nyje tjetër, funksioni llgoaritë nivelet prej asaj nyjeje deri tek fundi i pemës. Nivelet para kësaj nyjeje nuk mirren në konsiderim në llogaritje.

Procesi fillon me kontrollimin nëse nyja është e zbrazët. Nëse është, atëherënyja rrënjë është NULL dhe kthehet vlera zero. Nëse rrënja nuk është NULL,atëherë funksioni getTreeDepth() “shkon teposhtë” nëpër secilin nivel të pemësduke thirrur vetveten në mënyrë rekurzive. Kur arrihet në nyje gjethe, fitohet parametri NULL. Kjo nuk do të thotë që pema është e zbrazët, mirëpo vetëm sekeni arritur në nyje gjethe. Pastaj, thirrjet rekurzive kthejnë (me return), dukeinkrementuar vlerën numëruese nëpër secilin rekurzion, për të mbledhur nivelet.

Secilën herë që thirret funksioni getTreeDepth(), atij i përcillen nyja fëmijë emajtë dhe nyja fëmijë e djathtë dhe funksioni kthen një numër të plotë (integer) icili reprezenton nivelin, i cili është i shoqëruar ose me variablën ‘depth_left’(thellesia majtas) ose me variablën ‘depth_right’ (thellësia djathtas).

Variablat depth_left dhe depth_right krahasohen. Nëse vlera e variablësdepth_left është më e madhe se ajo e variablës depth_right, variabla depth_leftinkrementohet dhe kthehet nga ana e funksionit getTreeDepth(); përndryshe,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;}

}}

Page 363: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 363/699

Algoritmet dhe strukturat e të dhënave

363

Pema binare në C++

Tani që i pamë pjesët e klasës BinarySearchTree, le t’i bashkojmë në njëaplikacion punues. Aplikacioni do të organizohet në tre fajlla:BinaryTreeDemo.cpp, BinarySearchTree.h dhe BinarySearchTree.cpp. FajlliBinarySearchTree.cpp është fajlli i aplikacionit që përmbanë kodin i cili krijondhe manipulon pemën. BinarySearchTree.h përmbanë definicionin e strukturëssë përdorur për të ndërtuar nyjen dhe definicionin e klasës BinarySearchTree.BinarySearchTree.cpp përmbanë definicionin e funksioneve anëtare të klasësBinarySearchTree. Të gjitha janë listuar në kodin vijues. Mbetet vetëm ta provoni se si aplikacioni krijon dhe manipulon pemën, e cila ka ID-të (çelësat)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))

Page 364: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 364/699

Avni Rexhepi

364

{cout <<"Shtimi i nyjes–çelesi/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 instancës së klasës BinarySearchTree dhe iacakton atë referencës së quajtur ‘tree’ (pema). Pastaj, deklarohen dy vargjet e‘char’ dhe një ‘int’. vargjet ‘char’ quhen ‘key’ (çelësi) dhe ‘value’ (vlera) dhemadhësia e këtyre vlerave caktohet duke përdoru makron e definuar në fajllinBinarySearchTree.h. Vargjet ruajnë një ID dhe emrin i cili i ndahet nyjes në pemë. Variabla e tipit ‘int’ kontr ollon unazën ‘for’. 

Page 365: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 365/699

Algoritmet dhe strukturat e të dhënave

365

Pastaj, unaza ‘for’ shton secilën ID dhe emër në pemë. Për seclin iteracion(kalim, përsëritje), thirret funksioni strcpy() për të kopjuar stringun që përmbanëose ID-në ose emrin që duhet kopjuar në vargje.

Kur të jetë kopjuar bashkësia e stringjeve në vargje, aplikacioni thërretfunksionin contains() (përmbanë) për të kontrolluar nëse çelësi veç ekziston në pemë. Rikujtoni që secli çelës duhet të jetë unik. Funksioni contains() kthenvlerën Bool-eane ‘true’ nëse çelësi është në pemë. Me logjikën e përmbysjes (ekundërt, e invertimit) me operatorin ‘! –  not” trajtojmë vlerën Bool-eane ‘true’ si‘false’. Kjo do të thotë që urdhërat përbrenda urdhërit ‘if’ nuk do tëekzekutohen nëse çelësi veç ekziston në pemë.

 Nëse çelësi nuk ekziston në pemë, atëherë aplikacioni paraqet çelësin (key) dhevlerën (value) në ekran, para se të thërrasë funksionin add() (shtoje) për të

vendosur në pemë çelësin dhe vlerën, si në vijim:Shtimi i tre çelesave dhe tri vlerave ne peme.Shtimi i nyjes–çelesi/key: 345 vlera: BeniShtimi i nyjes–çelesi/key: 123 vlera: MeriShtimi i nyjes–çelesi/key: 999 vlera: Suzi

Figura 6-29 ilustron çelësat dhe vlerat të organizuara në pemë.

 Figura 6.29 - pa marrë parasysh radhën me të cilën shtohen të dhënat në pemë,

nyja fëmijë e majtë është më e vogël sesa nyja prind dhe nyja e djahtë fëmijë

është më e madhe sesa nyja prind.

 Nëse çelësi veç ekziston në pemë, atëherë paraqitet porosia në ekran që tregonse çelësi është çelës 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 çelësat dhe vlerat e secilës nyje, si në vijim:

Pershkimi *In order* i pemes:

çelesi: 123 vlera: Meri

Page 366: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 366/699

Avni Rexhepi

366

çelesi: 345 vlera: Beniçelesi: 999 vlera: Suzi

Pastaj aplikacioni paraqet thellësinë dhe madhesinë e pemës duke thirrur

funksionet getDepth() dhe getSize(). Rezultati paraqitet në ekran, si në vijim:Thellesia e pemes para largimit te nyjeve: 2Madhesia e pemes para largimit te nyjeve: 3

Rikujtoni që thellësia e pemës është numri i niveleve të pemës. Në këtëshembull, janë dy nivele. Niveli i parë përmbanë nyjen rrënjë dhe niveli i dytë përmbanë nyjen fëmijë të majtë dhe nyjen fëmijë të djathtë.

Pastaj, aplikacioni i tërheqë (lexon) vlerat e shoqëruara me çelësin 123 dukethirrur funksionin get(). Funksioni get() kthen vlerën Bool-eane ‘true’ nëseçelësi është gjetur; përndryshe, kthehet vlera Bool-eane ‘false’. Nëse çelësi ështëgjetur, atëherë paraqitet në ekran vlera, si në vijim. Rikujtoni që emri ishoqëruar me çelësin 123 i caktohet vlerës varg përmes funksionit get().

Leximi i nje vlere nga pema:Vlera: Meri

Pastaj, aplikacioni largon nyjen që përmbanë çelësin 123. Së pari thirretfunksioni contains() për të përcaktuar nëse pema përmbanë çelësin me vlerë123. Nësë po, atëherë kthehet vlera bool-eane ‘true’, përndryshe kthehet vlera bool-eane ‘false’. Pasi që ekziston nyja me çelësin 123, thirret funksioni

remove() dhe i përcillet stringu 123 për të larguar nyjen.Funksioni displayInOrder() thirret përsëri, për të paraqitur pemën 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

Vëreni se nyja 123 më nuk ekziston në pemë (Figura 6.30).

 Figura 6.30 - Nyja e majtë fëmijë është larguar nga pema: pema akoma ka

thellësinë 2 nivele.

Page 367: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 367/699

Algoritmet dhe strukturat e të dhënave

367

 Në fund, aplikacioni thërret funksionet getDepth() dhe getSize() për të paraqiturthellësinë dhe madhësinë e pemës pasi të jetë larguar nyja. Rezultati si vijon:

Thellesia e pemes pas largimit te nyjeve: 2

Madhesia e pemes pas largimit te nyjeve: 2Aplikacioni përfundon largimin e pemës duke thirrur operatorin ‘delete’.Rikujtoni që destruktori i klasës BinarySearchTree e thërret funksioninremoveAllNodes() i cili i paraqet çelësat dhe vlerat që largohen. Ja se çka do të paraqitet në ekran:

Largohet nyja – çelesi (key): 999 SuziLargohet nyja – çelesi (key): 345 Beni

 Fig.6.31 –  Rezultati i ekzekutimit  

Page 368: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 368/699

Avni Rexhepi

368

Kërkimi tek pema binare

Pema binare (angl. Binary Tree) është strukturë e të dhënav e përdorurgjerësisht. Karakteristikë e pemës binare, e cila e dallon nga pema e

zakonshmë, është se secila nyje ka më së shumti dy fëmijë. Përdorim më ishpeshtë i pemës binare është si strukturë bazike për pemën binare të kërkimit(angl. Binary search tree). Secila pemë binare ka grupet vijuese të nyjeve:

  Rrënja, (nyja rrënjë, angl. Root node): nyja e parë (më e lartë) e pemës.Është si “nyje kryesore” e pemës, sepse të gjitha nyjet tjera arrihen prejrrënjës. Gjithashtu, rrënja nuk ka prind. Është nyja në të cilën zakonishtfillojnë operacionet në pemë.

   Nyjet interne (nyjet e brendshme, angl. Internal nodes): këto nyje kanë

 prind (rrënja nuk është nyje e brenshme) dhe së paku një fëmijë.   Nyjet gjethe (angl. Leaf (gjethe), leaves (gjethet)): këto nyje kanë prind, por nuk kanë fëmijë (janë nyjet fundore).

 Në vijim kemi një shembull të pemës binare.

Shembull i pemës binare

Operacionet

Operacionet bazike (të cilat do të sqarohen në detaje më vone), janë tëndërlidhura me përshkimin e pemës (bredhjen nëpër pemë, kalimin me radhënëpër nyje). Janë të definuara tri mënyra standarde të përshkimit të pemës:

Page 369: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 369/699

Algoritmet dhe strukturat e të dhënave

369

-  Preorder (root-left-right –  rrënja-nyja e majtë – nyja e djathtë);-  Postorder (left-right-root –  nyja e majtë – nyja e djathtë – rrënja); dhe,-  Inorder (left-root-right –  nyja e majtë-rrënja-nyja e djathtë)

Implementimi themelor është në pemën e kërkimit binar  –   (angl. Binarysearch tree (BST)).

Pema e kërkimit binar - Binary search tree

Fillimisht duhet theksuar se pema binare e kërkimit (BST) ësthë strukturëdinamike e të dhënave, që do të thotë se madhësia e saj është e kufizuar vetëmnga sasia e mëmories së lirë (free memory) në sistemin operativ dhe numri ielementeve mund të ndryshojë gjatë ekzekutimit të programit. Përparësikryesore e pemës binare të kërkimit është kërkimi i shpejtë, gjersa shtimi i

elementeve është mjaft “i lirë”. Definicionet themelore të pemës binare tëkërkimit janë dhënë në vijim.

Pema binare e kërkimit është strukturë e të dhënave, e cila e plotëson kërkesatvijuese:

  është pemë binare;  secila nyje ka vlerë;  rendi total është i definuar në këto vlera (çdo dy vlera mund të

krahasohen më njëra tjetrën);  nënpema/nëndega e majtë (left subtree) e nyjes përmbanë vetëm vlerat

më të vogla sesa vlera e nyjes;  nënpema/nëndega e djathtë (right subtree) e nyjes përmbanë vetëm

vlerat më të mëdha sesa vlera e nyjes;

Vëni re se definicioni i këtillë nuk lejon duplikate.

Shembull i pemës binare të kërkimit

Page 370: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 370/699

Avni Rexhepi

370

Për çka përdoret pema binare e kërkimit?

Pema binare e kërkimit përdoret për të konstruktuar strukturën e të dhënavemap (angl. map-hartë, plan, skemë). Në praktikë, të dhënat shpeshherë mund të

 jenë të lidhura (shoqruara, bashkuara) me një vlerë unike, çelës unik (angl.unique key). Për shembull, në adresarin e telefonave, një “çelës” i tillë ështënumri i telefonit. Ruajtja e të dhënave të tilla në pemën binare të kërkimitmundëson kërkim më të shpejtë sipas çelësit për ndonjë rekord, sesa sikur tëdhënat të ishin ruajtur në ndonjë listë të parenditur. Gjithashtu, pema binare ekërkimit mund të shfrytëzohet për të konstruktuar strukturën e të dhënave set (grup, set, bashkësi), e cila lejon ruajtjen e një koleksioni të parenditur të velraveunike dhe të kryej operacione me koleksione të tilla.

Performansa e pemës binare të kërkimit varet nga lartësia e saj. Për të mbajtur

 pemën të balansuar dhe për të minimizuar lartësinë e saj, idea e pemës binare tëkërkimit është avansuar në pemët e balansuara të kërkimit (AVL tree, Red-Black tree, Splay tree, etj).

Operacionet në pemën binare të kërkimit

  Shtimi i një vlere të re  Kërkimi për ndojë vlerë  Largimi (fshirja) e vlerës  Marrja me radhë e vlerave nga pema binare e kërkimit

Pema binare e kërkimit –  reprezentimi i brenshëm

Sikur çdo strukturë tjetër dinamike e të dhënave, pema binare e kërkimit kërkonruajtjen e disa të dhënave ndihmëse të nevojshme për të mbajtur strukturën e saj.Secila nyje e pemës binare përmbanë informatat vijuese:

  Vlerën (e dhënë e shfrytëzuesit-user’s data);  Lidhjen për tek fëmija i majtë (e dhënë ndihmëse-auxiliary data) ;  Lidhjen për tek fëmija i djathtë (e dhënë ndihmëse-auxiliary data) ;

Varësisht nga madhësia e të dhënave të shfrytëzuesit, mund të ndryshojëmbingarkimi i memories, por në përgjithësi është plotësisht i arsyeshëm. Në disaimplementime, nyja mund të ruaj lidhjen për tek prindi, por kjo varet ngaalgoritmi të cilin programeri dëshiron ta aplikojë për pemën binare të kërkimit(BST). Për operacionet themelore, sikur shtimi, largimi dhe kërkimi, lidhja përtek prindi nuk është e novojshme. Ajo është e nevojshme për implementimin eiteratorëve.

Page 371: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 371/699

Algoritmet dhe strukturat e të dhënave

371

Me pamje të përshtatur për reprezentim të brendshëm, shembulli i dhënë nefillim ndryshon:

 Nyjet gjethe kanë lidhje për tek fëmijët, por ato nuk kanë fëmijë. Në gjuhë programuese kjo do të thotë se lidhjet përkatëse janë të përcaktuara në NULL.

Pjesë kodi

Është e zakonshme që tërë struktura e pemës binare për kërkim të vendoset nëdy klasa. Klasa kryesore  BinarySearchTree është interfejsi publik dhe BSTNode është mjet përdorim privat përmbrenda klasës main. Kjo ndarje është enevojshme, sepse disa operacione, si largimi/fshirja, mund të rezultojnë në një pemë të zbrazët, që do të thotë se pema nuk ka as nyje rrënjë 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;

}};

Page 372: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 372/699

Avni Rexhepi

372

Pema binare e kërkimit –  shtimi i vlerës

Shtimi i një vlere në pemën binare mund të ndahet në dy faza:

  kërkimi për vendin për ta vendosur elementin e ri;  insertimi i elementit të ri në këtë vend.

Kërkimi për vend

 Në këtë fazë algoritmi duhet të përcjellë tiparin e kërkimit të pemës binare. Nësevlera e re është më e vogël sesa vlera e nyjes aktuale, shko në nënpemën emajtë, përndryshe shko në nënpemën e djathtë. Duke përcjellur këtë rregull tëthjeshtë, algoritmi arrinë në nyjen e cila nuk ka as nënpemë të majtë as tëdjathtë. Në momentin kur të gjindet vendi për insertim, mund të themi me siguri,

se vlera e re nuk ka duplikat në pemë.Fillimisht, nyja e re nuk ka fëmijë, kështu që ajo është gjethe. Në figurënvijuese, rrathët ngjyrë hiri tregojnë pozitat e mundëshme për nyjen e re.

Tani, le të përshkojmë algoritmin. Në këtë rast dhe në pothuajse secilinoperacion në pemën binare të kërkimit, shftytëzohet rekurzioni. Duke filluar ngarrënja,

1.  verifiko, a mos janë të barabarta vlera në nyjen aktuale dhe vlera e re. Nëse po, është gjetur duplikati. Përndrsyhe,

2.  nëse vlera e re është më e vogël, atëherë vlera e nyjes:o  nëse nyja aktuale nuk ka fëmijë të majtë, vendi për insertim është

gjetur;o   përndryshe, trajtoje fëmijën e djathtë me të njëjtin algoritëm.

Shembull

Page 373: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 373/699

Algoritmet dhe strukturat e të dhënave

373

Inserto 4 në pemën e treguar më lartë.

Page 374: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 374/699

Avni Rexhepi

374

Pjesë kodi

Dallimi i vetëm ndërmjet algoritmit të mësipërm dhe rutinës reale është se së pari duhet të verifikohet a ekziston rrënja. Nëse jo, vetëm krijoni rrënjën dhemos e ekzekutoni algoritmin e zakonshëm për këtë rast special. Kjo mund të bëhet në klasën BinarySearchTree. Algoritmi themelor implementohet në klasën 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);

}

Page 375: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 375/699

Algoritmet dhe strukturat e të dhënave

375

else if (value > this->value){

if (right == NULL){

right = new BSTNode(value);return true;} else 

return right->add(value);}return false;

Pema binare e kërkimit –  operacioni i kërkimit

Kërkimi i vlerën së pemën binare është i ngjashëm me operacionin eshtimit/insertimit. Algoritmi i kërkimit e përshkon pemën “në thellësi” (angl. “indepth”), duke zgjedhur rrugën e duhur përmes vetisë së pemës binare të kërkimitdhe krahason vlerën e secilës nyje të vizituar me atë që jemi duke e kërkuar.Algoritmi ndalet në dy raste:

  nyja me vlerën e duhur është gjeturl;  algoritmi nuk ka rrugëdalje për të vazhduar tutje.

Algoritmi i kërkimitLe të shohim përshkrimin e detajuar të algoritmit të kërkimit. Si në rastin eoperacionit të insertimit dhe pothuajse në çdo operacion në pemën binare,algoritmi i kërkimit përdorë rekurzionin. Duke filluar nga rrënja:

1.  verifiko, nëse vlera në nyjen aktuale dhe vlera e kërkuar janë të barabarta. Nëse po, atëhere vlera u gjet. Përndryshe,

2.  nëse vlera e kërkuar është më e vogël sesa vlera e nyjes:o  nëse nyja aktuale nuk ka fëmijë të majtë, vlera e kërkuar nuk

ekziston në pemën binare të kërkimit; o   përndryshe, trajtoje fëmijën e majtë me të njëjtin algoritëm.3.  nëse vlera e kërkuar është më e madhe sesa vlera e nyjes aktuale:

o  nëse nyja aktuale nuk ka fëmijë të djathtë, vlera e kërkuar nuk

ekziston në pemën binare të kërkimit; o   përnrdryshe, trajtoje fëmiën e djathtë me të njëjtin algoritëm.

Shembull

Kërkimi i vlerës 3 në pemën e paraqitur më lartë:

Page 376: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 376/699

Avni Rexhepi

376

Pjesë kodi

Si në rastin e operacionit të shtimit/insertimit, së pari vërtetoni a ekziston rrënja. Nëse jo, pema është e zbrazët dhe rrjedhimisht, vlera e kërkuar nuk ekziston në pemë. Ky verifikim mund të bëhet në klasën  BinarySearchTree. Algoritmithemelor implementohet në klasën BSTNode.

bool BinarySearchTree::search(int value) {if (root == NULL)

return false;

Page 377: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 377/699

Algoritmet dhe strukturat e të dhënave

377

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 kërkimit –  largimi i nyjes

Operacioni i largimit/fshirjes në pemën binare është më i kompletuar sesainsertimi dhe kërkimi. Në parim, ai mund të ndahet në dy faza:

  kërkimi i nyjes që duhet larguar;  nëse nyja është gjetur, ekzekuto algoritmin e largimit/fshirjes

Algoritmi i largimit/fshirjes

Faza e parë është identike me atë tek algoritmi për kërkim, përveq faktit se duhet përcjellur prindin e nyjes aktuale. Pjesa e dytë është më e ngatërruar. Ekzistojnëtri raste, si në vijim.

1.   Nyja që duhet larguar nuk ka fëmijë.

Ky rast është krejt i thjeshtë. Algoritmi e vendosë lidhjen përkatëse të prindit në NULL dhe e hedhë nyjen.

Shembull. Largimi/fshirja e -4 nga pema binare.

Page 378: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 378/699

Avni Rexhepi

378

2.   Nyja që duhet larguar e ka një fëmijë.

 Në këtë rast, nyja largohet/pritet nga pema dhe algoritmi e lidhë fëmijën e vetëm

(me nënpemën e tij) drejtpërdrejt me prindin e nyjes së larguar.

Shembull. Largimi i 18 nga pema binare.

Page 379: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 379/699

Algoritmet dhe strukturat e të dhënave

379

3.   Nyja që duhet larguar i ka dy fëmijë.

Ky është rasti më i komplikuar. Për ta zgjidhur, së pari le të shohim një veti tëdobishme të pemës binare. Do të përdorim idenë që i njëjti set i vlerave mund të paraqitet si pemë të ndryshme binare. Për shembull pemët vijuese:

 përmbajnë vlerat e njëjta {5, 19, 21, 25}. Për ta transformuar pemën e parë në të

dytën, mund të veproni si në vijim:

Page 380: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 380/699

Avni Rexhepi

380

o  gjeni vlerën minimale në nënpemën e djathtë (19 në këtëshembull);

o  zëvendësoni 5 me 19o  lidheni/vareni 5 si fëmijë të majtë

Qasja e njëjtë mund të përdoret për të larguar nyjen, e cila ka dy fëmijë:

o  gjeni vlerën minimale në nënpemën e djathtë;o  zëvendësoni vlerën e nyjes që duhet larguar me minimumin e

gjetur. Tani, nëndega e djathtë përmbanë një duplikat!o  apliko largimin/fshirjen në nëndegën e djathtë për të larguar

duplikatin

Vëreni se nyja me vlerën minimale nuk ka fëmijë të majtë dhe prandaj largimi isaj mund të rezultojë vetëm në rastin e parë ose të dytë.

Shembull. Largoni 12 nga nga pema binare.

Gjeni elementin më të vogël (minimumin) në nëndegën e djathtë të nyjes qëduhet larguar. Në këtë shembull është 19.

Page 381: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 381/699

Algoritmet dhe strukturat e të dhënave

381

Zëvendësoni 12 me 19. Vëreni se vetëm vlera është ndryshuar, jo nyjet. Tani

kemi dy nyje me vlerë të njëjtë.

Largoni 19 nga nëndega e majtë.

Page 382: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 382/699

Avni Rexhepi

382

Pjesë kodi

Së pari, verifikoni a ekziston rrënja. Nëse jo, atëherë pema është e zbrazët dhe prandaj vlera që duhet larguar nuk ekziston në pemë. Pastaj, verifikoni nëse

vlera e rrënjës është ajo që duhet larguar. Ky është rast special dhe prandaj kadisa qasje për ta zgjidhur. Këtu do të shohim metodën e nyjës fallse (imitimit tënyjes) (dummy root method), ku krijohet një nyje false dhe rrënja e vëretëvaret/lidhet në të si fëmijë i majtë. Kur të bëhet largimi/fshirja, vendosni lidhjene rrënjës në lidhjen në fëmijën e majtë të nyjes fallse.

 Në gjuhët programuese të cilat nuk kanë “mbledhje automatike të mbeturinave”(automatic garbage collection), si p.sh., C++, nyja e larguar duhet tëhudhet/shkatërrohet (angl. dispose; është fjala për largimin nga memoria). Përkëtë arsye, metoda “remove” e largimit në klasën  BSTNode duhet të kthejë jo

vlerë boolean-e, por lidhjen për në nyjen e shkatërruar dhe të duhet të lirojëmemorien në klasën 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)

Page 383: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 383/699

Algoritmet dhe strukturat e të dhënave

383

{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ë

Për të konstruktuar algoritmin për listimin me radhë të vlerave të pemës binare,le të rikujtojmë tiparet e pemës binare të kërkimit:

  nëndega e majtë e nyjes përmbanë vetëm vlerat më të vogla sesa vlera errënjës;

  nëndega e djathtë e nyjes përmbanë vetëm vlerat më të mëdha sesa vlerae rrënjës.

Algoritmi duket si në vijim:

Page 384: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 384/699

Avni Rexhepi

384

1.  merrni me radhë vlerat nga nëndega e majtë2.  merrni me radhë vlerat nga nëndega e djathtë3.  rezultati për nyjen aktuale është: (result for left subtree) join (current

node's value) join (result for right subtree) (angl. join-lidh, bashko, bashkangjit).

Ekzekutimi i këtij algoritmi në mënyrë rekurzive, duke filluar nga rrënja, do të jep rezultatin për tërë pemën. Le të shohim një shembull të këtij algoritmi.

Shembull

Page 385: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 385/699

Algoritmet dhe strukturat e të dhënave

385

Page 386: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 386/699

Avni Rexhepi

386

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ë vetëm

“pirg”. Ky pirg përdoret për të implementuar radhën me prioritet “PriorityQueue” (angl. Queue  –   radhë, rend i pritjes) dhe algoritmin e sortimit“Heapsort”. Pirgu është pemë binare komplete, e cila i përgjigjet tiparit të pirgut(vlerat e mëdha/vogla lartë).

Pema binare komplete

Thuhet se pema binare është komplete nëse të gjitha nivelet e saj, përveqndoshta nivelit më të fundit, janë komplete. Megjithatë, niveli i poshtëm jo ikompletuar nuk mund të ketë “vrima”, që do të thotë se duhet të jetë i mbushur

nga nyja më e majtë dhe përpjetë deri në ndonjë nyje në mes. Shikoni ilustrimetnë vijim.

Shembull korrekt i pemës binare komplete

Shembull jo korrekt, niveli i mesëm është i pakompletuar

Shembull jo korrekt, niveli i fundit ka "vrimë"

Page 387: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 387/699

Algoritmet dhe strukturat e të dhënave

387

Lartësia e pemës 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 rrënja e pirgut minimal përmbanë vlerën minimale dheanasjelltas. Pirgu me prioritet zakonisht ka të bëjë me pirgun minimal, ndërsaalgoritmi heapsort, gjatë sortimit sipas rendit rritës, përdorë pirgun max.

Tipari heap për pirgun min

Për secilën nyje në pirg, vlera e nyjes është mëe vogël ose baraz  sesa vlerat e

“fëmijëve”. 

Tipari heap për pirgun max

Për secilën nyje në pirg, vlera e nyjes është mëe madhe ose baraz  sesa vlerat e“fëmijëve”. 

Page 388: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 388/699

Avni Rexhepi

388

Për thjeshtësi, në vazhdim do të marrim në konsiderim vetëm pirgun min.

Pirgu binar - Reprezentimi i brendshëm i bazuar në

vargje

Pirgu është pemë binare dhe prandaj mund të ruhet në kompjuter duke përdorurlistat. Kjo ofron përparësi të ndryshme; një prej tyre është mundësia për tëndryshuar me lehtësi numrin e elementeve në pirg. Në anën tjetër, secila nyjeruan dy lidhje ndihmëse, gjë që nënkupton kosto shtesë të memories. Si u tha më parë, pirgu është pemë binare komplete, gjë që çon në idenë e ruajrjes së tij duke

 përdorur një varg. Duke përdorur reprezentimin e bazuar në vargje, mund tëzvogëlojmë koston e memories gjersa navigimi i pemës mbetet mjaft i thjeshtë.

Sa për algoritmin “heapsort”, implementimi i bazuar në vargje është në njëfarëmënyre i natyrshëm.

Pasqyrimi i pirgut në varg

Pirgu ruhet (vendoset, deponohet) në varg nivel pas niveli. Niveli më i lartë përmbanë vetëm rrënjën. Ajo pasqyrohet në elementin e parë të vargut (meindeksin 0). Fëmijët e rrënjës pasqyrohen në elementin e dytë dhe të tretë, e

kështu me radhë. Pirgu është pemë binare komplete, gjë që garanton se nyjet e pirgut i zënë vendet në varg në mënyrë kompakte, duke e bërë pasqyrimin plotësisht efikas.

Page 389: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 389/699

Algoritmet dhe strukturat e të dhënave

389

 Një pasqyrim i këtillë i përgjigjet formulave vijuese:

Left(i) = 2 * i + 1 Right(i) = 2 * i + 2 Parent(i) = (i - 1) / 2

Pra, mund të shihet se navigimi nëpër pirg të pasqyruar në varg, në të vërtetëështë shumë i lehtë.

Pjesë kodi

class BinaryMinHeap {private:

int *data;int heapSize;int arraySize;

int getLeftChildIndex(int nodeIndex) {return 2 * nodeIndex + 1;

Page 390: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 390/699

Avni Rexhepi

390

}

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ë strukturën e pirgut. Kjo quhet“sifting” (angl. Sift –  shoshë, sitë, analizim) apo shoshitje, por haset në literaturëedhe me termat “trickle” (pikon, rrjedhë), “heapify” (pirgëzoj), “bubble”(fluskë), percolate (filtroj, kulloj), etj.

Algoritmi i insertimit

Algoritmi i përgjithshëm për insertimin e elementit të ri në pirgë, është si vijon:

Page 391: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 391/699

Algoritmet dhe strukturat e të dhënave

391

1.  Shto elementin e ri në fund të vargut;2.  Gjersa është e prishur rregulla e pirgut, shoshitë përpjetë elementin e ri.

Shoshitja bëhet si vijon: krahaso vlerën e nyjes me vlerën e prindit. Nësenuk janë në renditje të duhur, shkëmbje pozitat.

Shembull

Inserto “-2” në pirgun në vijim:

Inserto elementin e ri në fund të vargut:

 Në rastin e përgjithshëm, pas insertimit, afër elementit të ri (nyjes së re)

zakonisht prishet (thyhet) rregulla e pirgut:

Page 392: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 392/699

Avni Rexhepi

392

Për të rivendosur rregullën e pirgut, algoritmi e shoshitë përpjetë elementin e ri,duke bërë shkëmbimin e pozitave me prindin e tij:

Tani, rregulla e pirgut është e thyer në nyjen rrënjë:

Page 393: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 393/699

Algoritmet dhe strukturat e të dhënave

393

Vazhdo me shoshitje:

Rregulla e pirgut është e plotësuar, shoshitja përfundon.

Page 394: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 394/699

Avni Rexhepi

394

Pirgu fillestar Pirgu pas insertimit të -2

 

Analiza e kompleksitetit

Kompleksiteti i insertimit është i rendit O(h), ku h është lartësia e pirgut (angl.,h-height). Duke pasur parasyshë pemën 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;

Page 395: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 395/699

Algoritmet dhe strukturat e të dhënave

395

siftUp(heapSize - 1);}

Largimi i minimumit nga pirgu

Operacioni i largimit përdorë idenë e ngjashme me atë të insertimit. Vlera errënjës, që është minimumi sipas rregullës së pirgut, zëvendësohet me vlerën efundit të vargut. Pastaj vlera e re shoshitet teposhtë, gjersa të arrijë në pozitën eduhur.

Algoritmi i largimit

1.  Kopjo vlerën nga pozita e fundit të vargut në pozitën e rrënjës;2.  Zvogëlo madhësinë e pirgut për 1;3.  Shoshitë teposhtë vlerën e rrënjës, si vijon:

o  nëse nyja aktuale nuk ka fëmijë, shoshitja ka përfunduar;o  nësë nyja aktuale ka një fëmijë: verifiko, nëse është prishur

rregulla e pirgut, atëherë shkëmbe vlerën e nyjes aktuale mëvlerën e fëmijës; shoshitë teposhtë fëmijën;

o  nëse nyja aktuale ka dy fëmijë: gjeje më të voglin prej tyre. Nëserregulla e pirgut është thyer, atëherë shkëmbe vlerën e nyjesaktuale me atë të fëmijës së zgjedhur; shoshitë teposhtë fëmijën.

Shembull

Largo minimumin nga pirgu vijues:

Kopjo vlerën e fundit të vargut në rrënjë dhe zvogëlo madhësinë e pirgut për 1:

Page 396: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 396/699

Avni Rexhepi

396

Tani rregulla e pirgut është thyer tek nyja rrënjë:

Rrënja ka dy fëmijë. Shkëmbe vlerën e rrënjës me vlerën më të vogël prejfëmijëve:

Page 397: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 397/699

Algoritmet dhe strukturat e të dhënave

397

Rregulla e pirgut është thyer në nyjen 1:

Rivendose rregullën e pirgut:

Page 398: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 398/699

Avni Rexhepi

398

 Nyja 3 nuk ka fëmijë. 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ëlartësia e pirgut, n është numri i elementeve në pirg.

Page 399: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 399/699

Algoritmet dhe strukturat e të dhënave

399

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

Page 400: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 400/699

Avni Rexhepi

400

Pemët e balansuara

Insertimi i vazhdueshëm i vlerave në pemën binare, mund të dërgojë nëinsertimin e vlerave të njëpasnjëshme në njërën anë të pemës, në rastet kur

insertohen vlera në renditje rritëse ose zvogëluese (që nuk është fenomen i pazakontë). Në këto raste lartësia e pemës është e papërshtatshme dhe krijonrastin më të keq të performansës së pemës për insertime. Kjo do të krijonte pëmë binare të pabalansuar. Shtrohet pyetja a ka mundësi që të dizajnohet pema binaree cila e ka të garantuar që ka lartësinë O(log h), pavarësisht prej rendit tëinsertimeve dhe fshirjeve. Ekzistojnë teknika të cilat përmirësojnë renditjen evlerave në pemë, duke bërë rireshtimin dhe ripozicionimin e nyjeve të tyre, meqëllim të krijimit të pemës së balansuar. Shembulli i parë dhe njëherit më ithjeshtë i pemës së balansuar, që ka rendin logaritmik për insertim, largim dhe

kërkim, është pema AVL (AVL Tree), (e emërtuar kështu sipas zbuluesve të saj:Adelson-Velskii dhe Landis).

Pemët AVL

Pemët AVL janë pemë me lartësi të balansuar në të dy anët. Idea është që nësecilën nyje duhet të përcjellim informacionin e balasimit, i cili tregondiferencën në lartësi ndërmjet nëndegës së majtë dhe të djathtë. Në pemën binare të balansuar perfekt (pemën komplete), të dy fëmijët e cilësdo nyje kanëlartësi të barabarta. Mirëpo, mirëmbajtja e pemës komplete është e ndërlikuar,sepse edhe një insertim i vetëm mund të shkaktojë çrregullime të mëdha nëstrukturën e pemës. Mirëpo, nuk është e domosdoshme të bëhet shumë përmirësim në këto raste. Në vend se të kërkohet që të dy fëmijët të kenë lartësisaktësisht të barabartë, kërkohet vetëm që diferenca mes tyre të jetë më sëshumti për një. Pema rezultuese quhet pemë AVL.

Pra, pema është e balansuar, nëse dhe vetëm nëse për secilën nyje, lartësia e dynënpemëve të saj, dallon më së shumti për 1.

Pemët AVL mirëmbajnë invariantën vijuese:

Kushti i balansimit AVL: për secilën nyje të pemës, lartësitë e nëndegës sëmajtë dhe të djathtë dallojnë për më së shumti 1. (Lartësia e nëndegëve nullështë e definuar që të jetë -1, sipas marrëveshjes).

 Në mënyrë që të mirëmbahet kushti i balansit, mund të shtohet një fushë e re, balansi në secilën nyje, e cila ruan diferencën e lartësisë së nëndegës (nënpemës)së majtë dhe asaj të djathtë.

 Në pemën AVL, ky numër do të jetë gjithmonë -1, 0 ose +1 (prandaj, mund tëruhet duke përdorur vetëm 2 bita për secilën nyje). Në vend të ruajtjes së fushëssë balansit për secilën nyje, në shembullin në vijim do të përdoret një metodë më

e thjeshtë, e cila do të ruaj lartësinë e nëndegëve. Para diskutimit të mirëmbajtjes

Page 401: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 401/699

Algoritmet dhe strukturat e të dhënave

401

së kushtit të balansimit, do të shqyrojmë pyetjen se a është ky kusht mjaft i fortë, për të garantuar që lartësia e pemës AVL me n nyje do të jetë O(log n). Për tëvërtetuar këtë, le të themi se N(h) paraqet numrin minimal të nyjeve që mund të jenë në një pemë AVL me lartësi h. Mund të gjenerohet rekurrenca për N(h).Ështe e qartë, që N(0)=1. Në përgjithësi N(h) do të jetë 1 (për rrënjën) plus N(hL) dhe N(hR ), ku hL dhe hR  janë lartësitë e dy nënpemëve. (L: për left-majtë;R për Right-djathtë). Pasi që pema e tërë ka lartësi h, njëra prej nënpemëveduhet të ketë lartësinë h-1, le të themi hL. për të bërë nënpemën tjetër sa më tëvogël që të jetë e mundur, e minimizojmë lartësinë e saj. Lartësia e saj mund të jetë jo më e vogël se h-2, pa e thyer kushtin AVL. Prandaj, kemi rekurrencën

 N(0) = 1

 N(h) = N(h−1) + N(h − 2) + 1 

Kjo rekurrencë nuk është e definuar mirë pasi që N(1) nuk mund të llogaritet prej këtyre rregulave, kështu që e shtojmë rastin plotësues N(1)=2. Kjorekurrencë duket shumë e ngjashme me rekurrencën Fibonacci (F(h) = F(h-1)=F(h-2)). Në fakt, është argumentuar se:

h

h N   

  

  

2

51)(  

Vlera 618.12/)51(     është ‘raporti i artë’ i famshëm (faktori i artë).

Prandaj, duke invertuar këtë, gjejmë se lartësia e rastit më të keq për pemët AVLme n nyje është përafërsisht logn , ku   është ‘raporti i artë’.  Nga kjo del se

 pema AVL me lartësi H, ka së paku (afërsisht) H+3/   5 nyje. prandaj, thellësiaështë më së shumti logaritmike. Për lartësinë e pemës AVL plotësohet:

H < 1.44 log (N+2)-1.328

Kështu që lartësia e rastit më të keq është përafërsisht 44% më shumë sesaminimumi i mundshëm për pemët binare.

Mbetet të paraqitet se si të kryhen insertimet dhe fshirjet në pemët AGL dhe si tërestaurohet kushti i balansit AVL pas secilit insertim ose fshirje.

Page 402: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 402/699

Avni Rexhepi

402

Rasti LL –  Rotacioni djathtas

(Left Left Case –  Right Rotation)

Rasti RR –  Rotacioni majtas

(Right Right Case – Left Rotation) 

Rasti LR

(Left Right Case)

Rotacioni majtas (Left Rotation)

Rasti RL

(Right Left Case)

Rotacioni djathtas (Right Rotation) 

Page 403: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 403/699

Algoritmet dhe strukturat e të dhënave

403

Rotacioni djathtas (Right Rotation)  Rotacioni majtas (Left Rotation) 

Insertimi 

Rregulla e insertimit për pemët AVL fillon saktësisht si regulla e insertimit për pemët binare të kërkimit, por pas insertimit të nyjes në nënpemë, duhet të pyetetnëse pema është bërë e pabalansuar. Nëse është bërë, atëherë duhet të kryhethapi i ribalansimit.

Vetë ribalansimi është operacion i pastër lokal (kjo do të thotë, nëse nevojitetkohë konstante dhe veprime në nyjet e afërta), por kërkon kujdes. Operacioni bazik që kryhet, quhet ‘rotacion’  ose ‘rotacion i njëfishtë’ (angl. single rotation). Tipi i rotacionit varet nga natyra e çekuilibrit (jobalansimit). Le të supozojmë se burim i jobalansit është fakti se nënpema e majtë e fëmijës së majtë është shumëe thellë (Edhe rasti i nënpemës së djathtë trajtohet simetrikisht). Operacioni ikryer në këtë rast, si në figurën vijuese, është ‘rotacion i njëfishtë i djathtë’.Vëreni se si ndryshojnë faktorët e balansit pasi që është kryer ky rotacion.Lartësitë e nënpemëve ‘b’ dhe ‘d’ tani janë të barabarta mes veti.

Page 404: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 404/699

Avni Rexhepi

404

 Fig.6.32 –  Rotacion i njëfishtë

 Në anën tjetër, supozojmë se “nipi” (f ëmija i fëmijës) i rëndë është nënpema edjathtë e nënpemës së majtë (përsëri nënpema e majtë e fëmijës së djathtë ështësimetrike). Në këtë rastë, rotacioni i njëfishtë nuk do të jetë i mjaftueshëm për të përmirësuar jobalansin. Mirëpo, dy rotacione do ta arrijnë këtë. Shikoni figurënvijuese. Në veçanti, bëhet rotacioni i majtë në ‘nipin’ nipin ‘e majtë-të djathtë’(fëmija i djathtë i fëmijës së majtë) dhe pastaj rotacion të djathtë në fëmijën emajtë, për të rivendosur balansin. Ky operacion quhet ‘rotacion i dyfishtë ’(angl. double rotation). (Në figurë paraqiten faktorët e balansit për të treguarvlerat e mundshme, të cilat duhet të azhurohen).

 Fig. 6.33 –  Rotacion i dyfishtë majtas-djathtas

Pra, në përgjithësi rastet e mundshme të ribalansimit janë:

-  rotacion i njëfishtë i majtë, L

-  rotacion i njëfishtë i djathtë, R

Page 405: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 405/699

Algoritmet dhe strukturat e të dhënave

405

-  rotacion i dyfishtë (majtë-djathtë) LR-  rotacion i dyfishtë (djathtë-majtë) RL

Vetëm për nyjet që ndodhen në rrugën/shtegun prej rrënjës deri tek pika e

insertimit, mund të ndryshohen faktorët e balansimit.Fshirja

Fshirja/largimi është i ngjashëm me insertimit në atë se fillohet me aplikimin ealgoritmit të fshirjes për pemën binare të pabalansuar. Kjo përfshinë tri raste:gjethen, fëmijën e vetëm dhe dy fëmijë. Në rastin e dy fëmijëve, duhet të gjindetçelësi zëvendësues. Pasi të jetë kryer fshirja, kalojmë nëpër pemë (duke u kthyernga thirrjet rekurzive), duke azhuruar faktorët e balansimit (ose lartësitë) gjatëkalimit. Sa herë që haset ndonjë nyje e pabalansuar, aplikohet rotacioni inevojshëm, për të sanuar situatën.

Supozojmë se duhet të fshihet një çelës (nyje, vlerë) nga nënpema e majtë dhe sirezultat i kësaj lartësia e nënpemës është zvogëluar për një, por kjo ka shkaktuarqë ndonjë para-ardhës (nyje para-ardhëse), të thyej kushtin e balansit. Janë dyraste të mundshme. Së pari, nëse faktori i balansit për fëmijën e djathtë është ose0 ose +1 (nuk peshon majtas), mund të bëhet rotacion i njëfishtë, si në figurënvijuese. Ka disa mundësi të faktorëve të balansimit.

 Fig. 6.34 –  Rotacion i njëfishtë për fshirje 

 Në anën tjetër, nëse fëmija i djathtë ka faktor të balansit -1 (peshon majtas)atëherë duhet të bëjmë rotacion të dyfishtë, si në figurën vijuese.

Page 406: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 406/699

Avni Rexhepi

406

 Fig. 6.35 –  Rotacion i dyfishtë për fshirje 

 Në vijim do të paraqitet një rast i kompletuar, si në figurë. Së pari fshihet nyja1. Kjo shkakton që nyja 2 të jetë e pabalansuar. Atëherë, kryhet rotacion injëfishtë i majtë në nyjën 2. Mirëpo, tani rrënja bëhet e pabalansuar. Tash, bëhetrotacion i dyfishtë djathtas-majtas, në rrënjë.

 Fig. 6.36 –  Shembull i fshirjes në pemën AVL

Page 407: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 407/699

Algoritmet dhe strukturat e të dhënave

407

Fshirja e zvarritur

 Numri i rasteve të ndryshme të fshirjes është shumë i madh. Ndonjëherë mundtë përdoret një alternativë e fshirjes, e quajtur fshirja e zvarritur ose fshirje

 përtace. Idea është që të mos shkohet në procesin e fshirjes së plotë të nyjeve, por për secilën nyje të shtohet një informacion plotësues, një vlerë bool-eane ecila tregon se a është nyja ‘e gjallë’  apo ‘e vdekur ’. Kur të fshihet një çelës(vlerë), thjeshtë e deklarojmë të ‘vdekur ’, por e lëmë në pemë. Nëse bëhet njëtentim për të insertuar të njejtin çelës përsëri, atëherë nyja bëhet përsëri ‘egjallë’. Natyrisht, pas një seri të gjatë të fshirjeve dhe insertimeve, është emundur që pema të ketë shumë nyje ‘të vdekura’. Për të përmirësuar këtë , nëmënyrë periodike kryhet faza e pastrimit të mbeturinave (angl. garbagecollection phase), e cila e përshkon pemën, duke zgjedhur vetëm elementet ‘egjalla’ dhe pastaj duke ndërtuar pemën e rë AVL, vetëm me këto elemente. 

Shembull i ndërtimit i pemës së balansuar

Për të gjetur lartësin maksimale ‘h’ për të gjitha pemët e balansuara me ‘n’ nyje,le të shqyrtojmë lartësinë ‘h’ dhe le të provojmë të ndërtojmë pemën e balansuarme numrin maksimal të nyjeve. Kjo strategji rekomantohet pasi që si në rastin elartësisë minimale, vlera mund të përfitohet për disa vlera specifike të n-it. Le të jetë pema me lartësi h, e emërtuar Th. Qartazi, T0 është pema e zbrazët, ndërsaT1, është pema me një nyje të vetme (rrënjën). Për të konstruktuar pemën Th përh>1, do të sigurojmë nyjen me dy nëndegë të cilat përsëri kanë numër minimal

të nyjeve. Rrjedhimisht, nënpemët janë poashtu T-ja. Është evidente se njënënpemë duhet të ketë lartësinë h-1 dhe tjetra pastaj lejohet të ketë lartësinë përnjë më të vogël, h-2. Në figurën vijuese janë paraqitur pemët me lartësi 2, 3 dhe4. Pasi që principi i kompozimit të tyre i përngjanë numrave Fibonacci, këtoquhen ‘Pemët Fibonacci’. Këto pemë definohen si: 

1.  Pema e zbrazët është Pema-Fibonacci me lartësi 0.2.   Një nyje e vetme është Pemë-Fibonacci me lartësi 1.3.   Nëse Th-1 dhe Th-2 janë Pemë-Fibonacci me lartësitë h-1 dhe h-2,

atëherë Th=<Th-1, x, Th-2> janë Pemë-Fibonacci.

4.   Nuk ka pemë të tjera që janë Pemë-Fibonacci.

Page 408: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 408/699

Avni Rexhepi

408

 Fig. 6.37 –  Pemët-Fibonacci me lartësi 2, 3 dhe 4. 

 Numri i nyjeve në Th është i definuar përmes relacionit të thjeshtë vijues tërekurrëncës:

 N0 = 0, N1 = 1

 Nh = Nh-1 + 1 + Nh-2 

 Numrat Ni  janë ata nymra të nyjeve për të cilët mund të arrihet rasti më i keq(kufiri i epërm 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ë pëmn e

 balansuar. Me nyjen e dhënë r, me nënpemën e majtë L dhe të djathtë R, mundtë dallohen restet vijuese. Supozojmë se nyja e re insertohet në L, dukeshkaktuar rritjen e lartësisë së saj për 1:

1.  hL=hR: L dhe R bëhen me lartësi jo të barabarta, por kriteri i balansitnuk prishet.

2.  hL<hR: L dhe R fitojnë lartësi të barabartë, d.m.th., balansi përmirësohet.

3.  hL>hR: kriteri i balansit prishet dhe pema duhet të ristrukturohet.

Shqyrtoni pemën në figurën vijuese (Fig. 6.38). Nyjet me çelës 9 dhe 11 (që dotë shoheshin si fëmijë të nyjes 10) mund të insertohen pa ribalansim. Pema merrënjë 10 do të bëhej një-anëshe (rasti 1); Nyjes 8 do t’i përmirësohet balansi(rasti 2).

Page 409: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 409/699

Algoritmet dhe strukturat e të dhënave

409

 Fig. 6.38 –  Pema e balansuar.

Insertimi i nyjeve, 1, 3, 5, ose 7, sidoqoftë, do të kërkonte ribalansim pasues (1,3si fëmijë të 2, kurse 5,7, si fëmijë të 6).

 Një vëzhim i kujdesshëm i situatës do të zbulonte se janë vetëm dy

konstelacione të ndryshme të cilat kërkojnë trajtim individual. Të tjerat, mund tënxirren përmes shqyrtimit simetrik të dy të parave. Rasti i parë karakterizohetme insertimin e nyjeve 1 ose 3. Rasti i dytë, me insertimin e nyjeve 5 ose 7.

Të dy rastet janë përgjithsuar në figurën vijuese (Fig. 6.39), në të cilën formatdrejtkëndëshe paraqesin nënpemët dhe lartësia e shtuar nga insertimi, ështëshënuar me x. Transformimet e thjeshta (si më herët) të të dy strukturaverestaurojnë balansin. Rezultati i tyre është paraqitur në figurë. Vëreni që lëvizjete vetme të lejuara janë ato që ndodhin në drejtimin vertikal, ndërsa pozitatrelative horizontale të nyjeve të paraqitura dhe të nënpemëve, mbesin të

 pandryshuara.

 Fig. 6.39a - Çekuilibri (jobalansi) i shkaktuar prej insertimit - Rasti 1.

Page 410: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 410/699

Avni Rexhepi

410

 Fig. 6.39b - Çekuilibri (jobalansi) i shkaktuar prej insertimit - Rasti 2.

Algoritmi për insertim dhe ribalansim varet nga mënyra se si ruhet informacioni

 për balansin e pemës. Një opcion është që edhe informacioni i balansit të jetë pjese e vetë strukturës së pemës. Në këtë rasit, faktori i balansit duhet tërillogaritet sa herë që të ketë insertim ose fshirje në pemë, gjë që shkaktonmbingarkim. Një zgjidhje tjetër poashtu është që shtohet një faktor eksplicit i balansit për secilën nyje.

 Nëse faktorin e balansit të nyjes e interpretojmë si lartësia e nënpemës së saj tëdjathtë minus lartësia e nënpemës së saj të djathtë, atëherë do të kemi algoritminvijues për këtë tip të nyjes. Procesi i insertimit të nyjes konsiston esencialisht nëtri pjesët e njëpasnjëshme vijuese:

1.  Përcille shtegun e kërkimit deri sa të vërtetohet që nyja veç nuk është në pemë (nuk ekziston paraprakisht).

2.  Inserto nyjen e re dhe përcakto faktorin rezultues të balansit.3.  Kthehu prapa përgjatë shtegut të kërkimit dhe verifiko faktorët e balansit

në secilën nyje. Nëse është e nevojshme, ribalanso.

Kjo procedurë përshkruan operacionin e duhur të kërkimit në secilën nyje dhe për shkak të formulimit rekurziv të saj mund të akomodojë me lehtësioperacionet plotësuese në rrugën e kthimit prapa, përgjatë shtegut të kërkimit. Në secilin hap, duhet të përcillet nëse informacioni nëse është rritur lartësia e

nënpemës (në të cilën është kryer insertimi). Për këtë arsye, shtohet një parametër bool-ean h, me kuptimin që lartësia e nënpemës është rritur. Qartazi,h duhet të shënojë parametër variabil pasi që përdoret për të transmetuarrezultat.

Supozojmë tani se procesi është duke u kthyer në nyjen p^ nga dega e majtë(shiko figurën 6.39), me indikacionin se lartësia e saj është rritur. Tani duhet tëdallojmë ndërmjet tri kushteve të cilat përfshijnë lartësitë e nënpemëve parainsertimit:

1. hL < hR , p.bal = +1, jobalansi paraprak është ekuilibruar.

Page 411: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 411/699

Algoritmet dhe strukturat e të dhënave

411

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ë rrënjës së nënpemës së majtë

(le të themi p1.balansi) përcakton nëse është prezent rasti 1 ose rasti 2 i figurës6.39. Nëse ajo nyje ka edhe nënpemët të majtë më të lartë se e djathta, atëherëkemi të bëjmë me rastin 1, përndryshe me rastin 2. (Merrni se në këtë rast nukmund të ndodhë nënpema e majtë me faktorë balansi të barabartë me 0 nërrënjën e saj). Operacionet e nevojshme të rebalansimit janë tërësisht tëshprehura si sekuenca të ricaktimeve të pointerëve. në fakt, pointerëtndryshohen në mënyrë ciklike, duke rezultuar ose në rotacion të njëfishtë ose nërotacion të dyfishtë të dy ose tri nyjeve të përfshira. Si plotësim i rotacionit të pointerëve, duhet të bëhet edhe azhurimi i faktorëve të balansit.

Principi i punës, është paraqitur në figurën vijuese. Shqyrtoni rastin e pemës binare (a) e cila përbëhet prej dy nyjeve. Insertimi i nyjes me ‘key’ (çelës) 7,rezulton në pemë të pabalansuar. Nevojitet rotacioni i njëfishtë RR, i cilirezulton në pemën e balansuar në mënyrë perfekte (b). Insertimi i mëtejshëm inyjeve 2 dhe 1 rezulton në jobalans të nëndegës me rrënjë 4. Kjo nëndegë balansohet me rotacion të njëfishtë LL (d). Iinsertimi vijues i nyjes 3 menjëherëzhvendosë kriterin e balansi në nyjen rrënjë 5. Balansi këtu rikthehet merotacion të dyfishtë LR dhe rezultati është pemë (e). Kandidati i vetëm për tëhumbur balansin në insertimin vijues është nyja 5. Vërtetë, insertimi i nyjes 6

kërkon rastin e ribalansimit të dyfishtë RL. Pema finale është paraqitur nën (f).

 Fig. 6.40 –  Insertimet në pemën e balansuar

Parashtrohen dy pyetje lidhur me performansën e algoritmit të insertimit në pemën e balansuar:

Page 412: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 412/699

Avni Rexhepi

412

1.   Nëse të gjitha n! permutacione e n çelësave ndodhin me probabilitet të barabartë, sa është lartësia e pritur e pemës së balansuar të konstruktuar?

2.  Sa është probabiliteti që një insertim kërkon ribalansim?

Analiza matematike e këtij algoritmi është e komplikuar. Testet empirike përkrahin supozimin se lartësia e pritur e pemës së balansuar të gjeneruar ështëh=log(n)+c, ku c është konstante e vogël (c≈0.25). Kjo nënkupton që në praktikë, pema e balansuar AVL sillet aq mirë sa pema perfekt e balansuar, edhe pse është shumë më e thjeshtë për t’u mir ëmbajtur. Poashtu, evidenca empirikesygjeron që mesatarisht, rebalansimi është i nevojshëm në përafërsisht çdo dyinsertime. Rotacioni i njëfishtë dhe i dyfishtë janë me gjasa të barabarta.Shembulli i prezentuar është zgjedhur me qëllim që të demonstrohen sa mëshumë rotacione të mundshme në numrin minimal të insertimeve.

Kompleksiteti i operacioneve të balansimit sygjeron që pemët e balansuaraduhet të përdoren vetëm nëse leximet janë shumë më të shpeshta se insertimet.Kjo është posaqërisht e vërtetë pasi që nyjet e pemëve të tilla zakonishtimplementohen në struktura sa më dendur që të jetë e mundur, për tëekonimizuar hapësirën. Shpejtësia e qasjes dhe e azhurimit të faktorëve të balansimit –  ku secila kërkon vetëm dy bita  –  rrjedhimisht është shpesh faktorivendimtar i efikasitetit të operacioneve të ribalansimit. Vlerësimet empiriketregojnë se pemët e balansuara humbin shumë atraktivitetin e tyrë nëse është idomosdoshëm paketimi i ngjeshur i të dhënave. Vëretë është e vështiër që tëmundet algoritmi i thjeshtë dhe i drejtpërdrejtë i insertimit.

Fshirja në pemën e balansuar

Përvoja ka treguar se fshirja në rastin e pemës së balansuar do të jetë më ekomplikaur se insertimi, megjithëse operacionet e rebalansimit mbesin nëesencë të njëjta, si për rastin e insertimit. Në veçanti, ribalansimi konsiston përsëri në rotacion të njëfishtë ose të dyfishtë.

Bazë për fshirje në pemën e balansuar mbetet algoritmi i fshirjes. Rastet e lehta janë nyjeve fundore (gjethet) dhe nyjet me vetëm një pasardhës (fëmijë). Nësenyja që duhet të fshihet ka dy nënpemë, përsëri ajo do të zëvendësohet me nyjenmë të djathtë të nënpemës së saj të majtë. Sikur në rastin e insertimit, shtohet një parametër bool-ean me kuptimin “lartësia e nënpemës është zvogëluar”.Ribalansimi duhet të konsiderohet vetëm nëse vlera e tij është ‘true’.

Operimi i procedurës është ilustruar në figurën vijuese (Fig. 6.41). Me pemën edhëne të balansuar (a), fshirja sukcesive (e njëpasnjëshme) e nyjeve 4, 8, 6, 5, 2,1 dhe 7, rezulton në pemët (b)...(h). Fshirja e nyjes 4 është e thjeshtë vetvetiu,sepse ajo paraqet nyje terminale (gjethe). Mirëpo, kjo rezulton në nyjen e pabalansuar 3. Operacioni i saj i ribalansimit përfshinë një rotacion të njëfishtë

LL. Ribalansimi bëhet përsëri i nevojshëm pas fshirjes së nyjes 6. Këtë herë,

Page 413: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 413/699

Algoritmet dhe strukturat e të dhënave

413

nënpema e rrënjës 7 ribalansohet me një rotacion të njëfishtë RR. Fshirja e nyjes2, edhe pse në vete e drejtpërdrejtë pasi që ka vetëm në pasardhës, shkaktonrotacion të dyfishtë RL. Rasti i katërt, rotacioni i dyfishtë LR, thirret në fund paslargimit të nyjes 7, e cila së pari është zëvendësuar me elementin e saj më tëdjathtë të nënpemës së majtë, d.m.th., nga nyja 3.

 Fig. 6.41 –  Fshirjet në pemën e balansuar

Page 414: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 414/699

Avni Rexhepi

414

Edhe fshirja e një elementi në pemën e balansuar mund të kryhet me O(log n)operacione, në rastin më të keq. Megjithatë, nuk duhet të mos vihet re një dallimesencial ndërmjet insertimit dhe fshirjes. Gjersa insertimi i një çelësi mund tërezultojë në më së shumti një rotacion (të dy ose tri nyjeve), fshirja mund tëkërkojë rotacion në secilën nyje përgjatë shtegut të kërkimit. Për shembull,fshirja e nyjes më të djathtë të Pemës-Fibonacci kërkon numrin maksimal tërotacioneve, sepse fshirja e secilës nyje dërgon në zvogëlimin e lartësisë së pemës. Prandaj, kjo reprezenton zgjidhjen më të keqe të nyjes në rastin më tëkeq të pemës së balansuar, që është një kombinim i pafat i gjasave.

Sa është probabiliteti i rotacioneve në përgjithësi? Rezultatet befasuese tëtesteve empirike tregojnë se ndërsa një rotacion kërkohet për afërsisht çdo dyinsertime, një rotacion kërkohet për vetëm çdo të pestën fshirje. Fshirja në pemën e balansuar pra është përafërsisht poaq e lehtë ose poaq e komplikuar sainsertimit.

Implementimi i zakonshëm i AVL pemëve nuk është jashtëzakonisht kompleks, por problemi është që nuk është efikas. Pas pemëve AVL, janë zbuluar metodamë të mira të balansimit të pemëve, kështu që implementimi i pemëve AVL nukia 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 nwlexim 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 oseinterpreterit).

Pemët kuq e zi

Pema kuq e zi është strukturë e të dhënave e cila është e tipit pemë binare vetë- balansuese. Vetë-balansimi sigurohet përmes ngjyrosjes së nyjeve me njërën prej dy ngjyrave (zakonisht ‘kuq’ dhe ‘zi’), në mënyrë që rezultati i pemës së

vizatuar/shtypur plotëson disa tipare të cilat nuk e lejojnë pemën që të bëhet eshumë e pabalansuar. Kur të modifikohet pema, pema e re riaranzhohet dheringjyroset për të rivendosur tiparet e ngjyrave. Tiparet janë të dizajnuara ashtuqë riaranzhimi dhe ringjyrosja të mund të kryhen në mënyrë efikase.

Balansimi i pemës nuk është perfekt, por është mjaftueshëm i mirë për tëgarantuar kërkimin në kohë O(log n), ku ‘n’ është numri i nyjeve në pemë.Insertimi dhe fshirja, përgjatë riaranzhimit dhe ringjyrosjes së pemës, poashtukryhen në kohë O(log n).

Page 415: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 415/699

Algoritmet dhe strukturat e të dhënave

415

Përcjellja e ngjyrës për secilën nyje kërkon 1 bit të informacioit për nyje, sepse janë vetëm dy ngjyra. Pema kuq-e-zi nuk përmbanë ndonjë të dhënë tjetërspecifike për të qenit pemë kuq-e-zi, kështu që “gjurma” e saj në memorie është pothuajse identike me pemën 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 pemës kuq e zi

Pemët kuq e zi i ka zbuluar Rudolf Bayer, në vitin 1972, i cili në fillim i kishtequajtur “B-Trees”, kurse në vitin 1978, Leonidas J. Guibas dhe Robert

Sedgewick, në  punimin e tyre “A dichromatik frameëork for balanced trees”, i përdorën ngjyrat kuq e zi (pasi që dukeshin më së miri në printerin e tyre, që e përdornin gjatë punës, Xerox Parc) dhe prej atëherë u quajtën me emrin e ri (... pra nuk ka të bëjë me shqiptarët!)

Pemët kuq e zi, janë tip special i pemëve binare dhe përdoren për të organizuartë dhënat e krahasueshme, si fragmentet e teksteve ose numrat.

 Nyjet gjethe të pemës kuq-e-zi nuk përmbajnë të dhëna. Këto gjethe nuk duhettë jenë në memorie në mënyrë eksplicite  –   një pointer fëmijë “NULL” (ose

 përdoret edhe versioni “NIL), mund të paraqesë faktin se fëmija është gjethe  –   por kjo i thjeshton disa algoritme që operojnë në pemët kuq-e-zi, nëse gjethet janë realisht nyje eksplicite. Për të ruajtur memorien, ndonjëherë një nyje evetme “sentine” (angl. sentine-roje, rojëtar), kryen rolin për të gjitha gjethet,ashtu që të gjitha referencat prej nyjeve interne në ato gjethe, pointojnë në nyjen‘sentinel’. (Nyja ‘sentinel’, shërben si përfundim dhe nuk përmbanë e as nukreferon ndonjë të dhënë të menagjuar nga strutura e të dhënave. Përdoren sialternativë ndaj përdorimit të “NULL”, për të përfituar në shpejtësi të kryerjes sëoperacioneve, redukim të kompleksitetit të algoritmit dhe madhësisë së koditdhe për të krijuar strukturë më robuste (angl. robust-i fortë, i fuqishëm)).

Page 416: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 416/699

Avni Rexhepi

416

Sikur pemët binare të kërkimit, edhe pemët kuq-e-zi mundësojnë përshkiminefikas ‘in-order’ të elementeve të tyre (pra: majtas-rrënja-djathtas). Koha ekërkimit rezulton prej përshkimit prej rrënjës në gjethe deh prandaj pema e balansuar me ‘n’ nyje, që ka lartësinë më të vogël të mundshme, rezulton nëkohë të kërkimit O(log n).

Tiparet e pemëve kuq e zi

Përveq kërkesave të imponuara nga pema binare e kërkimit, pema kuq-e-ziduhet t’i plotësojë edhe këto kushte:

1.   Nyja është e kuqe ose e zezë.2.  Rrënja është e zezë. (kjo nganjëherë evitohet, pasi rrënja mund të

ndryshohet kurdo nga e kuqe në të zezë, por jo domosdo edheanasnjelltas, kështu që kjo rregull nuk ka efekt në analizë).

3.  Të gjitha nyjet “NIL” janë të zeza (Të gjitha gjethet kanë ngjyrë të njëjtëme rrënjën).

4.  Secila nyje e kuqe duhet të ketë dy nyje fëmijë të zeza.5.  Secili shteg prej nyjes së dhënë deri tek cilado nyje pasardhëse përmbanë

numër të njëjtë të nyjeve të zeza.

Këto kufizime detyrojën (shtrëngojnë) tiparin kritik të pemëve kuq-e-zi, qështegu prej rrënjës deri tek gjethja më e largët nuk është më shumë se dyfish igjatë sa shtegu prej rrënjës deri tek gjethja më e afërt. Rezultat i kësaj është që

 pema është përafërsisht e balansuar për nga lartësia. Pasi që operacionet siinsertimi, fshirja dhe kërkimi kërkojnë kohë të rastit më të keq proporcionale melartësinë e pemës, ky kufi i epërm teorik në lartësi, u mundëson pemëve kuq-e-ziqë të jenë efikase edhe në rastin më të keq, për dallim prej pemëve binare tëkërkimit.

Për të kuptuar përse kjo është e garantuar, mjafton të shqyrtohen tiparet 4 dhe 5së bashku. Për një pemë binare T, le të jetë B numri i nyjeve të zeza në tiparin 5.Le të jetë shtegu më i shkurtër prej rrënjës 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. Mirëpo, tipari 4 e bën të pamundur që të insertohet më shumë senjë nyje e njëpasnjëshme e kuqe. Prandaj, shtegu më i gjatë i mundshëm përbëhet prej 2B nyjeve, një e kuqe një e zezë.

Shtegu më i shkurtër i mundshmë i ka të gjitha nyjet të zeza dhe shtegu më igjatë i mudnshëm kalon nëpër nyjet e kuqe dhe të zeza. Pasi që të gjitha shtigjetmaksimale kanë numër të njëjtë të nyjeve të zeza, tipari 5, kjo tregon se asnjështeg nuk është më shumë se dyfish më i gjatë se ndonjë shteg tjetër.

Page 417: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 417/699

Algoritmet dhe strukturat e të dhënave

417

Aplikimet dhe strukturat e ndërlidhura të të dhënave

Pema kuq e zi garanton për kohën e rastit më të keq për insertim, fshirje dhekërkim. Kjo jo vetëm që i bën ato të vlefshme për aplikacione sensitive në

aspektin kohor, si aplikacionet në kohë reale (angl. real-time applicacions), por i bën edhe si blloqe të dobishme ndërtimi në strukturat tjera të të dhënave të cilatofrojnë garacë për rastin më të keq. Për shembull shumë struktura të të dhënavetë përdorura në gjeometrinë llogaritëse mund të bazohen në mepët kuq-e-zi.Edhe “Completely Fair Scheduler” i përdorur në kernel të Linux-it, përdorë pemët kuq-e-zi.

Pemët kuq-e-zi poashtu janë shumë të rëndësishme në programimin funksional,ku ato janë struktura më e zakonshme persistente e të dhënave, e përdorur përndërtimin e vargjeve asiciative dhe seteve, të cilat mund të rifitojnë versionet e

mëparshme, pas ndryshimeve. Në vitin 2008, Sedgewick prezentoi versionin e thjeshtësuar të pemëve kuq-e-zitë quajtur “pema kuq-e-zi me tendencë majtas” (angl. Left-leaning red-blacktree-LLRB), duke eliminuar shkallën e mëhershme të paspecifikuar të lirisë nëimplementim. Versioni LLRB ka edhe një invariantë plotësuese që të gjithalidhjet e kuqe duhet të anojnë majtas, përveç gjatë insertimit dhe fshirjes. Edhe pema tango (angl. tango tree), që është një tip i pemës së optimizuar për kërkimetë shpejta, zakonisht përdorë pemët kuq-e-zi si pjesë të strukturës së saj të tëdhënave.

Operacionet në pemët kuq-e-zi

Sikur në rastin e pemëve binare, insertimet dhe fshirjet nga pema kuq-e-zi i prishin tiparet e pemës kuq-e-zi, kështu që duhet të bëhet restaurimi, për çka përdoren operacionet përmatëse.

Rotacionet

Rotacioni është operacion lokal, i cili ruan renditjen e çelësave për përshkiminin-order.

 Fig. 6.43 –  Rotacionet

Page 418: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 418/699

Avni Rexhepi

418

Vëreni 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/rotacionleft_rotate( Tree T, node x ){

node y;y = x->right;/* Shnderro nën-pemën e majtë të y,

në nën-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 nëse ndodhemi në rrënjë */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 përfshinë një numër të rasteve. Fillohet meinsertimin e nyjes së re ‘x’ në pemë, sikur për çdo pemë të zakonshme binare,duke përdorur funksionin për insertim. Nyja e re etiketohet e kuqe dhe me gjasëe prishtë tiparin kuq-e-zi. Unaza kryesore lëvizë përjpetë pemës, për tërivendosur tiparin kuq-e-zi.

//parent=prindi,//rb_insert=(insertimi_kuq-e-zi)//red=kuq, black=zirb_insert( Tree T, node x ){

/* Inserto në pemë në mënyrën e zakonshme */tree_insert( T, x );

Page 419: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 419/699

Algoritmet dhe strukturat e të dhënave

419

/* Tani, restauro tiparin kuq-e-zi */x->colour = red;while ( (x != T->root) && (x->parent->colour == red) ) {

if ( x->parent == x->parent->parent->left ) {

/* Nëse 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;/* Lëvize x-in përpjetë pemës */x = x->parent->parent;}

else {/* y është nyje e zezë */if ( x == x->parent->right ) {

/* dhe x është në të djathtë *//* rasti 2 – lëvize 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 {/* përsërtite pjesën me kushtin ‘if’ me djatht-as

dhe majt-as të ndryshuara/shkëmbyera*/}

/* Ngjyrose rrënjën me të zezë */T->root->colour = black;}

 Nga kodi mund të shihet se kemi vetëm një unazë. Në atë unazë, nyja në rrënjë

të nënpemës për të cilën tentohet të restaurohet (rivendoset) tipari kuq-e-zi,mund të lëvizet te lartë (përpjetë), së paku një nivel në secilin iteracion tëunazës. Pasi që pema ka lartësi O(log n), ka O(log n) iteracione. Funksionitree_insert ka poashtu kompleksitet O(log n), kështu që në përgjithësi, funksionirb_insert ka poashtu kompleksitetin O(log n).

Shembull:

Ja një shembull i insertimit në pemwn kuq-e-zi: (Cormen, p269).

Page 420: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 420/699

Avni Rexhepi

420

Pema fillestare .. Në vazhdim, gjethet (‘sentinel’)s’do të paraqiten, për thjeshtësi të

figurave.

Është thirrur funksioni përinsertim, për të insertuar në pemënyjen ‘4’.

Kjo më nuk është pemë kuq-e-zi,sepse në shtegun 11 - 2 - 7 - 5 - 4ka dy nyje të kuqe.

Shëno nyjen e re me ‘x’, dhe‘axhën’ e saj (vëllaun e prindit)me y. y është i kuq (rasti 1) ...

 Ndrysho ngjyrat e nyjeve 4, 7 dhe 8.

Lëvize nyjen x-in te lartë deri tekgjyshi i saj, 7.

Prindi i x-it (2) është akoma i kuq,kështu që prishet tipari kuq-e-zi.

Shëno ‘axhën’ me, y.

 Në këtë rast, axha është mengjyrë të zezë, rasti 2 ...

Page 421: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 421/699

Algoritmet dhe strukturat e të dhënave

421

Lëvize x-in te lartë dhë rrotullomajtas.

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 dherrotullo djathtas...

Tani kemi pemë kuq-e-zi, kështuqë kemi përdunduar!

 Koha O( logn) !

Algoritmet paralele

Page 422: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 422/699

Avni Rexhepi

422

Algoritmet paralele për konstruktimin e pemëve kuq-e-zi prej listave të sortuaratë elementeve mund të ekzekutohen në kohë konstante O(log log n), varësisht prej modelit të kompjuterit, nëse numri i procesorëve është proporcional menumrin e elementeve. Janë të njohur edhe algoritmet paralele për kërkim,insertim dhe fshirje të shpejtë paralele.

Page 423: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 423/699

Algoritmet dhe strukturat e të dhënave

423

Splay Trees

Pema “Splay” (angl. Splay Tree, angl. splay-i gjerë, i anuar, i pjerrët, i hapurnga jashtë, etj) është pemë binare vetë-rregulluese (vetë-përshtatëse) me një

tipar plotësues që elementet e qasura së fundi janë të shpejtë për qasje tësërishme. Kjo pemë i kryen operacionet themelore të insertimit, kërkimit dhefshirjes në kohë O(long n) të amortizuar. Për shumë sekuenca të operacioneve jotë rastësishme, “splay tree” performon më mirë se cilado pemë tjetër e kërkimit,edhe kur mostra specifike e sekuencës është e panjohur. Pema ‘splay’ ështëzbuluar nga Daniel Dominic Sleator dhe Robert Endre Tarjan, në vitin 1985.

Të gjitha operacionet normale në pemën e kërkimit binar janë të kombinuara menjë operacion themelor, të quajtur “splaying” (angl. splaying-zgjerim, hapje nga jashtë). Splay-imi i pemës për një element të caktuar rirregullon pemën ashtu që

elementi vendoset në rrënjë të pemës. Një mënyrë për të bërë këtë është që së pari të kryhet kërkimi standard i pemës binare për elementin në fjalë, e pastaj të përdoren rotacionet e pemës në një model specifik, për t’a sjellur atë element nërrënjë. Në formë alternative, algoritmi “top-doën” (nga lartë-teposhtë) mund tëkombinojë kërkimin dhe rioarganizimin e pemës në një fazë të vetme.

Rikujtojmë se në diskutimin për pemët binare, të cilat kanë vetinë e mirë që nësevlerat insertohen dhe fshihen me rastësi, atëherë kohët e pritshme për insertimdhe fshirje janë të rendit O(log n). Pasi që skenaret e rastit më të keq mund tëdërgonin në sjellje O(n), ishim të udhëhequr kah idea e pemës së balansuar për

kah lartësia, pema AVL, e cila garantonte kohë O(log n) për të gjithaoperacionet, sepe mirëmbante balansin e pemës gjatë tërë kohës. Operacionetthemelore të cilat mirëmbanin balansin e pemës quheshin rotacione (çfarëdo që

ishin, të njëfishta ose të dyfishta).

E metë e pemëve AVL ishte se duhet të mbahej informacioni për balansin nëpërnyje dhe funksionet e azhurimit të pemës AVL ishin mjaft të komplikuara (më tëkomplikuara sesa që dëshirohet zakonisht).

Pema “splay” poashtu përdorë rotacionet për të përshtatur pemën. Mirëpo, pasi

që  pema ‘splay’ nuk ka informacion të balansit, është e mundur të krijohen pemë të  pabalansuara ‘splay’. Splay pemët kanë një natyrë interesante të vetë-rregullimit në vetëvetë. Në veçanti, kurdo që pema bëhet e pabalansuar, qasja në pjesët e pabalansuara të pemës do të tentojë natyrshëm të balansojë vetëveten.Kjo është vërtetë gjë shumë e menqur, kur kemi parasysh faktin se pema nuk kaide nëse është e balansuar apo jo. Prandaj, si një pemë binare e pabalansuar,është e mundur që një operacion i vetëm i qasjes të kërkojë kohë të rendit O(n)(e jo si O(log n), si do të dëshironim).

Page 424: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 424/699

Avni Rexhepi

424

Sidoqoftë, pemët splay kanë një veti të mirë të njohur si “kufiri i performansëssë amortizuar të splay pemëve”: duke filluar nga një pemë e zbrazët, koha totalee nevojshme për të performuar sekuencën e ‘m’ operacioneve tëinsertimit/fshirjes/gjetjes në pemën 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 kushtueshëm, përgjatëcilësdo sekuence të operacioneve duhet të jetë një numër i madh i operacioneveefikase, për të balansuar disa me kosto të lartë. Me fjalë të tjera, përgjatësekuencës së m operacioneve, kostoja mesatare e një operacioni është O(log n).

Përparësitë

Performansa e mirë e pemës “splay” varet nga fakti që ajo është vetë-optimizuese, në atë që nyjet e qasura më shpesh do të lëvizin më afër rrënjës,

 prej ku mund të qasen më shpejtë. Duke pasur nyjet e përdorura më shpesh afërrrënjës është përparësi për pothuajse të gjitha aplikacionet praktike (për faktin elokalitetit të referencës) dhe është posaqërisht i rëndësishëm për implementimine algoritmeve të keshimit (angl. cache, cacheing) dhe të grumbullimit tëmbeturinave (angl. garbage collection). Përparësitë janë:

-  implementimi i thjeshtë (më i thjeshtë sesa pemët AVL dhe ato kuq-e-zi),-   performansa e krahasueshme (rasti mesatar, si edhe për pemët tjera),-  shfrytëzimi i ulët i memories (nuk kanë nevojë për të dhëna plotësuese),-  mundësia e krijimit të strukturave të persistente të të dhënave të pemëve

“splay” (që pas azhurimit, lejojnë qasje në të dy versionet: të vjetrin dhe tëriun),

-   puna e mirë me nyjet që kanë çelësa identik (në të kundërt me tipet tjera të pemëve të vetë-balansuara). Edhe për çelësat identik, performansa mbetete amortuzuar, O(log n). Të gjitha operacionet në pemë ruajnë renditjen enyjeve identike përbrenda pemës, që është tipar i ngjashëm me algoritmetstabile të sortimit. Dizajni i kujdesshëm i operacionit të kërkimit mund tëkthejë nyjen më të majtë ose më të djathtë për çelësin e dhënë.

Të metat

E meta kryesore e pemëve “splay” është se lartësia e pemës “splay” mund të jetëlineare. Për shembull, ky do të ishte rasti pas qasjes së të gjitha n-elementeve nërenditje jo-zbritëse. Pasi që lartësia e pemës i korrespondon kohës së qasjes përrastin 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 këtij rasti më të keq ështëlogaritmike, O(log n). Gjithashtu, kostoja e pritur e qasjes mund të redukohet nëO(log n) duke përdorur variantin me rastësi (e randomizuar).

Pema “splay” mund të jetë më e më “e keqe” se pema statike për më së shumti

një faktor konstant.

Page 425: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 425/699

Algoritmet dhe strukturat e të dhënave

425

Reprezentimi i pemëve “splay” mund të ndryshojë edhe kur ato qasen vetëm përlexim (angl. read-only), p.sh, për operacionin e kërkimit. Kjo e komplikon përdorimin e pemësve të tilla në ambient me shumë “rrjedha, fije” (angl. multi-threaded). Në mënyrë specifike, nevojitet menagjim shtesë, nëse lejohen rrjedhate shuëmfishta, për të kryer operacionet paralele të kërkimit.

Operacionet

Zgjerimi, hapja (splaying)

Kur të qaset një nyje x, operacioni “splay” performohet në nyjen x, për talëvizur atë deri në rrënjë. Për të realizuar operacionin e zgjerimit (splay), duhettë kryhet një seri e hapave ‘splay”, ku secili e lëvizë nyjen x më afër rrënjës.Duke performuar operacionin “splay” në nyjen me interes, pas secilës qasje,nyjet e qasura së fundi do të mbahen më afër rrënjës dhe pema do të mbetetafërsisht e balansuar, ashtu që do të arrihen kufitjë e amortizuar të dëshiruar.

Secili hap i veçantë varet nga tre faktorë:

-  nëse x është fëmijë i majtë apo i djathtë i nyjes prind, p-  nëse prindi p është rrënjë, dhe nëse jo-  nëse prindi p është fëmijë i majtë apo i djathtë i prindit të tij, g (gjyshit të

x-it).

Është me rëndësi të mbahet mend që  pas çdo “splay” operacioni, nyja gg(stërgjyshi) i x-it, të pointojë tek nyja x. Nëse gg është null, atëherë tani është edukshme se x është rrënja dhe duhet të azhurohet si nyje e tillë.

Ekzistojnë tri lloje të hapave “splay”, ku secili prej tyre është rast i anës së majtëose të djathtë. Për thjeshtësi, do të paraqitet vetëm nga një rast (tjetri është pasqyrim). Tipet e lëvizjeve janë të ashtuquajturat: ZIG, ZIG-ZIG dhe ZIG-ZAG.

Hapi Zig (angl. Zig Step): ky hap kryhet kur p është rrënjë. Pema rrotullohet(kryhet rotacioni) në degën ndërmjet x dhe p. Hapat Zig ekzistojnë për të trajtuarqështjen e paritetit dhe do të bëhen vetëm si hap i fundit i “splay” operacionit

dhe vetëm kur x ka thellësi teke në fillim të operacionit.

p

x

C

A B

X

P

A

CB 

Page 426: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 426/699

Avni Rexhepi

426

Hapi Zig-zig (angl. Zig-zig Step): Ky hap kryhet kur p nuk është rrënja dhe x e p janë ose të dy fëmijë të djathtë ose të dy të janë fëmijë të majtë. Figura vijuese paraqet rastin kur x dhe p janë të dy fëmijë të majtë. Pema rrotullohet në degënqë lidhë p-në me prindin e saj g, pastaj rrotullohet në degën që lidhë x me p.Vëreni se hapat zig-zig janë e vetmja gjë që i dallon pemët “splay” nga metoda“rrotullo në rrënjë”, para prezentimit të pemëve ”splay”. 

g

p

D

C

x

A B

x

p

A

B

g

DC 

Hapi Zig-zag (angl. Zig-zag Step): kryhet kur p nuk është rrënjë dhe x ështëfëmijë i djathtë dhe p fëmijë i majtë ose anasjelltas. Pema rrotullohet në degënndërmjet p dhe x dhe pastaj rrotullohet në degën rezultuese ndërmjet x dhe g.

g

p

D

A

x

CB

x

g

DC

p

BA

 

Insertimi

Për të insertuar nyjen x në pemën “splay”: 

1.  Së pari inserto nyjën si në rastin e pemës normale binare të kërkimit.2.  Pastaj “splay-o” nyjen e sapoinsertuar x në krye të pemës (në rrënjë).

Fshirja 

Për të fshirë nyjen x, përdoret metoda e njëjtë si me pemën binare të kërkimit:nëse x ka dy fëmijë, shkëmbejmë vlerën e tij ose me atë të fëmijës së tij më të

djathtë të nënpemës së tij të majtë (paraardhësin e tij in-order) ose nyjen më të

Page 427: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 427/699

Algoritmet dhe strukturat e të dhënave

427

majtë të nënpemës së tij të djathtë (pasardhësin e tij in-order). Pastaj në vend tësaj e largojmë atë nyje. Në këtë mënyrë, fshirja redukohet në problemin elargimit të nyjes që ka 0 ose 1 fëmijë.

Për dallim prej pemës binare të kërkimit, në pemën “splay” pas fshirjes, bëhet“splay” i prindit të nyjes së larguar deri në krye të pemës. Ose, nyja që duhet tëfshihet, së pari “splay-ohet”, d.m.th., sillet në rrënjë të pemës dhe pastaj fshihet.Kjo e lë pemën me dy-nënpemë (pa rrënjë). Pastaj, elementi maksimal inënpemës së majtë (metoda e parë) ose minimumi i nënpemës së djathtë(metoda e dytë), “splay-ohet” deri në rrënjë. Nënpema e djathtë bëhet fëmijë edjathtë i nënpemës së majtë rezultuese (për metodën e parë). Rrënja e nënpemëssë majtë është rrënja e pemës së bashkuar.

Përse funksionojnë pemët “splay”: nëse fillohet me një pemë të zbrazët dhe

kryhet një sekuencë e m operacioneve të pemës “splay” (insertimit, fshirja,kërkimi), atëherë koha totale e ekzekutimit do të jetë O(m log n), ku n ështënumri maksimal i elementeve në pemë në cilëndo kohë. Prandaj, koha mesatare për operacion është O(log n). Vërtetimi i kësaj është kompleks, por përmesshembujve vijues do të mund të shihet si punojnë pemët “splay”. 

 Fig. Insertimi i nyjes x

 Fig.6.44 - Fshirja e nyjes x

 Ndërsa, operacionet për “splay-imin” e nyjes, do të dukeshin si në vijim.

Page 428: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 428/699

Avni Rexhepi

428

1. Zig-zag

2. Zig-zig

3. Zig 3.   Rezultati pwrfundimtar

 Fig.6.45 –  Splay (3,t) –  sjellja e nyjes 3 në rrënjë.

Për të analizuar performansën, modelohen dy madhësi:

Kostoja reale: koha që merr “splay” (kjo është proporcionale me thellësinë enyjes që splay-ohet), dhe

Rritja në balans: niveli deri në të cilin operacioni “splay” përmirëson balansine pemës.

Analizat kanë treguar se kostoja reale është e lartë, e pastaj pema bëhet shumëmë e balansuar (dhe rrjedhimisht kostot reale të mëpasme bëhen më të ulëta). Nëanën tjetër, nëse pema bëhet më pak e balansuar, kostoja reale do të jetë e ulët.Kështu, në cilëndo prej mënyrave, përfitohet.

Page 429: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 429/699

Algoritmet dhe strukturat e të dhënave

429

3

2

5

0

4

9

7

8

1

6

3

2

0

4

9

7

8

1

6

1

0

4

2

9

7

8

3

6

1

0

4

2

9

7

8

3

6

Pas

find(5)Pas largimit

të rrënjësPas kërkimit

të maksimumitPas lidhjes sëRrënjës së re

Nënhapat gjatë fshirjes së nyjes 5:

7

6

54

3

2

1

0

8

7

5

31

8

6

4

2

01

0

5

4

8

7

3

2

9

6

7

3

2

0

8

5

64

1

Insertimi dhe kërkimi: Operacionet në Splay-Tree

Pas insertimit të:

0, 1,… 8

Pas

find(0)

Pas

find(1)Pas

insert(1)

  Fig. 6.46 –  Operacionet në “Splay-Tree” 

Implementimi Në vazhdim do të paraqitet një implementim i pemëve “splay”, i cili i përdorë pointerët për të reprezentuar secilën nyje në pemë. Ky implementim është i bazuar në metodën e dytë të fshirjes së pemës “splay”. Poashtu, për dallim prejdefinicionit të mësipërm, ky version nuk e bën “splay-imin” në rastet e kërkimti –  por vetëm për 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;

Page 430: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 430/699

Avni Rexhepi

430

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

}

Page 431: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 431/699

Algoritmet dhe strukturat e të dhënave

431

}}

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;

Page 432: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 432/699

Avni Rexhepi

432

}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

Page 433: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 433/699

Algoritmet dhe strukturat e të dhënave

433

Lista me kapërcime - Skip List

Pemët e zakonshme dhe pemët e balansuara, shërbejnë për ruajtjen e strukturëssë fjalorit (angl. Dictionary). Pemët e pabalansuara janë të thjeshta dhe punojnë

mirë në rastin mesatar, por në rastet e kundërta kanë kohë të keqe tëekzekutimit. Pemët AVL garantonin performansë të mirë, por ishin të vështira për t’u implementuar. Pemët “splay” ofruan një alternativë për pemët AVL,sepse ishin të thjeshta dhe vetë-organizative. Një strukturë tjetër që përdoret përruajtje të fjalorëve, është struktura e quajtur “Lista me kapërcime” (Skip List;angl. skip-kërcim, kapërcim, kalim, hedhje, etj).

Skip listat u zbuluan nga Ëilliam Pugh në vitin 1989. Në vitin 1990 u propozuan si një alternativë për pemët e kërkimit binar dhe për pemët tjera të balansuara.

Lista me kapërcime është ndër strukturat më praktike pasi që është mjaft ethjeshtë dhe duket të jetë më e shpejta. Lista me kapërcikme është një përgjithësim i listave të lidhura. Si e tillë, ajo ka shumë nga thjeshtësia e listavetë lidhura, por ofron performansë optimale O(log n). Një veti tjetër interesanteështë se ato janë strukturë e të dhënave me rastësi (angl. randomized). Me fjalëtë tjera, për krijimin e tyre përdoret një gjenerator i numrave të rastit. Skip listat janë efikase në rastin e pritur, mirëpo për dallim prej pemëve të pabalansuara binare, pritja nuk ka të bëjë asgjë me shpërndarjen e çelësave. Ajo varet vetëm prej gjeneratorit të numrave të rastit. Së këndejmi, në të kundërtën, 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 vogël.

Skip lista perfekte

Skip lista ka filluar me idenë “si të përmirësohet lista e lidhur e sortuar”? Nëlistën e lidhur është e lehtë të bëhet insertimi dhe fshirja, por është sumë eveshtirë të lokalizohen elementet në mënyrë efikase, sepse duhet të kalojmë një

nga një me radhë në një moment kohor, nëpër të gjithë anëtarët e listës. Nëse dotë ishte e mundur të kapërcehen nga disa elemente, atëherë do të zgjidhej problemi. Në mënyrë e të menduarit për skip listat është si një hierarki e listavetë lidhura të sortuara, të vendosura mbi njëra tjetrën.

Për të konkretizuar, imagjinoni një listë të lidhur, të sortuar sipas vlerave tëçelësave dhe supozojmë se në fund kemi një nyje speciale “sentinel” (angl.sentinel-kujdestar, roje), të quajtur ‘nil’ (angl. nil-zero, asgjë, hiq), e cilakonsiderohet me vlerë ∞. (madhësi infinit ∞). Pastaj le të marrim çdo të dytënvlerë të listës së lidhur (ose p.sh vlerat teke) dhe i ngrisim një nivel më lartë në

një listë të re të lidhur me 1/2 (gjysmën) e elementeve. Në vazhdim, marrim

Page 434: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 434/699

Avni Rexhepi

434

 përsëri çdo të dytin element të listës së re dhe e ngrisim një nivel më lartë, ku dotë kemi listën me 1/4 e elementeve të listës fillestare. Këtë procedurë mund ta përsërisim [lg n] herë, deri sa të ketë vetëm një element në listën e nivelit më tëlartë.

 Fig. 6.47 –  Skip lista

Kërkimi për një vlerë (çelës) x do të fillonte në nivelin më të lartë. Mund të

skenohet në mënyrë lineare përgjatë listës në nivelin aktual ‘i’, duke k ërkuar përelementin e parë që është më i madh se x (duke rikujtuar që vlera e çelësit për Nil është ∞). Pointeri ‘p’ le të pointojë në nyjen para këtij hapi. Nëse vlera e ‘p’-së është e barabartë me x, atëherë ndalemi. Përndryshe, zbrezim në nivelin eardhëshëm më të ulët i-1 dhe e përsërisim kërkimin. Në nivelin 0 i kemi tëruajtur të gjithë çelësat, kështu që nëse nuk e gjejmë atë në atë nivel, endërpresim kërkimin (vlera nuk është gjetur). Për shembull, në figurën emëparshme është paraqitur me vija të ndërprerë kërkimi për x=19.

 Në rastin më të keq koha e kërkimit do të ketë mundësi të kalojë nëpër të gjitha[lg n] nivelet (nëse çelësi nuk është në listë). Themi se kërkimi viziton më sëshumti dy nyje për nivel, në listën e idealizuar (perfekte). Kjo është e vërtetë pasi që dihet se në nivelin paraprak (më i lartë) ndodhemi ndërmjet dy nyjeve tënjëpasnjëshme ‘p’ dhe ‘q’, ku vlera e ‘p’ është më e vogël se x (vlera e kërkuarx), ndërsa vlera e ‘q’ është më e madhe se x. ndërmjet dy nyjeve tënjëpasnjëshme në nivelin e njëjtë ka saktësisht vetëm një nyje në nivelin eardhëshëm më të ulët. Prandaj, gjersa zbresim për një nivel, kërkimi do tëvizitojë nyjen aktual në më së shumti një nyje shtesë. Prandaj, vizitohen më sëshumti dy nyje për nivel dhe O(log n) nivele, për totalin e kohës O(log n).

Skip lista e randomizuar

Problemi me listën perfekte është se ajo është plotësisht e balansuar (sikur pema binare e plotë, e balansuar perfekt). Nëse do të insistojmë në strukturë të tillë,insertimi i një një nyjeje do të rezultonte me restrukturim të plotë të listës. Skiplistat, sikur edhe të gjitha listat e balansuar mirë, lejojnë një nivel të jobalansimit. Në fakt, skip lista e arrinë këtë faktor shtesë të “pjerrtësisë” përmes rastësisë (randomizimit).

Le të marrim parasyshë strukturën probabilistike të skip listës në cilindo

moment kohor. (kjo nuk është ajo që ndodhë saktësisht me ndërtimin e

Page 435: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 435/699

Algoritmet dhe strukturat e të dhënave

435

strukturës, por shërben si mënyrë për të sqaruar atë që ndodhet në prapavi tëstrukturës së tillë). Në skip listë nuk kërkohet që saktësisht çdo e dyta nyje enivelit ‘i’ të ngritet në nivelin ‘i+1’, por imagjinoni thua se hidhet monedha përtë vendosur a do të promovohet nyja në nivelin më të lartë apo jo. Nëse bie

“koka’ (d.m.th., me probabilitet 1/2) nyja promovohet në nivelin e ardhëshëmmë lartë të listës së lidhur, përndryshe qëndron në nivelin ku ndodhet. Duke pasur parasyshë rastësinë (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 kështu me radhë. Përmë tepër, pasi që nyjet paraqiten me rastësi në secilin nivel, është e pritshme qënyjet në nivelin e dhënë janë të shpërndara mirë (nuk janë të gjitha tëgrumbulluara në një skaj). Prandaj, skip listat e randomizuara sillen shumëngjajshëm me skip listat e idealizuara (perfekte), në rastin mesatar (në rastin e pritur). Procedura e kërkimit mbetet saktësisht e njëjtë me atë të rastit ideal, si

në figurën 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ë çdokohë. Për insertimin e çelësit ‘x’ së pari kërkojmë në listë për çelësin ‘x’ për tëgjetur paraardhsit e drejtpërdrejtë në skip listë (në secilin nivel të strukturës). Nëse ‘x’ nuk ndodhet në listë, e krijojmë nyjen për ‘x’ dhe e insertojmë atë nënivelin më të ulët. Pastaj “hedhim monedhën” (gjenerohet një numër i plotë irastit, nga gjeneratori i numrave të rastit). Nëse rezultati është ‘pilë’ (numri irastit është tek), ndalemi. Përndryshe e insertojmë ‘x’-in në nivelin e ardhëshëmmë lartë, të strukturës. Përsërisim procesin deri sa monedha të vazhdon të bie“pilë” ose të arrijmë në nivelin maksimal të strukturës. Pasi që kjo në fakt është

vetëm insertim i përsëritur në listën e lidhur, kodi është i thjeshtë.

Page 436: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 436/699

Avni Rexhepi

436

Për të fshirë nyjet, thjeshtë e fshijmë nyjen nga secili nivel në të cilin paraqitetdhe përshtasim pointerët.

Vëreni se në çdo kohë, lista e lidhur ka strukturën e dëshiruar probabilistike. Kjo

 për arsye se në të kundërtën nuk mund të shohim gjeneratorin e numrave të rastitdhe ai nuk ka mënyrë të fshijë nyjet në mënyrë selektive në një nivel të caktuarsi dhe secila nyje e “hedhë monedhën” në mënyrë të pavarur prej nyjeve të tjera,kështu që nivelet në skip listë janë të pavarur prej njëri tjetrit. Kjo duket të jetënjë prej dallimve më të rëndësishëme ndërmjet skip listave dhe pemëve, pasi qënë pemë është e vështirë të bëhet diçka e pavarur, pa ndikuar në fëmijët e nyjes.

Pra, skip lista është strukturë probabilistike e të dhënave ku elementet mbahen tësortuara sipas çelësave. Skip lista lejon kërkim, insertim dhe fshirje të shpejtë tëelementeve me algoritme të thjeshta. Në esencë është listë e lidhur me pointerë

 plotësues të tillë që kapërcejnë nyjet e ndërmjetme. Për t’i marr ë disa vendime, përdorë gjeneratorin e numrave të rastit. Skip lista është strukturë praktike e tëdhënave që jep rezultate të mira ndërsa e ruan implementim të thjeshtë.

Kërkimi në listën e tillë do të përdorë vetëm pointerët (lidhjet) e nivelit më tëlartë, për të kapërcyer përtej shumë elementeve në listë dhe pastaj do të zbriztenëpër nivelet e mëposhtme sipas nevojës.

 Një shembull i listës (me kapërcim të çdo të dytës vlerë) do të ishte si në vijim:

Sentinel-i

36

1115

2430

3242 NIL

3 6 11 15 24 30 32 42 NIL

3

6

11

15

24

30

32

42 NIL

Header-i

 Fig. 6.48 –  Skip lista ideale (perfekte)

Operacionet në listën me kapërcime, në rastin më të keq janë të rendit O(n). Kjodo të ndodhte kur të gjitha nyjet (ose pothuajse të gjitha nyjet) ndodhen nënivelin 1 të insertimit. Në këtë rast, skip lista do të ishte listë e lidhur ezakonshme. Mirëpo, moto e skip listave është: “ Don’t ëorry, be happy!”, pasi që

Page 437: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 437/699

Algoritmet dhe strukturat e të dhënave

437

ky rast ka pak gjasa që të ndodhe, me rritjen e n-it. Për 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ë rrënja dhe nivelet enyjeve i përgjigjen niveleve të pemës, ndërsa nyjet e nivelit 1 janë gjethet.

Algoritmet për skip lista

Algoritmet për skip lista janë shkruar nga zbuluesi i tyre, Ëilliam Pugh dhe do të jepen përmes pseudokodeve përkatëse, ashtu si janë shkruar nga autori.

Vërejtjet lidhur me këto algoritme janë:

-  Pointerët përpara të nyjes së nivelit ‘i’ ruhen në një varg, të emërtuar‘for ëard’ (angl. for ëard-përpara) dhe jantë të indeksuar prej 1 deri në i.

-   Niveli i nyjes nuk është i ruajtur-   Niveli i listës = maksimumi{niveli i nyjeve në listë}

Inicializimi

Lista e re inicializohet si vijon:

1.  Së pari krijohet nyja e quajtur ‘NIL’ dhe çelësi i saj caktohet në një vlerëmë të madhe sesa çelësi më i madh që do të mund të përdorej në listë(d.m.th., nëse lista do të përmbajë vlerat e çelësave ndërmjet 1 dhe 999,atëherë 1000 do të merrej si çelës në NIL). Secili nivel përfundon me

‘NIL’. 2.   Niveli i listës së re është 1.3.  Të gjithë pointerët përpara (angl. forëard pointers) të ‘Header -it”

 pointojnë në NIL.

Kërkimi:

1.  Filloni me nivelin më të lartë të listës.2.  Lëvizni para (djathtas) duke përcjellur pointerët në nivelin e njëjtë,

derisa sa çelësi i ardhëshëm të jetë më i madh sesa çelësi i kërkuar.

3.   Nëse niveli aktual nuk është më i ulëti, shkoni një nivel më poshtë dhe përsëritni kërkimin në atë nivel prej nyjes aktuale.4.   Ndaloni kur niveli është 1 dhe çelësi i ardhëshëm është më i madh sesa

çelësi i kërkuar.5.   Nëse çelësi aktual është çelësi i kërkuar, ktheni vlerën e asaj nyjeje.

Përndryshe, ktheni informacionin për “dështim” (çelësi i kërkuar nukekziston në listë).

Funksioni për kërkim: SEARCH(list, searchKey)

1.  x  <- list.header 

Page 438: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 438/699

Avni Rexhepi

438

2.  for i <- list.level downto 13.  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 

2

4

5

9

15

19

28 42

NIL

3533

 

2

4

5

9

15

19

28 42

NIL

3533

Kërkimi për 35

2

4

5

9

15

19

28   42

NIL

3533

Kërkimi për 42

 

Insertimi/Fshirja:

-  Insertimi ose fshirja e nyjes konsiston kryesisht në kërkimin e pasuar meazhurim të pointerëve.

-  Për të ruajtur nyjen e fundit të qasur në secilin nivel, përdoret një varg iquajtur “update” (angl. update-azhurimi, përditësimi). Ai përdoret për

ndryshimin e pointerëve pasi të jetë insertuar apo fshirë një nyje.-   Niveli i nyjes së re të insertuar përcaktohet me rastësi, nga funksioni

Random-Level (niveli i rasitit).

Page 439: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 439/699

Algoritmet dhe strukturat e të dhënave

439

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  

2

4

5

9

15

19

28   42

NIL

3533

Insertimi i 16

16

 

Funksioni për fshirje: DELETE(list, searchKey)

1.   x  <- list.header 

2.  for i <- list.level downto 13.  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 for i <- 1 to list.level 9.  do if update[i]. forward [i] <> x  10.  then break 11.  update[i]. forward [i] <- x . forward [i]

12.  FREE( x )

Page 440: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 440/699

Avni Rexhepi

440

13.  while list.level > 1 and list.header. forward [list.level] =NIL 

14.  do list.level <- list.level - 1

2

4

5

9

15

19

28   42

NIL

3533

Fshirja e 9 Përpara: level[list]=4

Pas : level[list]=3

 

Funksioni: RANDOM-LEVEL()

1. random-level()

2.  newLevel <- 13.  while RANDOM() < p4.  do newLevel <- newLevel + 15.  return MIN(newLevel, MaxLevel)

Niveli i rastit - RandomLevel  Sqarimet për këtë algoritëm:

-  Funksioni RANDOM() kthen një numër ndërmjet 0 dhe 1.0.-   p është konstantë ndërmjet 0 dhe 1.0 (supozojmë p = 0.5).

Funksioni RandomLevel funksionon sikur hedhja e monedhës. Le të marrim që‘koka’ është rasti kur fitohet ndërsa ‘pilë’ rasti kur humbet. “Monedha

elektronike” hidhet deri sa të del ‘pilë’. Secilën herë që bie ‘koka’, niveli ngritet për një dhe hidhet monedha përsëri.

Vini re: nëse p=1/4, atëherë do të ketë mesatarisht 1.33 pointerë për nyje. Kjokursen hapësirën pa e zvogëluar dukshëm (në masë të rëndësishme) kohën ekërkimit.

Analiza

Analiza e skip listave është një shembull i analizës probabilistike. Duhet tëvërtetojmë se në rastin e pritur (mesatar), koha e kërkimit është O(log n).

Page 441: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 441/699

Algoritmet dhe strukturat e të dhënave

441

Qartazi, kjo është koha që dominon insertimin dhe fshirjen. Së pari vërejmë senumri i pritur i niveleve në skip listë është O(log n). Arsyeja është se në nivelin0 kemi n çelësa, në nivelin 1 presim të ketë n/2 çelësa, në nivelin 2 presim n/4, ekështu me radhë. Kjo sipas argumentit të njëjtë që u përdor për rastin e listësideale, që pas O(log n) niveleve, nuk do të ketë më çelësa të mbetur.

Argumenti për t’u provuar kufirin e pritur të kohës së kërkimit është interesant.Le të shikojmë shtegun e anasjelltë të kërkimit. (Kjo është teknikë e zakonshmenë algoritmet probabilistike dhe ndonjëherë quhet “anailza te prapa”). Vëreni seshtegu i kërkimti te para zbret në nivel sa herë që lidhja e ardhëshme do të nadërgonte “përtej” nyjes të cilën jemi duke e kërkuar. Kur e kthejmë mbrapshtshtegun e kërkimit, vëreni se ai do të na gjithnjë do të na dërgojë në hapa telartë, nëse mundet (d.m.th., nëse nyja që e viziton paraqitet në nivelin eardhëshëm më të lartë), përndryshe do të bëjë një hap në të majtë.

 Fig.6.49 –  Kthimi prapa në shtegun e kërkimit për 11.

Tani, kur të arrijmë në nivelin ‘i’ të cilësdo nyje në skip listë, themi se probabiliteti që do të ketë nivel më lartë është vetëm 1/2. Arsye për këtë është se

kur është insertuar nyja, ky ka qenë probabiliteti që ajo është promovuar nënivelin e ardhëshëm më të lartë. Prandaj, me probabilitetin 1/2 kalojmë nënivelin e ardhëshëm më të lartë. Me probabilitetin e mbetur 1  –   (1/2) = 1/2mbesim në nivelin e njëjtë. Numri i pritur i hapava të nevojshëm për të ecurnëpër ‘ j’ nivele të skip listës është i dhënë sipas rekurrencës vijuese:

)(2

1)1(

2

11)(   jC  jC  jC     

Vlera 1 është për hapin aktual. Me probabilitetin 1/2 kalojmë në nivelin më tëlartë të ardhëshëm dhe kështu kemi një nivel më pak nëpër të cilin duhet kaluardhe me probabilitetin 1/2 mbesim në nivelin e njëjtë. Kjo mund të rishkruhet si:

)1(2)(     jC  jC   

Duke zgjeruar (zbërthyer), lehtë vërtetohet se  j jC    2)(   . Pasi që ‘ j’ është së

shumti (maksimalisht) numri i niveleve në pemë (listë), atëherë kemi se koha e pritur e kërkimit është në të shumtën O(log n).

Page 442: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 442/699

Avni Rexhepi

442

Implementimi

 Një prej elementeve joshëse të skip listave është lehtësia e tyre e implementimit.Shumica e procedurave (funksioneve) që operojnë në skip listë përdorim kodin e

thjeshtë të njëjtë me atë që përdoret për operacionet në listat e lidhura. Njëelement shtesë është se duhet të përcillet niveli në të cilin ndodhemi. Mënyra nëtë cilën kjo bëhet në formën më efikase është që të kemi nyje me madhësivariabile (të ndryshueshme), ku madhësia e nyjes është e përcaktuar me rastësime rastin e krijimit. Përfitojmë nga rasti se C++ (edhe Java) na mundësonalokimin dinamik të vargjeve me madhësi variabile.

Objekti “Skip List” përbëhet nga nyja e kreut (Header) dhe konstruktori e krijonnyjen “Sentinel”, vlera e të cilit përcaktohen në një vlerë speciale “infinit” (ecila varet nga tipi i i çelësit). Supozojmë se konstroktorit i jepet numri maksimal

i lejuar i niveleve Ndonjë impelemtim më i avansuar (“më i menqur”) do të përcaktonte në mënyrë adaptive numrin e duhur të niveleve).

Klasat e Skip Listës

 Nyja:

//SkipListNode=NyjaeSkipListes, forward=përparaclass SkipListNode{

Element data; // data=vlerat e çelësave

SkipListNode forward[]; // vargu i pointerëve përpara// Konstruktori (të cilit i jepet vlera dhe niveli)SkipListNode(Element d, int level){

data = dforward = new SkipListNode[level+1];

}} Lista:

class SkipList{int maxLevel; // niveli maksimalSkipListNode header; // Nyja “header”

SkipList(int maxLev) //konstr. me nivelin maks. të dhënë{maxLevel = maxLev; // aloko nyjen “header” header = new SkipListNode(null, maxLevel);// bashkangjite nyjen "nil" në nyjen “header” 

Page 443: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 443/699

Algoritmet dhe strukturat e të dhënave

443

SkipListNode sentinel = new SkipListNode(INFINITY,maxLevel);

for (int i = 0; i <= maxLevel; i++)header.forward[i] = sentinel;

}}

Kodi për kërkimin (gjetjen) e nyjes në skip listë është dhënë në vijim. Vëreni qëkërkimi është pak a shumë i njëjtë me kërkimin standard në listën e lidhur, përveq unazës që na lëvizë 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) // kërko 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;

elsereturn null;

}

Është me rëndësi të theksohet se nuk ka nevojë të ruhet niveli i nyjes si pjesë efushës së  pointerit “for ëard”. Funksionet e skip listës ruajnë njohuritë e tyre tëveta për nivelin gjersa lëvizin nëpër strukturën e të dhënave. Gjithashtu vëreni se(nëse implementohet korrekt) nuk do të asnjëherë nuk do të tentohet të

indeksohet përtej kufijve të nyjes.Element me rëndësi për insertimin në skip listë ishte funksioni për gjenerimin enivelit të rastit:

//Gjenero nivelin e rasitit, newLevel=NiveliiRiint generateRandomLevel(){

int newLevel = 0;while (newLevel < maxLevel && Math.random() < 0.5)

newLevel++;

return newLevel;

Page 444: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 444/699

Avni Rexhepi

444

}

Kodi për insertim dhe fshirje, mund të bazohet ngjashëm në pseudo kodin edhëne nga autori i skip listave.

Përfitimet prej Skip Listave

Pema binare e kërkimit është efikase, por shumë lehtë mund të bëhet e pabalansuar pas vetëm disa insertimeve dhe fshirjeve. Pemët e balansuaragarantojnë që mbesin të balansuara dhe prandaj kryejnë operacionet themelorenë rastin më të keq me O(log n). Teorikisht ato janë efikase, mirëpoimplementimi i tyre është i komplikuar. Në anën tjetër, skip listat janë më tëlehta për t’u implementuar. Algoritmet për insertim dhe fshirje janë të thjeshtadhe të shpejta. Ato nuk garantojnë performansë O(log n), por në fakt ato kanë performansë O(log n) në rastin mesatar (për insertim, fshirje, kërkim) dhe

 probabiliteti i devijimit të madh prej mesatares është shumë i vogël. Prandaj, performansa shumë e keqe (O(n)) është shumë pak gjasa që të ndodhë dhe probabiliteti i saj zvogëlohet eksponencialisht gjersa ‘n’ rritet. Për shumicën eaplikacioneve, skip listat janë poaq efikase sa edhe strukturat e pemëve të balansuara. Ato poashtu janë efikase në aspektin hapësinor, pasi që nuk kanevojë të ruhet informacion për balansim në nyje dhe ato mund të punojnë mirëedhe me një mesatare prej vetëm 1.33 pointerëve për nyje. Për dallim prej pemëve të kërkimit binar, performansa e skip listave nuk varet nga rendi iinsertimit.

Page 445: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 445/699

Algoritmet dhe strukturat e të dhënave

445

Pemët M-are

Pema binare ka një vlerë në secilën nyje dhe dy nëndegë (nënpemë). Ky nocionshumë lehtë mund të përgjithësohet në pemën M-are, e cila ka M-1 vlera për

nyje dhe M nëndegë (nënpemë). M quhet shkalla e pemës. Për këtë arsye, pema binare ka shkallën 2.

 Nëse degët (lidhjet, pointerët) prej rrënjës kah nyjet fëmijë (kah nëndegët),shikohen si rrugë (angl. way-rrugë, drejtim), atëherë pemët M-are zakonishtquhen “Multiway Trees” (angl. Multiway Trees –  Pemët shumërrugëshe). Pemashumërrugëshe e cila ka rendin rendin ‘M’ quhet “M-way Tree” ose “M-aryTree”. Në pemën M-are:

 Në fakt, nuk është e nevojshme që secila nyje të përmbajë saktësisht (M-1) vlera

dhe të ketë saktësisht M nëndegë. Në një nënpemë M-way, nyja mund të ketë prej 1 deri në M-1 vlera dhe numri i nëndegëve (jo të zbrazëta) mund të jetë nërangun prej 0 (për gjethet) deri te 1+(numri i vlerave). Prandaj, M është vetëmkufiri i epërm në numrin e të dhënave që mund të ruhen në nyje.

Vlerat në nyje ruhen në renditje rritëse, V1<V2<...<Vk (k<=M-1) dhe nënpemët(nëndegët) janë të vendosura ndërmjet vlerave fqinje, me nga një nënpemështesë në secilin skaj. Prandaj, me secilën vlerë mund të shoqërojmë nënpemën“e majtë” dhe nënpemën “e djathtë”, ku nënpema e djathtë e Vi është e njëjtë menënpmemën e majtë të V(i+1). Të gjtiha vlerat në nënpemën e majtë të V1 janë

më të vogla sesa V1; të gjitha vlerat në nënpemën e Vk janë më të maëdhajasesa Vk, dhe të gjitha vlerat ndërmjet në nëpnpemë ndërmjet V(i) dhe V(i+1) janë më të mëdhaja sesa V(i) dhe më të vogla sesa V(i+1).

Për shembull, pema 3-are është si vijon:

10 44

3 7 22 55 70

50 66 68

  Fig. 6.50 –  Pema 3-are

Për të ilustruar pemën M-rrugëshe, do të jetë e përshtatshme të përdoren vlerat evogla të M. Por, keni parasyshë që në praktikë, M është zakonisht vlerë shumë emadhe. P.sh., secila nyje i korrespondon një blloku të të dhënave në disk dhe M

reprezenton numrin maksimal të elementeve që mund të ruhen në një bllok të

Page 446: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 446/699

Avni Rexhepi

446

vetëm. Për të përshpejtuar kërkimin, M maksimalizohet: lëvizja prej një nyje nëtjetrën, përfshinë leximin e bllokut nga disku (qasjen në disk), që në fakt ështëoperacion shumë i ngadalshëm në krahasim me lëvizjen nëpër strukturën e tëdhënave të ruajtur në memorie.

Kokat lexuese të diskut janë koka lëvizëse, pra janë pajisje mekanike (elektro-mekanike). Poashtu edhe për rrotullimin e diskut, përdoret motorri. Si elementpërcaktues për llojin e diskut shikohet shpejtësia e rrotullimeve për minut, që ështënumri i cili tregon numrin e rrotullimeve për minut, ‘rpm’ (shkurtesa rpm - angl. rottationsper minute), p.sh., HD-xxxGB, 7200rpm).

1024 bajta

 

Algoritmi për kërkimin e një vlere (çelësi) në pemën M-are të kërkimit është përgjithsim i algoritmit të kërkimit të pemës binare të kërkimit. Nëse jemi dukekërkuar vlerën X dhe momentalisht jemi në nyjen e cila përmbanë vleratV1...Vk, atëherë janë katër mundësi:

1.   Nëse X < V1, kërkoni rekurzivisht për X në nënpemën e majtë të V1-shit.

2.   Nëse X > Vk, kërkoni rekurzivisht për X në nënpemën e djathtë të Vk-

së.3.   Nëse X=Vi, për ndonjë ‘i’, atëhere kemi mbaruar (X është gjetur).4.  Mundësia e vetme e mbetur është që, nëse për një ‘i’, Vi < X < V(i+1).

 Në këtë rast, kërkoni në mënyrë rekurzive për X në nënpemën qëndodhet ndërmjet Vi dhe V(i+1).

Për shembull, supozojmë se jemi duke kërkuar vlerën 68 në pemën e paraqiturmë lartë. Në rrënjë, do të aplikohej rasti 2, kështu që do të vazhdonim kërkiminnë nënpemën e djathtë të V2-shit. Në rrënjë të kësaj nënpeme, apliohet rasti 4,68 është ndërmjet V1=55 dhe V2=70, kështu që do të vazhdonim kërkimin në

Page 447: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 447/699

Algoritmet dhe strukturat e të dhënave

447

nënpemën ndërmjet tyre. Tani aplikohet rasi 3, 68=V2, kështu që kemi mbaruar(vlera është gjetur). Nëse do të kishim kërkuar për vlerën 69, do të ndodhte procedura e njëjtë si më parë deri në këtë pikë, e pastaj do të aplikohej rasti 2, por nënpema në të cilën do të dëshironim të vazhdojmë kërkimin është ezbrazët. Prandaj, do të konkludohej se vlera 69 nuk ndodhet në pemë.

Algoritmet tjera për pemën binare të kërkimit, insertimi dhe fshirja, përgjithsohen në mënyrë të ngjashme. Si në rastin e pemëve binare, insertimi ivlerave në renditje rritëse do të rezultonte në pemë të degjeneruar M-are;d.m.th., pema lartësia e së cilës do të ishtë O(N) në vend të O(log N). Ky është problem sepse të gjitha operacionet e rëndësishme janë O(lartësia), kurse qëllimiështë që ato të bëhen O(log N). Një zgjidhje për këtë problem do të ishtedetyrimi i pemës që të jetë e balansuar për kah lartësia. Në këtë rast do të fitohenPema M-are me balansim perfekt për kah lartësia, e njohur si Pema-B (B-Tree).

Page 448: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 448/699

Avni Rexhepi

448

B-Pemët (B-Trees)

B-Pema (angl. B-Tree) është një pemë M-are me dy tipare speciale:

1.  Është e balansuar në mënyrë perfekte: secila gjethe ndodhet në nivel tënjëjtë për nga thellësia.

2.  Secila nyje, përveq ndoshta rrënjës, është së paku përgjysmë e mbushur,d.m.th., përmbanë M/2 ose më shumë vlera (natyrisht, nuk mund të përmbajë më shumë sesa M-1 vlera). Rrënja mund të ketë numër tëçfarëdoshëm të velrave (prej 1 deri në M-1).

Secila nyje në strukturën standarde përmbanë pointerët për në nënpemë dhevlerat, të renditura si:

pointeri|vlera|pointeri|vlera|...|vlera|pointeri

Versioni i balansuar i pemës së mëparshme M-ëay, që përmbanë vlerat e njëjtadhe që është shndërruar në Pemë-B është:

50

10 66

55 68 703 7 22 44

  Fig. 6.51 –  B-tee (Pema-B)

 Ndërsa, pema-B 5-are (secila nyje, përveq rrënjës, duhet të përmbajë ndërmjet 2dhe 4 vlera):

10 50

3 7 22 44 55 66 68 70

  Fig. 6.52 –  Pema-B 5-are

Page 449: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 449/699

Algoritmet dhe strukturat e të dhënave

449

Kufizimet e definuara e bëjnë pemën-B që të jetë së paku gjysëm e mbushur, tëketë disa nivele dhe të mbetet perfekt e balansuar.

 Nyjet dhe pointerët e B-pemës paraqiten nëpër literaturë në forma të ndryshme, por me rëndësi është që të kihet parasysh struktura e nyjeve, me pointerët dhevlerat

 Fig. 6.53 –  Struktura e nyjeve tw B-pemws

 Nyjet e pemës-B zakonisht implementohen si klasë që përmbanë një varg me m-1 qelula për vlerat (çelësat), një varg me m pointerë për tek nyjet e nivelit tëardhëshëm dhe informacionet plotësuese të nevojshme për të mundësuarmirëmbajtjen e pemës.

//template=shabllonitemplate <class T, int M>

class BTreeNode{public:BTreeNode();BTreeNode( const T & );

private:T keys[M-1];BTreeNode *pointers[M];...

};Për shkak të natyrës së veçantë, algoritmet e ristrukturimit të pemës-B dallojnë prej algoritmeve të restrukturimit të pëmës AVL dhe të tjerave. Gjersainsertohen vlerat e reja, algoritmi zhvendosë elementet nëpër nyje, ashtu që të plotësojë tërësisht nyjen aktuale dhe nyjet e nivelit të njëjtë (nyjetvëllezër/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ë këtoraste paraqitet nevoja për ndarje të nyjes, procesi i krijimit të nyjeve të rejaquhet “ndarja e nyjes” (angl. node splitting). Kur nuk ka vend për elementin e ri,

Page 450: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 450/699

Avni Rexhepi

450

nyja ndahet dhe zëvendësohet me dy nyje të reja, me elementin e mesit që silletsi prind për dy nyjet e reja fëmijë.

Kërkimi në B-tree

Algoritmi i kërkimit (gjetjes së një vlere/çelësi) në B-tree është i thjeshtë.Fillohet me rrënjën dhe përcaktohet se cili pointer duhet të përcillet bazuar nëkrahasimin ndërmjet vlerës së kërkuar dhe fushave të çelësave në nyjen rrënjë.Përcillet pointeri i duhur për në nyjen fëmijë. Analizoni fushat e çelësave nënyjen fëmijë dhe vazhdoni përcjelljen e pointerëve të duhur deri sa të gjindetvlera e kërkuar ose të arrihet në nyjen gjethe, e cila nuk e përmbanë vlerën ekërkuar.

Insertimi në B-tree

Kushti që të gjitha nyjet duhet të jenë në nivel të njëjtë imponon sjelljenkarakteristike të pemëve-B, që pemët-B në fakt nuk lejohet të rriten në gjethe, por ato detyrohen të rriten në rrënjë.

Kur të insertohet një vlerë në B-tree, vlera insertohet drejtpërdrejt në gjethe. Kjodërgon në tri situata të zakonshme, të cilat mund të ndodhin:

1.  Çelësi vendoset në gjethen e cila ka akoma vend të lirë.2.  Gjethja në të cilën duhet të vendoset çelësi është e mbushur.3.  Rrënja e pemës-B është e mbushur.

Rasti 1: Çelësi vendoset në gjethen e cila ka akoma vende të lira 

Ky është rasti më i lehtë për t’u zgjidhur sepse vlera thjeshtë insertohet në pozitën korrekte të sortuar në nyjen gjethe. (Në vazhdim nuk do të paraqiten pozitat e pointerëve, por nunëkuptohet që ato janë në kufijtë e vlerave).

Page 451: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 451/699

Algoritmet dhe strukturat e të dhënave

451

12

5 8 13 15

 

Insertimi i numri 7 do të rezultojë në:

12

5 7 8 13 15   Fig. 6.54 –  Insertimi në rastin kur ka vend të lirë  

Rasti 2: Gjethja në të cilën duhet të insertohet çelësi është e mbushur

 Në këtë rast, gjethja në të cilën duhet të insertohet vlera e re ndahet në dy pjesë(përgjysmë), duke rezultuar në një gjethe të re. Gjysma e çelësave do tëzhvendosen prej nyjes së plotë në nyjen e re. Nyja e re pastaj inkorporohet në B-Tree.

 Nyja e re gjethe inkorporohet duke lëvizur vlerën e mesit në nyjen prind dhe poashtu në prind shtohet pointeri për në nyjen gjethe. Ku proces vazhdon te lartë pemës deri sa të gjitha vlerat të këne “gjetur” vendin e tyre.

Page 452: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 452/699

Avni Rexhepi

452

Insertimi i 6 në B-pemën vijuese:

12

2 5 7 8 13 15

rezulton me ndarjen e nyjes së parë gjethe:

12

2 5 7 13 158

 

12

2 5 6 7 8 13 15  

 Nyja e re duhet të inkorporohet në pemë, gjë që kryhet duke marrë vlerën emesit dhe duke e insertuar atë në nyjen prind si dhe duke përshtatur pointerët:

6 12

2 5 7 8 13 15

  Fig. x –  Insertimi në rastin kur ka vend të lirë  

Rasti 3: Rrënja është e mbushur

Lëvizja te lartë e vlerave nga rasti i dytë do të thotë që është e mundur që vleramund të lëvizë te lartë deri në rrënjën e B-pemës. Nëse rrënja është e mbushur,do të aplikohet procedura e njëjtë si për rastin e dytë, ku do të krijohet një nyje ere. Ky tip i ndarjes do të rezultojë me shtimin e dy nyjeve të reja në B-pemë.

Page 453: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 453/699

Algoritmet dhe strukturat e të dhënave

453

Insertimi i 13 në pemën vijuese:

6 12 20 30

7 8 10 11 14 15 18 19 21 23 25 282 3 4 5   31 33 34 35

 

Rezulton në:

6 12 20 30

7 8 10 11 13 14 15   21 23 25 282 3 4 5   31 33 34 3518 19

 

 Nyja 15 duhet të lëvizet në rrënjë, por ajo është e mbushur. Kjo do të thotë serrënja duhet të ndahet:

6 12

7 8 10 11 13 14   21 23 25 282 3 4 5   31 33 34 3518 19

20 3015

 

 Nyja 15 insertohet në nyjen prind, gjë që do të thotë se ajo bëhet nyje e re rrënjë:

6 12

7 8 10 11 13 14   21 23 25 282 3 4 5   31 33 34 3518 19

20 30

15

 

Fshirja e nyjes nga pema-B

Si zakonisht, fshirja është procesi më i vështirë për t’u aplikuar. Procesi ifshirjes në esencë do të jetë e kundërta e insertimit, prandaj në vend të ndarjes sënyjeve është e mundur që të ndodhë bashkimi i nyjeve, ashtu që tiparet e pemës-B, d.m.th., kërkesa që nyja duhet të jetë së paku përgjysmë e mbushur, mund tëmirëmbahet.

Janë dy raste të zakonshme të cilat duhet të mirren parasysh:

1.  Fshrija nga nyja gjethe2.  Fshirja nga nyja që nuk është gjethe.

Page 454: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 454/699

Avni Rexhepi

454

Rasti 1: Fshirja nga nyja gjethe

a). Nëse gjethja është së paku përgjysmë e mbushur pas fshirjes së vlerës sëdëshiruar, vlerat tjera të mbetura që janë më të mëdhaja, lëvizen për të

“plotësuar zbrazëtirat”. P.sh., fshirja e 6 nga pema vijuese:

3 8

5 6 7 13 14 15   23 241 2   27 3718 20

22 25

16

 

Rezulton në:

3 8

5 7 13 14 15   23 241 2   27 3718 20

22 25

16

 

 b). Nëse gjethja është më e vogël se gjysma pas fshirjes së vlerës së dëshiruar(rasti i njohur si “underfloë” (angl. underfloë  –  nën-rrjedha), mund të ndodhin

dy gjëra:

Fshirja e nyjes 7 nga pema paraprake, rezulton në:

3 8

5 13 14 15   23 241 2   27 3718 20

22 25

16

 

 b-1). Nëse ka nyje vëlla/motër të majtë ose të djathtë me numër të çelësave që etejkalon kërkesën për minimum, të gjithë çelësat prej gjetheve dhe nyjeve tënivelit të njëjtë (vëllezër/motra) do të rishpërndahen duke lëvizur çelëst ndarës prej prindit në gjethe dhe duke lëvizur çelësin e mesit prej nyjes dhevëllaut/motrës të kombinuar në prind.

Page 455: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 455/699

Algoritmet dhe strukturat e të dhënave

455

3 13

5 8 14 15   23 241 2   27 3718 20

22 25

16

 

Tani fshijmë 8 nga pema:

3 13

5   14   15   23 241 2   27 3718 20

22 25

16

 

 b-2). Nëse numri i çelësave ën vëlla/motër nuk tejkalon kërkesën për minimum,atëherë gjethja dhe nyja vëlla/motër bashkohen duke vendosur çelësat ngagjethja, vëllau/motra dhe vlera ndarëse prej prindit, në gjethe. Nyja vëlla/motërasgjësohet dhe çelësat në prind lëvizen për të “plotësuar zbrazëtirën”. Është emundur që kjo do të shkaktojë nën-rrjedhje të prindit. Nëse ky është rasti,trajtoni prindin si gjethe dhe vazhdoni të përsëritni hapin b-2 gjersa të plotësohetkërkesa për minimum ose të arrihet rrënja e pemës.

Rasti special për b-2: Gjatë bashkimit të nyjeve (angl. merge-bashkim,shkrirje), nëse prindi është nyje me vetëm një çelës, çelësat prej nyjes, nyjes sënivelit të njëjtë (vëlla/motër) dhe çelësi i vetëm i rrënjës vendosen në një nyjedhe kjo nyje do të bëhet rrënja e re e B-pemës. Të dyja nyjet, rrënja e vjetër dhenyja e nivelit të njëjtë do të asgjësohen.

3

5 13 14 15   23 241 2   27 3718 20

22 25

16

 

5 13 14 15 23 241 2 27 3718 20

3 16 22 25

 

 Fig. x –  Fshirja dhe bashkimi

Page 456: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 456/699

Avni Rexhepi

456

Rasti 2: Fshirja prej nyjeve që nuk janë gjethe

Ky rast mund të dërgojë në probleme me riorganizimin e pemës, mirëpo zgjidhet

në mënyrë të ngjashme me rastin e fshirjes prej pemës binare të kërkimit.Çelësi që duhet të fshihet do të zëvendësohet me paraardhësin (ose pasardhësin)e tij të drejtpërdrejt dhe pastaj paraardhësi (ose pasardhësi) do të fshihet, pasi qëai mund të gjindet vetëm në nyje gjethe.

P.sh., fshirja e 16 nga pema paraprake do të rezultojë në:

5 13 14 15 23 241 2 27 3718 20

3 22 25

 

“Zbrazëtira” e krijuar do të mbushet nga parardhësi i drejtpërdrejt (në këtë rast15):

5 13 14   15   23 241 2 27 3718 20

3   15   22 25

 dhe pastaj parardhësi i drejtpërdrejt fshihet.

5 13 14 23 241 2 27 3718 20

3 15 22 25

 

 Nëse për zëvendësim do të ishte zgjedhur pasardhësi i drejtpërdrejt (në këtë rast

18), atëherë do të kishim:

5 13 14 15 23 241 2 27 3718   20

3   18   22 25

 

Fshirja e pasardhësit do të rezultojë në:

Page 457: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 457/699

Algoritmet dhe strukturat e të dhënave

457

5 13 14 15 23 241 2 27 3720

3 18 22 25

 

 Në vazhdim, vlerat në vëllaun/motrën e majtë kombinohen me çelësin ndarës(18) dhe me vlerat e mbetura. Ato ndahen ndërmjet dy nyjeve:

5 13 14 23 241 2 27 3715 18 20

3 22 25

 

dhe pastaj vlera e mesit zhvendoset tek prindi:

5 13 14 23 241 2 27 3715 18 20

3 22 25

 

Page 458: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 458/699

Avni Rexhepi

458

7. Grafet

Grafet janë strukturë shumë e përdorur në shkencat kompjuterike dheaplikacionet e ndryshme të kompjuterit. Në këtë rast nuk themi “strukturë e tëdhënave” sepse grafet janë të destinuara për të ruajtur dhe analizuar “metadata”(meta të dhënat).

Metadata  janë të dhëna për të dhënat ("data about data"). Termi përdoret përdy koncepte të ndryshme: Structural metadata  janë lidhur me dizajnin dhespecifikimin e strukturave të të dhënave dhe më saktë quhen “të dhëna lidhurme kontejnerët (bartësit) e të dhënave”, kurse Descriptive metadata,  janëlidhur me instancat individuale të të dhënave të aplikacionit, përmbajtja e tëdhënave.

Metadata (metapërmbajtja) definohet si të dhënat që ofrojnë informacion lidhurme një ose më shumë aspektë të të dhënave, si:

  Metodat e krijimit të të dhënave  Qëllimi i të dhënave  Koha dhe data e krijimit  Krijuesi ose autori i të dhënave  Lokacioni në rrjetin kompjuterik ku janë krijuar të dhënat  Standardet e përdorura, etj.

Për shembull, një imazh digjital mund të përmbajë metadata të cilatpërshkruajnë se sa është madhësia e fotografisë, thellësia e ngjyrës,rezolucioni, data e krijimit apo të dhëna të tjera. Metadata e një dokumentitekstual mund të përmbajë inforamcion lidhur me gjatësinë e dokumentit,autorin, kohën e shkruarjes ose një përmbledhje të shkrutër të tij.

Për shembull, në rastin e qyteteve të vendit, rrjeti rrugor i cili i lidhë qytetetmund të prezentohet si graf dhe pastaj të analizohet. Mund të analizojmë nësenjë qytet është i arritshëm (i lidhur) prej një tjetri ose të gjejmë rrugën më të

shkurtër ndërmjet dy qyteteve.Së pari, do të prezentojmë disa terme dhe definicione të grafeve. Pastaj do tëshohim se si reprezentohen grafet përbrenda kompjuterit. Në fund do t’ikthehemi 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 degët/rrugët e grafit (angl. Vertex(shumësi: Vertices) - Kulm, maje, nyje etj. ; angl. Edge  –  anë, buzë, teh, kufietj),. Një degë specifikon se cilat nyje kanë lidhje ndërmjet tyre. Kur punohet

me grafe, shpesh herë jemi të interesuar në atë se si mund të bashkohen këto

Page 459: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 459/699

Algoritmet dhe strukturat e të dhënave

459

degë që të mund të lëvizet nëpër graf. Për këtë arsye, shpesh do të flasim përudhëtimin nëpër një degë/rrugë, që do të thotë se kemi ndryshuar nyjën tonë tëinteresit duke përcjellur njërën prej rrugëve të lidhura me të. Me fjalë të tjera,nëse grafi i jonë ka nyjet A dhe B që janë të lidhura përmes një dege/rrugë, nd

do të flasim për “lëvizjen prej A në B”, “udhëtimin prej A në B” ose“qarkullimin/përshkimin e rrugës prej A në B” për të paraqitur faktin se fokusi i jonë ka ndryshuar prej nyjës A në nyjën B. Për të lehtësuar diskutimin, ne do tëshkruajmë vetëm emrat (etiketat) e nyjeve si stenografi (trajtë e shkurtër, eshkruar) për rrugën/degën që lidhë ato. Kështu, AB do të paraqesë rrugënndërmjet nyjes A dhe nyjes B dhe ne do të themi se B është fqinje me A (e afërt,në afërsi).

Grafi mund të jetë i padrejtuar ose i drejtuar. Një graf i padrejtuar, zakonisht iquajtur vetëm graf, ka degët/rrugët të cilat mund të përshkohen në cilindodrejtim. Në këtë rast, një degë/rrugë është set (bashkësi), e cila përmbanëetiketat (labelat) e nyjeve të cilat janë dy skajet e degës/rrugës.1 

Grafi i drejtuar, gjithashtu i quajtur edhe digraf, ka degët të cila mund të përshkohen (qarkullohen) vetëm në një drejtim. Për digrafin, seti idegëve/rrugëve do të ketë çiftet e renditura në të cilat elementi i parë ështëfillimi dhe i dyti është fundi i rrugës/degës.

Pra, janë dy sete (bashkësi) të rëndësishme të objekteve, të cilat specifikojnëgrafin dhe strukturën e tij. Seti i parë është seti i nyjeve të grafit. Në shembullin

e rrjetit të rrugëve, qytetet janë nyjet e grafit. Secila nyje mund të vizatohet sinjë rreth me numrin e nyjes përbrenda.

nyjet

Seti tjetër i rëndësishëm është seti i degëve të grafit, i quajtur edge-set  (angl.edge –  skaj, anë, teh, që ne do ta quajmë degë e grafit ose lidhje, link). E ështënënbashkësi e V x V. Thënë ndryshe apo më thjeshtë, secila degë i lidhë dy

1 Nëse seti që paraqet një degë ka vetëm një element, kjo e paraqet një nyje e cila “rrotullohet”

ose me fjalë të tjera niset dhe përfundon në të njëjtën nyje.

Page 460: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 460/699

Avni Rexhepi

460

nyje, duke përfshirë edhe rastin kur nyja është e lidhur me vetveten (në të cilinrast quhet –  loop (lak, unazë)).

Të gjitha grafet ndahen në dy grupe të mëdha: grafet e drejtuara dhe ato të

 padrejtuara. Dallimi qëndron në faktin se në grafin e drejtuar, degët janë tëdrejtuara (orientuara) dhe të shënuara me “shigjetë” e cila tregon kahun elëvizjes prej nyjes në nyje. Të dy llojet kanë shumë gjëra të përbashkëta, porkanë edhe elementet të cilat i dallojnë. Në parim për çdo rast të grafit, shihet se aështë grafi i orientuar/drejtuar apo jo, sepse zakonisht, nëse grafi është iorientuar, secila degë ka shigjetën 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 secilës nyjetek 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 përfundimtare (cak). Nëse nyja fillestare është edhe nyje përfundimtare, shtegu quhet cikël.Shtegu quhet i thjeshtë, nëse e përmbanë secilën nyje vetëm një herë. Cikliquhet i thjeshtë , nëse e përmbanë secilën nyje, përveq asaj të fillimit(fundit),vetëm një herë. Në vazhdim do të paraqiten disa shembuj të shtegut dhe ciklit.

 shtegu (i thjeshtë) cikli (i thjeshtë)

Page 461: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 461/699

Algoritmet dhe strukturat e të dhënave

461

Grafi quhet “Graf i lidhur ” nëse ekzistojnë lidhje të tilla që mund të vizitohen tëgjitha nyjet e grafit, gjegjësisht që nuk ka ndonjë pjese të shkëputur të nyjeve tëgrafit. Nëse ka ndonjë nyje të shkëputur dhe nuk ka rrugë/degë për të arritur deritek ajo ose ato nyje, atëhere grafi është ‘Graf i pa lidhur” (Graf jo i lidhur).

Shtegu i Euler-it quhet shtegu (rruga) e cila kalon nëpër secilën degë saktësishtnjë herë, pa përsëritje. Nëse shtegu përfundon në nyjen nga e cila ka filluar,atëherë ai është cikël i Euler-it (qark i Euler-it).

Shtegu i Hamilton-it kalon nëpër secilën nyje të grafit, saktësisht një herë. Nësembaron në nyjën e fillimit, atëherë quhet cikël (qark) i Hamilton-it.

Historia e teorisë së grafeve filloi në vitin 1736, kur Leonard Euler së pari zgjidhiproblemin e shtatë urave të Königsberg-ut. Königsberg (tash Kaliningrad) ështëqytet në brigjet e lumit Pregel. Në kohën e Eulerit, ishin shtatë ura që lidhninbrigjet dhe dy ishuj, të cilët mund të modelohen si një multigraf me shtatë degëdhe katër nyje. Euleri kërkohi mënyrën e kalimit nëpër secilën urë saktësishtvetëm një herë dhe të kthehet në fillim, d.m.th, ciklin Eulerian. Pasi që të gjithanyjet kishin shkallë teke, Euleri vërtetoi se një tur i tillë është i pamundur. Uratishin shkatërruar gjatë luftës së dytë botërore.

 Algoritmi i Fleury-it është qasje direkte dhe elegante e konstruktimit të cikleveEuleriane. Filloni të ecni nga cilado nyje dhe fshini degët që janë kaluar(përshkuar). Kriteri i vetëm në zgjedhjen e degës së ardhëshme është që tëevitohet përdorimi i degës që fshihet, përveq nëse nuk ka alternativë tjetër.

 Asnjë graf Eulerian nuk përmbanë “urë” (degë), mirëpo ajo çka mbetet nëndonjë pikë të rrugëtimit mbaron të jetë graf i bikonektuar. Teknika e turit tëEulerit është një paradigmë e rëndësishme në algoritmet paralele të grafeve.Ekzistojnë algoritme efikase për numërimin e cikleve të Eulerit në graf.

Grafi i plotë është graf me një degë ndërmjet secilit çift të nyjeve. Nëse ka Nnyje, do të ketë (N2-N)/2 rrugë/degë në grafin komplet pa rrugët/degëtrrotulluese. Digrafi komplet është një digraf me një rrugë/degë që lejon përshkimin ndërmjet secilit çift të nyjeve. Pasi që rrugët/degët e grafit lejojnëudhëtimin në dy drejtime, gjersa rrugët e digrafit lejojnë udhëtimin vetëm një

një drejtim, digrafi me N nyje do të ketë dyfish më shumë rrugë/degë, N2-N. Nëngrafi (VS, ES) i grafit ose digrafit (V,E) është një graf i cili ka njënënbashkësi të kulmeve (VS    V) dhe degëve/rrugëve (Es   E) të grafit të plotë.

Shtegu/Rruga ndërmjet dy nyjeve të grafit ose digrafit është një varg i rrugëve tëcilat mund të kalohen në rresht (një pas një). Me fjalë të tjera, rruga (shtegu)ndërmjet nyjes A dhe nyjes B do të fillontë në nyjen A dhe do të rrugëtone(udhëtonte) nëpër një set të rrugëve deri sa të arrijë në nyjen B. Formalisht,

themi se rruga prej nyjes vi deri te v j është sekuencë e rrugëve vi,vi+1, . . ., v j-1,v j 

Page 462: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 462/699

Avni Rexhepi

462

që ndodhen në graf. Ne kërkojmë që të gjitha nyjet përgjatë rrugës të jenë unike.Rruga thuhet të ketë gjatësinë që paraqet numrin e rrugëve të cila e përbëjnërrugën. Rruga AB, BC, CD, DE ka gjatësinë 4.

Grafi ose digrafi i “peshuar” është ai ku secila rrugë ka një vlerë, të quajtur peshë, të shoqëruar me të. Në vizatimet e grafeve, pesha do të shkruhet afërrrugës/degës. 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 punohetme grafe me peshë, ne konsiderojmë që pesha të jetë një kosto për përshkimin(qarkullimin) e rrugës/degës. Shtegu përgjatë (nëpër) grafit me peshë ka njëkosto që është shumë e peshave të secilës rrugë/degë përgjatë shtegut. Në grafinme peshë, shtegu më i shkurtër ndërmjet dy nyjeve është shtegu me koston më tëvogël, edhe nëse ai nuk ka më së paku degë/rrugë. Për shembull, nëse shtegu P1 ka pesë rrugë/degë me kosto totale 24 dhe shtegu P2 ka tri degë/rrugë me kostototale 36, shtegu P1 do të konsiderohet shteg më i shkurtër sepse kostoja e tijështë më e vogël.

Për shembull, në rastin e grafit të rrjetit rrugor, pesha e secilës rrugë mund të jetë gjatësia e saj ose koha e nevojshme për të kaluar nëpër të.

Grafi i peshuar

Reprezentimi i grafit të padrejtuar

Ka disa mënyrë të mundshme të reprezentimit (përfaqësimit) të grafit nëkompjuter. Dy prej tyre, që përdoren zakonisht, janë matrica e fqinjësisë (angl.adjacency matrix) dhe lista e fqinjësisë (angl. adjacency list).

Matrica e fqinjësisë

 Një matricë e fqinjësisë, AdjMat (nga angl. Adjacency-fqinjësi) për grafin G=(V, E ), me |V | =  N , do të ruhet si një varg dy-dimensional me madhësi NxN.

Page 463: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 463/699

Algoritmet dhe strukturat e të dhënave

463

Secili lokacion [i,j] i këtij vargu do të ruaj vlerën 0, përveq nëse ka ndonjëdegë/rrugë prej nyjes vi tek nyja v j, lokacioni do të ruajë vlerën 1. Më formalisht:

 për çdo i dhe j në rangun 1 deri në N

Për grafet dhe digrafet me peshë, vlerat në matricat e fqinjësisë do të ishin  nëse nuk ka rrugë/degë dhe pesha përkatëse për të gjitha rastet tjera. Elementet ediagonales do të ishin 0, sepse nuk do të ketë kostro të udhëtimit prej nyjes nëvetveten.

Pra, secili element aij i një matrice të fqinjësisë përmbanë 0, nëse nuk ekzistonlidhje (degë) ndërmjet nyjeve i  dhe  j  dhe 1, nëse ato janë të lidhura. Le të

shohim një shembull.

Grafi Matrica e fqinjësisë

Page 464: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 464/699

Avni Rexhepi

464

Dega (2, 5) Elementet për degën (2, 5)

“Dega” (1, 3)  Elementet për “degën” (1, 3) 

Grafi i paraqitur përmes shembullit paraprak është i padrejtuar. Kjo do të thotë

se matrica e tij e fqinjësisë është simetrike. Në të vërtetë, në grafin e padrejtuar,nëse ekziston dega (2, 5) atëherë ekziston edhe dega (5, 2). Kjo edhe ështëarsyeja, pse ke dy elemente të matricës për secilën degë të grafit. Unazat(rrotullat, laqet), nëse lejohen në graf, ju përgjigjen elementeve të diagonalës tëmatricës së fqinjësisë.

Përparsitë. Matrica e fqinjësisë është shumë e përshtatshme për përdorim.Shtimi (largimi) i një dege mund të bëhet në kohë O(1), e njëjtë me atë përverifikimin nëse ekziston lidhje/degë ndërmjet dy nyjeve. Gjithashtu është e

thjeshtë për t’u programuar. 

Page 465: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 465/699

Algoritmet dhe strukturat e të dhënave

465

Të metat.

  Martica e fqinjësisë konsumon hapësirë të madhe të memories përruajtjen e grafeve të mëdha. Të gjitha grafet mund të ndahen në dykategori, grafe të rralla dhe grafe të dendura. Grafet e rralla nuk kanëshumë degë (numri i degëve është shumë më i vogël se katrori i numrittë nyjeve (që është numri i elementeve të matricës së fqinjësisë),|E|<<|V|2). Në anën tjetër, grafet e dendura kanë numër të degëve tëkrahasueshëm me katrorin e numrit të nyjeve. Matrica e fqinjësisë ështëoptimale për grafet e dendura, por është e panevojshme (e tepërt) përgrafet e rralla.

  Dobësi tjetër e matricës së fqinjësisë është se në shumë algoritme duhettë dihen nyjet, fqinjë me nyjen aktuale. Për të nxjerrë një informacion të

tille nga matrica e fqinjësisë, duhet të shqyrtohet rreshti përkatës, gjë qërezulton në kompleksitet O(|V|). Për algoritmet si DFS (Depth FirstSearch  –  kërkimi thellësia së pari) ose ato të bazuara në të, përdorimi imatricës së fqinjësisë rezulton në kompleksitet të përgjithshëm prejO(|V|2), gjersa ai mund të zvogëlohet në O(|V| + |E|), kur përdoret lista efqinjësisë.

  Problem tjetër i cili duhet theksuar, është se matrica e fqinjësisë kërkonshumë përpjekje për shtimin/largimin e nyjes. Nëse grafi përdoret vetëm për analizë, atëherë kjo gjë nuk është e nevojshme, mirëpo nëse

dëshironi të konstruktoni një strukturë tërësisht dinamike, përdorimi imatricës së fqinjësisë e bënë atë mjaft të ngadalshme për grafe të mëdha.

Si përfundim, matrica e fqinjësisë është zgjidhje e mirë për grafet e dendura gjëqë këkron të pasurit e një numri konstant të nyjeve.

Lista e fqinjësisë

Lista e fqinjësisë është një alternativë e matricës së fqinjësisë, për reprezentimine grafit. Ajo kërkon më pak memorie dhe në raste të veçamta edhe funksiononmë mirë sesa matrica e fqinjësisë. Për secilën nyje, lista e ruan një listë tënyjeve, të cilat janë fqinje ma atë aktuale. Le të shohim një shembull.

Page 466: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 466/699

Avni Rexhepi

466

Grafi Lista e fqinjësisë

 Nyjet, fqinje të {2} Rreshti në listën fqinjësisë

Përparësitë. Lista e fqinjësisë mundëson që grafi të ruhet në formë mëkompakte sesa matrica e fqinjësisë, por diferenca zvogëlohet gjersa grafidendësohet. Përparësi tjetër është se lista e fqinjësisë mundëson që të merretlista e nyjeve fqinjë në kohë O(1), gjë që është përparësi e madhe për disa

algoritme.

Të metat.

  Shtimi/largimi i një nyje në/nga lista e fqinjësisë nuk është aq i lehtë sinë rastin e matricës së fqinjësisë. Kjo kërkon, mesatarisht, kohë O(|E| /|V|), që mund të rezultojë në kompleksitet kubik për grafet e dendura, përtë shtuar të gjitha degët.

Page 467: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 467/699

Algoritmet dhe strukturat e të dhënave

467

  Verifikimi, nëse ekziston lidhje ndërmjet dy nyjeve mund të bëhet 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 fqinjësisë nuk mundëson implementim efikas, nëse kërkohetndryshim dinamik i numrit të nyjeve. Shtimi i nyjes së re mund të bëhetnë O(V), por largimi rezulton në kompleksitet O(E).

Si përfundim, lista e fqinjësisë është zgjidhje e mirë për grafet e rralla dhemundëson ndryshimin e nmrit të nyjeve në mënyrë më efikase sesa përdorimi imatricës së fqinjësisë. Por megjithatë, për ruajtje të grafit tërësisht dinamik, kazgjidhje më të mira.

Pjesë kodi

Për thjeshtësi, pjesët e kodit kanë të bëjnë me matricat e fqinjësisë, për 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; 

Page 468: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 468/699

Avni Rexhepi

468

} } 

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 për grafet e padrejtuara

Kur punojmë me grafet, nganjëherë mund të dëshirojmë që të bëjmë diçka nësecilën nyje të grafit saktësisht vetëm një herë. Për shembull, mund të jetë njëinformacion që duhet të shpërndahet në të gjithë kompjuterët në rrjetë. Nedëshirojmë që ky informacion të arrijë në secilin kompjuter, por nuk duam që

atë t’ia japim dy herë ndonjë kompjuteri. E njëjta gjë do të ishte e vërtetë nëse jemi duke kërkuar për ndonjë informacion në vend të shpërndarjes.

Ekzistojnë dy teknika të cilat do t’i analizojmë dhe të cilat e realizojnë/kryejnë përshkimin e grafit. Dy algoritmet themelore për përshkimin e nyjeve të grafit(bredhjen, lëvizjen nëpër graf) janë algoritmet:

-  “Thellësia së pari” (angl. Depth-first search, ose shkurt DFS), dhe 

-  “Gjerësia së pari” (angl. Breadth-first search, ose shkurt BFS). 

 Në “depth-first” (thellësia-së-pari), rrugëtimi i jonë do të shkojë sa më larg që të jetë e mundur shtegut teposhtë, para se të konsiderojë ndonjë rrugë tjetër dhe në“breadth-first” (gjerësia-së-pari) rrugëtimi i jonë do të shkojë barabartë nëshumë drejtime. Në vazhdim do të shikojmë këto dy metoda më detajisht. Përkëto dy metoda të përshkimit/rrugëtimit, ne zgjedhim një nyje në graf si pikëtonën startuese/fillestare. Në diskutimet tona, ne përdorim një frazë (shprehje)tjetër “vizito nyjen” për të paraqitur veprimin që duhet të bëhet në secilën nyje.Për shembull, nëse jemi duke kërkuar, vizitimi i nyjës do të nënkuptonte që neduhet ta verifikojmë atë për informacionin që na duhet. Këto metoda

Page 469: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 469/699

Algoritmet dhe strukturat e të dhënave

469

funksionojnë pa ndonjë ndryshim edhe në grafet e drejtuara edhe në ato të padrejtuara. Do t’i ilustrojmë ato përmes grafeve të padrejtuara. 

Secila prej këtyre metodave të rrugëtimit/përshkimit gjithashtu do të përdoret

 për të përcaktuar nëse grafi është i lidhur. Nëse ne krijojmë një listë të nyjeve tëcilat i vizitojmë gjatë rrugëtimit tonë, kjo listë mund të krahasohet me setin(bashkësinë) e nyjeve në graf. Nëse ato janë të njëjta, grafi është i lidhur. Nëseato nuk janë të njëjta, atëherë ka disa nyje që nuk mund të arrihen prej vendit kukemi startuar (filluar), që do të thotë se grafi nuk është i lidhur.

Përshkimi thellësia-së-pari

 Në përshkimin thellësia-së-pari (Depth-first traversal), ne vizitojmë nyjënstartuese dhe pastaj shkojmë para për të përcjellur lidhjet nëpër graf deri sa tëarrijmë në një rrugë-qorre (rrugë pa dalje, pikë fundore). Në një graf të padrejtuar, nyja është pikë fundore nëse të gjitha nyjet fqinje me të veq janëvizituar paraprakisht. Në grafin e drejtuar, nëse nyja nuk ka rrugë dalëse,gjithashtu kemi pikë fundore (rrugë pa dalje). Kur të arrijmë në rrugë pa dalje,ne kthehemi prapa nëpër shtegun tonë deri sa të gjejmë një nyje fqinje të pavizituar dhe pastaj vazhdojmë në atë drejtim të ri. Procesi do të kompletohet kurtë kthehemi prapa në nyjen fillestare/startuese dhe të gjitha nyjet fqinje me të të jenë vizituar. Në ilustrimin e këtij algoritmi dhe të gjithë të tjerët në këtëkapitull, nëse na paraqitet opcioni i zgjedhjes mes dy nyjeve, do të zgjedhim

nyjen me vlerë/etiketë numerike ose alfabetike më të vogël. Kur tëimplementohet/aplikohet ky algoritëm, kjo zgjedhje do të varet nga fakti se si janë ruajtur degët/rrugët e grafit.

 Figura 7.1 - Grafi

Shqyrtoni grafin në Fig. 7.1. Nëse fillojmë me përshkimin thellësia-së-pari nganyja 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 për të gjetur se nyja8 nuk është vizituar, por kjo menjeherë do të çoj në rrugë pa dalje. Pastaj

Page 470: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 470/699

Avni Rexhepi

470

kthehemi deri te nyja 4 dhe gjejmë se nyja 9 nuk ka qenë e vizituar, por përsërikemi përnjeherë rrugë pa dalje. Pastaj vazhdojmë kthimin prapa, deri sa tëarrijmë në nyjën startuese/fillestare dhe pasi që të gjitha nyjet fqinje me të janëvizituar, kemi përfunduar.

Algoritmi rekurziv për rrugëtimin/përshkimin thellësia-së-pari është:DepthFirstTraversal(G, v)G is the graphv is the current node

Visit( v )Mark( v )for every edge vw in G doif w is not marked then

DepthFirstTraversal(G, w)end ifend for 

PershThellSePari(G, v)G eshte grafiv eshte nyja aktuale

Vizito( v )Sheno( v )‘for’ cdo dege vw ne G bëj if w nuk eshte shenuar

PershThellSePari(G, w)end ifend for 

Ky algoritëm rekurziv bazohet në stekun e sistemit të kompjuterit për të ndjekur pozitën 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 ngjashëm jorekurziv duke përdorur strukturën e stekut dhe vet duke futur/shtyer në stek dhe dukenxjerrë/tërhequr nga steku kulmet (pikat, nyjet).

Shembull: Depth-first search (DFS)

Kërkimi Thellësia së pari, është një mënyrë për përshkimin e grafit. Fillimishtalgoritmi u krijua për grafet dhe mundëson vizitimin e nyjeve të grafit, porekzistojnë me qindra algoritme të bazuara në DFS. Kjo edhe është arsyeja që tëkuptuarit e parimeve të kërkimit thellësia së pari është i rëndësishëm përstudimin e mëtejmë në teorinë e grafeve. Parimi i algoritmit është mjaft ithjeshtë: të shkohet përpara (në thellësi) gjersa të jetë e mundur, përndryshe tëkthehet prapa.

Algoritmi

 Në DFS, secila nyje ka tri “ngjyra” të mundshme për reprezentimin e gjendjes: 

e bardhë: nyja e pavizituar;

hiri: nyja është në progress;

e zezë: DFS ka përrunduar procesimin e nyjes.

Page 471: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 471/699

Algoritmet dhe strukturat e të dhënave

471

 Për shumicën e algoritmeve klasifikimi bool-ean “e  pavizituar”/”e vizituar”

është i mjaftueshëm, mirëpo do të paraqitet rasti i përgjithshëm.

Fillimisht të gjitha nyjet janë të bardha (e pavizituar). Algoritmi DFS fillon në

një nyje arbitrare dhe punon si vijon:

1.  Shëno (marko) nyjen u si ngjyrë hiri (e vizituar).2.  Për secilën degë (u, v), ku u  është e bardhë, ekzekuto DFS për u  në

mënyrë rekurzive.3.  Shëno nyjen u si të zezë dhe ktheu prapa tek prindi.

Shembull.  Përshko grafin e mëposhtëm duke përdorur algoritmin DFS(Thellësia së pari). Fillo nga nyja me numër 1.

Grafi burimor (fillestar).

Shëno (marko) nyjen 1 me ngjyrë hiri.

Page 472: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 472/699

Avni Rexhepi

472

Kemi një degë (1, 4) dhenyja 4 është e pavizituar.Shko tek ajo.

Shëno nyjën 4  mengjyrë hiri.

Kemi degën (4, 2)  dhenyja 2 është e pavizituar.Shko tek ajo.

Shëno nyjën 2 mengjyrë hiri.

Page 473: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 473/699

Algoritmet dhe strukturat e të dhënave

473

Kemi degën (2, 5)  dhenyja 5 është e pavizituar.Shko tek ajo.

Shëno nyjën 5 mengjyrë hiri.

Kemi degën (5, 3)  dhenyja 3 është e pavizituar.Shko tek ajo.

Shëno nyjën 3 mengjyrë hiri.

Page 474: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 474/699

Avni Rexhepi

474

 Nuk ka rrugë/degë tutje

 prej nyjes 3. Shënojënyjen 3 me ngjyrë të

zezë dhe kthehu prapa

tek nyja 5.

Ka një degë (5, 4), pornyja 4 është me ngjyrëhiri.

 Nuk ka rrugë/degë tjera për tek ndonjë nyje e pavizituar nga nyja 5. Shënoje me të zezë dhekthehu prapa tek nyja 2.

 Nuk ka degë të tjera,fqinje me nyjen 2. Shënoje me të zezë dhekthehu prapa tek nyja 4.

Page 475: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 475/699

Algoritmet dhe strukturat e të dhënave

475

Ka një degë (4, 5), pornyja 5 është e zezë.

 Nuk ka degë të tjera,fqinje me nyjen 4. Shënoje me të zezë dhekthehu prapa tek nyja 1.

 Nuk ka degë të tjera,fqinje me nyjen 1. Shënoje me të zezë. DFS përfundoi.

Siç mund të shihet nga shembulli, DFS nuk shkon (nuk kalon) nëpër të gjitha

degët. Nyjet dhe degët të cilat i ka vizituar DFS janë “pema” (angl. tree). Kjo pemë përmbanë të gjitha nyjet e grafit (nëse ai është i lidhur) dhe quhet pema eshtrirjes së grafit ( graph spanning tree). Kjo pemë saktësisht i përgjigjetthirrjeve rekurzive të DFS-it.

 Nëse grafi nuk është i lidhur, DFS nuk do t’i vizitojë të gjitha nyjet e tij.

Analiza e kompleksitetit

Supozojmë se grafi është i lidhur. Algoritmi DFS viziton secilën nyje të grafitdhe verifikon secilën nyje të tij. Prandaj, kompleksiteti i DFS është O(V + E). Si

është përmendur më herët, nëse për reprezentimin e grafit përdoret matrica e

Page 476: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 476/699

Avni Rexhepi

476

fqinjësistë, atëherë të gjitha degët, fqinje me nyjen, nuk mund të gjenden nëmënyrë efikase, gjë që rezulton në kompleksitet të rendit O(V2).

Pjesë kodi

// VertexState=GjendjaeNyjes;// White=eBardhe; Gray=eHirit; Black=eZexe// state=gjendjaenum 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;}

Përshkimi gjerësia-së-pari

 Në përshkimin/rrugëtimin gjerësia-së-pari (Breadth-first traversal) së parivizitojmë nyjen startuese/fillstare dhe pastaj në kalimin e parë vizitojmë të gjithanyjet e lidhura drejtpërdrejt 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ë gjatësive të ndryshme nganyja startuese. Për shkak se do ta vizitojmë atë nyje për herë të parë përgjatështegut më të shkurtër nga nyja startuese, nuk do të kemi nevojë ta shqyrtojmë përsëri. Prandaj, do të kemi nevojë ose të mbajmë një listë të nyjeve që i kemivizituar ose do të duhet të përdorim një variabël në nyje për t’a shënuar atë si tëvizituar, për të parandaluar (penguar) vizitat e shumëfishta.

Page 477: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 477/699

Algoritmet dhe strukturat e të dhënave

477

 Fig. 7.1 - Grafi

Shqyrtoni përsëri grafin nga Fig. 7.1. nëse fillojmë rrugëtimin (përshkimin) 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ë gjatësisë 2, ne nuk do të kthehemi në to për arsye se ato janë vizituarnë kalimin e parë). Në kalimin e tretë, vizitojmë nyjet 4 dhe 5 dhe në kalimin efundit vizitojmë nyjet 6 dhe 9.

Gjersa përshkimi thellësia-së-pari varej nga steku, përshkimi gjerësia-së-pari bazohet në radhën e pritjes (angl. queue –  radhë, bisht, gërshet, etj.). Algoritmi për përshkimin gjerësia-së-pari është:

BreadthFirstTraversal(G, v)

G is the graphv is the current nodeVisit( v )Mark( v )Enqueue( v )while queue is not empty doDequeue( x )for every edge xw in G doif w is not marked then

Visit( w )Mark( w )Enqueue( w )

end ifend forend while 

PershGjeresiaSePari(G, v)

G - Grafiv – nyja aktualeVizito( v )Shëno( v )Enqueue( v ) // në Queuewhile queue jo i zbrazëtDequeue(x)//largo nga Queue‘for’ secila degë xw në G ‘do’ ‘if’ w nuk është shënuar then 

Vizito( w )

Shëno( w )Enqueue( w )end if

end forend while 

Ky algoritëm do të shtojë rrënjën e pemës së përshkimit gjerësia-së-pari nëqueue (radhë të pritjes) por pastaj menjëherë do ta largojë atë. Pasi që “shikon”(i sheh) në nyjet që janë fqinje të rrënjës, ato do të shtohen në fund të radhës.

Kur të gjitha nyjet fqinjë të rrënjës të jenë vizituar, ne do të kthehemi në radhë

Page 478: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 478/699

Avni Rexhepi

478

dhe do të marrim të parën nga këto nyje. Do të duhej të vërenit se për shkak senyjet shtohen në fund të radhës, asnjë nyje që ndodhet dy rrugë/degë larg prejrrënjës nuk do të shqyrtohet përsëri gjersa të gjitha nyjet në distancë njërrugë/degë të jenë larguar nga radha dhe të jenë përpunuar (procesuar).

Si është puna me efikasitetin e këtyre algoritmeve? Supozimi i jonë është se puna e bërë gjersa vizitojmë secilën nyje është pjesa më komplekse e këtij procesi. Kështu, puna e bërë për të vërtetuar dhe për të parë nëse një nyje fqinjeka qenë e vizituar dhe puna për të përshkuar rrugët nuk është e rëndësishme nëkëtë rast. Kështu, rendi i algoritmit është numri i herave që nyja vizitohet. Pasiqë kemi thënë se këto algoritme e vizitojnë secilën nyje saktësisht një herë, përgrafin me N nyje, procesi i vizitimit do të kryhet N herë. Prandaj, përshkimet/rrugëtimet janë të rendit O(N).

Pema e shtrirjes minimale

Pema e shtrirjes (Spanning tree  –  pema e përhapjes etj) është një nënbashkësi elidhur e grafit e cila nuk ka cikle dhe përmbanë të gjitha nyjet e grafit dhenënbashkësi e degëve (rrugëve). Pema minimale e shtrirjes është pema e përhapjes ku shuma e peshave (kostove) për degët (rrugët) e përfshira ka totalinmë të vogël të mundshëm. Një shembull i përdorimit të pemës minimale tështrirjes është në konstruktimin e intranetit të kompanisë me routerët që duhet të

vendosen në pikat strategjike nëpër disa zona. Nëse dëshirojmë të minimizojmëshpenzimet e lidhjes së routerëve , ne do të mund të ndërtonim grafin me secilinrouter si nyje dhe me peshat në bashkësinë/setin e degëve si çmim/kosto elidhjes së secilit çift të routerëve. Pema minimale e përhapjes së këtij grafi do tëna tregojë se cilat çifte të routerëve të lidhen me përques ashtu që intraneti i jonëtë jetë i lidhur në tërësi me çmimin më të lirë të mundshëm.

Aplikim i ngjashëm është gjetja e rrugës më të shkurtër ndërmjet dy nyjeve tëgrafit. Kjo ka aplikim praktik gjatë planifikimit të rrugës për udhëtimin eveturave ose dërgimin e mesazhve nëpër rrjetën kompjuterike.

Për shembull, në figurën vijuese, janë paraqitur pemët e shtrirjes për grafin edhënë dhe shihet se rati i dytë dhe i tretë përfaqësojnë pemën minimale tështrirjes. Varësisth prej kostove të degëve, mund të ndodhë që grafi ka mëshumë se një pemë minimale të shtrirjes.

Page 479: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 479/699

Algoritmet dhe strukturat e të dhënave

479

 Fig. 7.2 –  Pemët e shtrirjes –  Pema minimale e shtritjes me kosto 22

Pema me shtrirje minimale e grafit të lidhur me peshë, është nëngraf i cili përmbanë të gjitha nyjet e grafit origjinal dhe nënbashkësinë e degëve/rrugëve tëtillë që (ashtu që) nëngrafi është i lidhur dhe totali i peshave të degëve/rrugëve

është më i vogli i mundshëm (angl. Span  –  shtrirje e krahëve, hapësirë, hapje,interval, etj). Nëse grafi origjinal nuk është i lidhur, procesi i mëposhtëm mundtë përdoret në secilën prej komponenteve të ndara për të prodhuar pemën eshtrirjes për secilën.

Ekziston një mënyrë e forcës së thjeshtë (e drejtpërdrejt) e cila mundëson që tëgjindet pema e shtrirjes minimale (angl. MST  –   shqip PSHM) për grafin elidhur. Pasi që degët/rrugët në PSHM janë nënbashkësi e degëve/rrugëve në tërëgrafin, ne do të mund të shikonim në të gjitha nënbashkësitë e mundshme të bashkësisë së degëve/rrugëve deri sa të gjejmë PSHM. Do të duhej të shihni se

ky proces kërkon shumë kohë. Së pari, nëse ka N degë/rrugë, do të kishte 2 N

 nënbashkësi. Për secilën prej këtyre nënbashkësive, do të duhej që së pari tëvërtetohet se ai shtrihet në të gjitha degët dhe nuk ka cikle. Pastaj do të mund tëllogarisnim peshat totale të tyre. Ne do të mund të përshpejtonim procesin pasiqë të kemi gjetur së pari pemën e parë të shtrirjes (përfshirjes). Ciladonënbashkësi e degëve/rrugëve me peshë totale që është më e madhe sesa ajomomentale e jona me pemën më të mirë të shtrirjes me gjasë nuk mund të performojë më mirë, kështu që nuk ka nevojë të verifikohet për të parë nëse aishtrihet në të gjitha nyjet dhe është jociklik. Edhe me këtë përmirësim, kjometodë e forcës së thjeshtë (e drejtpërdrejtë) do të ishte e rendit O(2 N).

Algoritmi Dijkstra-Prim

Algoritmi vijues për 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 pavarësisht nga njëri tjetri.

Për të gjetur PSHM, ata do të përdorin atë që njihet si “greedy” algoritmëmlakmitar (angl. Greedy –  llupës, i pangopur, tahmaqar, lakmitar, etj). Algoritmet

lakmitare punojnë duke shikuar në nënbashkësinë e problemit më të madh dhe

Page 480: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 480/699

Avni Rexhepi

480

duke bërë vendimin më të mirë bazuar në atë informacion. Në këtë rast, nësecilin hap të procesit, do të shikojmë në një grumbull (koleksion) tëdegëve/rrugëve potenciale për t’u shtuar në pemën e shtrirjes dhe do të zgjedhimatë me peshën më të vogël (minimale). Duke bërë këtë në mënyrë tëvazhdueshme (në mënyrë të përsëritur) ne do të rrisim pemën e shtrirjes e cila katotalin e përgjithshëm minimal.

Për të përmbushur (realizuar) këtë proces, ne do të konsiderojmë se nyjet e grafit janë në njërën prej tri kategorive: në pemë, në periferi të pemës dhe akoma të pa-shqyrtuara (të pa marrura në konsiderim). Ne fillojmë duke zgjedhur një nyjetë grafit dhe duke e vendosur atë në pemën e shtrirjes. Për shkak se rezultatiështë një pemë e parrënjë, zgjedhja e nyjes fillestare nuk ka ndikim në rezultatinfinal (përveq nëse ka PSHM të shumëfishta). Pastaj ne vendosim të gjitha nyjettë cilat janë të lidhura me këtë nyje fillestare në katëgorinë e periferisë(fqinjësisë). Kur të gjitha nyjet të jenë shtuar në pemë, kemi përfunduar.

Algoritmi i përgjithshëm për këtë proces është si vijon:

1. Zgjedhe nyjen startuese2. Nderto “vijat” fillestare prej nyjeve te lidhura me nyjen

startuese, periferine e saj3. While (Gjersa) ka akoma nyje të mbetura

zgjedhe degën me peshen me te vogëlshtoje nyjen e shoqëruar/bashkuar/lidhur në pemëazhuro “vijat” (periferinë) duke: -shtuar nyjet në periferi (të lidhura) me nyjën e re-azhuro degët me periferi ashtu që të jenë më të voglat

end while

Figura 7.3 jep një shembull të këtij algoritmi në veprim (gjatë punës). Nëmënyrë arbitrare kemi zgjedhur nyjen A për të filluar këtë proces. Si kemi thënë,zgjidhja e ndryshme për nyjen fillestare nuk do të ndryshojë rezultatitn, përveqnëse 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 bëhen bashkësia e fillestare e periferisë (fqinjësisë). Shohim se dega me peshën më të vogël i lidhë nyjet A dhe B, kështu që B shtohet në PSHM së bashku me rrugën AB.

Page 481: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 481/699

Algoritmet dhe strukturat e të dhënave

481

 Figura 7.3A

Grafi origjinal

 Figura 7.3B

 Nyja e parë u shtua (vijat e ndërprera

 paraqesin rrugët/degët deri tek nyjet e periferisë (fqinjësisë)

 Figura 7.3C  

U shtya nyja e dytë. Degët/rrugët tek

nyjet D, E dhe G u azhuruarn (Vijat e

 plota paraqesin rrugët 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ë përcaktojmë nëse ka nyjeqë duhet të shtohet në bashkësinë e periferisë/fqinjësisë dhe gjejmë se nyjet Edhe G duhet të shtohen. Për shkak se nyje e vetme e pemës për të cilën ato janëtë lidhura është nyja B, ne i shtojmë keto degë/rrugë tek ato të cilat do t’i marrimnë konsideratë në vazhdim. Në këtë kohë, gjithashtu duhet të verifikojmë për të parë nëse degët/rrugët prej nyjes A kah nyjet C, D dhe F janë akoma më tëshkurtërat ose nëse ka degë/rrugë më të mira nga nyja B tek këto tri nyje. Në

Page 482: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 482/699

Avni Rexhepi

482

grafin origjinal, nuk ka lidhje direkte nga nyja B tek nyjet C dhe F, kështu qëkëto nuk do të ndryshojnë. Por dega/rruga nga nyja B tek nyja D ka peshë më tëvogël sesa ajo nga nyja A dhe kështu dega/rruga BD tani e zëvendëson atë AD.

Prej pesë degëve/rrugëve tek nyjet në fqinjësi/periferi, shohim se BE ka peshënmë të vogël dhe kështu ajo dhe nyja E i shtohen pemës (Fig. 7.3(d)). Dega/rrugaEG ka peshë më të vogël sesa dega/rruga BG, kështu që tani kjo përdoret. Prejkatër degëve/rrugëve për në fqinjësi/periferi, shohim se AC ka peshë më tëvogël kështu që ajo shtohet në vazhdim.

 Figura 7.3E

 Nyja C e shtuar në pemë.

 Figura 7.3F

 Nyja F e shtuar në pemë dhe

degët/rrugët D dhe G janë azhuruar.

Shtimi i nyjes C dhe degës/rrugës AC në pemën e shtrirjes (Fig. 7.3(e)) nuk ka bërë që ndonjë degë/rrugë të azhurohet. Në vazhdim zgjodhëm degën/rrugënAF, kështu që ajo dhe nyja F janë shtuar në pemë. Ne gjithashtu azhurojmëlidhjet sepse dega/rruga FD ka peshë më të vogël sesa BD dhe dega/rruga FG ka peshë më të vogël sesa EG. Në fqinjësinë/periferinë rezultuese (Fig. 7.3(f)),shohim se dega/rruga FD tani është dega/rruga e mbetur me peshën më të vogël,kështu që kjo shohet në vazhdim.

Tani kemi vetëm një nyje që nuk i është shtuar pemës (Fig. 7.3(g)). Kur tështohet kjo, procesi kompletohet dhe ne kemi përcaktuar PSHM me rrënjë nënyjen A (Fig. 7.3(h)).

Page 483: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 483/699

Algoritmet dhe strukturat e të dhënave

483

 Figura 7.3G

Vetëm një nyje ka mbetur në

 fqinjësi/periferi.

 Figura 7.3H

 PSHM e kompletuar me rrënjë në

nyjen A.

Algoritmi i Kruskal-it

Gjersa algoritmi Dijkstra-Prim filloi në një nyje të caktuar dhe e ndërtoi PSHMtutje (te jashtë), algorimti i Kruskal-it koncentrohet më shumë në degët/rrugët egrafit.

 Në këtë algoritëm, ne fillojmë me një pemë të zbrazët të shtrirjes dhe i shtojmëdegët/rrugët në renditje sipas madhësisë së peshës gjersa të gjitha nyjet të jenëlidhur në graf. Nëse 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ë PSHMe secilës prej komponenteve të lidhura të grafit origjinal.

Fillojmë në Fig. 7.4(a) me grafin e njëjtë që e përdorëm për algoritmin Dijkstra-Prim. Në këtë rast, së pari e shtojmë degën/rrugën me peshën më të vogël, e cilaështë ajo ndërmjet nyjeve D dhe F, duke dhënë rezultatin parcial (e pjesshëm) nëFig. 7.4(b).

Dega/rruga me peshë 2 shtohet në vazhdim (Fig. 7.4(c)) ndërmjet nyjeve A dhe

B dhe pastaj shtohet dega/rruga me peshë 3, duke dhënë Fig. 7.4(d).Degët/rrugët me peshat 4 dhe 5 shtohen në vazhdim të rezultatit tonë, ashtu simund të shihni në Fig. 7.4(e) dhe Fig. 7.4(f). Vetëm nyja G është akoma e palidhur. Degët/rrugët e ardhshme për t’u shqyrtuar janë ato me peshë 6. 

Prej katër degëve/rrugëve me peshë 6, dy përjashtohen (hudhen poshtë) përshkak se ato do të krijon cikle të cilat përmbajnë nyjen A dhe dega/rrugandërmjet nyjes B dhe nyjes D do të formonte cikël që i përfshinë nyjet A dhe F.Dy nyjet tjera janë të dyja alternativa të mira dhe varësisht prej asaj tëzgjedhurës, fitojmë PSHM në cilëndo prej Fig. 7.4(g) ose Fig. 7.4(h).

Page 484: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 484/699

Avni Rexhepi

484

 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 katërt.

 Figura 7.4F

Shtohet dega/ruga e pestë.

Page 485: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 485/699

Algoritmet dhe strukturat e të dhënave

485

 Figura 7.4G

 Pema e shtrirjes minimale.

 Figura 7.4H

 Një pemë tjetër/alternative e shtrirjes

minimale.

Algoritmi i përgjithshëm i cili do të përmbushë/realizojë këtë është (ku E paraqet numrin e degëvge/rrugëve dhe N numrin e kulmeve/nyjeve).

1.Sorto degët sipas peshës (prej të voglës)Inicializo strukturën e ndarjes (particioneve)numriDegeve = 1numratoriePerfshire = 0while numriDegeve ≤ E and numratoriePerfshire ≤ N-1 do

prindi1 = GjejeRrenjen ( dega [numriDegeve].start )prindi2 = GjejeRRenjen ( edge[numriDegeve].end )if prindi1 ≠ prindi2 thenshto dega[numriDegeve] në pemën e shtrirjesnumratoriePerfshire = numratoriePerfshire + 1Union(prindi1, prindi2)

end ifnumriDegeve = numriDegeve + 1

end while

Unaza e jonë kryesore do të vazhdojë gjersa variabla numriDegeve të tregojë

se ne kemi shikuar në të gjitha degët/rrugët ose numratoriePerfshire tëtregojë se ne kemi shtuar mjaftë degë/rrugë për të krijuar pemën e shtrirjes. Dotë duhej të shihnit se nëse kemi N nyje në graf, pema e shtrirjes do të ketë njëdegë/rrugë më pak sesa nyje.

Brenda unazës, së pari gjejmë prindërit e dy nyjeve që janë të lidhura ngadega/rruga e ardhëshme që jemi duke e marrë në konsiderim. Nëse ato nyje janënë pjesë (copa, particione) me rrënjë të ndryshme, duke shtuar një degë/rrugëndërmjet tyre nuk do të krijojë cikël, kështu që kjo degë/rrugë aktuale mund tështohet në PSHM dhe këto dy pjesë mund të bashkohen ashtu që ato tani kanë

Page 486: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 486/699

Avni Rexhepi

486

rrënjë të njëjtë. Detajet e rutinave (nënprogrameve) GjejeRrenjen dhe Union do të jepen në seksionin 6.7.

Kompleksiteti i këtij algoritmi do të jetë kompleksiteti i algoritmit të sortimit të

 përdorur për shkak se unaza While është e lidhur linearisht me numrin edegëve/rrugëve. Kjo e bën kompleksitetin e algoritmit të PSHM-së së Kruskal-itO(E lg E).

Algoritmi i shtegut më të shkurtër

Algoritmi i shtegut/rrugës më të shkurtër (angl. Shortest-Path Algorithm) për dynyje të caktuara do të gjejë seritë e degëve/rrugëve ndërmjet tyre të cilat do tërezultojnë në peshën totale më të vogël (minimale) të rrugës.

Mund të duket se do të mund të përdornim pemën e shtrirjes minimale për të“krasitur” (shkurtuar) disa nga degët/rrugët dhe pastaj vetëm të shikohet përshtegun ndërmjet nyjeve në pemën e shtrirjes. Fatkeqësisht, kjo nuk do të prodhojë gjithmonë shtegun/rrugën më të shkurtër. Rikujtoni se algoritmi i pemës së shtrirjes minimale mundohet të gjejë një total të përgjithshëm që ështëmë i vogli, kështu që ai do të shikojë për peshat më të vogla të mundshme. Përshembull, mendoni për grafin i cili është “rrethor” për nga forma. Me fjalë tëtjera, nyja e parë është e lidhur me të dytën, e cila është e lidhur me të tretën ekështu me radhë deri tek nyja e fundit, e cila është e lidhur me të parën. Ky graf

është një unazë ku secila nyje është e lidhur saktësisht me dy nyje, nga një nësecilën anë të saj. Për shembull, Fig. 7.5(a) paraqet grafin me gjashtë nyje.Vëreni se të gjitha peshat në të gjitha degët/rrugët janë 1, përveq përdegën/rrugën prej nyjes A tek nyja B, e cila ka peshën 2. Algoritmi i pemës sështrirjes minimale (PSHM) do të zgjedhë të gjitha degët/rrugët me peshë 1 dhedo të hedhë poshtë (përjashtojë) degën me peshë 2. Por kjo do të thotë se shtegundërmjet nyjes A dhe nyjes B në pemën e shtrirjes minimale (Fig. 7.5(b)) duhettë shkojë/kalojë nëpër të gjitha nyjet tjera për shtegun me gjatësi 5. Kjo qartazinuk është shtegu me i shkurtër, sepse në Fig. 7.5(a) mund të shihni se ekziston

shteg direkt ndërmjet nyjes A dhe nyjes B i cili ka peshë 2.

Page 487: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 487/699

Algoritmet dhe strukturat e të dhënave

487

 Figura 7.5A

Grafi i unazës.

 Figura 7.5B

 Pemë e shtrirjes minimale të tij.

Algoritmi i Dijkstra’sAlgoritmi i pemsës së shtrirjes minimale nuk do të funksionojë për gjetjen eshtegut më të shkurtër sepse greedy algoritmi i tij merr parasysh peshën e vetëmnjë dege/rruge në secilin kalim. Nëse e ndryshojmë algoritmin ashtu që aizgjedhë degën/rrugën deri në fqinjësi e cila është pjesë e shtegut më të shkurtërtë tërsishëm që prej nyjes fillestare/startuese, atëherë do të fitojmë rezultatin edëshiruar. Më saktësisht, algoritmi i jonë tani bëhet si vijon:

select a starting nodebuild the initial fringe from nodes connected to the startingnodewhile we are not at the destination node do

choose the fringe node with the shortest path to thestarting node

add that node and its edge to the treeupdate the fringe by:adding nodes to the fringe connected to the new nodefor each node in the fringe do

update its edge to the one connected to the treeon the shortestpath to the starting node

end forend while

Figura 7.6 paraqet një shembull të ekzekutimit të këtij algoritmi. Ne fillojmë megrafin e njëjtë të cilin e përdorëm për algoritmin e pemës së shtrirjes minimale(të riprodhuar në Fig. 7.6(a)) dhe do të kërkojmë për shtegun më të shkurtër qëfillon në nyjen A dhe mbaron në nyjen G.

Page 488: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 488/699

Avni Rexhepi

488

 Figura 7.6A

Grafi origjinal.

 Figura 7.6B

Shtegu më i shkurtër ësthë deri tek

nyja B.

 Figura 7.6C

Shtegu me gjatësi 4 për tek nyja Cështë më i shkurtëri prej

opcioneve.

 Figura 7.6D

Shtegu me gjatësi 5 çoftë deri teknyja E ose nyja F, është më i

 shkurtëri.

Fillimi i shtegut tonë nga nyja A jep katër degë/rrugë të mundshme për t’ushqyrtuar. Prej këtyre katërve, dega/rruga AB është më e shkurtëra.

 Nyja B shtohet në pemën tonë të shtegut më të shkurtër (Fig. 7.6(c)) dhe tani bëjmë azhurimin e shtigjeve. Tash mund të arrihen edhe nyjet E dhe G, kështu

që edhe ato shtohen. Gjithashtu shikojmë në nyjen D dhe krahasojmë shtegun e

Page 489: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 489/699

Algoritmet dhe strukturat e të dhënave

489

saj direkt prej nyjes A me gjatësi 7 me shtegun i cili shkon nëpër nyjen B, i ciliështë me gjatësi 8. Pasi që shtegu direkt është më i shkurtër, nuk ka ndryshim nërrugën për tek nyja D. Duke shikuar opcionet, shohim se shtegu nga nyja A teknyja C është me gjatësi 4 dhe është më i shkurtëri. Dega/rruga BE është më eshkurtër, por tani jemi duke marrë në konsiderim rrugën e tërë prej nyjes A dhekështu gjatësia e shtegut deri tek nyja E aktualisht është 5. Nyja C shtohet në pemën e shtegut më të shkurtër (Fig. 7.6(d)). Duke analizuar grafin, shohim semund të arrijmë tek nyja F nëpër nyjen C, por gjatësia totale e shtegut është 10,që është më e gjatë sesa shtegu momental për tek nyja F, prandaj nuk ka ndonjëndryshim.

Duke pasur situatën në Fig. 7.6(d), ne do të mund të zgjedhim çoftë shtegun prejA tek F ose shtegun prej A tek E i cili kalon nëpër nyjen B, sepse ata të dy janëme gjatësi 5. Ai që zgjedhet, gjatë ekzekutimit të programit do të varet ngamënyra se si janë ruajtur të dhënat. Për qëllimet tona, kur të na paraqitetzgjedhja, do të zgjedhim nyjen e cila është më e vogël/afërt alfabetikisht, si nëFig. 7.6(e). Për shkak se shtimi i nyjes E në graf nuk ka ndryshuar ndonjë prejlidhjeve ekzistuese, ne tani zgjedhim nyjen F për të arritur në Fig. 7.6(f). Do tëduhej të shihni se edhe pse zgjedhja e nyjes F ndryshoi degën/rrugën për teknyja D, sikur të kishim zgjedhur së pari nyjen F, ne do të kishim zgjedhur nyjenE të dytën.

 Figura 7.6E

Shtegu tjetër me gjatësi 5 për

tek nyja F është i ardhëshmi

 Figura 7.6F

Shtegu me gjatësi 6 për tek nyja D është

më i shkurtër sesa shtegu për tek nyja

G.

 Në Fig. 7.6(f), duhet të jetë e qartë se shtegu për tek nyja D është më i shkurtërsesa shtegu tek nyja G. Zgjedhja e nyjes D rezulton me Fig. 7.6(g) dhe pastaj

Page 490: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 490/699

Avni Rexhepi

490

nyja G është e fundit që duhet të shtohet, duke dhënë pemën e shtegut minimalfinal në Fig. 7.6(h). Shtegu më i shkurtër prej nyjes A deri tek nyja G ka gjatësi10. Nëse shikojmë prapa në Fig. 7.3(h), do të shihni një shembull tjetër të pemëssë shtrirjes minimale e cila nuk ka shtegun më të shkurtër, sepse ajo figurë kashtegun prej nyjes A deri tek nyja G me gjatësi 11.

 Figura 7.6G

Shtegu për tek nyja G është

i vetmi i mbetur.

 Figura 7.6H

 Pema e shtegut më të shkurtër

komplet që fillon në nyjen A.

 Në shembullin në Fig. 7.6, kemi pemën e plotë të shtegut më të shkurtër për

nyjen A sepse nyja e jonë e cakut ishte e fundit që duhej të shtohet. Sikur tëkishim arritur nyjen G më herët, algoritmi do të ishte ndalur në atë pikë. Kaapkiacone ku ne do të mund të ishim të interesuar për shtegun më të shkurtërnga një nyje tek secila nyje tjetër. Për shembull, nëse kemi një rrjetë të vogëlkompjuterike e cila ka shpejtësi të transmetimit relativisht stabile ndërmjetnyjeve, ne do të mund të llogarisnim shtegun më të shkurtër për tek secila nyjetjetër për secilin kompjuter. Pastaj, kur të duhet të dërgohet një mesazh, ne nukka nevojë të bëjmë asgjë tjetër pos t’i qasemi tabelës sonë të paracaktuar tështegut-më-të-shkurtër për të gjetur mënyrën më të shpejtë për të dërguar

mesazhin.

Page 491: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 491/699

Algoritmet dhe strukturat e të dhënave

491

Topologjia

Topologjia është një prej degëve më të reja të matematikës. Një mënyrë ethjeshtë për të përshkruar topologjinë është si një “geometri e sipërfaqës së

lakueshme” –  topologjistët i studiojnë tiparet e formave të cilat mbesin të njëjtakur format tërhiqen/zgjaten ose shtypen/kompresohen.

Fillet e topologjisë i vendosi Leonhard Euler-i në vitin 1735 gjatë punës së tij tëinspiruar nga problemi i “shtatë urave të Konigsberg-ut” (tash i quajturKaliningrad –  pas pushtimit ne fund të luftës së dytë botërore dhe tash pjesë eRusisë) .

 Në Konigsberg, lumi rrjedhë në qytet në atë mënyrë që në qendër të tij ndodhetnjë ishull dhe pas kalimit të ishullit rrjedha e lumit ndahet në dy pjesë. Për tëkaluar prej njërës pjesë në tjetrën ishin ndërtuar shtatë ura. Një hartë e thjeshtë eqendrës së Konigsberg-ut do të dukej si në vijim:

 Njerëzit pyesnin veten a është e mundur që dikush të kalojë nëpër të gjitha pjesët e qytetit në atë mënyrë që të kalojë nëpër secilën urë vetëm një herë.

Problemi 1 Provoni. Skiconi hartën e mësipërme të qytetit në një fletë dhe vizatoni rrugën ekaluar me një laps ashtu që të kaloni nëpër secilën urë vetëm një herë dhe tëkompletoni rrugën pa e ngritur lapsin nga fleta.

A po keni problem? Në rregull është, mos u brengosni, sepse këtë problem e patiedhe Euler-i. Nuk është e mundur që të kalohet secila urë vetëm një herë. Për ta

kuptuar se përse, duhet shikuar zgjidhja e problemit nga Euler-i, e njohur siShkalla e Nyjës.

Tentime të dështuara:

Page 492: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 492/699

Avni Rexhepi

492

Problemi 2 Supozoni se do të ishte ndërtuar një urë më pak në Konigsberg, ashtu që harta të

dukej si në vijim:

Tani, zgjidhja e problemit është e mundur. Ja një prej mudnësive:

Çka e bënë këtë të ndryshme nga problemi real i Konigsbergut? Udhëzim: Saura dërgojnë në secilën pjesë të qytetit? Përse është problematike kur ka numërtek të urave që dërgojnë në një pjesë të qytetit?

Problemi 3 A ka rëndësi se cilën urë e largoni? Çka nëse e shtoni një urë? Provoni!

Zgjidhja e Euler-it: Shkalla e nyjes

Euler-i iu qas problemit duke bashkuar pjesët e tokave të ndara nga lumi në

 pika, të cilat i shënoi me shkronja të mëdha. Në teorinë moderne të grafeve,

Page 493: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 493/699

Algoritmet dhe strukturat e të dhënave

493

këto quhen nyje dhe kanë vazhduar që të përfaqsojnë ato dhe urat në mënyragrafike.

Për rastin e Konigsberg-ut, le të përfaqësojmë pjesët e qytetit (tokët) me pika të

kuqe dhe urat me lakore të zeza (harqe, degë):

Prandaj, në versionin e thjeshtuar, problemi i shtatë urave të Konigsberg-utduket si në vijim:

Tani problemi shndërrohet në problemin e vizatimit të kësaj figure, pa e larguarlapsin nga fleta dhe pa kaluar dy herë nëpër ndonjërën pjesë të rrugës.

 Ju kujtohet sigurisht sfida e vizatimit të shtëpisë, në këtë mënyrë:

Merrni në shqyrtim sa vijon: të gjitha katër nyjet, në figurën e mëparshme tërastit të Konigsberg-ut kanë një numër tek të degëve (harqeve) të cilat i lidhinato. Merrni njërën prej tyre dhe filloni rrugëtimin me laps. Herën e parë që vininë nyje, ju mund të largoheni nëpër një rrugë tjetër, mirëpo herën e ardhëshmeqë arrini në të njëjtën nyje, nuk mund të dilni?! Prandaj, secila nyje me një

numër tek të rrugëve të lidhur në të duhet të jetë ose fillimi ose fundi i shtegut të

Page 494: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 494/699

Avni Rexhepi

494

vizatuar me laps. Kështu, mund të keni vetëm deri në dy nyje “teke”. Prandaj,është e pamundur që të vizatohet figura e mësipërme, me një të “shkruar” tëlapsit, pa e ngritur nga fleta ose pa ri-kaluar në ndonjërën degë.

Përgjithësimi në teorinë e grafeveEuler-i vazhdoi me përgjithësimin e kësaj mënyre të të menduarit, dukevendosur kështu bazat e teorisë së grafeve. Me fjalorin modern, bëhendefinicionet vijuese dhe vërtetohet teorema:

Definicion: Një rrjetë është figurë e përpbërë prej pikave (nyjeve) të lidhura medegë (lakore) që nuk priten.

Definicion: Nyja quhet teke nëse ka një numër tek të degëve të cilat shkojnë nëtë, përndyshe quhet nyje çifte.

Definicion: Shteg i Euler-it është shtegu i vazhdueshëm i cili kalon nëpërsecilën nyje një dhe vetëm një herë.

Teoremë: Nëse grafi (rrjeti) ka më shumë se dy nyje teke, ai nuk ka shteg të

Euler-it.

Euler-i poashtu vërtetoi këtë:

Teoremë: Nëse grafi (rrjeti) ka dy ose zero nyje teke, ai ka së paku një

shteg të Euler-it. Në veçanti, nëse rrjeti ka saktësisht dy nyje teke, atëherë

shtegu i tij i Euler-it mund të fillojë në njërën prej tyre dhe të përfundojë

në tjetrën.

Problemet

Për secilin prej grafeve vijuese, përcaktoni nëse ka një shteg të Euler-it. Nëse ka,gjejeni një prej tyre.

 Figura 1  Figura 2

Page 495: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 495/699

Algoritmet dhe strukturat e të dhënave

495

 Figura 3  Figura 4

 Figura 5 Figura 6

 Zgjidhjet:

 Figura 1 Figura 2

 Figura 3 Figura 4

 Këtu ka katër nyje teke,

randaj nuk ka zgjidhje

Page 496: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 496/699

Avni Rexhepi

496

 Figura 5 Figura 6

 Nuk ka shteg të Euler-it.

Cikli Euler-ian

Përshkrimi i problemit: Gjeni rrugëtimin më të shkurtër në grafin G, dukekaluar secilën degë së paku një herë.

Diskutimi: Supozoni se ju është dhënë harta e një qyteti dhe ju është dhënëdetyra e dizajnimit të itinerarit (rrugës së qarkullimit) për kamionët e mbledhjessë mbeturinave, pastrimit të borës ose postierit. Në të gjitha këto raste, secilarrugë e qytetit duhet të kalohet së paku një herë, që të sigurohet se janë kryergrumbullimet/dorëzimet.

Për efikasitet, kërkohet të minimizohet koha totale e udhëtimit ose në mënyrëekuivalente, distanca totale ose numri i degëve të përshkuara. Aplikaiconet e

këtilla janë variante të problemit të ciklit të Eulerit, i karakterizuar më së miri përmes enigmës (angl. puzzle) së fëmijëve, kur atyre ju kërkohet të vizatojnëndonjë figurë të caktuar, pa e ngritur lapsin dhe pa përsëritur ndonjë degë (vijë,rrugë) gjatë vizatimit. Pra, kërkojmë ciklin nëpër graf që viziton secilën degësaktësisht një herë.

Janë të përcaktuara mirë kushtet për përcaktimin nëse grafi përmbanë cikël tëEuler-it ose shteg të Euler-it:

1.  Grafi i pa-drejtuar (pa-orientuar) përmbanë një cikël të Euler-it nëse (1)

është i lidhur dhe (2) secila nyje e tij është e shkallës çifte.2.  Grafi i pa-drejtuar (pa-orientuar) përmbanë një shteg të Euler-it nësë (1)

është i lidhur dhe (2) të gjitha përveq dy nyjeve të tij janë të shkallësçifte. Këto dy nyje do të jenë fillimi dhe fundi i shtegut.

3.  Grafi i drejtuar (orientuar) përmbanë cikël të Euler-it nëse (1) është ilidhur dhe (2) secila nyje ka shkallë të njejtë të hyrjes sikur atë të daljes.

4.  Së fundi, grafi i drejtuar (orientuar) përmbanë cikël të Euler-it nëse (1)është i lidhur dhe (2) të gjitha, përveq dy nyjeve kanë shkallë të njëjtë të

Page 497: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 497/699

Algoritmet dhe strukturat e të dhënave

497

hyrjeve dhe daljeve dhe këto dy nyjet kanë dallim për një në shkallë tëhyrjes dhe daljes.

Me karakterizimin e dhënë të grafeve Euler-iane, është e lehtë që në kohë

lineare të testohet nëse ekziston cikli i tillë: testoni a është grafi i lidhur duke përdorur DFS ose BFS dhe pastaj numëroni numrin e nyjeve me shkallë teke. Në fakt, konstruktimi i ciklit të tillë merr kohë lineare. Përdorni DFS për tëgjetur ciklin në graf.

Fshini këtë cikël dhe përsëritni deri sa i tërë seti i degëve të jetë ndarë në një settë cikleve të degëve të ndara. Pasi që fshirja e ciklit redukon shkallën e secilësnyje për një numër çift, grafi i mbetur do të vazhdojë të kënaqë të njejtat kushteEuleriane të shkallës së nyjeve. Për cilindo graf të lidhur këto cikle do të kenënyje të përbashkëta dhe kështu duke ndarë këto cikle në “figurën tetë” në nyjen

e bashkëndarë, mund të konstruktojmë një qark të vetëm që i përmbanë të gjithadegët.

 Një cikël Eulerian, nëse ekziston një, e zgjidhë problemin e pastrimit të borës, pasi që cilido rrugëtim që do të vizitojë të gjitha degët nga një herë duhet të ketëgjatësinë minimale. Sidoqoftë, pak ka të ngjarë që ndonjë rrjet real i rrugëve dotë kënaqë kushtet që ta bëjnë atë qark të Eulerit. Na duhet të zgjidhim probleminmë të përgjithësuar të Postierit Kinez (angl. Chinese postman problem), i ciliminimizon gjatësinë e rrugës që e kalon secilën degë së pakunjë herë.

 Në fakt, mund të tregohet se ky cikël minimal nuk do të vizitojë asnjëherëndonjë degë më shumë se dy herë, kështu që rruga (xhiroja, itinerari) ekziston për cilindo rrjet të rrugëve.

Turi optimal i postierit mund të konstruktohet duke shtuar degët e duhura nëgrafin G ashtu që ta bëjë atë Eulerian. Në mënyrë specifike, ne gjejmë shtegunmë të shkurtër ndërmjet secilit çift të nyjeve me shkallë teke në grafin G.

Duke shtuar shtegun ndërmjet dy nyjeve me shkallë teke në G i kthen ato nënyje me shkallë çifte, prandaj duke na dërguar më afër një grafi Eulerian. Gjetjae setit më të mirë të shtigjeve më të shkurtëra për t’u shtuar në G redukohet në

identifikimin e përshtatjes perfekte me peshë minimale në graf në nyjet meshkallë teke, ku pesha e degës (i,j) është gjatësia e shtegut më të shkurtër prejnyjes i në nyjen j. Për grafet e drejtuara, kjo mund të zgjidhet me përshtatjen bipartite, ku nyjet ndahen varësisht nga fakti se a kanë më shumë degë hyrëseapo dalëse. Kur grafi një herë është Eulerian, cikli aktual mund të nxirret nëkohë lineare duke përdorur procedurën e përshkruar më parë.

Page 498: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 498/699

Avni Rexhepi

498

8.  Algoritmet e kërkimit

Sistemet kompjuterike shpeshherë përdoren për të ruajtur sasi të mëdha të tëdhënave, prej të cilave duhet të nxirren rekordet individuale në bazë të ndonjëkriteri të kërkimit, prandaj një çështje me shumë rëndësi është ruajtja efikase etë dhënave për mundësuar kërkim të shpejtë. Njëri nga operacionet më të performuara në të dhënat e ruajtura në kompjuter është kërkimi. Kërkimi bëhetduke krahasuar vlerën e kërkuar (çelësin) me anëtarët e bashkësisë sëelementeve. Në momentin që kushti i krahasimit tregon përputhje (angl. match- përputhje, barazim, etj), do të thotë që është gjetur vlera e kërkuar. Me rëndësiështë numri i krahasimeve të bëra deri në gjetjen e elementit të kërkuar.

 Në përgjithësi, për të gjetur një vlerë në një varg të pasortuar duhet shikuarnëpër elementet e vargut, një nga një, deri sa të gjindet vlera e kërkuar. Nësevlera e kërkuar nuk ndodhet në varg, do të kalohet nëpër të gjitha vlerat e vargut.Mesatarisht, kompleksiteti i një algoritmi të tillë është proporcional me gjatësinëe vargut.

Situata ndryshon dukshëm kur vargu është i sortuar. Nëse e dijmë se vargu ështëi sortuar, mundësia e qasjes së rastit mund të shfrytëzohet në mënyrë shumëefikase për të gjetur shumë shpejt vlerën e kërkuar. Kostoja e algoritmit tëkërkimit zvogëlohet në logaritmin binar (logaritmin me bazë 2) të gjatësisë së

vargut. Për referencë, log2(1,000,000) ≈ 20. Kjo do të thotë se, në rastin më tëkeq, algoritmi i bën 20 hapa, për të gjetur vlerën e kërkuar në vargun e sortuar prej 1 milion elementeve ose për të treguar që ajo nuk gjendet në varg.

Pra, kërkimi i një liste të parenditur të të dhënave bëhet në mënyrë lineare, dukekërkuar prej fillimit të listës kah fundi i listës, deri sa të “takohet” elementi ikërkuar ose deri sa të arrihet në fund të listës dhe të arrihet në përfundimin seelementi i kërkuar nuk gjindet në listë. Kjo mënyrë e kërkimit njihet si kërkimi

sekuencial. Nëse lista është e sortuar dhe e ruajtur në një strukturë të përshtatshme të të dhënaev, kërkimi mund të bëhet në mënyrë shumë më

efikase. Kërkimi i listës së sortuar mundëson përgjysmimin e hapësirës sëkrahasimit (kërkimit), në çdo hap. Ky kërkim njihet si kërkimi binar.

Kërkimi sekuencial

Kërkimi sekuencial, bën krahasimin e njëpasnjëshëm të të gjithë anëtarëve tëlistës, me anëtarin që kërkohet (cakun). Pseudokodi i këtij kërkimi është sivijon:

KerkimiSekuencial( lista, caku, N )//lista elementet në të cilat kërkohet caku

//caku vlera e cila kërkohet

Page 499: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 499/699

Algoritmet dhe strukturat e të dhënave

499

//N numri i elementeve në listë

for i = 1 to N doif (caku = lista[i])

return i //lokacioni-pozita ku u gjet cakuend ifend forreturn 0;

Le të shqyrtojmë se sa kohë duhet për të gjetur elementin që i përshtatet çelësittë kërkimit (vlerës që kërkohet) në bashkësinë e elementeve. Ne jemi tëinteresuar për:

a.  kohën mesatare, b.  kohën e rastit më të keq, dhe

c.  kohën e rastit më të mirëSidoqoftë, në përgjithësi do të jemi më të brengosur për kohën e rastit më të keq, pasi që llogaritjet e bazuara në kohën e rastit më të keq mund të dërgojnë në parashikimet e performansës së garantuar. Për fat, koha e rastit më të keq në përgjithësi llogaritet më lehtë sesa koha e rastit mesatar.

 Nëse kemi n elemente në bashkësinë e kërkimit, çoftë ajo e ruajtur si varg ose silistë e lidhur, është e qartë se në rastin më të keq, kur asnjëri element në bashkësi nuk është me vlerën e kërkuar të çelësit, atëherë duhet të bëhen nkrahasime të çelësit me elementet e bashkësisë.

Për të thjeshtuar analizën e algoritmeve të krahasimit, kërkojmë operacionindominant dhe numërojmë numrin e herave të kryerjes së operacionit dominant. Në rastin e kërkimit, operacioni dominant është operacioni i krahasimit, e pasiqë në rastin më të keq kërkimi kërkon n krahasime, themi se është algoritëm irendit O(n) (thuhet Big-O n).

Rasti më i mirë, në të cilin krahasimi i parë kthen përputhje, kërkon njëkrahasim të vetëm dhe është O(1).

Koha mesatare varet nga gjasa (probabiliteti) i gjetjes së çelësit në bashkësinë e

vlerave, gjë që është diçka që nuk është e prithshme ta dijmë në shumicën erasteve. Prandaj, në këtë rast, si në shumicën e të tjerave, vlerësimi i kohësmesatare është me dobi të vogël. Nëse performansa e sistemit është vitale,d.m.th., është pjesë e ndonjë sistemi kritik për jetë, atëherë në llogaritë përdizajn duhet të përdoret rasti më i keq, pasi që ai përfaqëson performansën më tëmirë të mundhsme të garantuar.

Page 500: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 500/699

Avni Rexhepi

500

Kërkimi binar

 Nëse elementet i vendosim në një varg dhe i sortojmë në renditje rritëse osezbritëse së pari sipas çelësit, atëherë mund të përfitojmë performansë shumë më

të mirë me algoritmin e kërkimit binar. Në kërkimin binar, caktohen vlerat e ‘kurfirit të poshtëm’ (vlera e parë), ‘kufirittë epërm’ (vlera e fundit) dhe ‘vlera e mesit’. Së pari krahasohet çelësi meelementin në pozitën e mesit të vargut. Nëse ka përputhje, d.m.th., elementi ikërkuar është gjetur, mund të kthejmë rezultatin dhe të përfundojmë kërkiminmenjëherë. Nëse çelësi i kërkuar është më i vogël se çelësi i vlerës së mesit,atëherë elementi i kërkuar duhet të jetë në gjysmën e poshtme të vargut.Përndryshe, nëse është më i madh, atëherë elementi i kërkuar duhet të jetë nëgjysmën e epërme të vargut. Në mënyrë të njëjtë e përsërisim në mënyrë

rekurzive procedurën në gjysmën e poshtme (e epërme) të vargut. Çdo krahasimlargon nga kërkimi i mëtejmë gjysmën e mbetur të vlerava, në secilin hap.Vazhdojmë duke përshtatur vlerat e kufijve të pjesës së mbetur për kërkim.

KerkimiBinar(lista, caku, N )//lista elementet në të cilat kërkohet caku//caku vlera e cila kërkohet//N numri i elementeve në listë

kp = 1 //kp - kufiri i poshtem, fillimike = N //ke – kufiri i epërm, fundi

while kp ≤ ke domesi = (kp + ke) / 2selekto (Krahaso(lista[mesi], caku)) nga

case -1: kp = mesi + 1case 0: return mesicase 1: ke = mesi - 1

end selekto //perfundo selektiminend whilereturn 0 

Pra, tiparet e kërkimit binar janë:

a.  kërkimi binar është rekurziv: ai përcakton nëse vlera e kërkuar ndodhetnë gjysmën e poshtme ose të epërme të vargut dhe pastaj e thërretvetëveten për gjysmën përkatëse të mbetur.

 b.  Kemi kushtin e ndërprerjes së kërkimit (në fakt dy!)i.   Nëse ‘kufiri i poshtëm’ > ‘kufiri i epërm’, atëherë particioni që

duhet kërkuar nuk ka më elemente në të (vlera nuk është gjetur),dhe

ii.   Nëse ka përputhje me elementin e pozitës së mesit të particionitaktual, atëherë mund të kthejmë rezutlatin menjëherë.

Page 501: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 501/699

Algoritmet dhe strukturat e të dhënave

501

Analiza:

Do të marrim se vargu i elementeve është vargu A me n elemente: A[n]. Le tëshënojmë me kp-kufiri i poshtëm, ke-kufiri i epërm, mesi-lokacioni në mes dhe

vk-vlera e kërkuar (çelësi). Në fund, do të arrijmë në hapin kur vlera e kërkuarështë e barabartë me vlerën në kufirin e epërm ose të poshtëm (d.m.th., vleragjindet në varg) ose kur kufiri i poshtëm bëhet më i madhe se kufiri i epërm(d.m.th., vlera nuk gjinder në varg fare). Shihet, se në secilin hap të krahasimit,numri i vlerave për krahasim përgjysmohet.

 A[n]; n elemente

~n/2 elemente

~n/4 elemente

kekp   mesi

*

  *

  *log2n hapa

kekpmesi

kp kemesi

vk<A[mesi]

vk>A[mesi]

vk=A[mesi]

 

 Fig. x –  Kërkimi binar

Secili hap i algoritmit e ndanë bllokun e elementeve që kërkohen në gjysmë.Bashkësinë e n elementeve mund ta përgjysmojmë më së shumti log2 n  herë.

Prandaj, koha e ekzekutimit të kërkimit është proporcionale me log n dhe themise ky algoritëm është i rendit O(log n). Kjo do të thotë se numri maksimal ikërkimeve për vargun me 1 milion elemente është më pak se 20 (pasi që 2 20>1milion).

Page 502: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 502/699

Avni Rexhepi

502

 Fig. x –  Shkalla e rritjes për funksionet lineare dhe llogaritmike

Kërkimi binar për numër të vogël të vlerave të hyrjes, n, mund të ekzekutohetmë ngadal se algoritmi i thjeshtë linear, mirëpo për vlera të mëdha të n-it,

0log

lim   n

n

Prandaj, për n të madh, log n është shumë më i vogël se n, kështu që algoritmiO(log n) është shumë më i shpejtë sesa ai O(n).

Sa i përket insertimit, në rastin më të keq, insertimit mund të kërkoj noperacione për të insertuar një vlerë në listën e sortuar.

1.  Mund të gjejmë vendin në listë ku duhet të vendoset elementi i ri, duke përdorur kërkimin binar me O(log n) opearcione.

2.  Mirëpo, për t’i bërë vend nyjes së re duhet të zhvendosim të gjithaelementet për nga një vend. Në rastin më të keq, elementi i ri është ai qëduhet të vendoset në pozitën e parë në listë, gjë që kërkon n operacione

të lëvizjes/zhvendosjes.Analiza e ngjashme do të tregojë që edhe fshirja/largimi është operacion i renditO(n).

 Nëse koleksioni është statik, d.m.th., nuk ndryshon shpesh madhësia e tij,atëherë mund të mos jemi të brengosur me kohën e kërkuar për ndryshimin e përmbajtjes së tij dhe mund të jemi të përgatitur për ndërtimin fillestar tëkoleksionit me ndonjë fshirje ose insertim të rastit që do të merr kohë. Në anëntjetër, do të jemi në gjendje të përdorim një strukturë të thjeshtë të të dhënave(një varg) që ka mbingarkesë të vogël memorike.

Page 503: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 503/699

Algoritmet dhe strukturat e të dhënave

503

Mirëpo, nëse koleksioni është i madh dhe dinamik, d.m.th., elementet insertohendhe fshihen vazhdimisht, atëherë performansë më e mirë arrihet duke përdorurstrukturën e pemës.

Analiza e algoritmit të kërkimit binar

Algoritmi është krejt i thjeshtë. Mund të bëhet në mënyrë rekurzive ose iterative:

1.  Gjeje elementin e mesit;2.   Nesë elementi i mesit është i barabartë me vlerën e kërkuar, algoritmi

ndalet;3.  Përndryshe, janë të mundshme dy raste:

Vlera e kërkuar është më e vogël sesa elementi i mesit. Në këtë rast,shko në hapin 1 për pjesën e vargut para elementit të mesit (gjysmën e përparme të vargut).

o  Vlera e kërkuar është më e madhe, sesa elementi i mesit. Në këtë rast,shko në hapin 1 për pjesën e vargut pas elementit të mesit (gjysmën e pasme të vargut).

Tani duhet të definojmë, kur duhet të ndalen iteracionet (përsëritjet). Rasti i parëështë kur elementi i kërkuar gjendet. Rasti i dytë është kur nënvargu nuk ka mëelemente. Në këtë rast, mund të konkludojmë që vlera e kërkuar nuk është prezente në varg.

Shembuj

Shembulli 1. Gjejeni vlerën 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 vlerën 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 kërkuar mungon (nuk është prezente)): -

1 5 6 18 19 25 46 78 102 114

Page 504: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 504/699

Avni Rexhepi

504

Analiza e kompleksitetit

Përparësi shumë e madhe e këtij algoritmi është se kompleksiteti i tij varetlogaritmikisht nga madhësia e vargut, në rastin më të keq. Në praktikë, kjo do

të thotë që algoritmi do të bëjë më së shumti log2(n) iteracione, që është numërshumë i vogël edhe për vargjet e mëdha. Kjo mund të vërtetohet shumë thjeshtë. Në të vërtetë, në secilin hap, madhësia e pjesës së kërkuar zvogëlohet përgjyshmë. Algoritmi ndalet kur nuk ka më elemente për të kërkuar. Prandaj,zgjidhja e inekuacionit vijues për numra të plotë:

n / 2iteracione > 0

rezulton në

iteracione <= log2(n).

Kjo do të thotë që, kompleksiteti kohorë i algoritmit të kërkimit binar ështëO(log2(n)).

Pjesë kodi

Zgjidhja me rekursion dhe ajo iterative.

Me rekursion:

/* kërkon vlerën në vargun e sortuar** array – vargu ku kërkohet vlera* value – vlera që kërkohet* left - indeksi i kufirit të majtë* right – indeksi i kufirit të djathtë* return – kthen pozitën e vlerës së kërkuar, nëse ajo ndodhet* në varg, ose -1, nëse 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);} 

Page 505: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 505/699

Algoritmet dhe strukturat e të dhënave

505

Me iteracione:

/* * kërkon vlerën në vargun e sortuar array * arr – vargu ku kërkohet vlera * value – vlera që kërkohet* left - indeksi i kufirit të majtë * right – indeksi i kufirit të djathtë * return - kthen pozitën e vlerës së kërkuar, nëse ajo ndodhet* në varg, ose -1, nëse 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; 

Page 506: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 506/699

Avni Rexhepi

506

Algoritmi për bashkim të vargjeve të sortuara  

 Në vijim do të shohim algoritmin për 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 algoritëm përdoret në disa aplikacione praktike, si për 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 rritësedhe dëshirojmë që vargu rezultues të ruaj të njëjtën renditje. Algoritmi për 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 për të përshkuar vargjetA dhe B.

2.  Prezento indeksin për “shkruarje” (write-index) k  për të ruajtur pozitën ecelulës së parë të lirë në vargun rezultues. Në fillim i = j = k  = 0.

3.   Në secilin hap: nëse të dy indekset janë në rangun (i  < m dhe  j  < n),zgjedhe minimumin prej (A[i], B[ j]) dhe shkruaje atë në C[k ].Përndryshe shko në hapin 4.

4.  Rrite k-në  dhe indeksin e vargut në të cilin u gjet minimumi, për një.

Përsërite hapin 2.5.  Kopjo pjesën e mbetur nga vargu, indeksi i të cilit akoma është në rang,në vargu rezultues (d.m.th, nëse njëri varg është përfunduar, pjesa embetur e tjetrit vetëm vetëm kopjohet)

Përmirësimet

Algoritmi mund të përmirësohet në shumë mënyra. Për shembull, është earsyeshme të verifikohet, nëse A[m - 1] < B[0] ose B[n - 1] < A[0]. Në cilindo prej këtyre rasteve, nuk ka nevojë të bëhen të tjera krahasime. Algoritmi mundet

vetëm të kopjojë vargjet fillestare në vargun rezultues në renditjen e duhur.Plotësim më i komplikuar do të ishte kërkimi për pjesët që mund të“shtresohen”(”gërshetohen”) dhe ekzekutimi i algoritmit të bashkimit vetëm përto. Do të mund të kursehet shumë kohë, kur madhesitë e vargjeve të bashkuaradallojnë në llogari të kohës.

Analiza e kompleksitetit

Kompleksiteti kohor i algoritmit të bashkimit është O(n + m). Për më tepër, aikërkon hapësirë shtesë O(n + m) për të ruajtur vargun rezultues.

Page 507: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 507/699

Algoritmet dhe strukturat e të dhënave

507

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

} } 

Page 508: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 508/699

Avni Rexhepi

508

 Algoritmet teorike-numerike

Testimi i numrave primar (qasja naive)

Testimi i numrit se a është primar, është një detyrë e rëndësishme në shkencatkompjuterike. Në të shumtën e rasteve, numrat primar përdoren në algoritmet ekriptografisë së çelësave publik (angl. public key cryptography algorithms).Gjithashtu ka edhe aplikacione për hash tabelat dhe gjeneratorët e numrave pseudorandom (pseudo të rastit). Ka dy tipe të algoritmeve për tëstim të numrit primar: deterministik dhe probabilistik. Në të vërtetë, 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?

Numër primar është numri i cili plotëpjestohet me vetëm dy numra natyral, 1dhe vetveten. Qasja më naive që të vërtetohet se a është numri primar, ështëduke përcjellur definicionin. Algoritmi për të verifikuar numrin a është a primar:

   Nëse numri është 1, kthe “false” (return false);   Përndryshe, për të gjithë numrat e plotë m, prej 2 deri në n-1, verifiko

nëse n është i plotpjestueshëm me m. Nëse është i plotpjestueshëm,atëherë n është numër i përbërë (jo primar);

   Nëse nuk është gjetur asnjë pjestues, atëherë mund të përfundojmë se n 

është primar.

Vërejtje. Numri 1 nuk është numër primar, sepse nuk e plotëson definicionin!

Përmirësimi i mundëshëm i algoritmit

Si mund të përmirësohet kjo qasje e thjeshtë? Së pari le të vëmë re se mund tëverifikojmë pjesëtuesit më të vegjël ose baraz se rrënja katrore e n-it. Pason prova.

 Formulim. Nësë n ka plotëpjestuesin d (1<d< n), atëherë ka plotëpjestuesin d0 (1<d0<√n). 

 Nëse n  plotëpjestohet me rrënjën katrore, atëherë ai nuk është numër primar.Përndryshme, supozojmë se plotëpjestuesi i parë i gjetur është d1, √n < d1 < n.

Mirëpo n është i plotëpjestuar me d2 = n / d1, i cili është më i vogël se √n.Prandaj, supozimi është fals (jo i vërtetë) dhe nëse ka plotëpjestues më të madhse √n, atëherë ka një “çift” më të vogël se √n. Me këtë, vërtetohet formulimi. 

Page 509: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 509/699

Algoritmet dhe strukturat e të dhënave

509

Një përmirësim tjetër

Duhet theksuar edhe një përmirësim. Supozojmë se n është numër çift (2 nukështë plotëpjestues). Nëse n nuk është i plotëpjestueshëm me 2, atëherë ai nuk

është i plotëpjestueshëm fare (në tërësi) me asnjë numër tjetër çift. Algoritmi, pas këtyre dy përmirësimeve, duket si vijon: 

  Nëse numri është 1, kthe “false” (return false);   Nëse numri është 2, kthe “false”;   Nëse numri është çift, kthe “false”;   Përndryshe, për të gjithë numrat e plotë tek m prej 3 deri te √n, verifiko nëse

n është i plotpjestueshëm me m. Nëse është, atëherë n është numër i përbërë;  Nëse nuk është gjetur asnjë pjestues, atëherë përfundojmë se n është primar.

Përgjithësim i kësaj ideje është kur algoritmi verifikon vetëm plotëpjestuesit primar.

Analiza e kompleksitetit

Duke supozuar që plotpjestueshmëria e dy numrave është operacion njësi (gjë qënuk është korrekte për numrat e mëdhënj), kompleksiteti i algoritmit ështëO(√n). (Përmirësimi i fundit e ndryshon vetëm konstanten). Kur të verifikohenvetëm plotpjestuesit primar, kompleksiteti i algoritmit bëhet O(√n / ln(n)). 

Aplikimi kryesor i numarve primar është kriptografia, e cila pret nga negjenerimin e numrave primar me qindra shifra. Me sa duket, algoritmi nuk do t’iverifikojë numrat rreth 10100 në kohë të arsyeshme. Megjithatë, algoritmiaplikohet në praktikë për të bërë “para-verifikimin” (angl. “pre-check”). Numrishumë i madh që testohet se a është primar, kontrollohet se a është i plotpjestueshëm me një milion primarët e parë. Nëse nuk është gjetur asnjë plotpjestues, atëhere përdoren algoritmi probabilistik.

100 numrat e parë primar

2 3 5 7 11 13 17 19 23 29

31 37 41 43 47 53 59 61 67 71

73 79 83 89 97 101 103 107 109 113

127 131 137 139 149 151 157 163 167 173

179 181 191 193 197 199 211 223 227 229

233 239 241 251 257 263 269 271 277 281

283 293 307 311 313 317 331 337 347 349

353 359 367 373 379 383 389 397 401 409

419 421 431 433 439 443 449 457 461 463

467 479 487 491 499 503 509 521 523 541

Page 510: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 510/699

Avni Rexhepi

510

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, përdoret për të gjetur numrat primar deri tek një numër i plotëi caktuar, n. Sigurisht që duke përdorur ndonjë qasje, mund të testojmë të gjithënumrat në rangun prej 2 deri në n  se a janë primar, mirëpo kjo është shumë joefikase. Sita e Eratostenit është një algoritëm i thjeshtë për gjetjen e numrave primar. Megjithëse në kohën e sotme ekzistojnë algoritme edhe më të mira, sitae Eratostenit është një shembull shumë i mirë i qasjes me sitë.

Eratosteni (Eratosthenes of Cyrene) ishte matematikan, gjeograf, poet, astronomdhe teoricient i muzikës, ishte grek i lindur në vitin 276 para erës sonë nëCyrene, Shahhat, të Libisë dhe i vdekur në vitin 194 p.e.s, në Aleksandri, Egjipt.Ishte studiues i ditur dhe ishte bërë drejtor i Librarisë së Aleksandrisë.

Algoritmi

Së pari, algoritmi kërkon vargun e bitave isComposite  (ështëIPërbërë) për tëruajtur n - 1 numrat: isComposite[2 .. n]. Fillimisht, vargu përmbanë zero (0) nëtë gjitha celulat (pozitat). Gjatë punës së algoritmit, numrat, të cilët mund tëreprezentohen si k * p,  ku k   ≥ 2, dhe p është primar, shënohen si numra të përbërë, duke shkruar “1” në celulat gjegjëse (përkatëse). Algoritmi përbëhet prej dy unazave të ndërthurrura: e jashtme, e cila kërkon numrat e pa-shënuar(primar) dhe e brendshme, e cila i shënon shumëfishet e numrave primar, si të përbërë.

  Për të gjithë numrat m: 2 .. n, nëse m është i pashënuar:o  shtoje m në listën e numrave primar;o  shëno të gjithë shumëfishet e tij, më të vegjël ose baraz me n (k *

m ≤ n, k ≥ 2); 

Page 511: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 511/699

Algoritmet dhe strukturat e të dhënave

511

  Përndryshe, nëse m është shënuar, atëherë ai është numër i përbërë.

Modifikimi

Vëreni që algoritmi mund të fillojë shënimin e shumëfisheve duke filluar ngakatrori i numrit të gjetur si i pashënuar. Vërtetë,

Për m = 2, algoritmi shënon:

2 * 2 3 * 2 4 * 2 5 * 2 6 * 2 7 * 2 ...

Për m = 3, 

2 * 3

Veç ka qenë i shënuar në hapin m = 2.

Për m = 5, 

2 * 5 3 * 5 4 * 5 (= 10 * 2)

Veç janë shënuar në hapat m = 2 dhe m = 3.

Pa dhënë provën e fortë, mund ta provoni lehtë. Algoritmi i modifikuar është sivijon:

  Për të gjithë numrat m: 2 ... √n, nëse m është i pashënuar:o  shtoje m në listën e numrave primar;o  shëno të gjithë shumëfishet e tij, duke filluar nga katrori, më të

vegjël ose baraz me n (k * m ≤ n, k ≥ m); 

  Përndryshe, nëse m është shënuar, atëherë ai është numër i përbërë;  Verifiko të gjithë numrat në rangun √n .. n. Të gjithë numrat e mbetur

të pashënuar janë numra primar. Shtoji në listën e numrave primar. 

Shembull

Apliko sitën e Eratostenit për gjetjen e numrave primar nga 2 deri në 100.

Page 512: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 512/699

Avni Rexhepi

512

Rrjeta integrale

2 ështër primar, shëno të gjitha shumëfishet e 2, duke filluar nga 4

Page 513: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 513/699

Algoritmet dhe strukturat e të dhënave

513

3 ështër primar, shëno të gjitha shumëfishet e 3, duke filluar nga 9

5 ështër primar, shëno të gjitha shumëfishet e 5, duke filluar nga 25

Page 514: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 514/699

Avni Rexhepi

514

7 ështër primar, shëno të gjitha shumëfishet e 7, duke filluar nga 49

112  është më shumë (më i madh) sesa 100, kështu që të gjithë numrat e

pashënuar, janë primar

Page 515: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 515/699

Algoritmet dhe strukturat e të dhënave

515

Rezultati përfundimtar:

Analiza e kompleksitetit Kompleksiteti llogaritës i algoritmit është O(nlog(log(n))). Vërtetësia e këtijfakti është e bazuar në disa përafrime dhe në teorinë e numrave primar.Kompleksiteti hapësinor është O(n). Aktualisht, në hardverin modern, algoritmido të tejkalojë shumë më shpejtë kufizimet për nga memoria sesa që do tëtejkalojë kohën.

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;

Page 516: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 516/699

Avni Rexhepi

516

}}for (int m=upperBoundSquareRoot; m<=upperBound; m++)

if (!isComposite[m])

cout << m << " ";delete [] isComposite;

Page 517: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 517/699

Algoritmet dhe strukturat e të dhënave

517

9. Algoritmet e sortimit

Dy prej operacioneve më të performuara në të dhënat e ruajtura në kompjuter,që nga krijimi i kompjuterit e deri më sot, janë sortimi dhe kërkimi. Prandaj,

këto dy operacione janë edhe më të studiuarat nga shkencat kompjuterike.

Algoritmi i sortimit është një algoritëm i cili renditë elementet e një liste.Renditjet më të shpeshta janë: renditja numerike dhe renditja alfabetike.Elementet mund të renditën në mënyrë rritëse apo zvogëluese.

Shumica e të dhënave me të cilat punojmë janë të sortuara. Për shembull,definicionet e ndryshme nëpër fjalorë mund t’i gjejmë të sortuara sipas alfabetit,numrat e telefonave i gjejmë të sortuara sipas radhitjes alfabetike të emrave.Poashtu, edhe letrat postare sortohen në disa mënyra: sipas zip kodit, sipas

rrugës 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 sendonjë algoritëm tjetër për sortim. Secili algoritëm vepron ndryshe në raste tëndryshme. Disa punojnë më shpejt në një listë të caktuar, por më ngadalë në njëlistë tjetër. Prandaj, për secilin algoritëm duhet të analizohen tri raste: rasti më imirë, rasti më i keq dhe rasti mesatar.

Rasti më i mirë për një algoritëm të sortimit është kur algoritmi kryen më së paku punë dhe njëkohësisht sortohet lista. Me më së paku punë nënkuptohetnumri minimal i krahasimeve dhe shkëmbimeve të vendeve të elementeve që inevojitet algoritmit për të sortuar listën. Në këtë rast, algoritmi është më i shpejtse në cilindo rast tjetër, pasi që kryen më pak punë.

E kundërta e rastit më të mirë është rasti më i keq. Në këtë rast analizohet semaksimalisht sa krahasime dhe shkëmbime vendesh i nevojiten algoritmit përt’a sortuar listën. Në këtë rast, algoritmi kërkon më së shumti kohë për t’asortuar listën.

Rast mesatar për një algoritëm të sortimit konsiderohet të jetë atëherë kur pas

disa krahasimeve dhe shkëmbime vendesh, lista të përfundoj sortimin. Rasti mëi mirë dhe më i keq ka më pak gjasa të ndodhin në një listë të rastësishme.Prandaj, rasti mesatar ka rëndësi të madhe që të analizohet.

Përveç shpejtësisë dhe kohës që kërkon algoritmi, duhet të analizohet edhehapësira memorike që zë algoritmi. Meqë hapësira memorike është e kufizuar, për t’u konsideruar një algoritëm më efektiv duhet të zë sa më pak hapësirë.Disa algoritme zënë më pak hapësirë memorike e disa shumë më shumë.

 Në këtë mënyrë, algoritmet e sortimit mund të klasifikohen sipas: kompleksitetittë krahasimeve të elementeve, kompleksitetit të shkëmbimeve të vendeve,

Page 518: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 518/699

Avni Rexhepi

518

hapësirës memorike, rekurzivitetit (disa algoritme janë rekurzive e disa jo),krahasimit (disa i krahasojnë elementet e disa jo), metodës së përgjithshme(insertimi, shkëmbimi, selektimi, shkrirja), etj. 

Bubble SortSorti Bubble  (angl. bubble-fluskë (theksohet: babëll)) është algoritmi më ithjeshtë dhe i mirënjohur i sortimit. Përdoret rrallë në praktikë, por përdorimikryesor i tij është më shumë si mjet shkollor që të bëjë prezentimin ealgoritmeve të sortimit. Sorti Bubble i takon grupit të algoritmeve të sortimit tërendit O(n2), që e bën shumë joefikas për srotimin e vëllimit të madh të vleravetë të dhënave. Sorti Bubble është stabil dhe adaptiv (angl. stable-stabil, kështunjihen sortet të cilat për vlerat e njëjta, i ruajnë renditjet fillestare të pozitave(majtas, djathtas) në raport me njëra tjetrën; angl. adaptive  –   adaptive, që

 përshtaten, kështu njihen sortet të cilat gjatë shkëmbimeve, në rrugë e sipër, e përmirësojnë situatën duke e përafruar renditjen përfundimtare, pra pas njëshkëmbimi, vlera i afrohet më shumë pozitës përfundimtare).

Algoritmi

1.  Krahaso secilin çift të elementeve fqinje duke filluar nga fillimi i vargutdhe nëse janë në renditje të kundërt, shkembeji vendet/pozitat e tyre

(angl. Swap-shkëmbim, ujdi).2.   Nëse ka ndodhur së paku një shkëmbim i vendeve, përsërite hapin 1.

 Në secilin hap, “fluskat” e mëdha pluskojnë në sipërfaqe dhe mbesin atje. Nëhapin kur nuk lëvizë më asnjë fluskë, sortimi ndalon. Përmes shembullit nëvijim, do të sqarohet më mirë idea e sortit bubble.

Shembull. Sortoni vargun {5, 1, 12, -5, 16} duke përdorur sortin bubble.

Page 519: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 519/699

Algoritmet dhe strukturat e të dhënave

519

Analiza e kompleksitetitKompleksiteti i rastit mesatar dhe më të keq të sortit buble është i rendit O(n2).Gjithashtu, i bën O(n2) shkëmbime në rastin më të keq. Sorti bubble ështëadaptiv. Kjo do të thotë, që për vargun pothuajse të sortuar, jep një vlerësimO(n). Evitoni zbatimet të cilat nuk e verifikojnë nëse vargu është i sortuar nësecilin hap (në secilin shkëmbim të bërë). Ky verifikim është i nevojshëm, nëmënyrë që të ruhet tipari i adaptivitetit.

Page 520: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 520/699

Avni Rexhepi

520

Breshkat dhe lepujt

 Një problem tjetër i sortit bubble është se koha e tij e ekzekutimit është shumë evarur nga renditja fillestare e elementeve të vargut. Elementet e mëdha (lepujt)

shkojnë përpjetë shpejtë, gjersa ato të voglat (breshkat) shkojnë teposhtë shumëngadale. Ky problem është zgjidhur në sortin “Cocktail”.

Shembull i breshkës. Edhe pse vargu {2, 3, 4, 5, 1} është pothuajse i sortuar, aimerr nevojiten O(n2) iteracione për të sortuar vargun. Elementi {1} është breshkë.

Page 521: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 521/699

Algoritmet dhe strukturat e të dhënave

521

Shembull i lepurit. Vargu {6, 1, 2, 3, 4, 5} është poashtu, pothuajse i sortuar, por ky merr O(n) iteracione për t’u srotuar. Elementi {6} është lepuri. Kyshembull demonstron vetinë adaptive të sortit bubble.

Page 522: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 522/699

Avni Rexhepi

522

Pjesë kodi

Ka disa mënyra të implementimit të sortit bubble. Vëreni se verifikimi për"sëaps" (shkëmbime) është absolutisht i nevojshëm, për të ruajtuar vetinë

adaptive.//swapped=shkëmbyer (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 bëntërësisht të papërshtatshëm për sortim të numrit të madh të të dhënave. Sorti iselektimit është i shquar për thjeshtësinë e tij të programimit dhe mund të performojë më mirë sesa sortet e tjera në disa situatat të caktuar.

Page 523: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 523/699

Algoritmet dhe strukturat e të dhënave

523

Algoritmi

Idea e algoritmit të “selection sort”-it është mjaft e thjeshtë. Vargu ndahet nëmënyrë imagjinare në dy pjese: pjesa e sortuar dhe ajo e pasortuar. Në filllim, pjesa e sortuar është “empty” (e zbrazët), gjersa pjesa e pasortuar, e parmbanëtërë vargun. Në secilin hap, algoritmi e gjenë vlerën minimale në pjesën e pasortuar dhe e shton atë në fund të pjesës së sortuar. Kur pjesa e pasortuar të bëhet zbrazet (bëhet “empty”), algoritmi ndalet.

Kur algoritmi sorton vargun, ai e shkëmben elementin e parë të pjesës së pasortuar me elementin minimal dhe atëherë ai përfshihet (vendoset, futet) në pjesën e sortuar. Sorti i selektimit nuk është stabil. Në rast se sortohet lista elidhur dhe në vend të shkëmbimit, elementi minimal lidhet në pjesën 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 përdorur “selectionsort”-in.

Page 524: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 524/699

Avni Rexhepi

524

Analiza e kompleksitetit

Sorti i selektimit ndalet kur pjesa e pasortuar bëhet e zbrazët. Si dihet, në secilinhap, numri i elementeve të pasortuara zvogëlohet për një. Prandaj, sorti iselektimit i bën n hapa (n  –  numri i elementeve në varg) në unazën e jashtme,

 para se të ndalet. Në seciln hap të unazës së jashtme, kërkohet gjetja eminimumit të pjesës së pasortuar. Duke mbledhurn + (n - 1) + (n - 2) + ... + 1, fitojmë numrin e krahasimeve O(n2). Numri ishkëmbimeve mund të ndryshojë prej zero (në rastin e vargut të sortuar) deri nën-1 (nëse vargu është i sortuar në kahjen e kundërt), gjë që rezulton në numrinO(n) të shkëmbimeve. Kompleksiteti i përgjithshëm i algoritmit është O(n2).

Fakti që sorti i selektimit kërkon numrin prej më së shumti n-1 shkëmbimeve, e bën atë shumë efikas në situatat kur operacioni “write” (shkruaj) është dukshëmmë i kushtueshëm sesa operacioni “read” (lexo). 

Page 525: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 525/699

Algoritmet dhe strukturat e të dhënave

525

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). Për dallim prejshumë algoritmeve të sortimit me kompleksitet kuadratik, ky aplikohet në praktikë për sortimin e vargjeve të vogla të të dhënave. Për shembull, përdoret për të përmirësuar rutinën e quicksort-it. Algoritmi i sortimit “përdoret” gjatë

renditjes së letrave, në lojën e letrave.

Algoritmi

Algoritmi i sortit të insertimit i përngjanë algoritmit të sortit të selektimit. Vargundahet në dy pjesë imagjinare  –  pjesa e sortuar dhe ajo e pasortuar. Në fillim, pjesa e sortuar e përmbanë elementin e parë të vargut, kurse pjesa e pasortuarelementet tjera. Në secilin hap, algoritmi i merr elementin e parë të pjesës së pasortuar dhe e inserton  në vendin e duhur të pjesës së sortuar. Kur pjesa e pasortuar të bëhet e zbrazët (“empty”), algoritmi ndalet.

Ilustrimi i një hapi të algoritmit të sortimit me insertim, duket si vijon:

Page 526: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 526/699

Avni Rexhepi

526

bëhet 

Le të shohim një shembull të sortit të insertimit, për të pasur më të qartë idenë ealgoritmit.

Shembull . Sortoni {7, -5, 2, 16, 4} duke përdorur sortin “insertion”. 

Page 527: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 527/699

Algoritmet dhe strukturat e të dhënave

527

Idetë e insertimit

Operacioni kryesor i algoritmit është insertimi. Detyra është të insetohet vleranë pjesën e sortuar të vargut. Kjo mund të bëhet në disa mënyra.

"Sifting down" - shoshitja teporshtë duke përdorur shkëmbimet 

Mënyra më e thjeshtë për të insertertuar elementin e ardhshëm në pjesën esortuar është që të shoshitet teposhte (kah vlerat e vogla), gjersa të zërë vendin eduhur. Fillimisht elementi qëndron djathtas, pas pjesës së sortuar. Në secilinhap, algoritmi krahason elementin me atë para tij dhe nëse janë në renditje tëkundërt, i shkëmben (ua ndërron vendet, pozitat), si në vijim:

Page 528: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 528/699

Avni Rexhepi

528

Kjo qasje, i “shkruan” shumë herë elementet e shoshitura në pozitat e

 përkoshme. Implementimi vijues, i eliminon këto “shkruarje” të panevojshme. Shiftimi në vend të shkëmbimit

Algoritmi paraprak mund të modifikohet, ashtu që të shkruaj elementet eshoshitura, vetëm në pozitën e tyre të duhur përfundimtare, pozitën finale, si nëvijim:

Kjo është mënyra e përdorur më së shpeshti e sortit të insertimit.

Duke përdorur kërkimin binar

Është e arsyeshme që të përdoret algoritmi i kërkimit binar, për të gjetur vendine duhur për insertim. Ky variant i sortit të insertimit, quhet sorti binar iinsertimit. Pasi të jetë gjetur pozita për insertim, algoritmi e zhvendosë (shifton) pjesën e vargut dhe e inserton elementin. Kjo mënyrë ka numër më të vogël të

krahasimeve, por kompleksiteti mesatar i përgjithshëm mbetet O(n2

). Nga

Page 529: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 529/699

Algoritmet dhe strukturat e të dhënave

529

këndveshtrimi praktik, ky përmirësim nuk është i rëndësishëm, sepse sorti iinsertimit përdoret në bashkësi mjaft të vogla të të dhënave.

Analiza e kompleksitetit

Kompleksiteti i përgjithshëm i sortit të insertimit është mesatarisht O(n2), pavarësisht prej metodës së insertimit. Në vargun pothuajse të sortuar, sorti iinsertimit shfaqë performansë më të mirë, mesatarisht deri në O(n), por numri ikrahasimeve mund të ndryshojë varësisht prej algoritmit të insertimit. Ai ështëO(n2) kur kur përdoret metoda e shiftimit ose e shkëmbimit dhe O(n log n) përsortin binar të insertimit.

 Nga këndveshtrimi i aplikacionit praktik, një kompleksitet mesatar i sortit tëinsertimit nuk është aq i rëndësishëm. Si u theksua edhe më parë, sorti insertionaplikohet për bashkësi të vogla të të dhënave (8 deri 12 elemente), prandaj së

 pari duhet të mirret në konsiderim një “performansë praktike”. Në praktikë, sortii 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ë njëjta);  in-place (angl. in place –  në vend) kërkon madhësi konstante të hapësirës

shtesë);  online (elementet e reja mund të shtohen gjatë sortimit).

(Sqarim: sortet të cilat mund të sortohen përbrenda hapësirës memorike që ekanë para sortimit, do të thotë që sortohen në vendin ku janë vlerat, njihen si“in- place” –   “në vend”, për dallim prej atyre që kërkojnë hapësirë shtesëmemorike, për të vendosur përkohësisht vlerat që duhet sortuar, për të bërë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 shkëmbime://me shiftimvoid 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];

Page 530: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 530/699

Avni Rexhepi

530

j--;

}

//me shkëmbime(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ë algoritëm i shpejtë i sortimit, i cili përdoret jo vetëm për qëllime shkollore, por është edhe shumë i përdorur në praktikë. Në rastin mesatar, ka kompleksitetin O(n log n), që e bën quicksort-intë përshtatshëm për sortimin e vëllimeve të mëdha të të dhënave (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

Për sortin e shpejtë përdoret strategjia “përçaj e sundo” (angl. “divide-and-conquer”). Këtu përshkruhet hapi i rekurzionit: 

1.  Zgjedhni vlerën pivot. (angl. pivot-qendër, bosht). Marrim vlerën e

elementit në mes si vlerë pivot (mirëpo 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ë, vleratmë të vogla se pivoti shkojnë në pjesë (anën) e majtë dhe të gjithaelementet më të mëdha se pivoti, shkojnë në pjesën e djathtë të vargut.Vlerat e barabarta me pivotin mund të rrijnë në cilëndo anë. Vëreni sevargu mund të ndahet në pjesë jo të barabarta.

3.  Sortoni të dy pjesët. Apliko quicksort-in në mënyrë rekurzive në pjesëne majtë dhe në pjesën e djathtë.

Page 531: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 531/699

Algoritmet dhe strukturat e të dhënave

531

Algoritmi i ndarjes

Janë dy indeksa, i dhe  j, në fillim të algoritmit të ndarjes (angl. part-pjesë, copë; partition-ndarje, copëtim, prandaj ndonjëhere edhe në shqip thuhet “particioni”).

Indeksi i  pointon në elementin e parë të vargut dhe  j  pointon në elementin efundit. Pastaj, algoritmi e lëvizë indeksin i përpara (te lartë, djathtas), gjersa tëgjindet një element me vlerë më të madhe ose baraz me pivotin. Indeksi  j lëvizë prapa (te poshtë, majtas) gjersa të gjindet një element me vlerë më të vogël ose baraz me pivotin. Nëse i ≤ j atëherë ato shkëmbehen dhe i bën një hap në

pozitën e ardhshme (i + 1), j  bën një hap në pozitën paraprake ( j  –   1).Algoritmi ndalet kur i bëhet 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ë mëdha ose

 baraz me pivotin.Shembull. Sortoni vargun {1, 12, 5, 26, 7, 14, 3, 7, 2} duke përdorur quicksort-in.

Page 532: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 532/699

Avni Rexhepi

532

Vëreni se këtu është paraqitur vetëm hapi i parë i rekurzionit, ashtu që shembullitë mos dalë shumë i gjatë. Mirëpo, në fakt pjesët {1, 2, 5, 7, 3} dhe {14, 7, 26,12} pastaj vazhdonë sortimin në mënyrë rekurzive.

Përse funksionon kjo?

 Në hapin e particionit algoritmi e ndanë vargun në dy pjese dhe secili element a nga pjesa e majtë është më i vogël ose baraz se secili element b  nga ana edjathtë. Gjithashtu, a dhe b e plotësojnë (e kënaqin) jobarazinë a ≤ pivot ≤ b. pas kompletimit të thirrjeve rekurzive të dy pjesët bëhen të sortuara dhe dukemarrë parasysh argumentet e dhëna më sipër, sortohet i tërë vargu.

Analiza e kompleksitetit

 Në rastin mesatar, quicksort-i ka kompleksitetin O(n log n) (prova e fortë e këtijfakti nuk është triviale dhe nuk prezentohet këtu). Në rastin më të keq,quicksorti punon në kohë, por në rastet e të dhënave 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 rëndësishëm, prandaj mund tërealizohet edhe si si funksion i veçantë. Në vijim prezentohen të dy rastet, mefunksion të përbashkët dhe me funksion të ndare për 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)

Page 533: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 533/699

Algoritmet dhe strukturat e të dhënave

533

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

}};

Page 534: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 534/699

Avni Rexhepi

534

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

Page 535: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 535/699

Algoritmet dhe strukturat e të dhënave

535

SHELLSORT

Shell sorti u zhvillua nga Donald L. Shell. Ky sort ësthë i pazakontë në faktin qëfillon me shqyrtimin e listës së plotë të vlerave si një set i nënlistave të

ndërthurrura (shtresuara). Në kalimin e parë, ai mund të punojë me nënlista që janë vetëm një çift i elementeve. Në kalimin e dytë, mund të punojë me grupe prej nga katër elementeve. Ky proces përsëritet, duke rritur numrin e elementeve për nënlistë dhe rrjedhimisht duke zvogëluar numrin e nënlistave. Figura 3.1tregon nënlistat të cilat mund të përdoren në procesin e sortimit të listës prej 16elementeve.

 Në Fig. 3.1(a), shohim se ekzistojnë tetë nënlista me nga dy vlera secila, të cilatçiftojnë elementin e parë dhe të nëntë, të dytin dhe të tetin e kështu me radhë. Në Fig. 3.1(b), shohim se tani kemi katër nënlista me nga katër vlera secila.

 Fig. 3.1 –  Katër kalimet e Shell Sortit

 Nënlista e parë tani ka elementet e lokacionit të parë, të pestë, të nëntë dhe tëtrembëdhjetë. Nënlista e dytë, ka elementet e lokacionit të dytë, të gjashtë, të

Page 536: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 536/699

Avni Rexhepi

536

dhjetë dhe të katërmbëdhjetë. Në Fig. 3.1(c), shohim se ka dy nënlista, të cilatkanë 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 nënlistës bëhet vetëm përmes një variante të sortit të insertimit të bazuar në seksionin 3.1. Kjo e bën algoritmin vijues:

Shellsort( list, N )list – elementet qe duhet sortuarN – numri i elementeve ne liste

passes = lg N  //passes=kalimewhile (passes ≤ 1) do

increment = 2passes - 1for start = 1 to increment doInsertionSort( list, N, start, increment )

end forpasses = passes - 1

end while

Variabla increment  jep distancën ndërmjet elementeve në nënlistë (Në Fig. 3.1,inkermentet e përdorura janë 8,4,2,1). Në këtë algoritëm, fillojmë me njëinkrement që është për 1 më i vogël se fuqia më e madhe 2-shit që është më evogël sesa madhësia e listës. Kështu, nëse lista e jonë ka 1000 elemente,inkrementi i jonë i parë do të jetë 511. (Vërejtje: Vlera=1000, Fuqia e 2-shit:210=1024, kështu që 29=512, => 29-1=511). Inkrementi gjithashtu tregon numrin

e nënlistave që i kemi. Nëse nënlista e jonë e parë ka elementet në lokacionet 1dhe 1+increment, nënlista e fundit duhet të fillojë në lokacionin increment.Herën e fundit që ekzekutohet unaza while, kalimet do të kenë vlerën 1, gjë qëdo të bëjë vlerën increment 1 për InsertionSort-in e fundit.

Analiza e këtij algoritmi varet nga analiza që e bëmë për InsertionSort-in. Parase të fillojmë analizën e Shellsort, rikujtoni që në seksionin 3.1, kemi parë se për listën me N elemente rasti më i keq për sortin e insertimit ishtë (N2-N)/2 dherasti mesatar për sortin e insertimit ishtë: N2/4.

Page 537: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 537/699

Algoritmet dhe strukturat e të dhënave

537

RADIX SORT-i

Sorti Radix (angl. Radix  –  Rrënjë, bazë ) i përdorë vlerat kyçe (çelësat) për të bërë sortimin pa i krahasuar në të vërtetë ato mes veti. Në këtë sort, do të

krijojmë një set të “kovave” dhe do ti shpërndajmë të dhënat hyrëse në kovë bazuar në vlerat e tyre kyçe. Pasi të grumbullohen vlerat dhe të përsërtitet procedura për pjesët pasuese të vlerës kyçe, mund të krijojmë listën e sortuar.Shpërndarja dhe grumbullimi duhet të bëhen me shumë kujdes që kjo tëfunksionojë (punoj).

 Një proces i ngjashëm me këtë është përdorur për t’i sortuar letrat (letrat e lojësme letra) me dorë.

Versioni i kompjuterizuar i këtij procesi për sortim të një seti të vlerave

numerike do të përdorte 10 kova dhe do të kishte algoritmin vijues:

RadixSort( list, N )list – elementet qe duhet sortuarN – numri i elementeve ne liste

shift = 1for loop = 1 to keySize do

for entry = 1 to N dobucketNumber = (list[entry].key / shift) mod 10

Append( bucket[bucketNumber], list[entry] )end for entry

list = CombineBuckets()

shift = shift * 10

end for loop Do të fillojmë me shqyrtimin e këtij algoritmi. Llogaritja e bucketNumber do tëtërheqë një shifër njëshe nga çelësi (vlera kyçe). Pjestimi me shift do të bëjë qëvlera kyçe të lëvizet në të djathtë për një numër të shifrave dhe pastaj mod do tëeliminojë të gjitha tjerat përveq shifrave të njësheve të numrit rezultues. Në

kalimin e parë me vlerën e shift-it 1, pjestimi nuk do të bëjë asgjë dhe rezultatii mod do të kthejë vetëm shifrat njëshe të vlerës kyçe. Në kalimin e dytë, shift do të jeta 10, kështu që pjestimi i plotë dhe pastaj mod do të kthejë vetëm shifrate dhjetësheve. Në secilin kalim të ardhëshëm, do të përdoret shifra e ardhëshmee vlerës kyçe (çelësit).

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ë pikafillestare për kalimin e ardhëshëm. Pasi që kovat rikombinohen në renditje(radhë) dhe pasi që numrat shtohen në fund të secilës listë të kovave, vlerat kyçe

Page 538: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 538/699

Avni Rexhepi

538

do të jenë përfundimisht të sortuara. Figura 3.2 paraqet tri kalimet që do të bëheshin për vlerat kyçe (çelësat) me tri shifra. Për ta bërë këtë shembull më tëthjeshtë, të gjitha vlerat kyçe përdorin vetëm shifrat prej 0 deri në 3, kështu qëdo të nevojiten katër kova.

Duke shikuar Fig. 3.2(c), do të duhej të shihni se nëse kovat kombinohen përsërinë 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 kovës Përmbajtja

0 310 130 330 1201 301 201 111 231

2 222 032 002 102

3 213 023 013 323

(a) Kalimi 1, Shifra e njësheve

Lista e kalimit 1310 130 330 120 301 201 111 231 222 032 002 102 213 023 013 323

 Numri i kovës Përmbajtja

0 301 201 002 102

1 310 111 213 013

2 120 222 023 323

3 130 330 231 032

(b) Kalimi 2, Shifra e dhjetësheve

Lista e kalimit 2

301 201 002 102 310 111 213 013 120 222 023 323 130 330 231 032

 Numri i kovës Përmbajtja

Page 539: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 539/699

Algoritmet dhe strukturat e të dhënave

539

0 002 013 023 032

1 102 111 120 130

2 201 213 222 2313 301 310 323 330

(c) Kalimi 3, Shifra e qindwsheve

 FIGURA 3.2 Tri kalimet e sortit radix

Page 540: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 540/699

Avni Rexhepi

540

HEAPSORT

Heapsort (sorti i pirgut  –   angl. Heap  –   pirg, stive, tog, grumbull, etj) është i bazuar në një tip special të pemës binare të quajtur “Heap” ku për secilënnënpemë vlera në rrënjë është më e madhe se të gjitha vlerat në të dy fëmijët. Nuk ka relacion renditjeje ndërmjet dy fëmijëve, kështu që ndonjëherë fëmija imajtë mund të jetë më i madh dhe herave të tjera fëmija i djathtë do të jetë më imadh. Pirgu konstruktohet që të jetë pemë e plotë ku së pari secili nivel i pemësmbushet para se të fillohet niveli i ri dhe të gjitha pozicionet e nyjeve në nivel të jenë mbushur ne radhë prej të majtës kah e djathta.

Idea e përgjithshme e Heapsort-it është që së pari të konstruktohet pirgu.

Atëherë elementi më i madh do të jetë në rrënjë të pemës, sepse të gjithaelementet më të vogla duhet të jenë në fëmijë ashtu që ky të jetë pirg. Rrënja pastaj kopjohet në lokacionin e fundit të listës dhe pirgu rikonstruktohet pa këtëelementin më të madh. Elementi i dytë më i madh pastaj do të jetë në rrënjë,kështu që mund ta largojmë atë dhe të rikonstruktojmë pirgun. Ky proces përsëritet deri sa të gjitha elementet të jenë vendosur prapa në listë.

Algoritmi i përgjithshëm për këtë është:Konstrukto pirgunfor i = 1 to N do

kopjo rrenjen ne listefikso pirgun

end for

Ka një numër të detajeve që mbesin për të qenë ky algoritëm i kompletuar. Së pari duhet të përcaktojmë se çka përfshihet në procesin e konstruktimit dhefiksimit (riparimit) të pirgut, sepse kjo do të luaj rol në efikasitetin e këtijalgoritmi. Duhet të brengosemi për atë se si do të implementohet (zbatohet) kyalgoritëm. Tejngarkesa (overhead  –   ngarkesa e kreut, fillestare) e krijimit tëkrijimit të pemës binare do të ishte problem me rritjen e madhësisë së listës.Sidoqoftë, ne mundemi të përdorim hapësirën e vet listës dhe kështu të bëjmëkëtë sortim pa hapësirë shtesë të veçantë. Ne mund të “ndërtojmë” listën në pirgnëse e vërejmë se në pirg secila nyje e jashtme ka dy fëmijë, përveq ndoshtanjërës nyje kah fundi. Nëse shqyrtojmë planifikimin (pasqyrimin) vijues, përmbajtjen e këtyre vlerave mund të përdorim listën. Për nyjën në lokacionin i, dotë ruajmë dy fëmijët e saj në lokacionet 2*i dhe 2*i+1. Vëreni se ky proces prodhon lokacione të ndryshme për secilin fëmijë të nyjës. E dijmë se nyja i është gjethe nëse 2*i është më e madhe sesa N, dhe e dijmë se nyja i ka vetëmnjë fëmijë nëse 2*i është i barabartë me N. Figura 3.3 paraqet pirgun dheverzionin e tij në formë liste.

Page 541: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 541/699

Algoritmet dhe strukturat e të dhënave

541

 FIGURE 3.3 - Pirgu dhe implementimi i tij si listë

Fiksimi i pirgut (FixHeap) 

Kur e marrim elementin më të madh nga rrënja dhe e vendosim në listë, kjo e lërrënjën të zbrazët. E dijmë se elementi më i madh i dy fëmijëve të saj duhet tëlëvizët lartë, por atëherë nyja e këtij fëmije bëhet e zbrazët dhe kështu shikojmë

në dy fëmijët e saj, e kështu me radhë. Në këtë proces, duhet të mirëmbajmë pirgun sa më afër pemës së plotë që të jetë e mundur. Kur e fiksojmë (riparojmë) pirgun, gjithashtu do të kalojmë (përcjellim) nyjën më të djathtë prej nivelit të poshtëm, që të insertohet prapa në pirg. Kjo do të largojë nyjet në mënyrë të barabartë prej fundit. Nëse nuk e bëjmë këtë dhe të gjitha vlerat e mëdha 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 sortohetroot – indeksi i rrenjes se pirgutkey – vlera qe duhet te reinsertohet ne pirgbound – kufir i eperm (index) ne pirg//vacant=i/e lirëvacant = rootwhile 2*vacant ≤ bound do

largerChild = 2*vacant// gjeje me te madhin prej dy femijeveif (largerChild < bound) and (list[largerChild+1] >

list[largerChild]) then

Page 542: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 542/699

Avni Rexhepi

542

largerChild = largerChild + 1end if// a eshte ‘key’ përmbi këtë fëmijë?if key > list[ largerChild ] then

// po, ndalo unazenbreak

else// jo, levize femijen me te madh lartëlist[ vacant ] = list[ largerChild ]vacant = largerChild

end ifend whilelist[ vacant ] = key

Kur shikoni në parametrat e këtij algoritmi, mund të pyesni veten (të çuditeni, tëhabiteni) se përse kemi zgjedhur që të përcjellim (bartim, lëvizim) lokacioninrrënjë. Pasi që kjo rutinë (ky nënprogram, funksion) nuk është rekurzive, rrënjae pirgut do të duhej gjithmonë të jetë në lokacionin 1. Sidoqoftë, do të shihni seky parametër shtesë do të bëjë të mundur për ne që të përdorim këtë funksion për të konstruktuar pirgun prej poshtë lartë. Ne e përcjellim madhësinë e pirgut,sepse me lëvizjen e elementeve nga pirgu në listë, pirgu tkurret (ngushtohet,zvogëlohet).

Konstruktimi i pirgut

Mënyra që kemi zgjedhur për të zbatuar funksionin FixHeap do të thotë se nemund të përdorim këtë për konstruktimin inicial (fillestar) të pirgut. Cilat do dyvlera mund të trajtohen si gjethe të nyjes së lirë (boshe, zbrazët). Nuk duhet të bëjmë ndonjë punë në gjysmën e dytë të listës sepse ato janë të gjitha gjethe.Duhet vetëm të konstruktohen pirgjet e vogla prej gjetheve dhe pastaj tëkombinohen këto gjersa përfundimisht të gjitha vlerat të jenë në pirg. Kjo arrihet përmes unazës vijuese://down to = teposhtë deri në

for i = N/2 down to 1 doFixHeap( list, i, list[ i ], N )

end for 

Algoritmi Final

Bashkimi i këtyre pjesëve dhe shtimi i detajeve finale të nevojshme për lëvizjene elementeve prej pirgut në listë, jep algoritmin:

for i = N/2 down to 1 do

Page 543: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 543/699

Algoritmet dhe strukturat e të dhënave

543

FixHeap( list, i, list[ i ], N )end forfor 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 tonarekurzive të sortimit. Ai është i bazuar në idenë se bashkimi i dy listave tësortuara mund të bëhet shpejtë. Pasi që lista me vetëm një element është esortuar, sorti merge do të ndajë listën në pjesë një-elementëshe dhe pastaj do të bëjë sortimin gjersa i bashkon këto pjesë përsëri së bashku. Prandaj, e tërë puna për këtë algoritëm ndodhë në bashkimin e dy listave.

Sorti merge (sorti i bashkimit) mund të shkruhet si algoritëm rekurziv i cili e bën punën e tij gjatë rrugës lartë në procesin rekurziv. Duke shikuar në algoritmin që pason, do të vëreni se ai e ndanë listën përgjysmë gjersa “i pari” është më ivogël sesa “i fundit”. Kur të arrijmë në pikën ku “i pari” dhe “i fundit” janë të barabartë, kemi një listë prej një elementi e cila është vetvetiu (“qenësisht”) esortuar. Kur të kthehemi, prej dy thirrjeve të MergeSort  që kanë madhësi tëlistës 1, pastaj e thërrasim MergeSort për të bashkuar ato për të krijuar listën esortuar me madhësi 2. Në nivelin e ardhëshëm te lartë, do të kemi dy lista me

madhësi 2 të cilat bashkohen në një listë të sortuar me madhësi 4. Ky proces

Page 544: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 544/699

Avni Rexhepi

544

vazhdon deri sa arrijmë të thirrja më e lartë, e cila i bashkon dy gjysmat esortuara të listës, në një listë të sortuar. Shohim se MergeSort e ndanë listën nëgjysma rrugës te poshtë në procesin rekurziv dhe pastaj i bashkon gjysmat esortuara së bashku në rrugën te lartë. Algoritmi i cili e realizon këtë është:

MergeSort( list, first, last )list-elementet qe duhet te sortohenfirst–indeksi i elem. te pare ne pjesen e listes per sortimlast- indeksi i elem. te fundit ne pjesen e listes per sortim

if first < last thenmiddle = ( first + last ) / 2MergeSort( 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 tërë puna është duke u bërë në funksioninMergeLists.

 Në vazhdim do të krijojmë funksionin MergeLists.

Merrni në shqyrtim listat A dhe B, të dyjat të sortuara në renditje rritëse. Kjorenditje do të thotë se elementi më i vogël në secilën listë ndodhet në lokacionin

e parë dhe elementi më i madh i secilës listë ndodhet në lokacionin e fundit. Përtë bashkuar këto së bashku në një listë, ne e dijmë se elementi më i vogël i përgjithshëm duhet të jetë ose elementi i parë i listës A ose elementi i parë i Bdhe se elementi më i madh i përgjithshëm duhet të jetë ose elementi i fundit i Aose elementi i fundit i B. Nëse dëshirojmë 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]? Nëse A[1] kaqenë më i vogël se B[1], A[1] është vendosur në C[1] dhe elementi i ardhëshëmmund të jetë B[1] përveq nëse A[2] gjithashtu është më i vogël se B[1]. Kjoështë e mundur sepse krejt çka ne me të vërtetë e dijmë është se A[2] është më imadh se A[1] dhe më i vogël se A[3], por ne nuk e dijmë se si qëndrojnë për ngamadhësia elementet e A në krahasim me elementet e B. Duket se mënyra më emirë për të realizuar bashkimin, do të ishte që të kemi dy indeksa për A dhe Bdhe të inkrementohet (rritet për 1) indeksi për listën që ka elementin më tëvogël. Procesi i përgjithshëm vazhdon krahasimin e elementeve më të vogla prejatyre 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ë listës A ose tëlistës B. Elementet e mbetura tepricë do të jenë ato nga njëra listë të cilat janëmë të mëdha sesa elementi i fundit i listës tjetër. Duhet të sigurohemi se këto

Page 545: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 545/699

Algoritmet dhe strukturat e të dhënave

545

elemente janë vendosur në fund të listës rezultuese. Bashkimi i këtyre ideve nënjë algoritëm do të na jep:

MergeLists( list, start1, end1, start2, end2 )List – elementet qe duhet te sortohenstart1 – fillimi i “listes” A end1 – fundi i “listes” A start2 - fillimi i “listes” Bend2 - fundi i “listes” B//suposon qe elem. e A dhe B jane ne liste te vazhduar (ngjitur)

finalStart = start1finalEnd = end2indexC = 1while (start1 ≤ end1) and (start2 ≤ end2) do

if list[start1] < list[start2] thenresult[indexC] = list[start1]start1 = start1 + 1

elseresult[indexC] = list[start2]start2 = start2 + 1

end ifindexC = indexC + 1

end while// zhvendose pjesen e listes qe ka mbeturif (start1 ≤ end1) then

for i = start1 to end1 doresult[indexC] = list[i]indexC = indexC + 1

end forelsefor i = start2 to end2 doresult[indexC] = list[i]indexC = indexC + 1

end forend if// tash vendose rezultatin prapa ne listeindexC = 1for i = finalStart to finalEnd do

list[i] = result[indexC]indexC = indexC + 1

end for

Për një rast konkret, do të kishim shembullin si në figurën vijuese:

Page 546: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 546/699

Avni Rexhepi

546

Analiza e MergeLists

Për shkak se të gjitha krahasimet e elementeve ndodhin në MergeLists, fillojmëanalizën prej aty. Le të shohim në rastin kur të gjitha elementet e listës A janëmë të vegjël sesa elementi i parë i listës 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 vogëldo ta vendosim në C. Pastaj do ta krahasojmë A[2] me B[1] dhe do ta lëvizimA[2]-shin, sepse ai është më i vogël. Ky proces do të vazhdojë të krahasojësecilin element të A me B[1], sepse ata të gjithë janë më të vegjël. Kjo do tëthotë se algoritmi i bën NA krahasime, ku NA është numri i elementeve në listënA. Vëreni se nëse të gjitha elementet e listës B do të ishin më të vogla seelementi i parë i A, numri rezultues i krahasimeve do të ishte NB, kur NB ështënumri i elementeve në listën B.

Çka nëse elementi i parë i A është më i madh se elementi i parë i B por të gjithaelementet e A janë më të vogla sesa elementi i dytë i B? Do të krahasonim A[1]dhe B[1] dhe do të lëviznim B[1] në C. Tani e gjejmë veten në pozitën e njëjëtëku ishim në rastin e fundit, ku do të krahasojmë secilin element të A me B[2]gjersa ata të lëvizen (vendosen) në “rezultat” (në atë që del si rezultat, rezulton).Këtë herë, sidoqoftë, nuk e kemi bërë vetëm NA krahasime të elementeve të Ame B[2], por gjithashtu e kemi bërë edhe krahasimin e A[1] dhe B[1], kështu qënumri total i krahasimeve në këtë rast është NA + 1. Nëse i marrim në shqyrtimrenditjet tjera, do të fillojmë të shohim se rasti i prezentuar në paragrafin e parëtë këtij nënseksioni mund të jetë rasti më i mirë, dhe është. E pamë se nëse tëgjitha elementet e listës A ishin ndërmjet B[1] dhe B[2], bëmë më shumëkrahasime sesa nëse të gjitha elementet e A të ishin të ishin më të vogla sesa tëgjitha elementet e B. Le të shohim se nëse shkojmë në ekstremin tjetër a do të

fitojmë rastin më të keq? Shqyrtoni se çka ndodhë nëse elementet e A dhe B

Page 547: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 547/699

Algoritmet dhe strukturat e të dhënave

547

 janë të “ndërthurrura” (të shtresuara) bazuar në vlerat e tyre. Me fjalë të tjera,çka ndodhë nëse vlera e A[1] ndodhet ndërmjet B[1] dhe B[2], vlera e A[2]ndodhet ndërmjet B[2] dhe B[3], vlera e A[3] ndodhet ndërmjet B[3] dhe B[4], ekështu me radhë. Vëreni se secili krahasim e lëvizë një element ose nga A osenga B, në listën C. Bazuar në shembullin e renditjes së mësipërme, ne e lëvizimnjë element të B, pastaj një të A, pastaj një të B, pastaj një të A deri sa të kemilëvizur të gjitha përveq elementit të fundit nga A. Pasi që krahasimet rezultuanme lëvizjen e të gjitha elementeve përveq atij të fundit të A, do të kemi bërë NA+NB-1 krahasime, në rastin më të keq.

 Në total (bashkë me pjesën 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ë për hapësirë shtesë të veçantë

 për të realizuar bashkimin (shkrirjen).

Page 548: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 548/699

Avni Rexhepi

548

 Koncepte të programimit

10.  Algoritmet e përshtatjes së stringjeve

Algoritmet e përshtatjes (përputhjes) së stringjeve (angl. “string matchingalgorithms” ose “pattern matching algorithms”) janë një kategori shumë e përdorur e algoritmeve. Problemi kryesor i adresuar është ai që trajton njëmostër të stringut (angl. pattern) që kërkohet brenda një stringu tjetër (nëmënyrë tipike ndonjë string më i gjatë ose ndonjë tekst i tërë). Thuhet se pokërkohet nën-stringu brenda stringut ose mostra brenda stringut. Rezultati ikërkimit ose do të jetë gjetja e mostrës (nën-stringut) brenda tekstit (stringut), qëzakonisht tregohet me një pointer në pozitën e përshtatjes ose do të jetëinformacioni që teksti nuk e përmban nën-stringun e kërkuar. Kjo zakonisht

është e aplikuar si opcioni i kërkimit në editorët e tekstit.Ekzistojnë disa algoritme të përshtatjes, gjegjësisht kërkimit të tekstit. Mënyra ezakonshme, e kërkimit të tekstit duke krahasuar shkronjë për shkronjë nën-stringun me stringun njihet si algoritmi naiv i  përshtatjes ose algoritmi “brute-force” i përshtatjes. Fillohet me krahasimin e shkronjës së parë të nën-stringutme shkronjën e parë të stringut dhe nëse ato përputhen, atëherë vazhdohet mekrahasimin e shkronjave në pozitën e dytë, e kështu me radhë. Nëse nuk ka përshtatje në ndonjë pozitë, atëherë zhvendoset (shiftohet) nën-stringut për një pozitë më djathtas dhe fillohet prej fillimit krahasimi i pozitës së parë të nën-

stringut me të dytën e stringut, e tutje. Sa herë që paraqitet ndonjë mos- përshtatje në ndonjë pozitë, nën-stringu zhvendoset (shiftohet) për një pozitë mëdjathtas. Nëse përshtaten të gjitha pozitat, atëherë lajmërohet përshtatja,gjegjësisht gjindet përputhja e plotë e nën-stringut brenda stringut.

Algoritmet tjera tentojnë të gjejnë mënyrë më të shpejtë të krahasimit, duke përfituar nga informacioni i mbledhur paraprak për natyrën e mostrës (nën-stringut), ashtu që të anashkalohen krahasimet e panevojshme në rastet kur parashihet që nuk do të ketë përshtatje. Dy nga algoritmet më të njohura të kësajkategorie janë:

-  Algoritmi Knuth-Morris-Pratt (KPM), dhe-  Algoritmi Boyer-Moore (BM)

Këto algoritme janë emërtuar sipas emrave të zbuluesve të tyre.

Për çdo algoritëm të përshtatjes, përdoren termat standarde.

Stringu është sekuencë e karaktereve. Shembuj të stringut janë: teksti izakonshem, një program në C++, një HTML dokument, një sekuencë e ADN-së(AGCTTCGA...), një imazh i digjitalizuar, etj.

Page 549: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 549/699

Algoritmet dhe strukturat e të dhënave

549

Alfabeti, që zakonisht shënohet me  është bashkësia e të gjitha karaktereve tëmundshme për një familje të stringjeve. P.sh, alfabeti i gjuhës shqipe, si bashkësi e të gjitha shkronjave të gjuhës 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 madhësi (gjatësi) m. Pozitatindeksohen prej 0 deri në m-1.

-   Nën-stringu P[i..j] i stringut P është nën-sekuencë e P që përmbanëkarakteret ndërmjet i dhe j.

-  Prefiksi (parashtesa, pjesa e fillimit) i P është nën-stringu i tipit P[0..i].-  Sufiksi (prapashtesa, pjesa e fundit) i P është nën-stringu i tipit P[i..m-1].

Për stingun e dhënë T (Text-teksti) dhe mostrën e dhënë P (Pattern), problemi i përshtatjes/përputhjes së mostrës (angl. Pattern Matching) konsiston në gjetjene nënstringut të T që është i barabartë me P.

Aplikacionet në të cilat zbatohet ky problem janë: editorët e teksteve, makinatkërkuese, kërkimi bilogjik (i ADN), etj.

Algoritmi naiv i përshtatjes

Algoritmi “brute-force” ose algoritmi naiv i përshtatjes, krahason mostrën P metekstin T, për secilën zhvendosje (secilin shiftim) të P relativ ndaj T, deri sa tëgjindet përshtatja ose deri sa të jenë provuar të gjitha pozitat dhe rrjedhë seteksti T nuk e përmbanë mostrën P.

Algoritmi naiv ekzekutohet në kohën O(nm), n është gjatësia e tekstit T, ndërsam është gjatësia e mostrës 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 ezakonshme.

Pseudokodi:

BruteForceMatch(T, P)

Input (Hyrja)  teksti T me gjatësi n dhemostra P me gjatësi m  Output (Dalja)  indeksi fillestar i nën-stringut

të T të barabart me P, ose -1nëse nuk gjindet nën-stringu

for i  0 to n - m  { testo shifto i-në e mostrës P} j  0

while j < m T [i + j] = P[ j]

 j   j + 1

if  j = m  

Page 550: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 550/699

Avni Rexhepi

550

return i {përputhje në pozitën i}else 

break while loop {mospërputhje}return -1 {s’ka asnjë përshtatje/përputhje} 

Algoritmi Knuth-Morris-Pratt (KMP)

Algoritmi Knuth-Morris-Pratt e krahason mostrën P me tekstin T nga e majta nëtë djathtë (njësoj si algoritmi naiv), mirëpo e zhvendosë (shifton) mostrën nëmënyrë më intelegjente, duke anashkaluar krahasimet e panevojshme, kur të jetëe qartë se nuk do të ketë përputhje në tentimin e ardhëshëm, nëse zhvendosja bëhet vazhdimisht vetëm për nga një pozitë, si në rastin e algoritmit naiv.

Për të gjetur madhësinë maksimale të zhvendosjes së mundshme, në rast tëmospërputhjes në ndonjë krahasim, algoritmi KMP e bën përpunimin paraprak(preprocesimin) e mostrës, për të nxjerrë informatat e nevojshme.

Kur të ndodhë mospërputhja, sa është zhvendosja maksimale e mundshme emostrës, ashtu që të evitohen krahasimet e tepërta (redundante)?

Përgjigja është: sa prefiksi më i gjatë i mostrës P që është njëherit edhe sufiks imostrës P, pra prefiksi P[0..j] që është njëkohësisht edhe sufiks i P[1..j].

Nuk ka nevojëtë përsëritet

krahasimi i

kësaj pjese

Vazhdokrahasimin

këtu

 

 Nga figura mund të shihet, që nëse prefiksi (në këtë rast ab) është i njëjtë mesufiksin e pjesës që është përputhur deri në këtë moment (në këtë rast ab),atëherë, zhvendosja bëhet deri në pozitën ku pjesa e prefiksit (që është njëjtë sisufiksi) përputhet me sufiksin dhe nuk ka nevojë të krahasohet rishtazi, porvazhdohet me krahasimin e pozitave më tutje.

Page 551: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 551/699

Algoritmet dhe strukturat e të dhënave

551

Algoritmi Knuth-Morris-Pratt paraproceson mostrën për të gjetur përputhjet e prefikseve të mostrës me vetë mostrën. Funksioni “Failure” (angl. Failure-dështim) F(j) definohet si gjatësia 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ënëse ndodhë mospërputhje në P[j]  T[i], caktojmë jF(j-1).

Funksioni i dështimeve F(j) mund të reprezentohet përmes një vargu dhe mundtë llogaritet në kohë të rendit O(m).

 Në secilën përsëritje të unazës “while” ose: 

-  i rritet për një, ose-  madhësia e zhvendosjes i-j rritet për së paku një (vëreni që F(j-1)<j )

Rrjedhimitsh, nuk ka më shumë se 2n përsëritje të unazës “while”.

Prandaj, algoritmi KPM ekzekutohet në kohë optimale O(m+n).

Algoritmi KMP:

KMPMatch(T, P)F  FunksioniDeshtimit(P)i  0 j  0while i < n if T [i] = P[ j]

if  j = m - 1return i - j { përputhje/match }

Page 552: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 552/699

Avni Rexhepi

552

else 

i  i + 1 j   j + 1

else 

if  j > 0 j  F[ j - 1]

else 

i  i + 1return -1 { s’ka përputhje/no match}

Llogaritja e funksionit të dështimeve

Funksioni i dështimeve F(j) mund të reprezentohet përmes një vargu dhe mundtë llogaritet në kohë të rendit O(m).

Algoritmi i tij është i ngajshëm me vetë KMP algoritmin. Në secilën përsëritje (secilin iteracion) të unazës “while”, ose: 

-  i rritet për një, ose-  madhësia e zhvendosjes i-j rritet për së paku një (vëreni që F(j-1)<j )

Rrjedhimisht, nuk ka më shumë se 2m iteracione të unazës “while”. 

Algoritmi i funksionit të dështimit:

FunksioniDeshtimit (P)

F[0]  0i  1 j  0while i < m  if P[i] = P[ j]

{janë përputhur j + 1 karaktere}

F[i]   j + 1i  i + 1 j   j + 1

else if  j > 0 then 

{përdore funksionin e dështimit për të shiftuar P} j  F[ j - 1]

else 

F[i]  0 { s’ka përputhje/no match }

i  i + 1 

Page 553: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 553/699

Algoritmet dhe strukturat e të dhënave

553

Shembull i KMP:

 Nga tabela e llogaritjes së funksionit të dështimeve F(j), vëreni se prefiksi më igjatë që është është njëherit sufiks, është me gjatësi 2 (ab), për karakteret a dhe bnë lokacionet 4 dhe 5. Kjo, pasi që në fillim, deri në lokacionin 2, kemiP[2]=aba, prandaj prefiksi/sufiksi është vetëm ‘a’, me gjatësi 1, dmth F(2)=1. Në lokacionin 3 kemi: P[3]=abac, keshtu që kemi F(3)=0, pasi s’ka prefiks që

 përputhet me sufiks. Në lokacionin 4, P[4]=abaca, përseri kemi vetëm një përputhje prefiks/sufiks, për a, me gjatësi 1. Në fund, për gjatësinë deri nëlokacionin 5, kemi P[5]=abacab dhe prefiks/sufiks ab, me gjatësi 2,d.m.th.,F(5)=2.

 Nga krahasimi vërehet, se në fillim kemi përputhje të 5 karaktereve të para,mirëpo në krahasimin e 6 (T=a, P=b) nuk ka përputhje, kështu që bëjmëzhvendosjen deri në pozitën e prefiksit/sufiksit që veq përputhet: ‘a’ (F(4)=1).Vërehet, se në këtë rast, në krahasim me algoritmin naiv përfitohet duke mos bërë zhvendosjen e mostrës për vetëm një pozitë, por duke përfituar mezhvendosjen e menjëhershme deri në pozitën ku kemi prefiks që është njëheritedhe sufiks i pjesës ku ka pasur përputhje në krahasimin paraprak.

Vazhdojmë krahasimin e 7, (T=a, P=b), por pasi që menjëherë kemimospërputhje (F(1)=0), zhvendosemi për vetëm një pozitë më tutje.

 Në krahasimet 8, 9, 10,dhe 11 kemi përputhje (përshtatje-match), por nëkrahasimin 12 (T=c, P=a) kemi mospërputhje dhe përsëri zhvendosemi deri në pozitën ku kemi prefiks/sufiks të njëjtë.

Page 554: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 554/699

Avni Rexhepi

554

Menjëherë, në krahasimin e ardhëshm 13, përsëri ka mospërputhje (T=c meP=a) dhe zhvendosemi për një pozitë.

 Në vazhdim, të gjitha krahasimet rezultojmë me përputhje dhe sinjalizohet gjetja

e pozitës në tekst, ku kemi përputhje me mostrën (Pattern Match).Analiza:

Algoritmi KMP ekzekutohet në kohë optimale O(m+n), kështu që:

-  është shumë i shpejtë-  algoritmi kurrë nuk ka nevojë të kthehet prapa në tekstin që analizohet, T.-  kjo e bën algoritmin të përshtatshëm për procesimin e fajllave të mëdhenj

të cilët lexohen nga pajisjet e jashtme ose përmes rrjetit.

Algoritmi KMP nuk punon aq mirë me rritjen e madhësisë së alfabetit. Me

rritjen e alfabetit ka:-  më shumë gjasa për mospërputhje-  mospërputhjet kanë tendencë të paraqitjes herët në mostër, por KMP është

i shpejtë kur mospërputhjet paraqiten më vonë

Algoritmi Boyer-Moore

Algoritmi Boyer-Moore është i bazuar në dy llogaritje heuristike:

-  Looking-glass heuristic  (heuristika e dritares kërkuese) me pariminkrahasimi nga ana e djathtë në të majtë (angl. right-to-left matching):Krahaso P me nënsekuencën e T, duke lëvizura prej fundit kah fillimi, dhe

-  Character-jump heuristic  (heuristika e kërcimit të karaktereve) merregullën e shiftimit të karaktereve të “këqija” (angl. bad  character shift

rule): kur paraqitet mospërputhje në pozitën T[i]=c   Nëse P përmanë c, shifto P për t’u barazuar (drejtuar) me paraqitjen e

fundit të c në P me T[i];  Përndryshe, shifto P për t’u barazuar P[0] me T[i+1]. 

Shembull:

 Në tekstin T=”a pattern matching algorithm” (algoritmi i përputhjes së mostrës), kërkohet mostra (pattern-i) P=”rithm” (ritmi). 

Page 555: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 555/699

Algoritmet dhe strukturat e të dhënave

555

ose, në formë më kompakte:

Pra, në fillim rendited mostra me tekstin nga pozita e parë, mirëpo krahasimifillon nga karakteri i fundit i mostrës (nga ana e djathtë në të majtë). Nëkrahasimin e parë kemi mospërputhje (T[4]=t, P[4]=m). Tani, shikohet aekziston shkronja e analizuar e tekstit (në këtë rast ‘t’) në kuadër të mostrës?Meqenë se shkronja ‘t’ ekziston edhe në kuadër të mostrës, atëherë bëhet

zhvendosja deri në pozitën kur vendosen në vijë të drejtë karakteret ‘t’ të tekstitdhe mostrës. Krahasimi i dytë, përsëri fillon nga skaji i djathtë dhe krahasohet‘e’ e tekstit me ‘m’ të mostrës. Meqenë se nuk ka përputhje dhe nga ana tjetërshkrona ‘e’ e tekstit nuk ekziston fare në kuadër të mostrës, atëherë nënkuptohetse s’ka gjasa të ketë përputhje në tërë gjatësinë, prandaj mostra shiftohet(zhvendoset) deri në pozitën pas karakterit ‘e’ (duke evituar kështu një numër tëmadh të krahasimeve të panevojshme, të cilat do t’i bënte algoritmi naiv ikrahasimit). Krahasimi i tretë, vazhdon me parimin e njëjtë, duke krahasuar ‘a’nga teksti me ‘m’ nga mostra dhe përsëri zhvendosje e plotë e mostrës, sikurse

edhe në rastin e krahasimit të 4 (për karakterin ‘n’) dhe atij të 5 (për karakterin

Page 556: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 556/699

Avni Rexhepi

556

‘g’). Në krahasimin 6, karakteri ‘h’ i tekstit me karakterin ‘m’ nga mostra, përsëri nuk ka përputhje, mirëpo karakteri ‘h’ ekziston në kuadër të mostrës,keshtu që bëhet shiftimi për t’a vendosur mostrën në pozitën kur vijnë në vijë tëdre jtë karakteret ‘h’ të tekstit dhe të mostrës. Krahasimet në vazhdim, nga 7 derinë 11 rezultojnë me përputhje të plotë dhe del që është gjetur mostra në kuadërtë tekstit.

Funksioni i rastitsjes së fundit

Algoritmi Boyer-Moore paraproceson mostrën P dhe alfabetin , për të ndërtuarfunksionin 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ë mostër, ku paraqitetkarakteri i analizuar ‘c’), ose 

-  -1, nëse nuk ekziston indeks i tillë (d.m.th., karakteri ‘c’ nuk ndodhet farenë mostër).

Për shembull, nëse kemi alfabetin   = {a, b, c, d} dhe mostrën (Pattern)P=abacab:

-  Funksioni i rastisjes së fundit, mund të reprezentohet përmes 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ë gjatësia (madhësia) e mostres P, ndërsa s është madhësia e alfabetit .

 Nga tabela shihet se karakteri ‘a’ ekziston në pozitat 0, 2 dhe 4 të mostrës ‘‘abcacab’ (indeksimi i pozitave/lokacioneve fillon prej 0), kështu që rastisja efundit (e skajshme, më e djathtë) është në lokacionin 4. Për shkronjën ‘d’ e cilanuk ekziston fare në mostër, 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 - 1repeatif T [i] = P[ j]

if  j = 0return i { match at i (përputhje në i) }

else 

Page 557: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 557/699

Algoritmet dhe strukturat e të dhënave

557

i  i - 1 j   j - 1

else { character-jump (kërcimi i karaktereve) }

l  L[T [i]]i  i + m – min( j, 1 + l)

 j   m - 1until i > n - 1return -1 { no match (s’ka përputhje}

Mund të paraqiten dy raste kur karakteri i analizuar nuk përputhet:

Rasti 1 –  Nëse karakteri që nuk përputhet ndodhet në mostër, mirëpo në pjesënqë veq ka kaluar krahasimin e suksesshëm, dhe

Rasti 2  –   Karakteri që nuk përputhet ndodhet në mostër, në pjesën e majtë,akoma të pakrahasuar, me çrast zhvendosja bëhet deri sa të vijnë në vijë të drejtëkarakteret e njëjta.

Page 558: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 558/699

Avni Rexhepi

558

Shembull:

Krahasimi 1: T[5]=a, P[5]=b, nuk përputhen, kurse ‘a’ e tekstit ndodhet nëmostër, prandaj zhvendosim mostrën për të vënë në vijë të drejtë shkronjat ‘a’ tëT dhe P.

Krahasimi 2 dhe 3 janë të suksesshëm (ka përputhje), mirëpo në krahasimin 4, përsëri kemi mospërputhje (‘a’ me ‘c’). Përsëri ‘a’ ndodhet në pjesën e majtë të

mostrës, prandaj zhvendosemi deri sa të vihen në vijë të drejtë ‘a’ e T dhe ‘a’ emostrës P.

Krahasimi 5: Mospërputhje e ‘a’ me ‘b’, zhvendosemi një pozitë më tutje (për tëvënë në vijë të drejtë karakteret ‘a’ të T dhe P). 

Krahasimi 6: Mospërputhje e ‘d’ (nga teksti T) me ‘b’ (nga mostra P) dhe pasiqë karakteri ‘d’ nuk ndodhet në mostër, bëjmë zhvendosjen e plotë të mostrës.

Krahasimi 7: Mospërputhje në karakteret ‘a’ nga teksti T me ‘b’ nga mostra P.Zhvendosim mostrën, për të vënë në vijë të drejtë karakteret ‘a’. 

Krahasimet 8 deri në 13 janë të suksesshme (ka përputhje) dhe rezulton se ështëgjetur përputhje e mostrës me tekstin.

Analiza:

Algoritmi Boyer-Moore ka kohën e ekzekutimit të rendit O(nm+A), ku A- ështëalfabeti.

Algoritmi Boyer-Moore është i shpejtë kur alfabeti (A) është i madh, por ingadalshëm nëse alfabeti është i vogël, kështu që del se është i shpejtë për tekstetë zakonshme por i ngadalshëm për rastin e alfabetit binar ose ADN-së.

Page 559: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 559/699

Algoritmet dhe strukturat e të dhënave

559

Algoritmi Boyer-Moore është dukshëm më i shpejtë sesa algoritmi naiv dhe përtekste natyrale është algoritmi më i shpejtë i kërkimit të stringjeve.

Rasti më i keq do të ndodhë nëse kemi:

-  T = aaa...a-  P = baaa

Rasti më i keq mund të paraqitet në sekuenca të ADN-ve ose në imazhe, por pakka gjasa që të paraqitet në tekste të zakonshme.

Page 560: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 560/699

Avni Rexhepi

560

11.  Hash tabelat

Hash tabela është strukturë e të dhënave që përdoret për të implementuar vargjet

asociative, një strukturë që mund të pasqyrojë çelësat në vlera. Hash tabela e përdorë hash funksionin për të llogaritur indeksin në vargun e sloteve (vendeve), prej të cilave mund të gjindet vlera korrekte.

Më mënyrë ideale, hash funksioni do të caktojë secilin çelës në një vend unik, por kjo situatë është rrallë herë e arritshme në praktikë (përveq nëse hash çelësat janë fiks, d.m.th., nuk shtohen vlera të reja pas krijimit të tabelës). Në realitet,shumica e dizajneve të hash tabelave supozojnë se do të ndodhin kolizionet(angl. collision-ndeshje, përplasje, konflikt), rastet kur çelësat e ndryshëm tëcilët caktohen prej hash funksionit u ndahen sloteve të njëjta dhe disi duhet tëakomodohen.

 Në hash tabelën e dimensionuar mirë, kostoja mesatare (numri i instruksioneve) për seclin kërkim është i pavarur prej numrit të elementeve të ruajtura në tabelë.Shumë dizajne të hash tabelave poashtu lejojnë insertimet dhe fshirjet arbitraretë çifteve çelës-vlerë, më kosto mesatare për operacion.

 Në shumë raste, hash tabela del të jetë më efikase sesa pema binare e kërkimitose ndonjë strukturë tjetër e kërkimit të tabelave. Për këtë arsye, hash tableat përdoren në shumë softvere kompjuterike, posaqërisht në vargjet asociative,

indeksim të bazave të të dhënave, cash dhe sete.

 Fig. 9.1 –  Hash tabela dhe hash funksioni

Hash tabela është strukturë që mundëson vetëm një pjesë të operacioneve të pemës binare të kërkimit dhe kryen operacionet e insertimit, fshirjes dhe

Page 561: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 561/699

Algoritmet dhe strukturat e të dhënave

561

kërkimit në kohë mesatare konstante. Për dallim prej pemës binare tëkërkimit, koha e rastit mesatar të hash tabelave është e bazuar në tiparetstatistikore më shumë sesa në pritjen e hyrjeve në dukje të rastit. Ky përmirësim përfitohet në kurriz të humbjes së renditjes së informacioneve përbrenda elementeve. Operacionet si gjetja e minimumit ose maksimumitdhe shtypja e tërë tabelës në renditje të sortuar në kohë lineare, nuk përkrahen. Rrjedhimisht, hash tabelat kanë disa karakteristika të ndryshme të performansës ndaj pemës binare të kërkimit.

Hash tabela (angl. Hash table, hash map) është një prej implementimeve tëmundshme të fjalorit (dictionary ADT). Në parim, Hash tabela i “mapon”  (pasqyron) çelësat unik me vlerat e shoqëruara (angl. map-plan, skemë, hartë;mapping -vendosje në hartë, planifikim; ka të bëjë me pasqyrimin sipasrregullave të bashkësive, lidhjen e elementeve të një bashkësie me tjetrën, etj). Në aspektin e implementimit (zbatimit), hash tabela është një strukturë e tëdhënave e bazuar në varg, e cila e përdorë hash funksionin, për të konvertuarçelësin në indeks të elementit të vargut, ku duhet të kërkohet vlera eshoqëruar.

Hash tabela përkrahë nxjerrjen ose fshirjen e çfarëdo elementi të emërtuar.Interesi është që të jemi në gjendje që të kryejmë operacionet themelore nëkohë konstante, si për rastin e stekut dhe rreshtit (queue-s). pasi që qasja ështëshumë më pak e kufizuar, kjo përkrahje duket e paarritshme. Kjo ështësigurisht kur bashkësia e vlerave rritet dhe kërkimi në bashkësi do të kërkojëmë shumë kohë. Mirëpo, kjo nuk është domosdoshmërisht ajo çka ndodhë.

Hash tabela përdoret për të implementuar bashkësinë (set-in) në kohëkonstante për operacion.

Le të supozojmë se kemi të bëjmë me disa vlera të vogla të numrave të plotë jonegativ, në rangun prej 0 deri në 65535. Një opcion është që të përdorimvargu e thjeshtë për të implementuar secilin operacion si në vijim. Së pariinicializojmë vargun ‘a’ me indekset prej 0 deri në 655635, me të gjitha vleratzero (0). Për të  performuar ‘insert(i)’, ekzekutojmë ‘a[i]++’. Vëreni se a[i]

reprezenton numrin e herave të insertimit të ‘i’-së. Për të  performuar ‘find(i)’(gjeje(i)), verifikojmë që a[i] nuk është zero. Për të perfor muar ‘remove(i)’(largo(i)), sigurohemi që a[i] është pozitiv dhe pastaj ekzekutojmë a[i]--.Koha për secilin operacion është qartësisht konstante. Edhe vetë mbingarkesae inicializimit të vargut është punë konstante (për 65535 përcaktime tëvlerave).

Mirëpo, me këtë zgjidhje kemi dy probleme. Së pari, supozojmë se kemi vleratë mëdha të integjerëve 32-bitësh, në vend të atyre 16-bitësh. Në këtë rast,vargu do të duhet të përmbajë 4 miliardë anëtarë, gjë që është jopraktike. Së

Page 562: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 562/699

Avni Rexhepi

562

dyti, nëse elementet nuk janë numra të plotë por stringje ose objekte të përgjithshme, atëherë ato nuk mund të përdoren për të indeksuar vargun.

Problemi i dytë, në realitet edhe nuk është problem në vete. Mirëpo, njësoj

sikur që numri 1234 është bashkësi e shifrave, 1, 2, 3 dhe 4, edhe stringu“dita” është bashkësi e karaktereve ‘d’, ‘i’, ‘t’ dhe ‘a’. Mir ëpo, numri 1234është thjeshtë: 1*103+2*102+3*101+4*100. Nëse marrim parasysh sekarakteret në mënyrë tipike mund të reprezentohen në 7 bita, si numra prej 0deri në 127 dhe pasi që karakteri në mënyrë themelore është numër i vogël i plotë, atëhere mund të paraqesim stringun si një integjer. Një mundësi do tëishte: ‘d’*1283+‘i’*1282+‘t’*1281+‘a’*1280. Kjo do të mundësonteimplementim si varg i thjeshtë, mirëpo problemi me këtë strategji është se kyreprezentim do të përfaqësonte numra jashtëzakonisht të mëdhenj, e stringjetmë të gjata do të gjeneronin numra shumë më të mëdhenj. Kjo na kthen nëfillim, tek problemi i parë: Si të evitojmë përdorimin e vargjeve absurd tëgjata?

Kjo arrihet duke përdorur një funksion i cili i pasqyron (mapon) nurmat emëdhenj (ose stringjet e interpretuara si numra), në numra më të vegjël dhemë të menaxhueshëm. Funksioni i cili pasqyron një element në indeks tëvogël njihet si hash funksion. Nëse x  është një numër i plotë arbitrarë(jonegativ), atëherë ‘x % madhesiaTabeles’ (ku % - moduli, mbetja nga plotpjestimi) gjeneron numra ndërmjet 0 dhe madhesiaTebeles-1, të

 përshtatshëm për indeksim në një varg me madhësi ‘madhesiaTabeles’. Nësë s është string, mund ta konvertojmë s-in në një integer të madh ‘x’ duke përdorur metodën e sugjeruar më parë dhe pastaj të aplikojmë operatorin emodulit (%) për të përfituar indeks të përshtatshëm. Prandaj, nëse madhësia etabelës ‘madhesiaTabeles’ është 10000, fjala ‘dita’ do të indeksohen përbrenda rangut.

Pra, hash funksioni i konverton elementet në numra të plotë të përshtatshëm përindeksim të vargut ku do të ruhet elementi. Nëse hash funksioni do të ishte një nënjë, do të mund t’i qaseshim element it përmes indeksit të vargut.

Përdorimi i hash funksioneve paraqet një komplikim: dy ose më shumëelemente të ndryshme mund të ‘hash-ohen’ në pozitë të njëjtë, duke shkaktuarndeshje (angl. colision –  ndeshje, konflikt, përplasje). Kjo situatë nuk mund tëevitohet asnjëherë sepse ka shumë më tepër elemente sesa pozita nëdispozicion. Mirëpo, ka shumë metoda të cilat janë në dispozicion përzgjidhje të shpejtë të kolizioneve.

Pasi që hash funksioni nuk është pasqyrim një në një, disa elemente mund tëndeshen në indeksin e njëjtë, duke shkaktuar kolizione.

Page 563: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 563/699

Algoritmet dhe strukturat e të dhënave

563

Hash funksioni

Hash funksioni është pjesë shumë e rëndësishme e dizajnit të hash tabelës.Hash funksioni konsiderohet i mirë, nësë ofron shpërndarje uniforme të hash

vlerave. Tiparet tjera të hash funksioneve, të kërkuara për hashing kualitativdo të analizohen më vonë. Arsyeja se përse hash funksioni është subjekt ishqetësimeve është se hash funksionet e “këqija” shkaktojnë kolizione (angl.collision-konflikt, ndeshje, përplasje) dhe efekte të tjera të padëshiruara, tëcilat ndikojë keq në performansën e përgjithshme të hash tabelës.

Hash tabela dhe faktori i ngarkesës

Strukturë themelore e të dhënave që përdoret për ruajtjen e hash tabelës ështëvargu. Faktori i ngarkesës është herësi (raporti) ndërmjet numrit të

elementeve të ruajtura dhe madhësisë së vargut. Hash tabela mund të jetë oseme madhësi konstante ose në proces dinamik të ndryshueshimit të madhësisë,kur faktori i ngarkesës të tejkalojë një prag të caktuar. Ndryshimi i madhësisë bëhet para se tabela të mbushet plotësisht për të mbajtur numrin e kolizionevenën një vlerë të caktuar dhe për të parandaluar degradimin e performansës.

Kolizionet

Çka ndodhë, nëse hash funksioni kthen hash vlerë të njëjtë për çelësa tëndryshëm? Kjo rezulton në efektin e quajtur kolizion. Kolizionet janë praktikisht të paevitueshme dhe duhet të mirren në konsiderim kurimplementohet hash tabela. Për shkak të kolizioneve, çelësat gjithashtu ruhennë tabelë, ashtu që të mund të dallohen çiftet çelës-vlerë që kanë të njëjtinhash. Ka mënyra të ndryshme të zgjidhjes së kolizioneve. Në esencë, janë dystrategji të ndryshme:

  Closed addressing (open hashing)  –   Adresimi i mbyllur (hashingu i

hapur). Secili slot (angl. slot-ndarje, vend i caktuar, etj.) i hash tabelës përmbanë lidhjen për në një strukturë tjetër të të dhënave (p.sh. lista elidhur), e cila i ruan çiftet çelës-vlerë me hash të njëjtë. Kur ndodhë

kolizioni, kjo strukturë e të dhënave kërkohet për çiftin çelës-vlerë, që i përgjigjet çelësit.

  Open addressing (closed hashing)  –   Adresimi i hapur (hashingu i

mbyllur).  Secili slot në fakt përmbanë çiftin çelës-vlerë. Kur të ndodhëkolizioni, algoritmi i adresimit të hapur e llogaritë një lokacion tjetër(d.m.th. tjetri/i ardhshmi) për të lokalizuar një slot të lirë. Hash tabelat e bazuar në strategji të adresimit pësojnë rënie drastike të performansës, kurtabela është e mbushur fort (faktori i ngarkesës është 0.7 e më shumë).

Page 564: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 564/699

Avni Rexhepi

564

Shembull i thjeshtë i hash tabelës

Do të paraqesim në shembull të hash tabelës së thjeshtë, e cila përdorë një hashfunksion të thjeshtë, ku kolizionet zgjidhen duke përdorur kontrollimin linear

(strategjia e adresimit të hapur) dhe hash tabela ka madhësi konstante.Shembulli paraqet qartazi bazat e teknikës së hashingut.

Hash tabela

Vargu themelor ka madhësi konstante për ruajtjen e 128 elementeve dhe secilisllot përmbanë çiftin çelës-vlerë. Çelësi ruhet për të bërë dallimin ndërmjetçifteve çelës-vlerë, të cilat kanë hash të njëjtë.

Hash funksioni

Tabela lejon vetëm vlera të plota (integer). Hash funksioni që do të përdoretështë mbetja e pjestimit me 128 (moduli me 128). Në aspektin e implementimit,ky hash funksion mund të kodohet përmes përdorimit të operatorit të mbetjesose duke përdorur AND me 127 në nivel bitash.

(Vërejtje: Në praktikë shpesh përdoren tabelat me madhësi të fuqisë së dyshit.Kur përdoren këto, atëherë ka edhe një hash funksion special, i cili aplikohet sishtesë e atij kryesor. Kjo masë parandalon kolizionet e hash kodeve të cilat nukdallojnë në bitat e ulët).

Strategjia e zgjidhjes së kolizioneve

Për zgjidhjen (zbërthimin) e kolizioneve do të përdoret kontrollimi linear. Nësesloti i treguar nga hash funksioni veq është i zënë, algoritmi provon të gjejë njëtë zbrazët duke kontrlluar slotet në vazhdim të vargut.

(Vërejtje. Kontrollimi linear nuk është teknika më e mirë në rastin e tabelës memadhësi konstante. Kur faktori i ngarkesës tejkalon një vlerë të caktuar(afërsisht 0.7), performansa e hash tabelës do të zvogëlohet jolinearisht.Gjithashtu numri i çifteve të ruajtura çelës-vlerë është i kufizuar me madhësinë etabelës (128)).

Pjesë kodi

Implementimi i këtillë ka një problem (bug). Kur nuk ka më vend në tabelë,unaza e kërkimit për slot të lirë do të punojë pandërprerë. Kjo nuk do të ndodhënë hash tabelë reale të bazuar në adresim të hapur, sepse ajo zakonisht është memadhësi dinamike (të ndryshueshme). Gjithashtu, është lënë anashimplementimi i largimit (fshirjes), për të ruajtur thjeshtësinë. Implementimi i plotë do të paraqitet tek pjesa e adresimit të hapur.

class HashEntry {

private:

Page 565: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 565/699

Algoritmet dhe strukturat e të dhënave

565

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;

Page 566: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 566/699

Avni Rexhepi

566

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 përmes vargëzimit (adresimi imbyllur)

Vargëzimi (angl. chaining - si shtimi i hallkave të zingjirit) është një mënyrë emundshme e zgjidhjes/zbërthimit të kolizioneve. Secili slot i vargut përmbanënjë lidhje/link për në listën e lidhur njëfish (angl. singly-linked list) e cila përmbanë çiftet çelës-vlerë me hash të njëjtë. Çiftet e reja çelës-vlerë shtohen nëfund të listës. Algoritmi i kërkimit, kërkon nëpër listë për të gjetur çelësin që përshtatet. Në fillim slotet e tabelës janë të zbrazëta (përmbajnë “null”). Listakrijohet kur vlera me një hash të caktuar shtohet për herë të parë.

Analiza e kompleksitetit

Page 567: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 567/699

Algoritmet dhe strukturat e të dhënave

567

Duke supozuar se hash funksioni i shpërndanë hash kodet në mënyrë uniformedhe tabela mundëson ndryshim dinamik të madhësisë, kompleksiteti iamortizuar i operacioneve të insertimit, fshirjes dhë kërkimit është konstant.Koha aktuale e marrur nga këto operacione varet linearisht nga faktori ingarkesës së tabelës.

Vërejtje. Edhe hash tabela dukshëm e ngarkuar, e bazuar në vargëzim, shfaqë performansë të mirë. Supozoni një hash tabelë me 1000 slote që ruan 100000elemente (faktori i ngarkesës është 100). Kjo kërkon pak më shumë memorie(madhësi tabele) sesa lista e lidhur njëfish, por të gjitha operacionet themeloredo të kryehen afro 1000 herë më shpejtë, në mesatare. Keni parasysh qëkomleksiteti llogaritës i të dyjave, listës së lidhur njëfish dhe hash tabelës memadhësi konstante është O(n).

Pjesë kodiKodi i mëposhtëm implementon vargëzimin me krerë të listës. Kjo do të thotë,se hyrjet e hash tabelës përmbajnë elementin e parë të listës së lidhur njëfish, nëvend se të ruajnë pointerin për 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; 

Page 568: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 568/699

Avni Rexhepi

568

}

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

 

Page 569: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 569/699

Algoritmet dhe strukturat e të dhënave

569

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;

 

Page 570: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 570/699

Avni Rexhepi

570

LinkedHashEntry *entry = table[i]; while (entry != NULL){ prevEntry = entry; 

entry = entry->getNext(); delete prevEntry; } } delete[] table; 

} }; 

Hash tabela. Strategjia e adresimit të hapur

Vargëzimi është mënyrë e mirë për zgjidhje të kolizioneve, mirëpo kashpenzime shtesë të memories për të ruajtur strukturën e listave të lidhura. Nësehyrjet janë të vogla (p.sh. integer) ose nuk ka vlera fare (p.sh. seti si ADT),atëherë shpërdorimi i memories (memoria e zënë kot) është e krahasueshme mevetë madhësinë e të dhënave. Kur hash tabela është e bazuar në strategjinë eadresimit të hapur, të gjitha çiftet çelës-vlerë janë të ruajtura në vetë hashtabelën dhe nuk ka nevojë për strukturë të jashtme të të dhënave.

Zgjidhja e kolizioneve

Le të marrim në konsiderim operacionin e insertimit. Nëse sloti, çelësi në të cilinhashohet, del të jetë i zënë, algoritmi fillon të kërkoj për vend të zbrazët (angl. përdoret termi “free bucket” –   kovë e lirë). Ai fillon me slotin në të cilin uhashua dhe vazhdon me “kontrollimin në varg (në sekuencë)”, gjersa të gjenëvend të lirë. Ka disa sekuenca kontrolluese:

  Kontrollimi linear (linear probing):  distanca ndërmjet pikave tëkontrollimit është konstante (d.m.th., 1 kur kontrollohen slotetkonsekuente (të vazhdueshme, të njëpasnjëshme);

  Kontrollimi kuadratik (quadratic probing): distanca ndërmjet pikavekontrollueserritet sipas një konstante të caktuar në secilin hap (në këtërast distanca deri tek sloti i parë varet mënyrë kuadratike nga numri ihapit);

  Hashingu i dyfishtë (double hashing): distanca ndërmjet pikave tëkontrollimit llogaritet duke përdorur një hash funksion tjetër.

Strategjia e adresimit të hapur kërkon që hash funksioni të ketë karakteristikashtesë. Përveq performimit të shpërndarjes uniforme, ai duhet të evitojëgrumbullimin (angl. clustering) e hash vlerave, të cila janë konsekuente në

renditjen e kontrollimit.

Page 571: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 571/699

Algoritmet dhe strukturat e të dhënave

571

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 vlerën në bazë të modulit më madhësinë e tabelës, atëherë përvargun e vlerave vijuese: 89, 18, 49, 58, dhe 9, në tabelën me kontrollim linear të pozitave të insertimit, do të kishim:

hash(89,10) = 9hash(89,10) = 8hash(89,10) = 9hash(89,10) = 8hash(89,10) = 9 

89

0

1

2

3

4

5

6

7

8

9

18

89

0

1

2

3

4

5

6

7

8

9

49

18

89

0

1

2

3

4

5

6

7

8

9

49

58

18

89

0

1

2

3

4

5

6

7

8

9

49

58

9

18

89

0

1

2

3

4

5

6

7

8

9

Pas

insertimit

të 89

Pas

insertimit

të 18

Pas

insertimit

të 49

Pas

insertimit

të 58

Pas

insertimit

të 9

 

Pasi hash funksioni kthen: çelësiX modul madhesiaTabeles, kolizioni i parë do të ndodhë kur të insertohet 49. Në këtë rast, 49 do të insertohet në pozitën e ardhëshme 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ë pozitën 1. Ngjashëm zgjidhet edhe kolizioni për 9. Përderisa tabela është mjaft e madhe,

Page 572: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 572/699

Avni Rexhepi

572

gjithmonë do të gjindet një vend i lirë. Mirëpo, koha e nevojshme për të gjeturvend të lirë mund të rritet shumë. Për shembull, nëse ka vetëm një vend të lirë tëmbetur, mund të ketë nevojë që të kërkohet përgjatë tërë tabelës për ta gjetur atë.

Në kërkimin linear, kolizionet zgjidhen duke bërë skenimin sekuencial të vargut (merrotullim) deri sa të gjendet një vend i lirë.

Mesatarisht do të pritej që të kemi nevojë të kërkojmë nëpër gjysmën e tabelës për ta gjetur atë, gjë që është shumë larg (shumë më tepër) sesa koha konstante për qasje, për të cilën do të shpresonim. Por, nëse tabela mbahet relativisht ezbrazët, insertimet nuk do të jenë shumë të kushtueshme.

Algoritmi i kërkimit thjeshtë do të ndjekë të njejtën rrugë sikurse algoritmi iinsertimit. Nëse arrinë në slot të zbrazët, elementi që kërkohej nuk është gjetur, përndryshe, në fund do të gjindet elementi i kërkuar. Për shembull, për të gjetur

vlerën 58, fillojmë nga sloti 8 (si tregohet nga hash funksioni). Aty gjejmë njëvlerë, por nuk është ajo e kërkuara, kështu që vazhdojmë në slotin 9. Përsëri,kemi element, por jo atë të duhurin, kështu që vazhdojmë tutje dhe provojmëslotin 0 (lëvizja me rrotullim, qarkore), pastaj slotin 1 deri sa të arrijmë përshtatje (vlera e kërkuar të gjindet). Kërkimi për vlerën 19 do të përfshintetentimet në slotet: 9, 0, 1 dhe 2, para se të arrinte në vendin (slot-in) e zbrazët 3.Prandaj, do të thotë që vlera e kërkuar (19) nuk është gjetur.

Sa i përket fshirjes, nuk mund të performohet fshirja e zakonshme, sikur nërastin e pemës binare të kërkimit, sepse një element në hash tabelë, nuk e

reprezenton vetëm vetëveten, por ai poashtu i lidhë edhe elementet tjera, dukeshërbyer si “placeholder” (angl. place-vend, holder-mbajtës, pra mbajtës i pozitës, vendit) gjatë zgjidhjes së kolizioneve. Prandaj, nëse do të kishim larguarvlerën 89 nga hash tabela, virtualisht të gjitha operacionet vijuese të kërkimit dotë dështonin. Rrjedhimisht, implementohet “lazy deletion” (fshirja përtace, evonuar) ose elementet vetëm shënohen si të fshira në vend se të largohenfizikisht nga tabela. Kjo informatë regjistrohet në një element shtesë (extra) tëtabelës. Secili element ose është aktiv ose i fshirë (angl. deleted).

 Nëse të dhënat në hash tabelë janë stringje ose kombinime (jo vetem numra të

 plotë), do të kemi rastin si në vijim:

Page 573: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 573/699

Algoritmet dhe strukturat e të dhënave

573

 Nëse për zgjidhjen e kolizioneve përdoret metoda e kontrollimit kuadratik, do tëeliminohet problemi kryesor i grupimeve, i kontrollimit linear, ku në pozita tënjëpasnjëshme vendosen elementet që kanë kolizion. Kontrollimi kuadratikanalizon qelulat (vendet, slotet) larg prej pikës fillestare të kontrollimit. Emri itij rrjedh nga përdorimi i formulës  F(i)=i

2, e cila përdoret për zgjidhje tëkolizioneve. Në mënyrë specifike, nëse hash funksioni rezulton në H dhe qelulaH del se është jopërfundimtare (vendi është i zënë), nuk provohet qelula vijuesemenjëherë në vazhdim, por provohen qelulat H+12, H+22, H+32...H+i2  (duke përdorur rrotullimin, kalimin prej fundit në fillim). Kjo strategji dallon ngastrategjia e kërkimit 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 kërkimit. 

Figura vijuese paraqet tabelën në rastin e përdorimit të kontrollimit kuadratik, për insertimin e sekuencës së njëjtë që e përdorëm më parë: 89, 18, 49, 58, dhe 9. 

Page 574: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 574/699

Avni Rexhepi

574

hash(89,10) = 9hash(89,10) = 8hash(89,10) = 9hash(89,10) = 8

hash(89,10) = 9

89

0

1

2

3

4

5

6

7

8

9

18

89

0

1

2

3

4

5

6

7

8

9

49

18

89

0

1

2

3

4

5

6

7

8

9

49

 

58

18

89

0

1

2

3

4

5

6

7

8

9

49

 

58

9

18

89

0

1

2

3

4

5

6

7

8

9

Pas

insertimit

të 89

Pas

insertimit

të 18

Pas

insertimit

të 49

Pas

insertimit

të 58

Pas

insertimit

të 9

 

Kur ndodhë kolizioni i parë, gjatë insertimit të 49 (kolizion me 89), altertantivae parë është një qelulë më tutje (0). Pasi është e lirë, 49 vendoset aty. Pastaj, 58ndeshet me 18 (në pozitën 8). Pozita 9 (një vend më tutje) është e zënë, kështuqë qelula e zbrazët gjindet në tentimin e ardhëshëm, që është 22=4 pozita mëtutje prej hash pozitës fillëstare. Kështu, 58 vendoset në qelulën (vendin, slotin)2. Ngjashëm vazhdohet për vlerat tjera në vijim. Vëreni që lokacionet alternative për elementet të cilat hash-ohen në pozitën 8 dhe lokacionet alternative përelementet që hash-ohen në pozitën 9, nuk janë të njëjta. Sekuenca e provave(kontrollimeve) për insertim të 58 nuk ndikon në insertimin pasues të 9, gjë qëështë ndryshe nga ajo që ndodhte tek kontrollimi linear.

 Në këtë rast, madhesia e tabelës nuk është e përshtatshme, sepse zakonishtzgjedhet vlerë që është numër primar, për të pasur implementim më efikas.

Duhet sqaruar disa detaje. Në kontrollimin linear, secila provë tenton qelula tëndryshme. A garanton kontrollimi kuadratik këtë gjë, që kur tentohet një qelulë,atë nuk e kemi provuar gjatë procesit aktual të insertimit. A garanton kontrollimikuadratik që kur është duke u insertuar X dhe tabela nuk është e mbushur,atëherë X do të insertohet?

Kontrollimi linear implementohet lehtë. Kontrollimi kuadratik duket të kërkojëoperacionet e shumëzimit dhe modulit. Ky kompleksitet i shtuar, a e bën

kontrollimin kuadratik jopraktik për t’u im plementuar? Çka ndodhë (në të dy

Page 575: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 575/699

Algoritmet dhe strukturat e të dhënave

575

rastet e kontrollimit) kur faktori i ngarkesës bëhet shumë i madh? A mund tëzgjerohet tabela në mënyrë dinamike, siç veprohet zakonisht me strukturat e bazuara në vargje?

Për fat, përgjigjet janë relativisht të mira në të gjitha rastet. Nëse madhësia etabelës është numër primar dhe faktori i ngarkesës nuk e tejkalon vlerën 0.5,gjithnjë do të mund të insertohet elementi i ri X dhe asnjë qelulë nuk do të provohet dy herë gjatë qasjes. Sidoqoftë, për të vlejtur këto garancione, duhet tësigurohet që madhësia e tabelës është numër primar.

Nëse madhësia e tabelës është numër primar dhe faktori i ngarkesës nuk është më imadh se 0.5, të gjitha provat do të jenë në lokacione të ndryshme dhe gjithmonë do tëketë mundësi për të insertuar elementet. 

Operacioni i largimit

Ka disa nuanca, kur bëhet largimi i çelësit nga hash tabela bëhet me adresim tëhapur. Merrni në konsiderim rastin vijues:

 Nëse algoritmi thjeshtë e liron pozitën “Sandra Miller”, struktura e tabelës do të prishet. Algoritmi nuk do të ketë sukses në tentimin për të gjetur çelësin“Andrew Wilson”. Në fakt, çelësi “Andrew Wilson” është i hashuar në “slotin ekuq”. Sloti përmbanë çelës tjetër dhe algoritmi i kontrollimit linear do të tentojëtë gjejë “Andrew Wilson” në pozitën e ardhshme (në vazhdim, konsekuente), por ajo është e zbrazët:

Page 576: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 576/699

Avni Rexhepi

576

Zgjidhja është si vijon. Në vend se vetëm të fshijë çelësin, algoritmi e shkruanvlerën speciale “DELETED” (angl. e fshirë) në atë slot. 

Tani algoritmi i kërkimit do të punojë si duhet. Algoritmi i insertimit do të duhejtë përdorë slotet e fshira, kur të jetë e mundur.

Vërejtje. Ky algoritëm e zgjidhe problemin, por me kohë hash tabela do të bëhete mbushur me hyrjet "DELETED", gjë që ndikon keq në performansë. Nësehash tabela duhet të lejojë largimin (fshirjen) e elementeve, atëhëre vargëzimiështë mënyrë më e preferuar për zgjidhjen e kolizioneve.

Page 577: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 577/699

Algoritmet dhe strukturat e të dhënave

577

Analiza e kompleksitetit

Hash tabelat e bazuara në adresim të hapur janë shumë më të ndjeshme nëzgjedhjen e duhur të hash funksionit. Nën supozimin se hash funksioni është i

mirë dhe hash tabela është e dimensionuar mirë, komoleksiteti i amortizuar ioperacioneve të insertimit, fshirjes dhe kërkimit është konstant.

Performansa e hash tabelave të bazuara në skemën e adresimit të hapur ështëshumë e ndjeshme në faktorin e ngarkesës së tabelës. Nëse faktori i ngarkesëskalon pragun 0.7, shpejtësia e tabelës degradon drastikisht. Në të vërtetë,gjatësia e sekuencës së kontrollimit është proporcional me vlerën:(faktoriNgarkeses) / (1  –   faktoriNgarkeses). Në rastet ekstreme, kurfaktoriNgarkeses i afrohet 1-shit, gjatësia e sekuencës i afrohet vlerës pakufi. Në praktikë, kjo do të thotë se nuk ka slote të lira në tabelë dhe algoritmi kurrë nuk

do të gjejë vend për të insertuar elementin e ri. Prandaj, ky lloj i hash tabelaveduhet të përkrahë ndryshimin dinamik të madhësisë në mënyrë që të jetë efikas.

Adresimi i hapur kundrejt vargëzimit

Vargëzimi (Chaining) Adresimi i hapurOpen addressing 

Zgjidhja e kolizioneve Duke përdorur strukturë të jashtme të të dhënave

Duke përdorur vetë hashtabelën

Shpenzimi i kotë imemories 

Mbingarkim sa mëdhësia epointerit për vlerë (dukeruajtur krerët e listës nëtabelë)

Nuk ka mbingarkim (overhead)1 

Varësia eperformansës ngafaktori i ngarkesës sëtabelës 

Direkt proporcionalProporcional me(faktoriNgark.) / (1-faktoriNgark.)

Lejon të ruaj mëshumë vlera sesa

madhisa e hashtabelës

Po

Jo. Për më tepër, rekomandohet

mbajtja e faktorit të ngarkesësnën 0.7

Kërkesat për Hashfunksionin 

Shpërndarje UniformeShpërndarje Uniforme, duhetevituar grumbullimin (clustering)

Trajtimi i fshirjeve  Fshirjet janë OKFshirjet e mbushin hash tabelënmë vlerat "DELETED"

Implementimi  I thjeshtëImplementimi korrekt i hashtabelës së bazuar në adresim të

hapur është mjaft i ndërlikuar

Page 578: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 578/699

Avni Rexhepi

578

Hash tabelat me vargëzim mund të punojnë në mënyrë efikase me faktorë tëngarkesës më shumë se 1. Në të njëjtën kohë, tabelat e bazuara në skemën eadresimit të hapur kërkojnë që faktori i ngarkesës të mos kalojë vlerën 0.7, përtë qenë efikase. Prandaj, 30% e sloteve mbesin të zbrazëta, gjë që çon nëshpërdorim të dukshëm të memories.

Pjesë kodi

Kodi i mëposhtëm 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)

Page 579: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 579/699

Algoritmet dhe strukturat e të dhënave

579

{}

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

Page 580: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 580/699

Avni Rexhepi

580

}

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;

Page 581: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 581/699

Algoritmet dhe strukturat e të dhënave

581

}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 madhësisë

Me rritjen e faktorit të ngarkesës së hash tabelës, numri i kolizioneve rritet, gjë

që çond në zvogëlimin e performansës së përgjithshme të tablelës. Kjo është edurueshme për hash tabelat me vargëzim, por e papranueshme për tabelat e bazuar në adresim të hapur, për shkak të rënies esenciale të performansës.Zgjidhje për këtë është ndryshimi i madhësisë së tabelës, kur faktori i ngarkesëstë tejkalon pragun e dhënë.

Gjithashtu, kur tabela bëhet shumë e rrallë, është e arsyeshme që të “paketohet”vargu për të kursyer hapësirën.

Algoritmi i ndryshimit të madhësisë

Mbani mend që vlerat e hash tabelës varen nga madhësia e tabelës. Prandaj,hashet e hyrjeve ndryshojnë kur të ndryshojë madhësia e tabelës dhe algoritminuk mundet vetëm të kopjojë të dhënat nga vendi i vjetër i ruajtjes në të riun. Përhash funksionin e përgjithshëm e vetmja gjë që duhet bërë është të kalohet nëpërtërë hash tabelën e vjetër dhe të shtohet (insertohet) secila vlerë në tabelën e re.

Analiza e kompleksitetit

 Ndryshimi dinamik i madhësisë nuk ndikon në kompleksitetin e amortizuar tëoperacioneve të hash tabelës. Por, ndryshimi bëhet përnjëherë dhe operacioni icili e inicion ndryshimin e madhësisë merr kohën O(n) për t’u kompletuar, ku n

Page 582: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 582/699

Avni Rexhepi

582

është numri i vlerave në tabelë. Ky fakt mund t’a bëjë hash tabelën me madhësidinamike, të papërshtatshme për alikacione real-time (angl. real-time - kohëreale).

 Një tjetër implementim i plotë i Hash Tabelës është dhënë në shtojcën në fund tëlibrit.

Hash tabela ndaj pemës binare të kërkimit

Për të implementuar insertimin dhe kërkimin mund të përdoren edhe pemët binare të kërkimit. Megjithëse kohët mesatare rezultuese janë në kufijtë O(log N), pemët binare të kërkimit gjithashtu përkrahë funksionet të cilat kërkojnërenditje dhe prandaj janë më të fuqishme.

Duke përdorur hash tabela, nuk mund të gjejmë elementin minimal në mënyrë

efikase ose të zgjedrojmë tabelën për të lejuar llogaritjen e statistikës së rendit. Nuk mund të kërkojmë në mënyrë efikase ëpr ndonjë string, përveç nëse stringui saktë është i njohur. Pema binare e kërkimit do të mund të gjente shumë shpejttë gjitha elementet në një rang (brez) të caktuar, por kjo aftësi nuk përkrahet ngahash tabelat. Për më tepër, kufiri O(log N) nuk është domosdoshmërisht aqshumë në krahasim me O(1), posaqërisht pasi që nga pemët e kërkimit nukkërkohen shumëzime ose pjestime.

Përdorni hash tabelë në vend të pemës binare të kërkimit nëse nuk ju duhen statistikatë renditjes dhe nuk brengoseni për hyrjet jo të rastit. 

Rasti më i keq për hash-ing në përgjithësi rezulton prej një gabimi tëimplementimit, ndërsa hyrja e sortuar mund të bëjë që pema binare e kërkimit të performojë dobët. Pemët e balansuara janë implementim mjaft i kushtueshëm.Së këndejmi, nëse nuk kërkohet informacion për renditje dhe nuk ka dyshime sehyrja mund të jetë e sortuar, hash-i është struktura e duhur e të dhënave.

Aplikimet e hash tabelave

Aplikacionet me hash janë të shumta. Kompajlerët i përdorin hash tabelat për të përcjellur variablat e deklaruar në kodin burimor. Struktura e të dhënave në këtë

rast quhet tabelë e simboleve. Hash tabelat janë aplikacion ideal për këtë problem sepse kryhen vetëm operacinet e insertimit dhe kërkimit. Identifikatorëtzakonisht janë të shkurtër, kështu që hash funksioni mund të llogaritet shpejtë. Në këtë aplikacion, shumica e kërkimeve janë të suksesshme.

 Një përdorim i zakonshëm i hash tabelave janë programet e lojrave. Gjersa programi kërkon nëpër rreshtat e ndryshëm të lojës, ai përcjellë pozitat që i kahasur duke llogaritur hash funksionin e bazuar në pozitë (dhe duke ruajturlëvizjen e tij për atë pozitë). Nëse ndodhë përsëri pozita e njëjtë, zakonisht përmes nëj ndërrim vendesh (angl. transposition) të lëvizjeve, programi mund të

Page 583: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 583/699

Algoritmet dhe strukturat e të dhënave

583

evitojë rillogaritjet e kushtueshme. Kjo karakteristikë e përgjithshme e programeve të lojrave quhet tabelë e transpozicioneve.

Përdorim tjetër i zakonshëm i hash tabelave është në “spell checker”-ët online

(angl. spell check –  verifikim i gabimeve të shtypit). Nëse detektimi i gabimeveështë i rëndësishëm (në krahasim me korrektimin), mund të prehash-ohet njëfjalor i plotë dhe fjalët mund të verifikohen në kohë konstante. Hash tabelat janëtë përshtatshme për këtë qëllim pasi që fjalët nuk duhet të jenë të rendituraalfabetikisht. Shtypja e gabimeve në renditjen në të cilën kanë ndodhur nëdokument është e pranueshme.

Si përmbledhje, hash tabelat mund të përdoren për të implementuar operacionete insertimit dhe kërkimit në kohë konstante. Kujdesi për detajet si faktori ingarkesës është posaqërisht i rëndësishëm në përdorimin e hash tabelave,

 përndryshe kufijtë e kohës konstante nuk kanë domethënie. Zgjidhja ekujdesshme e hash funksionit poashtu është e rëndësishme kur çelësi nuk ështëstring i shkurtër ose integer. Duhet zgjedhur një funksion që llogaritet lehtë dheqë bën shpërndarje të mirë.

Për hash me vargëzim të veçantë, faktori i ngarkesës në mënyrë tipike është afër1-shit, edhe pse performansa nuk degradon dukshëm përveq nëse faktori ingarkesës është shumë i lartë. Për kontrollimin kuadratik, madhësia e tableësduhet të jetë numër primar dhe faktori i ngarkesës nuk duhet të tejkalojë 0.5-shin. Rihash-imi duhet të përdoret për kontrollimin kuadratik për të lejuar rritjen

e tabelës dhe për të mirmbajtur faktorin korrekt të ndarkesës. Kjo qasje është erëndësishem nëse hapësira është e ngjeshur dhe nuk është e mundshme thjeshtëvetëm të deklarohet hash tabela shumë e madhe.

Page 584: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 584/699

Avni Rexhepi

584

12.  STL

STL-i (Standard Template Library  –   Libraria standarde e templlejtave) ofronnjë numër të madh të shablloneve të kontejnerëve dhe të algoritmeve të përgjithshme të cilat operojnë në një numër të madh të kontejnerëve.

STL Kontejnerët

Kontejnerët standard

Kontejneri është objekt mbajtës që ruan një koleksion të objekteve të tjera

(elementet e tij). Kontejnerët implementohen si templlejta të klasave, të cilatofrojnë fleksibilitet të lartë për tipet e përkrahura si elementet të tyre.

Kontejneri menaxhon hapësirën memorike për elementet e tij dhe ofronfunksionet për qasje në to, ose drejtpërdrejt ose përmes iteratorëve(referencave).

Kontejnerët replikojnë strukturat e përdorura zakonisht në programim: vargjetdinamike (vector), reshtat (queue), stekun (stack), pirgun (priority_queue), listate lidhura (list), pemët (set) dhe vargjet asociative (map).

Shumë kontejnerë kanë disa funksione anëtare (member functions) të përbashkëta dhe bashkëndajnë funksionalitetin e tyre. Vendimi se cili tip ikontejnerit të përdoret për cilin problem specifik, nuk varet në përgjithësi vetëmnga funksionaliteti i ofruar nga kontejneri, por poashtu edhe nga efikasiteti i disa prej anëtarëve të tij (kompleksiteti). Kjo është posaqërisht e vërtetë përkontejnerët e sekuencave, të cilët ofrojnë “pazare” në kompleksitet ndërmjetinsertimit/largimit të elementeve dhe qasjes në to.

<stack >, <queue> dhe < priority_queue> janë implementuar si adaptorë tëkontejnerëve. Adaptorët e kontejnerëve nuk janë klasa të plota të kontejnerëve,

 por klasa të cilat ofrojnë interfejs specifik që mbështetet në ndonjë objekt tëklasave të kontejnerëve (si p.sh. deque ose list) për të trajtuar elementet.Kontejnerët nën sipërfaqe janë të enkapsuluar në atë mënyrë që elementet e tyreqasen nga ana e anëtarëve të adaptorëve të kontejnerëve pavarësisht prejkontejner klasës nën sipërfaqe, që përdoret.

Templejtat e klasave të kontejnerëve

Page 585: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 585/699

Algoritmet dhe strukturat e të dhënave

585

Sequence containers (kontejnerët e sekuencave/vargjeve/fushave):array Klasa ‘Array’ (e vargut) (class template) vector Vector/Vektor (class template)deque Double ended queue (rresht me dy skaje) (class template)forward_list Lista përpara (e lidhur vetwm nw njw kahje) (class template)list Lista (class template)

Container adaptors (adaptorët e kontejnerëve :

stack LIFO steku (class template)queue FIFO queue (class template)priority_queue Rreshti me prioritet (class template)

Associative containers (kontejnerwt asociativ):set set (class template)multiset set me çelësa të shumwfisht/duplikat (class template)map map (class template)multimap map me çelësa të shumwfisht/duplikat (class template)

Unordered associative containers (kontejnerwt asociativ tw

parenditur):unordered_set set i parednitur (class template)unordered_multiset multiset i parenditur (class template)unordered_map map i parennditur (class template)unordered_multimap multimap e parenditur (class template)

Tiparet e kontejnerëveSekuenca  Elementet në kontejnerë të sekuencave janë të renditur në sekuencë lineare strikte.

Elementet individuale janë të qasshme në bazë të pozitës së tyre në këtë sekuencë.Listat e l idhur a dyf ish

Secili element mbanë informacionet për të lokalizuar elementin paraprak dhepasardhës, duke mundësuar operacione të insertimit dhe fshirjes në kohë konstantepara ose pas elementit specifik (bile edhe për rangje të tëra), por nuk ka qasje direktetë rastit.Tëvetëdij sh me për al okato r (A llocato r-aware )

Kontejneri përdorë një objekt alokator (shpërndarës) për të trajtuar (manipuluar)kërkesat e veta për hapësirë memorike.

Page 586: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 586/699

Avni Rexhepi

586

Kontejnerët:

<array> (prej versionit C++ 11 e tutje)

<array> - Array header-iHeader-i që definon kontejner klasën e vargut me madhësi fikse.Klasat: array (class template )Funksionet:

 begin –  Iterator-i për fillimin e vargut (function template )end –  Iteratori për fundin e vargit (function template )

template < class T, size_t N > class array;

Array class

Vargjet janë kontejner të sekuencave me madhësi fikse. Ata mbajnë një numër

specifik të elementeve të renditur në sekuencë lineare strikte.

Së brendi (angl. internally), vargu nuk mbanë të dhëna të tjera përveqelementeve të tij (as edhe madhësinë e tij, e cila është parametër i templejtit, ifiksuar në kohën e kompajlimit). Sa i përket madhësisë në memorie është po aqefikas sa edhe një varg i zakonshëm, i deklaruar me sintaksën e C++, përmeskllapave të mesme ([ ]). Kjo klasë vetëm shton një shtresë të anëtarëve dhefunksioneve globale, ashtu që vargu të mund të përdoret si kontejnerët standard.

Për dallim prej kontejnerëve të tjerë standard, vargjet (arrays) kanë madhësi

fikse dhe nuk e menagjojnë alokimin e elementeve të tyre përmes alokatorit: ata janë një tip përmbledhës që enkapsulon elementet e vargut me madhësi fikse. Sëkëndejmi, ata nuk mund të rriten ose zvogëlohen në mënyrë dinamike (gjatëkohës së ekzekutimit), gjë që është e mundur me vargun e krijuar përmes klasës<vector>.

Vargjet me madhësi zero janë valid, por nuk duhet të dereferencohen (anëtarët:front, back, dhe data).

Për dallim prej kontejnerëve të tjerë të STL-it (librarisë standarde), shkëmbimi idy vargjeve është operacion linear që përfshinë shkëmbimin e të gjithaelementeve në rangjet individuale, gjë që në përgjithësi është operaciondukshëm më jo-efikas. Në anën tjetër, kjo lejon që iteratorët në të dy kontejnerëttë ruajnë ndërlidhjen e tyre me kontejnerin origjinal.

Tiparet e kontejnerit <array>:

Sekuenca: elementet në kontejnerin e vargut (sekuencës) janë të renditur nërenditje lineare strikte. Elementet individuale qasen sipas pozitës së tyre në varg.

Ruajtja në lokacione të njëpasnjëshme: elementet ruhen në lokacione tënjëpasnjëshme në memorie, gjë që mundëson kohë konstante të qasjes me

Page 587: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 587/699

Algoritmet dhe strukturat e të dhënave

587

rastësi të elementeve. Pointerët në një element mund të “zhvendosen” (angl.offset) për të ju qasur elementeve të tjera.

Madhësi fikse: Kontejneri përdorë konstruktorët dhe destruktorët e brendshëm

(implicit) për të alokuar hapësirën e kërkuar në mënyrë statike. Madhësia evargut është konstante e kohës së kompajlimit. Nuk ka mbingarkim memorikose kohor.

Parametrat e templejtit

template < class T, size_t N > class array;

T - Tipi i elementeve të vargut. “ Aliased ” (angl. alias –  nofkë, pseudonim) si tipii anëtarit: array::value_type.

 N - Madhësia e vargut, në terma të numrit të elementeve.

 Në referencat për funksionet (member functions), të array, këta emra të njëjtësupozohen për paramterat te templejtit.

Tipet e anëtarëve (Member types)

Aliaset vijuese janë tipe të anëtarëve të vargut (array). Këto përdoren gjerësishtsi tipe të parametrave dhe vlerave të kthyera (me return) nga ana e funksioneveanëtare (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 klasës, Member functions)

Iteratorët

 begin Kthe (me return) iteratorin në fillimin e vargut (funksion publik)end Kthe iteratorin në fund të vargut (funksion publik)rbegin Kthe iteratorin revers (vargu i rrotulluar anasjelltas, me renditje të

kundërt) në fillimin revers (funksion publik)rend Kthe iteratorin revers për fundin revers(funksion publik)cbegin Kthe iteratorin konstant për fillimin (funksion publik)cend Kthe iteratorin konstant për fundin (funksion publik)crbegin Kthe iteratorin revers konstant për fillimin revers (funksion publik)crend Kthe iteratorin revers konstant për fundin revers (funksion publik)

Kapaciteti

size Kthe madhësinë e vargut (numri i anëtarëve) (funksion publik)

Page 588: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 588/699

Avni Rexhepi

588

max_size Kthe vlerën maksimale ((funksion publik)empty Testo/verifiko a është vargu i zbrazët (funksion publik)

Qasja në elemente

operator[ ] Qasju elementit (funksion publik)at Qasju elementit (funksion publik)front Qasju elementit të parë (funksion publik) back Qasju elementit të fundit (funksion publik)data Merr pointerin në vlerë (funksion publik)

Modifikatorët

fill Mbushe vargun me vlerë (funksion publik)swap Shkëmbe përmbajtjen (funksion publik)

Shembulli 1:

1234

56789101112131415

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

 Nëse nuk deklarohet përdorimi i emërtimeve standarde nga libraria “std”,(rreshti 4 është koment në këtë rast), atëherë për secilin urdhër duhet shënuar së pari prej cilës librari vjen, në formën: std::cout (përmes operatorit :: - “scope

resolution operator”).

Page 589: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 589/699

Algoritmet dhe strukturat e të dhënave

589

 Në rreshtin 8, është deklaruar vargu “vargu-im” dhe janë inicializuar vlerat eelementeve (antëarëve).

 Në rreshtin 11 deklarohet unaza “for” (në versionin 11, me opcionin “auto” dhe

me iteratorin “it”). Iteratori “lëvizë” prej vargu-im.begin( ) (funksioni begin, qëkthen fillimin e vargut/anëtarin e parë) deri tek vargu-im.end( ) (funksioni end(), që kthen fundin e vargut/elementin e fundit). Pra, përmes funksioneve përkatëse, kthehen fillimi dhe fundi i vargut.

 Në rreshtin 12, dereferncohet vlera e anëtarit (përmes pointerit “*it”). 

Shembulli 2:

1

234567891011121314151617181920 

// array::front 

#include <iostream> #include <array> using namespace std;int main (){

array<int,3> varguIm = {2, 16, 77};

cout << "fillimi: "<< varguIm.front() <<endl; // 2 cout << "fundi: " << varguIm.back() <<endl; // 77 

varguIm.front() = 100;

cout << "varguIm tani permbane:";for ( int& x : varguIm ) cout << ' ' << x;

cout << '\n';

return 0;} 

Rezultati/dalja:

fillimi: 2

fundi: 77varguIm tani permbane: 100 16 77

Shembulli 3:

12345

6

// array::front #include <iostream> #include <array> using namespace std;

int main ()

Page 590: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 590/699

Avni Rexhepi

590

78910

11121314151617181920

{array<int,3> varguIm = {2, 16, 77};cout<<"fillimi: "<<varguIm.front()<<endl; // 2cout<<"fundi : "<<varguIm.back()<<endl; // 77 

varguIm.front() = 100;

cout << "varguIm tani permbane:";for ( int& x : varguIm ) cout << ' ' << x;

cout << '\n';

return 0;} 

Rezultati/dalja:fillimi: 2fundi : 77varguIm tani permbane: 100 16 77

 Në rreshtin 9, i qasemi elementit të parë (fillimt të vargut), përmes funksionit“front”, emriiVargut.front( ), ndërsa në rreshtin 10, elementit të fundit (fundit tëvargut), përmes funksionit “back”, emriiVargut.back( ).

Shembulli 4:12345678910

111213141516171819

// 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 përmbajtjen: cout << "varguIm permbane:";for (int i=0; i<10; i++)cout << ' ' << varguIm.at(i);

cout << '\n';

return 0;} 

Rezultati/dalja:

Page 591: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 591/699

Algoritmet dhe strukturat e të dhënave

591

varguIm permbane: 1 2 3 4 5 6 7 8 9 10

Funksioni at( ), (angl. at  –   në, te, tek), që nënkupton elementi në pozitën meindeksin përkatës, p.sh. varguIm.at(0), i bie elementi në pozitën 0, gjegjësisht

elementi i parë i vargut. Në këtë shembull, përmes unazës for, lëvizim ngaindeksi 0 deri tek indeksi 9, kështu që urdhëri 15 që ndodhet brenda unazës,shtypë me radhë të gjithë anëtarët e vargut, në pozitat prej 0 deri në 9, përmesfunksionit varguIm.at(i). “Zëvendësim” për funksionin at( ), është operatorii qasjes [ ] (si në shembullin vijues).

Shembulli 5:

1

23456789101112131415161718192021

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

Përmes unazës në rreshtin 12, secilit anëtarë në pozitën “i”, i japim vler ën përmes operatorit [ ], që nënkupton anëtarin në pozitën e shënuar brendakllapave të mesme, gjegjësisht operatorit të pozitës.

Shembulli 6:

12// array::fill#include <iostream> 

Page 592: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 592/699

Avni Rexhepi

592

3456

7891011121314151617

#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, bëhet deklarimi i vargut përmes formës:

array<tipi, numrianetareve> emri;

me ç’rast, rezervohet hapësira për numrin e anetareve të deklaruar, në ketë rast,

6 anëtarë të tipit “int”.  Në rreshtin 9, përmes funksionit fill(x), (angl. fill  –  mbush, mbushje), mbushetvargu me vlerën e zgjedhur të x-it (në këtë rast, 5).

Shembull 7:

12345

67891011121314151617

// array::rbegin/rend #include <iostream> #include <array> using namespace std;

int main (){array<int,5> varguIm = {1, 2, 3, 4, 5} ;

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

Page 593: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 593/699

Algoritmet dhe strukturat e të dhënave

593

18192021

22

for (auto rit=varguIm.rbegin();rit<varguIm.rend(); ++rit)cout << ' ' << *rit;

cout<<endl;return 0;

Rezultati/dalja: varguIm nuk eshte i zbrazetvarguIm permbane: 1 2 3 4 5varguIm mbrapsht: 5 4 3 2 1

Opcioni “auto” mundëson përshkimin automatik të anëtarëve të bashkësisë/vargut.

 Në rreshtin 10, përmes funksionit “empty” (angl. empty –  i zbrazët), testohet a

është vargu i zbrazët? Në unazën e for, (rreshti 13) deklarohet iteratori (it-shkurtesa për iterator) i cili përmes funksioneve begin( ) (angl. begin –  fillimi) dhe end( ) (angl. end –  fundi) bën përshkimin e vargut.

Pastaj, në unazën e dytë for (rreshti18) deklarohet iteratori revers (rit-shkurtesa për revers iterator) i cili përmes funksioneve rbegin( ) (angl. reverse begin  –  fillimi i kundërt) dhe rend( ) (angl. reverse end  –   fundi i kundërt) bën përshkimin e vargut në anën e kundërt.

Page 594: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 594/699

Avni Rexhepi

594

<stack> - Steku

Stack header-i

Header-i që definon kontejner klasën stack.

Klasa: stack (LIFO stack)Steku është tip i kontejnerit adaptor, i dizajnuar në mënyrë specifike për tëoperuar në kontekstin LIFO (angl. Last-in First-out - I fundit brenda, i pari jashtë), ku elementet insertohen dhe nxirren vetëm nga njëri skaj i kontejnerit.

Steku implementohet si adaptor kontejneri, që është klasë që përdorë një objekttë enkapsuluar të klasës specifike të kontejnerit si nën-kontejner, duke ofruar njëset specifik të funksioneve anëtarë për të ju qasur elementeve të tij. Elementetshtyhen/nxirren nga skaji i kontejnerit, i njohur is ‘top’ (angl. Top –  Krye, Maje,

Kulm). Nën-kontejneri mund të jetë cilido templejt i klasës së kontejnerit standard osendonjë klasë e dizajnuar posaqërisht. Kontejneri duhet të përkrahë operacionetvijuese:

  empty (i/e zbrazët, zbraze)

  size (madhësia)

   back (fundi, pjesa e pasme)

   push_back (shtyje)

   pop_back (tërhiqe)

Klasat standarde që i plotësojnë këto kërkesa jan: <vector>, <deque> dhe <list>.

Shembull 1:

1234

56789101112131415

16

#include <iostream>  // cout #include <iomanip>  // setw #include <stack>  // stack using namespace std;

int main (){stack<int> stekuIm;int shuma (0);for(int i=1;i<=10;i++)

stekuIm.push(i); //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()<<"; ";

Page 595: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 595/699

Algoritmet dhe strukturat e të dhënave

595

17181920

2122232425

cout<<"Ne krye: "<<setw(2)<<stekuIm.top()<<"\n";shuma += stekuIm.top();stekuIm.pop();

}

cout<<"Shuma e anetareve te stekut, gjithsej: "<<shuma<<”\n”; system("Pause");return 0;

Rezultati/dalja:Madhesia aktuale e stekut: 10; Ne krye: 10 .Madhesia aktuale e stekut: 9; Ne krye: 9Madhesia aktuale e stekut: 8; Ne krye: 8Madhesia aktuale e stekut: 7; Ne krye: 7

Madhesia aktuale e stekut: 6; Ne krye: 6Madhesia aktuale e stekut: 5; Ne krye: 5Madhesia aktuale e stekut: 4; Ne krye: 4Madhesia aktuale e stekut: 3; Ne krye: 3Madhesia aktuale e stekut: 10; Ne krye: 2Madhesia aktuale e stekut: 10; Ne krye: 1Shuma e anetareve te stekut, gjithsej: 55 

 Në rreshtin tre, përfshihet libraria <stack>. Në rreshtin 8, deklarohet steku meemrin stekuIm, anëtarët e të cilit janë të tipit int. Në rreshtin 11, brenda unazës

for (nga rreshti 10), për çdo iteracion (përsëritje të unazës), shtyhet në stek vlerae re, i. Në rreshtin 16, përdoret funksioni size( ), që kthen madhësinë e stekut. Në rreshtin 17, përdoret funksioni top( ), që kthen anëtarin në krye të stekut. Nërreshtin 19, thirret funksioni pop( ), që tërheqë (largon) nga steku anëtarin nëkrye të stekut.

Page 596: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 596/699

Avni Rexhepi

596

<deque>

Deque header-i

Header-i që definon kontejner klasën 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 vetëm kontejneri queue me dy skaje i njohur sidequeue.

deque (zakonisht i lexuar si "deck" ) është një shkurtesë (acronim) për double-ended queue  –   queue me dy skaje/përfundime. Dequeue-t janë kontejnerësekuencash me madhësi dinamike, që mund të zmadhohen ose zvogëlohen në tëdy skajet (qoftë në fillim, qoftë në fund).

Libraritë specifike mund të implementojnë dequeue-n në mënyra të ndryshme,në përgjithësi si ndonjë formë e vargut dinamik. Por në cilindo rast, mundësojnëqasjen direkte në elementet individuale përmes iteratorëve me qasje të rastit, me

hapësirë të ruajtjes të menaxhuar automatikisht përmes rritjes ose zvogëlimit tëkontejnerit, sipas nevojës.

Së këndejmi, ata ofrojnë funksionalitet të ngjashëm me vektorët (<vector>), porme insertim dhe fshirje efikase të elementeve gjithashtu edhe ne fillim tësekuencës dhe jo vetëm në fund të saj. Por, për dallim prej vektorëve, dequeue-tnuk garantojnë ruajtjen e të gjitha elementeve në lokacione të njëpasnjëshme tëmemories, kështu që qasja në elementet e dequeue-s përmes zhvendosjes së pointerit (aritmetikës së pointerëve) do të shkaktonte sjellje të paparashikueshme.

Dequeue dhe vektori ofrojnë interfejs të ngjashëm dhe mund të përdoren përqëllime të ngjashme, por përbrenda (angl. internally) punojnë në mënyrëtërësisht të ndryshme: gjersa vektorët përdorin një varg të vetëm që ka nevojë tërealokohet për rritje (zgjerim), elementet e deuque-s mund të shpërndahen nëpërcopëza të memories, me kontejnerin që ruan së brenshmi informacionet enevojshme për të ofruar qasjen direkte në cilindo element, në kohë konstantedhe me interfejs uniform sekuencial (përmes itereatorëve). Prandaj, dequeue-t janë pak më kompleks sesa vektorët, por kjo mundëson të rriten në mënyrë meefikase nën rrethana të caktuara, posaqërisht për sekuenca shumë të gjata, ku

realokimet bëhen më të kushtueshme.

Page 597: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 597/699

Algoritmet dhe strukturat e të dhënave

597

Për operacionet të cilat përfshijnë 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<forëard_list>.

Funksionet e definuara për dequeue, janë:

  deque::assign (caktimi i vlerave të anëtarëve)

  deque::at (vlera në pozitën 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 (madhësia maksimale)  deque::operator= (operatori =)

  deque::operator[] (operatori i pozitës, [ ] )

  deque::pop_back  (tërhiqe fundin)

  deque::pop_front (tërhiqe fillimin)

  deque::push_back  (vendose në fund)

  deque::push_front (vendose në fillim)

Page 598: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 598/699

Avni Rexhepi

598

  deque::rbegin (fillimi revers)

  deque::rend (fundi revers)

  deque::resize (ndrysho madhësinë)

  deque::shrink_to_fit (tkurrje për përshtatje)

  deque::size (madhësia)

  deque::swap (shkëmbe)

 Në ndarje sipas kategorive të veprimeve, kemi listën si në vijim.

Funksionet anëtare (Member functions):

(constructor)  Kontejneri për konstruktim të deque (funksion publik)(destructor)  Destruktori i deque (funksion publik)operator= Operatori për ndarje (caktim) të përmbajtjes (funksion publik)

Iteratorët (Iterators):

begin  Kthe iteratorin në fillim (funksion publik)end  Kthe iteratorin në fund (funksion publik)rbegin Kthe iteratorin revers në fillimin revers (funksion publik)

rend Kthe iteratorin revers në fundin revers (funksion publik)cbegin Kthe iteratorin const në fillim (funksion publik)cend Kthe iteratorin const në fund (funksion publik)crbegin Kthe const_reverse_iterator fillimin revers (funksion publik)crend Kthe const_reverse_iterator në fundin revers (funksion publik)

Kapaciteti (Capacity):

size  Kthe madhësinë (size) (funksion publik )max_size  Kthe madhësinë maksimale (funksion publik )resize  Ndrysho madhësinë (funksion publik )empty Testo nëse kontejneri është i zbrazët (funksion publik )shrink_to_fit Tkurre për përshtatje (funksion publik )

Qasja në elemente (Element access):

operator[]  Operatori [ ] - Qasja në elemente (funksion publik )at Qasja në element (funksion publik )front  Qaja në elementin e parë (funksion publik )

Page 599: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 599/699

Algoritmet dhe strukturat e të dhënave

599

back Qasja në elementin e fundit (funksion publik )

Modifikatorët (Modifiers):

assign  Cakto përmbajtjen e kontejneri (funksion publik )push_back   Shto element në fund (funksion publik )push_front Inserto element në fillim (funksion publik )pop_back Fshije elementin e fundit (funksion publik )pop_front Fshije elementin e parë (funksion publik )insert Inserto elemente (funksion publik )erase Fshij elementet (funksion publik )swap Shkëmbe përmbajtjen (funksion publik )

clear Pastro përmbajtjen (funksion publik )emplace Konstukto dhe inerto elementin (funksion publik )emplace_front Konstukto dhe inerto elementin në fillim (funksion publik )emplace_back Konstukto dhe inerto elementin në fund (funksion publik )

Alokator (Allocator):

get_allocator  Alokatori get (funksion publik )

Funksionet jo-anëtare, mbingarkime:

relational operators  Operatorët relacional për deque (funksion )sëap  Shkëmbe elementet (templejt funksioni)

Të gjitha këto funksione mund të thirren për secilin rast të dequeue-s tëdeklaruar në program.

Shembull:

1

23456789101112

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: 

Page 600: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 600/699

Avni Rexhepi

600

14151617

1819202122232425262728

2930313233343536373839

4041424344454647484950

5152535455565758596061

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, përmes 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); // 1 20 20 10 2 3 // "it" nuk eshte me valid! 

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;

}

Page 601: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 601/699

Algoritmet dhe strukturat e të dhënave

601

Rezultati/dalja: deque1 permbane: 5 4 3 2 1 1 2 3 4 5deque1 tani permbane: 1 20 30 30 20 10 2 3

 Në rreshtin 3, përfshijmë për përdorim librarinë <deque>. Në rreshtin 9deklarojmë deque1. Urdhërat tjerë të programit janë sqaruar përmes komenteve përkatëse.

Page 602: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 602/699

Avni Rexhepi

602

<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ëmënyrë specifike për të operuar në kontekstin FIFO, ku elementet insertohennga njëri skaj dhe dalin (nxirren, tërhiqen) nga skaji tjetër.

Queue implementohet si adaptor kontejneri, që janë klasa që enkapsulojnëobjektet e klasës specifike të kontejnerit, si në-kontejner, duke ofruar një setspecifik të funksioneve për qasje në elementet e tij. Elementet shtyhen (push)nga ana e prapme (back) e kontejnerit dhe dalin (pop) nga pjesa e përparme(front).

 Nën-kontejneri mund të jetë ndonjëri prej templejtave të klasave standarde tëkontejnerëve ose ndonjë i dizajnuar në mënyrë specifike. Nën-kontejneri duhett’i përkrahë 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 përmbushin këto kërkesa. Në mënyrë të parazgjedhur (default), nëse nuk specifikohet ndonjë klasë e caktuar, përdoret‘deque’. 

Parametrat e template-it

T –  Tipi i elementeve. Alias si “member type” queue::value_type.

Container –  Tipi i objektit të nën-kontejnerit të brendshëm ku ruhen elementet.Tipi i tij duhet të jetë T. Alias si “member type” queue::container_type.

Member functions:

Page 603: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 603/699

Algoritmet dhe strukturat e të dhënave

603

(constructor)  Kontejneri për queue (funksion publik)empty  Teston a është kontejneri i zbrazët (funksion publik)size Kthen madhësinë (funksion publik)front Qasja në elementin e ardhshëm (funksion publik)back Qasja në elementin e fundit (funksion publik)push Insertimi i elementit (funksion publik)emplace Konstruktimi dhe insertimi i elemenit (funksion publik)pop Largimi i elementit të ardshëm (funksion publik)sëap Shkëmbimi i përmbajtjes (funksion publik)

Mbingarkimet e funksioneve jo-anëtare

operatorëtrelacional

Operatorët relacional për queue (funksion)

swap

(queue)

Shkëmbimi i përmbajtjeve të queue-ve (funksioni publik)

Specializimet jo-anëtare të klasës

uses_allocator<queue. Përdorë alokatorin për queue (class template)

Shembull1: push, pop, empty, front, back

1234567

8910111213141516171819

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

Page 604: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 604/699

Avni Rexhepi

604

20212223

242526272829303132

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 0queue1 permban: 1 2 3 4 5 6 0queue1.front() tani eshte 61

Shembull 2: size (madhësia)

123

456789101112131415

161718

// queue::size #include <iostream> #include <queue>  // std::queue 

using namespace std;

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: 01. size: 52. size: 4

Shembulli 3: queue:: emplace

Page 605: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 605/699

Algoritmet dhe strukturat e të dhënave

605

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 përcjellurargumentet (args) për konstruktorin e tij.

Ky funksion anëtar në mënyrë efektive e thërret funksionin emplace_back  tënën-kontejnerit, duke ia përcjellur argumentet.

Parametrat

args –  Argumentet e përcjellura për të konstruktuar elementin e ri

12345678910111213

141516171819202122

// queue::emplace #include <iostream>  // std::cin, std::cout #include <queue>  // std::queue #include <string>  // std::string, std::getline(string) using namespace std;

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 pareFjalia e dyte

<Priority queue>

Versioni i rreshtit me prioritet <priority_queue> bën 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

Page 606: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 606/699

Avni Rexhepi

606

ai do të jetë elementi i ardhshëm që tërhiqet nga rreshti me rastin e thirrjes sëfunksionit ‘pop’. 

Ky anëtar efektivisht e thërret funksionin “front” të objektit të nën-kontejnerit.

ParametratAsnjë (nuk ka)

Vlera kthyese

Referenca në elementin ‘top’ (në krye) të rreshtit të prioritetit.

Shembull 1:

1

234567891011121314151617181920212223

// priority_queue::top 

#include <iostream>  // cout #include <queue>  // priority_queue using namespace std;

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 20rrp1.top() tani eshte 50rrp1.top() tani eshte 20

Shembull :

123

// priority_queue::push/pop #include <iostream> #include <queue>  //priority_queue 

Page 607: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 607/699

Algoritmet dhe strukturat e të dhënave

607

4567

8910111213141516171819

20212223

using namespace std;

int main (){

priority_queue<int> rrp1;

rrp1.push(30);rrp1.push(100);rrp1.push(25);rrp1.push(40);

cout << "Terheqja e elementeve...";while (!rrp1.empty()){

std::cout << ' ' << rrp1.top();rrp1.pop();

}cout << '\n';system("Pause");return 0;

Rezultati/dalja: Terheqja e elemeneteve... 100 40 30 25 

Page 608: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 608/699

Avni Rexhepi

608

<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ë osenë të dy drejtimet (kahjet).

Listat implementohen si lista të lidhura dyfisht (doubly-linked lists); Listat elidhrua dyfish mund të ruajnë secilin element që e përmbajnë në lokacione tëndryshme të pa ndërlidhura të memories. Renditja ruhet në mënyrë interne dukei shoqëruar secilit anëtarë elementin e lidhjes për në elementin e përparshëm dheatë të ardhëshëm.

List <list> është shumë e ngjashme me <forward_list> (lista përpara). Dallimikryesor ndërmjet tyre ësthë se objektet e krijuara nga “forward list” janë lista tëlidhura njëfish (singly linked list) dhe prandaj mund të iterohen vetëm përpara.Zakonisht janë pak më të vogla dhe më efikase.

Krahasuar me kontejnerët tjerë standard të sekuencave (array, vector dhedeque), list performon në përgjithësi më mirë në insertim, ekstraktim (nxjerrje)dhe lëvizje (zhvendosje) të elementeve në cilëndo pozitë përbrenda kontejneir për të cilin veq është marrë një iterator dhe rrjedhimisht edhe në algoritmet që e përdorin intensivisht atë, si p.sh., algoritmet e sortimit.

E metë kryesore e list-ave dhe ‘forward-list’-ave krahasuar me këta kontejnerëtë tjerë të sekuencave është se atyre u mungon qasja direkte në elemente në bazëtë pozitës së tyre; për shembull për 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 distancën mes tyre. Ato poashtu konsumojnë memorieshtesë për të mbajtur informacionet lidhur me secilin element (që mund të jetënjë faktor i rëndësishëm për listat e mëdha me elemente të vogla).

Page 609: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 609/699

Algoritmet dhe strukturat e të dhënave

609

Shembulli 1: Krijimi dhe shtypja e listave

123456789101112131415161718192021222324252627282930

// 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 5deque1 tani permbane: 1 20 30 30 20 10 2 3

 Në rreshtin 3 përfshijmë për përdorim librarinë <list>. Në rreshtin 9, deklarojmëlistën me anëtarë int, të krijuar nga 5 anëtarët e vargut vlerateMia. (Po të mirrejnë argumentin e dytë vlerateMia+2, do të mirreshin vetëm 2 anëtarët e parë). Nërreshtin 12 përdorim unazën for, me iteratorin e deklaruar it, për të kaluar nëpëranëtarët e listës, prej fillimit të listës (funksioni begin( )), deri në fund të listës(funksioni end( ) ).

 Në rreshtin 17 deklarojmë listën lista2, me anëtarë të tipit int. Në rreshtin 19,mbushim listën nga fundi, me vlerat ‘i’ të unazës for nga rreshti 18.

Page 610: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 610/699

Avni Rexhepi

610

 Në rreshtin 23, përemes iteratorit revers në unazën for kalojmë nëpër anëtarët elistës nga skajet reverse (mbrapsht). Në rreshtin 25 shtypim anëtarët e listës(mbrapsht).

Shembulli 2: Sortimi i vlerave –  list::sort

Funksioni i sortimit (renditjes sw vlerave sipas madhwsisw/alfabetit) i sortonelementet nw kontejner.

(1) void sort();

(2)template <class Compare>

void sort (Compare comp);

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 t’i krahasuarelementet. Ky krahasim duhet tw prodhojw ‘ strict weak ordering ’ twelementeve (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 ndonjwelementi, 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 dytitnw ‘  strict weak ordering ’ qw e definon, pwrndryse kthen ‘false’. Ky duhet tw jetw pointer funksioni ose objekt funksioni.

Vlera kthyese

Asnjw (none)

123456789

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

Page 611: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 611/699

Algoritmet dhe strukturat e të dhënave

611

11121314

151617181920212223242526

27282930

cout << "lista1 para sortimit :";for (list<double>::iterator it=lista1.begin(); it!=lista1.end();

++it)

cout << ' ' << *it;cout << '\n';

lista1.sort();// 1 2 3 4 5 6 7 8 9 10 cout << "lista1 e sortuar :";for (list<double>::iterator it=lista1.begin(); it!=lista1.end();

++it)cout << ' ' << *it;

cout << '\n';

cout << '\n';

system("Pause");return 0;

Rezultati/dalja: lista1 para sortimit : 9 2 10 3 4 1 8 7 6 5 lista1 pas sortimit : 1 2 3 4 5 6 7 8 9 10 

Shembulli 2b:

12345678910111213141516171819202122

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

{

Page 612: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 612/699

Avni Rexhepi

612

24252627

282930313233343536373839

4041424344454647484950515253

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 : Zero nje dy Trelista1 pas sortimit : Tre Zero dy njelista1 pas sortimit me atribut : dy nje Tre Zero

Shembulli 3: Largimi i vlerave duplikate - list::unique

 Një funksion shumë i dobishëm, që nevojitet shpeshherë në praktikë, ështëfunksioni ‘unique’ (angl. unique –  unkike) për eleminimin e vlerave duplikate,gjegjësisht për 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 përveq elementit të parë nga seciligrup i vlerave të njëjta të njëpasnjëshme në kontejner. Vini re që elementi

largohet nga lista nëse krahasohet me elementin e barabartë në pozitën para tij.

Page 613: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 613/699

Algoritmet dhe strukturat e të dhënave

613

Prandaj, ky funksion është posaqërisht i rëndësishëm për listat e sortuara. Pra,nëse nga lista dëshironi t’i largoni duplikatet, së pari e sortoni listën, e pastaj ethirrni funksionin unique.

Versioni i dytë (2), merr si argument një funksion të caktuar që përcaktonunicitetin e elementit. Në fakt, mund të implementohet cilado veti (jo vetëmkrahasimi për barazi), por vëreni se funksioni do të thërret tëashtuquajturin binary_pred(*i,*(i-1))  për çdo çift të elementeve (ku i ështëiteratori në një element, duke filluar nga i dyti) dhe largohet nga lista elementi ii-të, nëse parakushti (angl. predicate) kthen vlerën logjike “true”. 

Elementet që duhet larguar nga lista, asgjësohen.

Paramatrat

binary_predangl. Binary predicate  –   parakushti binar, që duke marrë dy vlera të tipit tënjëjtë, përveq atyre që kanë mbetur në listë, kthen “true”, për të larguarelementin e përcjellur si argument i parë prej kontejnerit dhe “false” përndryshe.Ky duhet të jetë pointer funksioni ose objekt funksioni.

Shembulli 4:

123456789101112131415

161718192021222324252627

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 

Page 614: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 614/699

Avni Rexhepi

614

2930313233

343536373839404142434445

46474849505152

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

listaIme : 2.72, 12.15, 72.25 

Shembull 5: Largimi i elementeve sipas vlerës

1234567891011121314151617

// remove - largo nga lista #include <iostream> #include <list> 

int main (){int vleratInt[]= {10,20,30,40, 50};std::list<int> lista1 (vleratInt,vleratInt+5);

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

Page 615: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 615/699

Algoritmet dhe strukturat e të dhënave

615

1819 

return 0;}

Rezultati/dalja: lista1 permbane: 10 20 40 50 

Shembull 6: Ndërthurrja e listave - list::splice

Funksioni “splice” (angl. ndërthurrje, ngjitje, bashkim, etj), mundëson bashkimin e dy listave në disa forma:

(1)

tërë lista

void splice (const_iterator position,list& x);void splice (const_iterator position,

list&& x);

(2)

një element

void splice (const_iterator position,list& x, const_iterator i);void splice (const_iterator position,list&& x, const_iterator i);

(3) rangu i

elementeve

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ë tjetër, prej pozitës së treguarose mund ta bartim vetëm një element të listës, në listën tjetër. Poashtu, memënyrën e tretë, mund ta bartim një brez të vlerave të një listë, në një listë tjetër, prej pozitës së përcaktuar.

1234567891011121314151617

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); // lista2: 10 20 30 

it = lista1.begin();

++it; // pointon ne 2 

Page 616: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 616/699

Avni Rexhepi

616

19202122

232425262728293031323334

353637383940414243444546

lista1.splice (it, lista2); // lista1: 1 10 20 30 2 3 4 // lista2 (e zbrazët) // "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 20lista2 permban: 2

Shembull 7: Bashkimi i listave të sortuara - list::merge

(1)void merge (list& x);void merge (list&& x);

(2)template <class Compare>void merge (list& x, Compare comp);template <class Compare>void merge (list&& x, Compare comp);

E bashkon ‘x’-in (listën e dytë) në ‘list’ (në listën e parë), duke transferuar tëgjitha elementet e tij në pozitat respektive, sipas renditjes së pozitave nëkontejner (të dy kontejnerët duhet të jenë të sortuar paraprakisht).

Kështu, efektivisht largohen të gjitha elementet e x-it (i cili bëhet i zbrazët) dhe iinserton ata në pozitat e tyre adekuate sipas renditjes përbrenda kontejnerit (i cili

e rritë madhësinë (size) për numrin e elementeve të transferuara). Veprimi

Page 617: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 617/699

Algoritmet dhe strukturat e të dhënave

617

kryhet pa krijuar ose asgjësuar ndonjë element: ata transferohen, pa marrë parasysh çfarë vlere është x dhe nëse tipi përkrahë lëvizjen-krijimin apo jo.

Versioni (2) i templejtit me dy parametra, ka të njëjtët karakteristika, por e merr

edhe atributin e krahasimit (comp), për të bërë krahasimin ndërmjet elementeve.Funksioni kërkon që kontejnerët e list-ave të kenë elementet e sortuara praprapakisht, sipas vlerës ose sipas krahasimit. Për bashkimin e listaev të parenditura, shikoni funksionin list::splice.

Elementet e barabarta ruajnë renditjen para bashkimit, ndërsa elementetekzistuese ekuivalente i paraprijnë ato të insertuara nga x.

Funksioni nuk bën asgjë nëse (&x = = this).

Parametrat:

x  –  objekti ‘list’ i tipit të njëjtë (me templejt të njëtë). Gjithmonë modifikohet x-i, pa marrë parasysh referencën tipin e referencës (lvalue apo rvalue).

comp –  atributi binar, i cili i merr dy vlera të tipit të njëjtë dhe kthen ‘true’ nëseargumenti i parë duhet të shkojë para të dytit. Duhet të jetë pointer funksioni oseobjekt funksioni.

Vlera kthyese  –  (none) asgjë 

12

34567891011121314

1516171819202122232425

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

Page 618: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 618/699

Avni Rexhepi

618

27282930

313233343536373839404142

434445

it!=lista1.end(); ++it)cout << ' ' << *it;

cout << '\n';

//(lista2 tani eshte e zbrazet) 

lista2.push_back (2.1);

lista1.merge(lista2,krahasimiiPlote);//pasi qe tash krahasohet vetem pjesa e plote,//2.1 vjen pas krejt vlerave ekzistuese me pjese te plote 2 cout << "\n\nTani lista1 permban:";for (list<double>::iterator it=lista1.begin();

it!=lista1.end(); ++it)cout << ' ' << *it;

cout << '\n';

system("Pause");return 0;

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

Page 619: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 619/699

Algoritmet dhe strukturat e të dhënave

619

<map>

Map header-i

Header i 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ë cilët i ruajnë elementete formuara prej kombinimit të ‘key value’ (vler ës çelës) dhe ‘mapped value’(vlerës së pasqyruar), sipas një rregulli të specifikuar.

Janë templejt klase <map>:

template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare = less<Key>, // map::key_compareclass Alloc = allocator<pair<const Key,T> >

// map::allocator_type> class map;

 Në ‘map’, vlerat çelës (key values) në përgjithësi përdoren për të sortuar oseidentifikuar elementet në mënyrë unike, ndërsa vlerat e pasqyruara, shoqëruarame to (mapped values) e ruajnë përmbajtjen e shoqëruar me çelësin. Tipet eçelësit dhe vlerës së mapuar mund të ndryshojnë dhe grupohen së bashku nëtipin e anëtarit ‘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 vlerës sëçelësit, sipas kriterit “ strict weak ordering ”  të përcaktuar nga objekti i brendshëm krahasues. Ekziston edhe versioni i pasortuar: unordered_map.

Kontejnerët ‘map’  në përgjithësi janë më të ngadalshëm sesa kontejnerët e parenditur, për të ju qasur elementeve individuale sipas vlerës së çelësit, mirëpomundësojnë iteracion direkt në nënbashkësi bazuar në renditjen e tyre.

Vlerat e mapuara në ‘map’ mund të qasen drejtpërdrejt përmes çelësit të tyre përkatës, duke përdorur operatorin “kllapa të mesme” ([ ]). 

Map zakonisht implementohet si ‘binary search tree”(pemë binare e kërkimit).

Page 620: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 620/699

Avni Rexhepi

620

Tiparet e kontejnerit

Asociativ (Associative)

Elementet në kontejnerët asociativ referohen sipas çelësit (key) të tyre dhe jo

sipas pozitës absolute në kontejner.

I renditur (Ordered)

Elementet në kontejner përcjellin një renditje strikte gjatë gjithë kohës. Të gjithaelementeve të insertuara u jepet një pozitë në këtë renditje.

Map

Secili element e shoqëron çelësin (key) me vlerën e mapuar. Çelësat përdoren për të identifikuar elementet përmbajtja e të cilëve është vlera e mapuar.

Çelësat unik (Unique keys)

 Në kontejner nuk ka dy elemente me çelësa ekuivalent.

Tëvetëdi jshëm për alokator in (Allocator -aëare)

Kontejneri përdorë një objekt alokator për të manipuluar nevojat për memorie nëmënyrë dinamike.

Parametrat e templejtit

Key (Çelësi)  –   secili element në map identifikohet në mënyrë unike përmesvlerës së çelësit. Alias tipi i anëtarit: map::key_type.

T –  tipi i vlerës së mapuar. Secili element në ‘map’ ruan disa të dhëna, si vlerë etij e mapuar. Alias si tipi i anëtarit: map::mapped_type.

Compare (krahaso)  –   atributi binar i cili merr dy çelësa të elementeve siargument dhe kthen vlerë ‘bool-eane’. Shprehja ‘comp(a,b), ku ‘comp’ është njëobjekt i këtij tipi dhe ‘a’ dhe ‘b’ janë vlera të çelësave, duhet të kthej ‘true’ nëse‘a’ konsiderohet të shkojë përpara ‘b’ në renditjen e definuar prej funksionit.

Map objekti e përdorë këtë shprehje për të përcaktuar edhe renditjen eelementeve edhe nëse të dy elementet janë ekuivalente (duke i krahasuar nëmënyrë refleksive: ata janë ekuivalent nëse !comp(a,b) && !comp(b,a)). Nukguxon të ketë dy elemente me çelësa ekuivalent në map.

Alloc –  tipi i objektit alokator, që pëdoret për të definuar modelin e alokimit tëmemories. Si ‘default’, përdoret” allocator class template”  që definon modelinmë të thjeshtë të alokimit të memories dhe është i pavarur nga vlerat. Alias sitipi i anëtrit map::allocator_type.

Member functions:

Page 621: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 621/699

Algoritmet dhe strukturat e të dhënave

621

(constructor)  Kontejneri për konstruktim të map (funksion publik)(destructor)  Destruktori i map (funksion publik)operatori = Kopjon përmbajten e kontejnerit (funksion publik)

Iteratorët:

begin  Kthen iteratorin në fillim (funksion publik)end  Kthen iteratorin në fund (funksion publik)rbegin Kthen iteratorin revers në fillimin revers (funksion publik)rend Kthen iteratorin revers në fundin revers (funksion publik)cbegin Kthen iteratorin konstatn në fillim (funksion publik)cend Kthen iteratorin konstant në fund (funksion publik)crbegin Kthen iteratorin konstant revers në fillimin revers (funks. publik)crend Kthen iteratorin konstant revers në fundin revers (funksion publik)

Capacity:

empty  Teston a është kontejneri i zbrazat (funksion publik)size  Kthen madhësinë (funksion publik)max_size Kthen madhësinë maksimale (funksion publik)

Qasja në elemente:

operatori [ ]  I qaset elementit (funksion publik)At  I qaset elementit (funksion publik)

Modifikatorët:

insert  Inserton elementet (funksion publik)erase  Fshinë elementet (funksion publik)swap Shkëmben përmbajtjen (funksion publik)clear Pastron përmbajtjen (funksion publik)emplace Konstrukton dhe inserton elementin (funksion publik)emplace_hint Konstrukton dhe inserton elementin sipas paralajmërimit (f.p.)

Observerët:

key_comp Kthen objektin e krahasimit të çelësave (funksion publik)

Page 622: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 622/699

Avni Rexhepi

622

value_comp Kthen objektin e krahasimit të vlerave (f.p.)

Operacionet:

find Merr iteratorin në element (funksion publik)count Konstrukton dhe inserton elementin sipas paralajmërimit (f.p.)lower_bound Kthen iteratorin në kufirin e poshtëm (funksion publik)upper_bound Kthen iteratorin në kufirin e epërm (funksion publik)equal_range Merr rangun e elementeve të barabarta (funksion publik)

Shembull 1: map::begin/end

1234567891011

12131415161718

#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 => 200b => 100

c => 300 

 Në rreshtin 6 është deklaruar dhe inicializuar map1. Në rreshtin 7 është deklaruar iteratori për map1. Në rreshtat 9-11, janë ndarë vlerat tek elementet përkatëse.

Shembull 2: map::at

12

// map::at #include <iostream> 

Page 623: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 623/699

Algoritmet dhe strukturat e të dhënave

623

3456

789101112131415161718

19202122

#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: 10beta: 20gama: 30

 Në rreshtat 8-11 është deklaruar dhe inicializuar map1. Në rreshtat 13-15, janë

ndarë vlerat e reja tek (angl. at) elementet përkatëse.

Shembull 3: map::find

123456

789101112131415161718

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

Page 624: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 624/699

Avni Rexhepi

624

19202122

2324252627

//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 => 50c => 150

Shembull 4: map::insert

Funksioni i insertimit mund të realizohet në disa forma:

123456789101112131415161718

192021222324252627282930

// 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 dhënë): map<char,int>::iterator it = map1.begin();map1.insert (it, pair<char,int>('b',300)); // insertim me efikasitet

maxmap1.insert (it, pair<char,int>('c',400)); // pa insertim me

efikasitet max 

// versioni i trete i funksionit insert (insertimi ne rang): map<char,int> map2;map2.insert(map1.begin(),map1.find('c'));

Page 625: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 625/699

Algoritmet dhe strukturat e të dhënave

625

31323334

353637383940414243

// paraqitja e permbajtjes: cout << "map1 permban:\n";for (it=map1.begin(); it!=map1.end(); ++it)cout << it->first << " => " << it->second << "\n";

cout << "map1 ka " << map1.size() << "elemente\n\n";

cout << "map2 permban:\n";for (it=map2.begin(); it!=map2.end(); ++it)cout << it->first << " => " << it->second << '\n';

cout << "map2 ka " << map2.size() << "elemente\n\n";system("Pause");return 0;

Rezultati/dalja: elementi 'z' veq ekziston me vleren 200map1 permban:a => 100b => 300c => 400z => 200map1 ka 4 elemente

map2 permban:a => 100b => 300map2 ka 2 elemente

Page 626: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 626/699

Avni Rexhepi

626

<set>

Set header-i

Header-i që definon klasat e kontejnerëve ‘set’ dhe ‘multiset’. 

Klasat:

set (class template )multiset –  Seti me çelësa të shumëfishtë (class template )

Funksionet:

begin –  iteratori në fillim (function template)end –  iteratori në fund (function template)

template < class T, // set::key_type/value_typeclass Compare = less<T>, // set::key_compare/value_compareclass 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ë çelës(key) i tipit T) dhe secila vlerë duhet të jetë unike. Vlera e elementit në set nukmund 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ë brendshëm tëkrahasimit (të tipit Compare).

Kontejnerët e set-eve në përgjithësi janë më të ngadalshëm sesa kontejnerët e parenditur të set-eve (unorderd set containers), për të ju qasur elementeveindividuale sipas çelësit, mirëp ato lejojnë iteracion të drejtpërdrejt në nën-sete bazuar në renditjen e tyre.

Set-et zakonisht implementohen si pemë binare të kërkimit.Tiparet e kontejnerit

Asociative

Elementet në kontejnerët asociativ referohen sipas çelësit të tyre dhe jo sipas pozitës absolute në kontejner.

Tërenditur (ordered)

Elementet në kontejner përcjellin një renditjs strikte gjatë tërë kohës. Të gjitha

elementeve të insertuara ju caktohet pozita në bazë të kësaj renditjeje.

Page 627: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 627/699

Algoritmet dhe strukturat e të dhënave

627

Set

Vlera e një elementi është poashtu edhe çelësi (key) që përdoret për taidentifikuar atë.

Çelësat unik

 Nuk mund të ketë dy elemente me çelësa të njëjtë (ekuivalent) në kontejner.

Allocator -aëare

Kontejneri përdor një objekt alokator për të manipuluar në mënyrë dinamikenevojat e veta për memorie.

Parametrat e templejtit

T  –  Tipi i elementeve. Secili element në kontejner të set-it poashtu identifikohet

në mënyrë unike përmes vlerës së tij (secila vlerë është vet edhe çelës ielementit). Alias si tipet e anëtarëve set::key_type dhe set::value_type.

Compare  –  atributi binar që merr dy argumente të tipit të njëjtë si elementet dhektehn vlerën bool-eane. Shprehja compa(a,b), ku comp është objekti i këtij tipi,ndërsa a dhe b janë vlerat e çelësave, do të kthej ‘true’ nëse a konsiderohet seduhet të renditet para b-së, në renditjen strikte të butë (ëeak ordering) që edefinon funksioni. Set objekti e përdorë këtë shprehje edhe për të përcaktuarrenditjen e elementeve edh epër të kontrolluar nëse dy çelësa të elementeve janëekuivalent (duke i krahasuar në mënyrë refleksive: ata janë ekuivalent

nëse !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 merertsi less<T>, që kthen rezultat të njëjtë si aplikimi i operator it “më i

vogë l” (a<b).

Alias si ‘member type’: set::key_compare dhe set::value_compare.

Alloc  –  tipi i objektit alokator që përdoret për të definuar modelin e alokimit tëmemories. Si i parazgjedhur (default), përdoret templlejti i klasës allocator, i cilidefinon 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 për konstruktim të set-it (funksion publik)(destructor)  Destruktori i set-it (funksion publik)operatori = Kopjon përmbajten e kontejnerit (funksion publik)

Iteratorët:

Page 628: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 628/699

Avni Rexhepi

628

begin  Kthen iteratorin në fillim (funksion publik)end  Kthen iteratorin në fund (funksion publik)rbegin Kthen iteratorin revers në fillimin revers (funksion publik)rend Kthen iteratorin revers në fundin revers (funksion publik)cbegin Kthen iteratorin konstatn në fillim (funksion publik)cend Kthen iteratorin konstant në fund (funksion publik)crbegin Kthen iteratorin konstant revers në fillimin revers (funks. publik)crend Kthen iteratorin konstant revers në fundin revers (funksion publik)

Capacity:

Empty  Teston a është kontejneri i zbrazat (funksion publik)Size  Kthen madhësinë (funksion publik)max_size Kthen madhësinë maksimale (funksion publik)

Modifikatorët:

insert  Inserton elementet (funksion publik)erase  Fshinë elementet (funksion publik)swap Shkëmben përmbajtjen (funksion publik)clear Pastron përmbajtjen (funksion publik)emplace Konstrukton dhe inserton elementin (funksion publik)emplace_hint Konstrukton dhe inserton elementin sipas paralajmërimit (f.p.)

Observerët:

key_comp Kthen objektin e krahasimit të çelësave (funksion publik)value_comp Kthen objektin e krahasimit të vlerave (f.p.)

Operacionet:

find Merr iteratorin në element (funksion publik)

count Konstrukton dhe inserton elementin sipas paralajmërimit (f.p.)lower_bound Kthen iteratorin në kufirin e poshtëm (funksion publik)upper_bound Kthen iteratorin në kufirin e epërm (funksion publik)equal_range Merr rangun e elementeve të barabarta (funksion publik)

Allocator:

get_allocator Merr alokatorin (funksion publik)

Page 629: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 629/699

Algoritmet dhe strukturat e të dhënave

629

Shembulli 1:

12

34567891011121314

15161718

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

1234567891011

1213141516171819202122

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); // set: 10 20 30 40 50 

it=set1.find(20);set1.erase (it); //fshirja sipas pozites set1.erase (set1.find(40)); //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;

Page 630: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 630/699

Avni Rexhepi

630

Rezultati/dalja: Set1 permban: 10 30 50

Shembulli 3:

123456789

1011121314151617181920

21222324

// 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 vargunfor (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, nëse është unik. Ky element i ri konstruktohetnë vend duke përdorur ‘args’ si argument për konstruktimin e tij. Ky insertimndodhë vetëm nëse nuk ekziston element i tillë në kontejner, që është ekuivalentme elementin që bëhet ‘emplaced’ (elementet e setit anë unike).

Page 631: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 631/699

Algoritmet dhe strukturat e të dhënave

631

 Nëse insertohet, ky efektivisht e rritë madhësinë (size) e kontejnerit për një.

Përbrenda, set-i i mbanë të gjitha elementet e tij të sortuara sipas kriterit tëspecifikuar në objektin e krahasimit (comparison). Elementi gjithmonë

insertohet në pozitën e tij respektive, sipas kësaj renditjeje.Elementi konstruktohet ‘in- place’ duke thirrur

allocator_traits::construct me args të përcjellur.

Ekziston edhe funksioni anëtar i ngjashëm, ‘insert’, i cili ose i kopjon ose ilëvizë objektet ekzistuese në kontejner.

Parameterat

args –  argumentet e përcjellura për konstruktim të elementi të ri.

Vlera kthyese (Return value)

 Nëse funksioni e inserton elementin me sukses (pasi që nuk ekziston ndonjëelement ekuivalent në set), funksioni kthen çiftin (‘pair’)e një iteratori përelementin e ri të insertuar dhe vlerën ‘true’. 

Përndryshe, e kthen iteratorin e elementit ekuivalent përbrenda kontejnerit dhevlerën ‘false’. 

‘Member type’ iteratori është iterator i tipit bidireksional që pointon në njëelement. ‘pair’ është templlejt i klasës i deklaruar në <utility>

1234567891011

121314151617181920

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

Page 632: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 632/699

Avni Rexhepi

632

Jepe qytetin: Prizreni

Jepe qytetin: Prishtina

Prishtina veq ekziston në set1

Shembulli 5: set::key_comp

12345678

910111213141516171819

2021222324252627

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

Page 633: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 633/699

Algoritmet dhe strukturat e të dhënave

633

Shembulli 6: set::count

12

34567891011121314

151617181920

// 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 nuk është element i set1.1 nuk është element i set1.2 nuk është element i set1.

3 është element i set1.4 nuk është element i set1.5 nuk është element i set1.6 është element i set1.7 nuk është element i set1.8 nuk është element i set1.9 është element i set1.

Shembulli 7: set::rbegin, set::rend

1234567891011

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

Page 634: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 634/699

Avni Rexhepi

634

12131415

1617181920212223242526

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/reverseset1 permban: 5 4 3 2 1

Page 635: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 635/699

Algoritmet dhe strukturat e të dhënave

635

<vector>

template < class T, class Alloc = allocator<T> > class vector;

// generic template

Vector

Vektorët janë kontejner të sekuencave që përfaqësojnë vargjet të cilat mund tëndryshojnë madhësinë.

 Njësoj sikur vargjet, vektorët përdorin lokacionet e njëpasnjëshme të memories për elementet e tyre, gjë që do të thotë se elementet e tyre mund të qasen përmeszhvendosjes së pointerëve të rregullt në anëtarët e tyre dhe me efikasitet të njëjtësikur në rastin e vargjeve. Por, për dallim prej vargjeve, madhësia e vektorëvemund të ndryshojë në mënyrë dinamike, me manipulimin e hapësirës së tyre në

mënyrë automatike nga ana e kontejnerit.Së brendshmi, vektorët përdorin vargun e alokuar në mënyrë dinamike, përruajtjen e elementeve të tyre. Ky varg mund të ketë nevojë që të realokohetashtu që që të rritet madhësia 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ë kohës së procesimit dhe prandaj vektorët nuk realokohen secilën herë që të shtohet një element i ri nëkontejner.

 Në vend të kesaj, kontejenerët e vektorëve mund të alokojnë një hapësirë shtesë

 për të akomoduar rritjen e mundshme dhe prandaj kontejnerët mund të kenëkapacitet aktual më të madh sesa hapësira që ju nevojitet për të përmbajturelementet e tyre (d.m.th, madhësinë e tyre). Libraritë mund të implementojnëstrategji të ndryshme për rritjen për të balansuar ndërmjet përdorimit tëmemories dhe realokimit, por në cilindo rast, realokimet duhet të ndodhin vetëmnë intervale llogaritmike të rritjes së madhësisë ashtu që insertimi i elementeveindividuale në fund të vektorit të mund të ofrohet me një kompleksitet tëamortizuar konstant kohor.

Prandaj, krahasuar me vargjet, vektorët konsumojnë më shumë memorie në

shkëmbim për aftësinë e menaxhimit të memories dhe rritjes dinamike nëmënyrë efektive.

Krahasurar me kontejerët tjerë të sekuencave dinamike (deque, list dheforward_list), vektorët janë shumë efikas në qasjen në elementet e tyre (sikursevargjet) dhe relativisht efikas në shtimin dhe largimin e elementeve në fundin(skajin ‘end’) të tyre. Për operacionet të cilat përfshijnë insertimin dhe largimine elementeve në pozita të tjera, ata performojnë më dobët sesa të tjerët dhe kanëmë pak iteratorë konsistent dhe referenca sesa list-at dhe forward_list-at.

Tiparet e kontejnerit

Page 636: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 636/699

Avni Rexhepi

636

Sekuenca (Sequence)

Elementet në kontejnerët e sekuencave janë të renditur në sekuencë striktelineare. Elementet individuale qasen në bazë të pozitës së tyre në këtë sekuencë.

Vargu dinamik (Dynamic array)

Lejon qasje direkte në cilindo element në sekuencë, edhe përmes aritmetikës së pointerëve dhe siguron shtim/largim relativisht efikas të elementeve në fund tësekuencës.

I vetëdi jshëm për alokator (Al locator -aëare)

Kontejneri e përdorë një objekt alokator për të manpuluar në mënyrë dinamikenevojat e tij për memorie.

Funksionet (Member functions):(constructor)  Konstruktori i vektorit (funksion publik)(destructor)  Destruktori i vektorit (funksion publik)operatori =  Ndaja vlerën (funksion publik)

Iteratorët:

begin  Kthen iteratorin në fillim (funksion publik)end  Kthen iteratorin në fund (funksion publik)rbegin Kthen iteratorin revers në fillimin revers (funksion publik)rend Kthen iteratorin revers në fundin revers (funksion publik)cbegin Kthen iteratorin konstatn në fillim (funksion publik)cend Kthen iteratorin konstant në fund (funksion publik)crbegin Kthen iteratorin konstant revers në fillimin revers (funks. publik)crend Kthen iteratorin konstant revers në fundin revers (funksion publik)

Kapaciteti

size Kthe madhësinë (numri i anëtarëve) (funksion publik)max_size Kthe vlerën maksimale ((funksion publik)resize  Ndrysho madhësinë (funksion publik)capacity Kthe madhësinë e hapësirës së alokuar (funksion publik)empty Testo nëse vektori është i zbrazët (funksion publik)reserve Kërko ndryshim të kapacitetit (funksion publik)shrink_to_fit  Ngushto për të ju përshtatur (funksion publik)

Qasja në elemente

operator[ ] Qasju elementit (funksion publik)

Page 637: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 637/699

Algoritmet dhe strukturat e të dhënave

637

at Qasju elementit (funksion publik)front Qasju elementit të parë (funksion publik)back Qasju elementit të fundit (funksion publik)data Merr pointerin në vlerë (funksion publik)

Modifikatorët

assign Cakto përmbajtjen e vektorit (funksion publik) push_back Shto elementet në fund (funksion publik) pop_back Fshije elementin e fundit (funksion publik)insert Inserto elementet (funksion publik)erase Fshiji elementet (funksion publik)swap Shkëmbe përmbajtjet (funksion publik)

clear Pastro përmbajtjen (funksion publik)emplace Konstrukto dhe inserto elementin (funksion publik)emplace_back Konstrukto dhe inserto elementin në fund (funksion publik)

Alokatorët:

get_allocator Merr alokatorin (funksion publik)

Mbingarkimet e funksioneve jo-anëtare

relational_operators Operatorët relacional për vektor (funksion publik)swap Shkëmbe përmbajtjet e vektorëve (funksion publik)

Specializimet e Template-ave

vector<bool> Vektor i vlerave bool (specializim i template-it të klasës)

Page 638: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 638/699

Avni Rexhepi

638

Krijimi i vektorit –  ndarja e vlerave (vector::assign)

range (1)

template <class InputIterator>void assign (InputIterator first,

InputIterator last);fill (2) void assign (size_type n, const value_type& val);

initializerlist (3)

void assign (initializer_list<value_type> il);

Definon përmbajtjen e re të vektorit, duke zëvendësuar përmbajtjen e tijekzistuese dhe duke ia modifikuar madhësinë e tij në mënyrë gjegjëse.

 Në versionin (1) të rangut, përmbajtja e re e elementeve konstruktohet prej tëgjitha elementeve ndërmjet kufijve ‘first’ dhe ‘last’, në renditje të njëjtë.

 Në versionin e mbushjes (fill (2)), përmbajtja e re janë n-elementet secili iinicializuar në kopjen e ‘val’ (vler ës).

 Në versionit (3), të listës inicializuese, përmbajtja e re kopjohet prej vlerave të përcjellura si listë inicializuese, në renditje të njëjtë.

Alokatori intern përdoret (përmes tipareve të tij) për të alokuar/dealokuarmemorien nëse ndodhë realokimi. Ai poashtu përdoret për të “asgjësuar” (angl.destroy) elementet ekzistuese dhe për të kontstruktuar ato të rejat.

Të gjitha elementet që ndodhen në kontejner para thirrjes, asgjësohen dhe

zëvendësohen me elementet e reja të konstruktuara (nuk ndodhë ndarja e vlerave(angl. assignement) të elementeve).

Kjo shkakton një realokim automatik të hapësirës së alokuar të memories nësedhe vetëm nëse madhësia e re e vektorit e tejkalon kapacitetin aktual të vektorit.

Parameterat

‘first’ (i pari), ‘last’ (i fundit) –  itaratorët e hyrjes për në pozitën fillestare dhe përfundimtare në sekuencë. Rangu i përdorur është [fist,last], i cili i përfshinë tëgjitha elementet ndërmjet first  dhe last , duke përfshirë edhe elementet elementete pointurara me first  dhe last (kufijtë). Argumenti ‘InputIterator’ i templlejtit tëfunksionit duhet të jetë një tip i iteratorit për hyrje i cili i pointon elementet etipit prej të cilit mund të konstruktohen objektet e tipit të vlerave të definuara(value_type).

n  –  madhësia e re për kontejnerin. Tipi i anëtarit ‘size_type’ është tip integral‘unasigned’. 

val –  Vlera për mbushje të kontejnerit. Secili prej n elementeve në kontejner dotë inicializohet më një kopje të kësaj vlere. Tipi i anëtarit ‘value_type’ është tipii elementeve në kontejner, i definuar në vektor si një alias i parametrit të tij të

 parë të templlejtit (T).

Page 639: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 639/699

Algoritmet dhe strukturat e të dhënave

639

il  –  objekti ‘initializer_list’ (lista inicializuese). Kompajleri do të konstruktojëautomatikisht objektet e tilla prej listës së inicializuar sipas deklarimit.

Tipi i vlerave të anëtarëve (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

12345678

91011121314151617181920

2122232425262728293031

323334353637383940414243

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

Page 640: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 640/699

Avni Rexhepi

640

45464748

cout << "Madhesia e vektori3: " << int (vektori3.size()) << '\n';system("Pause");return 0;

Rezultati/dalja: vektori1: 10 10 10 10 10 10 10 10vektori2: 10 10 10 10 10 10vektori3: 17 7 4 25Madhesia e vektori1: 8Madhesia e vektori2: 6Madhesia e vektori3: 4

Shembulli2:

1234567891011121314151617181920

212223242526272829303132

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

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

Page 641: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 641/699

Algoritmet dhe strukturat e të dhënave

641

33343536

373839404142434445464748

495051525354

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 5vektori1.front()=1vektori1.back() =5

vektori2.front()=78vektori2.back() =16vektori2.front()=vektori2.front()-vektori2.back()vektori2.front() tani eshte 62vektori2.back() tani eshte 16

vektori3 permban: 10 9 8 7 6 5 4 3 2 1vektori3.front()=10vektori3.back() =0

Shembulli 3:

12345678

// vector::pop_back #include <iostream> #include <vector> using namespace std;int main (){vector<int> vektori1;int shuma (0);

Page 642: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 642/699

Avni Rexhepi

642

9101112

13141516171819202122

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:

12345

678910111213141516171819202122232425262728

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

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

Page 643: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 643/699

Algoritmet dhe strukturat e të dhënave

643

30313233

34353637

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 10vektori1 permban:1 2 3 4 5 100 100 100 0 0 0 0

1. Kapaciteti i vektori2: 1002. Kapaciteti i vektori2: 100

3. Kapaciteti i vektori2: 10

Shembulli 5:

123456

7891011121314151617

181920212223242526272829

30

// swap - shkëmbe #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];

Page 644: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 644/699

Avni Rexhepi

644

31323334

35

cout << "\n";

system("Pause");return 0;

Rezultati/dalja: vektori1 permban:100 100 100vektori2 permban:200 200 200 200 200

Pas shkembimit,vektori1 permban:200 200 200 200 200vektori2 permban:100 100 100

Page 645: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 645/699

Algoritmet dhe strukturat e të dhënave

645

Fjalori STL - Dictionary ADT

Fjalori (angl. Dictionary) (map, association list  –   harta, pasqyrimi, lista eshoqërimit, etj) është një strukturë e të dhënave, e cila në përgjithësi është

asociacion (shoqërim) i çelësave unik me disa vlera. Përmes tyre mund të lidhetvlera me çelësin, të fshihet çelësi (dhe natyrisht një vlerë shoqëruar) dhe tëkërkohet për ndonjë vlerë përmes çelësit. Vlerat nuk kërkohet të jenë unike. Njëshembull i thjeshtë i përdorimit është një fjalor me shpjegime. Në këtë rast,fjalët janë “çelësat” dhe shpjegimet përkatëse janë “vlerat”. 

Operacionet e zakonshme për Fjalorin (Dictionary):  

  Dictionary create() 

Krijon fjalor të zbraët  boolean isEmpty(Dictionary d) 

tregon a është i zbrazët (angl. empty) fjalori d 

  put(Dictionary d, Key k, Value v) asocion (shoqëron) çelësin k  me vlerën v. Nnëse çelësi k  veq është prezent në fjalor vlera e vjetër zëvendësohet nga v 

  Value get(Dictionary d, Key k) kthen vlerën e asociouar me çelësin k , ose null, nëse fjalori nuk ka çelës

të tillë  remove(Dictionary d, Key k) 

largon (fshinë) çelësin k  adhe vlerën e asociuar me të

  destroy(Dictionary d) asgjëson fjalorin d (angl. destroy-asgjëso, shkatërro)

Fjalori implementohet në rastin e pemës së kërkimit binar (BST) dhe “hashmap”. 

Fjalori është struktura që është: bashkësia e n rekordeve, secili i identifikuar

 përmes një ose më shumë çelësave. Përdoret për të mirëmbajtur strukturën e tëdhënave në në mënyrë efikase të lokalizohet, insertohet ose fshihet rekordi ishoqëruar me cilindo çelës q të kërkimit (query).

Page 646: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 646/699

Avni Rexhepi

646

Tipi abstrakt i të dhënave “Dictionary” është një prej strukturave më tërëndësishme në shkencat kompjuterike. Për implementimin e fjalorëve (angl.dictionary) janë propozuar shumë struktura të të dhënave, si:

- vargjet e renditura ose pa-renditura,- hash tabelat,

- listat me kapërcime (skip list-at),-  pemët binare të kërkimit (të balansuara dhe të pabalansuara), etj.

kështu që zgjedhja e të duhurës është e komplikuar.

Varësisht prej aplikacionit, është poashtu një vendim që mund të ketë ndikim tërëndësishëm në performansë. Në praktikë, është më e rëndësishme që të evitohet përdorimi i strukturës së gabuar të të dhënave sesa të identifikohet opcioni ivetëm më i mirë në dispozicion.

 Një këshillë esenciale është që të izolohet me kujdes implementimi i strukturës

së fjalorit nga interfejsi i saj. Përdorni thirrjet eksplicite të funksioneve të cilatinicializojnë, kërkojnë dhe modifikojnë strukturën e të dhënave, më mirë se t’iinsertoni ato përbrenda kodit. Kjo dërgon në program më të pastër, porgjithashtu e bën më të lehtë të provohen implementime të ndryshme të fjalorit, për të parë se si ndikojnë në performansë. Mos u obsesiononi me koston embingarkimit (angl. overhead) të thirrjes së funksionit që është i pandarë në njëabstraksion të tillë. Nëse aplikacioni është aq kritik në aspektin kohor sa që njëmbingaresë e tillë mund të ndikojë në performansë, atëherë është edhe mëesenciale që të jeni të aftë të eksperimentoni me lehtësi me implementime tëndryshme të fjalorit tuaj.

Page 647: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 647/699

Algoritmet dhe strukturat e të dhënave

647

 Në zgjedhjen e strukturës së duhur të të dhënave ëpr fjalorin tuaj, parashtroni pyetjet vijuese:

  Sa elemente do të keni në mënyrë tipike në strukturën tuaj të të dhënave?

 –  A do ta dini këtë numër paraprakisht? A jeni duke shikuar një problemmjaft të vogël sa që një strukturë e thjeshtë e të dhënave do të jetë më emira apo do të jetë aq e madhe sa që duhet të brengoseni për përdorimine tepërt të memories ose shkëmbimeve?

  A e dini numrin relativ të insertimeve, fshirjeve dhe kërkimeve?  –  A dotë ketë modifikime në strukturën e të dhënave pasi të jetë konstruktuar së pari, ose do të jetë statike prej asaj pike e tutje?

  A keni njohuri për frekuencën relative me të cilën do të qasen çelësat endryshëm? - A mund të supozojmë se modeli i qasjes do të jetë unformdhe i rastit apo do të shfaqë një shpërndarje të shtrembëruar (të pjerrtë) tëqasjes (d.m.th., disa elemente janë shumë më popullore se të tjerat) oseme 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ë edhee shtrembëruar edhe e grupuar.

  A është kritike që operacionet indifiduale të jenë të shpejta ose vetëm ajoqë sasia totale e punës së bërë përgjatë tërë programit të minimizohet?  –  kur koha e përgjigjes është kritike, siç është në programin e kontrollimit

të nëj pajisjeje mjekësore, nuk mund të pritni shumë gjatë ndërmjethapave. Kur keni një program që është duke bërë shumë pyetje (query)në bazën e të dhënave, por për tema jo shumë të rënësishme, nuk ështëkritike zgjedhja e një shënimi të veçantë, sepse do t’i keni të gjithë me përpjekje minimale.

Kur njëherë të kuptoni se cilat janë nevojat e juaja, provoni të identifikonistrukturën më të mirë të të dhënave, nga lista vijuese:

  Vargjet ose lista e lidhura të pasortuara  –   për bashkësi të vogla të tëdhënave, 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 për t’u mirëmbajtur.Vargjet janë më të lehta për punë sesa listat e lidhura dhe nëse fjalori dotë mbahet në këtë madhësi, me gjasë nuk do të keni mundësi të kursenisasi të rëndësishme të memories ndaj alokimit të vargut të plotë. Nësefjalori do të jetë shumë i madh, atëherë koha e kërkimit do të jetë problematike në cilindo rast.

 Një variant interesant është edhe lista vetë-organizuar. Sa herë që njëçelës qaset ose insertohet, ai vendoset në fillim të listës. Kështu, nëseçelësi qaset përsëri në të ardhmen e afërt, ai do të jetë afër fillimit dhe

Page 648: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 648/699

Avni Rexhepi

648

kështu do të kërkojë vetëm një kërkim të shkurtër për ta gjetur. Pasishumica e aplikacionieve shfaqin edhe frekuencë jo të barabartë të qasjesdhe lokalitet të referencës, koha mesatare e kërkimit për listat e vetë-organizuara është zakonisht shumëmë e mirë sesa në listat e sortuara ose pasortuara. Natyrisht, strukturat e vetë-organizuara të të dhënave mund tëndërtohen edhe prej vargjeve edhe prej listave të lidhura.

  Vargjet ose listat e lidhura të sortuara  –  mirëmbajtja e listës së lidhur tësortuara zakonisht nuk shpaguhet (përveq nëse jeni duke tentuar tëeliminoni duplikatet), pasi që në strukturën e tillë nuk mund të bëhetkërkimi binar. Vargu i sortuar do të jetë i përshtatshëm nëse dhe vetëmnëse nuk ka shumë insertime ose fshirje. Kur vargu bëhet shumë i madhsa që nuk e zë memoria, mendoni për B-pemët (angl. B-trees) në vend tëkësaj.

  Hash tabelat  –   për aplikacioniet që përfshijnë numërtë të çelësavemesatar deri në shumë të madh (le të themi ndërmjet 100 dhe 1,000,000),hash tabela me ndarje (angl. hash table bucketing) me gjasë ështëmënyra e duhur për të përcjellur. Në hash tabela ne përdorim njëfunksion i cili pasqyron çelësat (çofshin ata string, numër ose çkadotjetër) në numra të plotë (integers) ndërmjet 0 dhe m-1. Ne mbajmë njëvarg të m kovave (angl. buckets), secila në mënyrë tipike e implementuar përmes përdorimit të listës së lidhur të pasortuar. Për një çelës të dhënë,

hash funksioni menjëherë e identifikon se në cilën kovë ndodhet ai. Nëse përdorim hash funksion i cili shpërndanë mirë çelësat dhe hash tabelëmjaftueshëm të madhe, secila kovë do të duhej të përmbajë shumë pakelemente, duke bërë kështu të pranueshëm kërkimin linear. Insertimi dhefshirja nga hash tabela redukon insertimin dhe fshirjen nga lista/kovat.

  Hash tabela e sinkronizuar mirë me gjasë do të tejkalojë performansën evargut të sortuar në shumicën e aplikacioneve. Mirëpo, në krijimin ehash tabelës së sinkronizuar mirë, përfshihen disa vendime lidhur medizajnin:

o  Sa duhet të jetë e madhe tabela? Zakonisht, m duhet të jetëafërsisht sa numri maksimal i elementeve që pritet të vendosen nëtabelë. Sigurohuni që m të jetë numër primar, ashtu që tëminimizohen rreziqet e hash funksionit të keq.

o  Cilin hash funksion duhet përdorur? Për stringje, diçka si

F(S)= maS char    i

i

i   mod)(   1

1

 

Page 649: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 649/699

Algoritmet dhe strukturat e të dhënave

649

duhet të funksionojë (ku a është madhësia e alfabetit dhe char(x)është funksioni i cili pasqyron secilin karakter x në ASCII kodin etij). Për stringjet e gjata, duhet të mjaftojnë 8 deri në 10 karaktereduhet të jenë të mjaftueshme për hash, duke supozuar se ka pak gjasaqë të jenë të mbushur me zbrazëtira ose ndonjë tjetër invariant.Përdorni rregullën e Hornerit për të implementuar llogaritjet efikasetë këtij hash funksioni.

Page 650: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 650/699

Avni Rexhepi

650

Shtojcat

Shtojca A - Templates - Shabllonet

Për funksionet dhe klasat në C++ mund të krijojmë shablonet (angl. Template-Shabllon, model) përmes së cilave përgjithësojmë modelin e tyre dhemundësojmë përdorimin universal për tipe të ndryshme. Nëse ndonjëherë kemidashur të krijojmë funksionin për mbledhjen e numrave të plotë (int), e pastajatë për mbledhjen e numrave jo të plotë (double, float), i cili do të ishte nëgjendje të kthej tipin e duhur të rezultatit, varësisht prej tipit të parametrave që imbledhë, është dashur të bazohemi në konceptin e mbingarkimit të funksioneve,ashtu që të kemi dy funksione me emër të njejtë, por me tipe të ndryshme të parametrave.

Përmes konceptit të “templejtave”, kjo gjë realizohet në mënyrë më të përshtatshme.

Templejtat e funksioneve

Templejtat e funksioneve janë funksione speciale të cilat mund të operojnë metipe të përgjithsuara, tipe gjenerike (angl. generic types). Kjo na mundësonkrijimin e funksioneve funksionaliteti i të cilave mund të përshtatet për mëshumë tipe ose klasa, pa pasur nevojë përsëritjen e kodit për secilin tip.

 Në C++ kjo mund të arrihet përmes përdorimit të parametrave të templejtit.

Parametri i templejtit është një lloj special i parametrave që mund të përdoret për të përcjellur tipin si argument: njësoj siç parametrat e zakonshëm tëfunksioneve mund të përdoren për të përcjellur vlerat në funksion, parametrat etemplejtave mundësojnë përcjelljen e tipeve tek funksionet. Këto templejte tëfunksioneve mund të përdorin këta parametra sikur të ishin ndonjë tip tjetër izakonshëm.

Formati i deklarimit të templejtave të funksioneve me këtë lloj të parametraveështë:

template <class identifier> function_declaration;template <typename identifier> function_declaration;

Dallimi i vetëm ndërmjet këtyre dy prototipeve është përdorimi i fjalës sërezervuar “class” ose “typename”. Përdorimi i tyre nuk bën dallim, pasi që të dyshprehjet kanë kuptim të njëjtë dhe sillen në mënyrë të njëjtë.

Për shembull, për të krijuar funksionin template (shabllon), që kthen vlerën mëtë madhe prej dy vlerave (objekteve) të përcjellura në të, mund të veprohet si nëvijim:

template <class TipiIm>

Page 651: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 651/699

Algoritmet dhe strukturat e të dhënave

651

TipiIm VleraMax (TipiIm a, TipiIm b)

{

return (a>b?a:b);

}

Këtu kemi krijuar funksionin template me ‘TipiIm’ si parametër templejt i tij.Ky paremetër templejt reprezenton tipin i cili nuk është specifikuar akoma, pormund të përdoret në funksionin templejt sikur të ishte tip i zakonshëm. Siç mundtë shihet, funksioni VleraMax kthen vlerën më të madhe prej dy parametrave tëkëtij tipit akoma të padefinuar.

Për të përdorur templejt funksionin, për thirrjen e funksionit përdoret formativijues:

Emri_funksionit <tipi> (parametrat);

Për shembull, për të thirrur funksionin për krahasim të dy numrave të plotë(integer), në kod mund të shkruhet:

int x,y;

VleraMax <int> (x,y); 

Kur kompajleri të hasë në këtë thirrje të funksionit templejt, ai e përdorëtemplejtin për të gjeneruar automatikisht funksionin duke zëvendësuar secilën

 paraqitje të “TipiIm” me tipin e përcjellur si templejt parametër aktual (në këtërast ‘int’) dhe e thërret atë. Ky proces kryhet automatikisht nga kompajleri dheështë i padukshëm për 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;

Page 652: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 652/699

Avni Rexhepi

652

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

6102.5 

 Në këtë rast, kemi përdorur ‘T’ si emër të parametrit të templejtit (në vend tëTipiIm, pasi që në fakt T është më i lehtë dhe më shkurtër) dhe është emër izakonshëm për parametrat e templejtave. Por, natyrisht, mund të përdorni çfardoindentifikatori tjetër, sipas dëshirës.

 Në shembullin e mësipërm, funksioni VleraMax është përdorur tri herë, porsecilën herë me versionin e duhur të funksionit, të përshtatur për tipin përkatëstë vlerave.

Siç mund të shihet, tipi T është përdorur në funksionin templejt VleraMax( )edhe për të deklaruar objektet e reja të atij tipi: T rezultati;

Prandaj, rezultati do të jetë një objekt i tipit të njëjtë si edhe parametrat a dhe b,kur funksioni templejt të thirret për rastin përkatës, me tipin e specifikuar.

 Në këtë rast specifik, ku tipi gjenerik T është përdorur si parametër përfunksionin VleraMax, kompajleri mund të gjejë automatikisht se cilin tip të tëdhënave duhet ta përdorë pa pasur nevojë të specifikojë atë në mënyrë eksplicitenë kllapa të drejta “< >”, ashtu si kemi vepruar para specifikimit <int>, <long>

dhe <double >. Pra, kemi mundur të shkruajmë vetëm: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ë vetëm i tipit int. Kjo metodëimplicite jep rezultat të njëjtë.

// Templejti II#include <iostream>

using namespace std;

Page 653: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 653/699

Algoritmet dhe strukturat e të dhënave

653

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

Vëreni se si në këtë rast, templejti i funksionit është thirrur pa specifikuar nëmënyrë eksplicite tipin, brenda kllapave < >. Kompajleri përcakton në mënyrëautomatike se cili tip nevojitet në secilën thirrje.

Pasi që templejti në këtë rast përmbanë vetem një parametër (class T) dhe vetë

funksioni tempeljt pranon dy parametra, të dy të këtij tipit T, nuk mund tathërrasim templejtin e funksionit me dy objekte të tipeve të ndryshme, siargumente.

int idouble j;VleraMax(i,j);

Kjo nuk do të ishte korrekte, pasi që templejti VleraMax pret dy argumente tëtipit të njëjtë dhe në këtë rast të thirrjes janë përdorur objektet e dy tipeve tëndryshme.

Page 654: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 654/699

Avni Rexhepi

654

 Normalisht, ne mund të definojmë templejte funksionesh që pranojnë tipe tëndryshme të parametrave, thjeshtë duke specifikuar parametrat përkatës tëtemplejtave dhe tipet e tyre. Për shembull:

template <class T, class U>T VleraMin (T a, U b){return (a<b?a:b);

}

 Në këtë rast, templejt funksioni VleraMin( ) i pranon dy parametra të tipeve tëndryshme dhe kthen një objekt të tipit të njëjtë sikur parametri i parë (T), që i përcillet. Për 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ë përcaktojë tipin e nevojshëm.

Templejtat e klasave

Edhe për klasat mund të krijohen templejtet, ashtu që klasa mund të ketë anëtarëtë cilët përdorin templejtet e parametrave si tipe. Për 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 shërben për të ruajtur dy elemente të çfarëdo tipi. Përshembull, nëse do të dëshironim të deklarojmë një objekt të kësaj klasë për tëruajtur dy vlera të tipit ‘int’, 115 dhe 36, do të shkruanim: 

dyshja <int> objektiIm (115, 36); 

Page 655: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 655/699

Algoritmet dhe strukturat e të dhënave

655

E njëjta klasë do të mund të përdorej për të krijuar një objekt që do të ruanteçfarëdo tipi tjetër:

dyshja <double> vleratFloat (3.0, 2.18); 

Funksioni i vetëm anëtarë në templejtin e klasës është definuar inline përbrendavetë deklarimit të klasës.

 Në rast se definojmë funksionin anëtarë jashtë deklarimit të templejtit të klasës,atëherë 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

Vëreni sintaksën e definicionit të funksionit vlMax.

template <class T>T dyshja <T>::vlMax()

Page 656: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 656/699

Avni Rexhepi

656

Pra, janë tri ‘T’ në këtë deklarim: e  para është parametri i templejtit, e dyta irefereohet tipit të kthyer prej funksionit dhe e treta (mes kllapave të këndore< >)është poashtu e nevojshme: ajo specifikon se parametri i templejtit të funksionitështë poashtu parametër i templejtit të klasës.

Specializimi i templejtave

 Nëse dëshirojmë të definojmë implementim tjetër për templejtin kur tipi ispecifikuar është i përcjellur si parametër templejt, mund të deklarojmëspecializimin e atij templejti.

Për shembull, le të supozojmë se kemi një klasë shumë të thjeshtë të emërtuar:kontejneriIm e cila mund të ruaj një element të çfarëdo tipi dhe ajo ka vetëm njëfunksion të quajtur “rrite”, i cili e rritë vlerën e tij. Por, e shohim se kur ai ruanelemente të tipit “char”, do të ishte më e përshtatshme të kemi implementim

krejtësisht tjetër për atë funksion, p.sh., “uppercase” (angl. upper case –  shkronjë e madhe), ashtu që vendosim të deklarojmë specializimin e templejtittë klasës për 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;}

};

Page 657: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 657/699

Algoritmet dhe strukturat e të dhënave

657

int main (){kontejneriIm<int> itegeriIm (7);

kontejneriIm<char> karakteriIm ('j');cout << itegeriIm.rrite() << endl;cout << karakteriIm.uppercase() << endl;return 0;}

Rezultati:

8J

Kjo sintaksë përdoret në specializimin e templejtave të klasave:template <> class mycontainer <char> { ... };

Së pari, vëreni se deklarimi i klasës paraprihet me emrin e templejtit të klasësme një “template< >” me listë të zbrazët të parametrave. Kjo për t’a deklaruaratë në mënyrë eksplicite si specializim të templejtit.

Mirëpo, më i rëndësishëm se ky prefiks, është specializimi <char> i parametrit pas emrit të templejtit të klasës. Ky parametër i specializimit vetë identifikontipin për të cilin do të deklarojmë specializimin e klasës së templejtit (char).Vëreni dallimimet ndërmjet templejtit të përgjithshëm (të gjeneralizuar) tëklasës dhe specializimit:

template <class T> class kontejneriIm { ... };template <> class kontejneriIm <char> { ... };

Rreshti i parë është templejti gjenerik, ndërsa i dyti specializimi.

Kur deklarohen specializimet për klasën templejt, duhet të definohen edhe tëgjithë anëtarët e saj, edhe ata saktësisht të njëjtë (të barabartë) me klasëngjenerike të templejtit, sepse nuk ka “trashëgimi” të eleementeve prej templejtitgjenerik tek specializimi.

Parametrat pa tip për templejta

Përveq argumenteve të templejtit të cilët janë të paraprirë nga fjalët e rezervuara‘class’ ose ‘typename’, të cilat përfaqësojnë tipet, templejtat poashtu mund tëkenë edhe tipe të rregullta të parametrave, të ngjashëm me ata tek funksionet. Sishembull, shikoni këtë templejt të klasës që përdoret për të ruajtur sekuencat eelementeve:

// Templejti i sekuencës#include <iostream>

using namespace std;

Page 658: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 658/699

Avni Rexhepi

658

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:

1003.1416

Mund të plotësojmë 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);

Page 659: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 659/699

Algoritmet dhe strukturat e të dhënave

659

}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 nënkuptuara (angl.default) për parametrat e templejtit të klasës. Për shembull, nëse në klasën paraprake definicioni i templejtit do të ishte:

template <class T=char, int N=10> class sekuencaIme {..};

kemi mundur të krijojmë objekte duke përdorur parametrat default të templejtit,duke deklaruar:

sekuencaIme<> sekIme;

që është ekuivalent me:

sekuencaIme<char,10> sekIme;

Templejtat dhe projektet me shumë fajlla

Prej këndvështrimit të kompajlerit, templejtat nuk janë klasa ose funksionenormale. Ata kompajlohen vetëm me kërkesë, që do të thotë se kodi i funksionit

templejt nuk kompajlohet deri në momentin kur të kërkohet krijimi i njëinstance (një rasti) me argumente specifike të templejtit. Në atë moment, kur tëkërkohet një rast konkret, kompajleri prej templejtit gjeneron funksionin nëmënyrë specifike për ato argumente.

Kur projektet të rriten, është e zakonshme që kodi i programit të ndahet në fajllatë ndryshëm të korit burimor. Në këto raste, interfejsi dhe implementimi janëzakonisht të ndarë. Nëse marrim si shembull libraritë e funksioneve, interfejsi nëmënyrë të përgjithshme përbëhet prej deklarimeve të prototipeve të të gjithafunksioneve që mund të thirren. Ato zakonisht deklarohen në “header file”

(fajlla të kreut, header fajlla), me tipin ‘.h’ dhe implementimi (definicioni ikëtyre funksioneve) është në një fajll të pavarur me kodin në c++.

Pasi që templejtat kompajlohen kur të kërkohen, kjo vendosë kufizime për fajllatme shumë projekte: implementimi (definicioni) i klasës ose funksionit templejtduhet të jetë në të njëjtin fajll si edhe deklarimi. Kjo do të thotë që nuk mund tëndajmë interfejsin në “header fajll” të veçantë dhe se duhet të përfshijmë të dyja,edhe interfejsin edhe implementimin, në cilindo fajll që i përdorë tempeljtat.

Pasi që deri në kërkimin e instancës konkrete të templejtit nuk gjenerohet kod,

kompajlerët janë të përgatitur për të lejuar përfshirjen më shumë se një herë të

Page 660: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 660/699

Avni Rexhepi

660

fajllit të njejtë të templejtit më të dyjat, edhe me deklarimet edhe medefinicionet, pa gjeneruar gabime (angl. errors) të linkimit (ndërlidhjes sëfajllave).

Page 661: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 661/699

Algoritmet dhe strukturat e të dhënave

661

Shtojca B - Rekurrenca

Rekurrencat themelore

Shumë algoritme janë të bazuara në principin e shpërbërjes (dekompozimit)rekurzive të problemit të madh në një ose më shumë probleme të vogla, duke përdorur zgjidhjet e nënproblemeve për të zgjidhur problemin origjinal(fillestar). Në këtë pjesë do të shohim metodat themelore për analizën eanalizimin e algoritmeve të tilla dhe derivojmë zgjidhje disa formula standardetë cilat paraqiten në analizën e shumë algoritmeve. Të kuptuarit e tiparevematematikore të formulave në këtë pjesë do të jep pasqyrë në tiparet e performansës së algoritmeve.

Dekompozimi rekurziv në një algoritëm reflektohet drejtpërdrejt në analizën e

tij. Për shembull, koha e ekzukutimit të algoritmeve të tilla përcaktohet ngamadhësia e numërit të nënproblemeve dhe koha e kërkuar për dekompozim.Matematikisht, varësia e kohës së ekzekutimit të një algoritmi për një madhësi tëhyrjes N, nga koha e ekzekutimit të tij për vlera më të vogla hyrëse, shprehetlehtë përmes formulave të quajtura “relacionet e rekurrencës” (angl. requrrence-rishfaqje, rikthim, përsëritje). Formulat e tilla përshkruajnë në mënyrë precize performansën e algoritmeve gjegjëse: për të nxjerrë kohën e ekzekutimit, izgjidhim rekurrencat. Argumente më rigoroze lidhur me algoritmet specifike paraqiten në algoritmet përkatëse, kurse këtu do të koncentrohemi në vetformulat e rekurrencës.

Formula 2.1  –  Rekurrenca që paraqitet për programet rekurzivet të cilat kalojnënëpër tërë hyrjen (nëpër të gjitha vlerat hyrëse), për të eliminuar një element,është:

C N = C N-1 + N,  për N≥2, me C1 = 1,

Zgjidhja:

C N është afërsisht N2/2. Për të zgjidhur një rekurrencë të tillë, e “teleskopojmë”(e zgjasim brenda njëra-tjetrës) duke e aplikuar atë në vetvetën e saj, si në vijim:

C N = C N-1 + N

C N = C N-2 + (N-1) + N

C N = C N-3 + (N-2) + (N-1) + N

...

Duke vazhduar në këtë mënyrë, në fund gjindet se

C N = C1 + 2 + ... + (N-2) + (N-1) + N

Page 662: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 662/699

Avni Rexhepi

662

C N = 1 + 2 + ... + (N-2) + (N-1) + N

C N =2

)1(    N  N  

Llogaritja e shumës 1 + 2 + ... + (N-2) + (N-1) + N është elementare: rezultati idhënë pason kur e mbledhim shumën me vetvetën, por në kahjen e kundërt, term pas termi. Ky rezultat  –  dy herë vlera e kërkuar  –  përbëhet prej N termave, kusecili prej tyre rezulton në N+1.

Formula 2.2  - rekurrencë që paraqitet kur programi rekurziv e përgjysmonhyrjen në një hap, është:

C N = C N/2 + 1, për N≥2, me C1 = 1,

Zgjidhja

C N është afërsisht lg N. Kështu si është shkruar, ekuacioni nuk ka kuptim përveqnëse N është numër çift ose supozojmë se N/2 është pjestim i plotë. Përmomentin, supozojmë se N = 2n, ashtu që rekurrenca është gjithmonë mirë edefinuar. (Vëni re se n = lg N). Atëherë, rekurrenca teleskopohet edhe më lehtëse ajo e mëparshmja:

C2n = C2

n-1 + 1

C2n = C2

n-2 + 1 + 1

C2

n

 = C2

n-3

 + 3...

C2n = C2

0 + n 

C2n = n + 1.

Zgjidhja precize për N të përgjithshëm varet nga interpretimi i N/2. Në rastinkur N/2 reprezenton N/2, kemi zgjidhjen e thjeshtë: C N është numri i bitave nëreprezentimin binar të N, dhe numri është  lg N  + 1, sipas definicionit (Kllapatspeciale   , nënkuptojnë kufirin e poshtëm dhe ato     kufirin e epërm). Ky

konkluzion pason drejtpërdrejt prej faktit se operacioni i eliminimit të bitit më tëdjathtë (angl. rightmost) në reprezentimin binar të cilitdo numër të plotë N > 0,konvertohet në  N/2   (Shih figurën 2.6).

Page 663: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 663/699

Algoritmet dhe strukturat e të dhënave

663

Figura 2.6. Funksioniet integer dhe reprezentimi binar

Për reprezentimin e dhënë binar të numrit N (kolona e mesit), duke larguar bitinmë 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 eformulës 2.2, për rastin kur N/2 interpretohet si  N/2 .

Formula 2.3 –  rekurrenca që paraqitet për programin rekurziv që e përgjysmonhyrjen, por ndoshta duhet të ekzaminoj seclin element të hyrjes, është:

C N = C N/2 + N,  për N≥2, me C1 = 0.

Zgjidhja:

CN është afërsisht 2N. Rekurrenca teleskopohet në shumën N + N/2 + N/4 + N/8 + ... . (Ngjashëm sikur formula 2.2, rekurrenca është e definuar në mënyrë precize vetëm kur N është fuqi e 2-shit). Nëse sekuenca është e pakufi, kjo

shumë e thjeshtë gjeometrike rezulton saktësisht në 2N. Pasi që përdorim pjestimin e plotë dhe ndalemi në 1, kjo vlerë është një përafrim i përgjigjes sësaktë. Zgjidhja precize përfshinë tiparet e reprezentimit binar të N.

Formula 2.4  –   rekurrenca që paraqitet për programin rekurziv i cili duhet të bëjë një kalim linear nëpër hyrjen, para, gjatë ose pas ndarjes së asaj hyrjeje nëdy gjysma, është:

C N = 2C N/2 + N,  për N≥2, me C1 = 0.

Zgjidhja:

Page 664: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 664/699

Avni Rexhepi

664

CN është afërsisht N lg N. kjo zgjidhje është më e cituara prej të gjithave të përmendura këtu, sepse kjo rekurrencë aplikohet në familjen e algoritmevestandarde përçaj-e-sundo.

Zgjidhja zhvillohet shumë ngjashëm me atë në formulën 2.2, por me trikun plotësues të pjestimit të të dy anëve me 2n  në hapin e dytë, për të bërë qërekurrenca të teleskopohet.

Formula 2.5  –   rekurrenca që paraqitet për programin rekurziv i cili e ndanëhyrjen në dy gjysma dhe pastaj bënë sasi konstante të punës tjetër, është:

C N = 2C N/2 + N,  për N≥2, me C1 = 1.

Zgjidhja:

C N është përafërsisht 2N.

Kjo zgjidhje mund të derivohet në mënyrë të njëjtë si zgjidhja në formulën 2.4.

Mund të zgjidhim variantet minore të këtyre formulave, duke përfshirë edhekushtet fillestare të ndryshme ose ndryshime të vogla në termin aditiv, duke përdorur teknikat e njëjta të zgjidhjes, edhe pse duhet të jemi të vetëdijshëm sedisa rekurrenca duken të ngjashme me këto, në fakt mund të jenë të vështira për

t’u zgjidhur. Ka llojllojshmëri të teknikave të përgjithshme të avansuara për t’umarrë me ekuacione të tilla me rreptësi (saktësi) matematikore.

Page 665: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 665/699

Algoritmet dhe strukturat e të dhënave

665

Shtojca C - Pemët binare

Kodi i shembullit në libër:

// BinarySearchTree.h" 

#include <string> #define SIZE_KEY 32#define SIZE_VALUE 256typedef 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" 

Page 666: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 666/699

Avni Rexhepi

666

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)

{

Page 667: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 667/699

Algoritmet dhe strukturat e të dhënave

667

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;

Page 668: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 668/699

Avni Rexhepi

668

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

Page 669: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 669/699

Algoritmet dhe strukturat e të dhënave

669

}}

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

{

Page 670: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 670/699

Avni Rexhepi

670

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

Page 671: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 671/699

Algoritmet dhe strukturat e të dhënave

671

bool isEmpty() const { return root==NULL; }void print_inorder();void inorder(tree_node*);void print_preorder();void preorder(tree_node*);void print_postorder();void postorder(tree_node*);void insert(int);void 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;

Page 672: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 672/699

Avni Rexhepi

672

}

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)

Page 673: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 673/699

Algoritmet dhe strukturat e të dhënave

673

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

}

Page 674: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 674/699

Avni Rexhepi

674

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)

{

Page 675: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 675/699

Algoritmet dhe strukturat e të dhënave

675

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;

Page 676: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 676/699

Avni Rexhepi

676

b.insert(tmp);break;

case 2 : cout<<endl;cout<<" Pershkimi: In-Order "<<endl;cout<<" -------------------"<<endl;b.print_inorder();break;

case 3 : cout<<endl;cout<<" Pershkimi: Pre-Order "<<endl;cout<<" -------------------"<<endl;b.print_preorder();break;

case 4 : cout<<endl;cout<<" Pershkimi: Post-Order "<<endl;cout<<" --------------------"<<endl;b.print_postorder();

break;case 5 : 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; };

};

Page 677: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 677/699

Algoritmet dhe strukturat e të dhënave

677

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

}}

Page 678: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 678/699

Avni Rexhepi

678

// 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() << " ";

}}

Page 679: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 679/699

Algoritmet dhe strukturat e të dhënave

679

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

Page 680: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 680/699

Avni Rexhepi

680

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

}

Page 681: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 681/699

Algoritmet dhe strukturat e të dhënave

681

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

}

}

Page 682: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 682/699

Avni Rexhepi

682

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

Page 683: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 683/699

Algoritmet dhe strukturat e të dhënave

683

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

}

Page 684: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 684/699

Avni Rexhepi

684

}

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

}

Page 685: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 685/699

Algoritmet dhe strukturat e të dhënave

685

Shtojca D - Hash Tabela –  implementim në C++

 Në vazhdim do të jepet një implementim komplet i hash tabelës me kontrollimkuadratik. Përdoret templejti i klasës dhe supozohet se është ofruar një hash

funksion i duhur në formën:unsigned int hash( const Object & x ) ;

 për secilin tip të ilustruar me shembull konkret (për secilën instancë).

Vëreni se nuk ka parametër të madhësisë së tabelës (tableSize); algoritmi ikontrollimit kuadratik performon operacionin ‘mod’ final në mënyrë interne pas përdorimit të hash funksionit të ofruar nga shfrytëzuesi. Versioni për ‘string’ofrohet në klasë, me deklarimin në rreshtin 48. (Një ‘default’ (i nënkuptuar) poashtu ofrohet më vonë në formë të templejtit të funksionit, por ka pak gjasa që

të ketë kuptim për objektet e komplikuara). Së fundi, supozojmë se operatori‘!=’ është i definuar për objektin. Interfejsi i klasës është paraqitur në kodin nëvazhdim. Që algoritmi të punojë në mënyrë korrekte, operatori ‘!=’ dhe hash-iduhet të jenë konsistent. Kjo do të thotë, nëse dy objekte janë të barabarta, edhehash vlerat e tyre duhet të jenë të barabarta.

Hash tabela përbëhet prej një vargu të strukturave. Secila struktura ruan njëelement dhe një anëtarë të të dhënave që na tregon se ‘hyrja/pozita’ është ezbrazët, aktive ose e fshirë (deleted). Për këtë qëllim përdoret variabla e grupit(enumeration) EntryType, e deklaruar në rreshtin 27. Ajo është e vendosur në

 pjesën publike (sepse ndonjë kompajler mund të ketë vërejtje për rreshtin 36,nëse është private). Vargu është deklaruar në rreshtin 40. Duhet të përcjellimnumrin e elementeve në hash tabelë (duke përfshirë edhe elementet e shënuara si“Deleted”); kjo vlerë është ruajtur në ‘occupied’  që është deklaruar nërreshtin 41.

Pjesa tjetër e interfejsit të klasës përmbanë deklarimet për funksionet e hashtabelës. Pasi që anëtarët janë të gjitha objekte ‘first-class’, janë të pranueshme‘default’-et. Metoda (funksioni) i vetëm interesant është ‘find’, i cili kthenelementin e gjetur në kër kimin për ‘x’, të “mbështjellur” në objektin Cref (qëshërben për të evituar referencën në objektin që nuk ekziston dhe mund të ruajreferencë NULL).

1 // Quadraticprobing Hash table class.2 //3 // Object must have operator!= and global function4 // unsigned int hash( const Object & key );5 //CONSTRUCTION: with no paramet. or another hash table.6 //7 // ******************PUBLIC DECLAERATIONS************

8 // void insert ( x ) --> Insert x

Page 686: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 686/699

Avni Rexhepi

686

9 // void remove( x ) --> Remove x10 // Object find( x ) --> Return item that matches x11 // void makeEmpty ( ) --> Remove all items12 // ***************ERRORS**************************** 

13 // Throws exceptions as warranted.1415 template <class Object>16 class HashTable17 {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 ) ;2627 enum EntryType ( ACTIVE, EMPTY, DELETED 1;2829 private:30 struct HashEntry31 {32 Object element;

33 EntryType info;3435 HashEntry ( const Object & e = Object ( ) ,36 EntryType i = EMPTY i37 : element( e ), info( i ) { }38 };3940 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ë përshkruhen kur të përdoren në implementimin e klasës. Tani mund të diskutohet imlementimi iklasës ‘HashTable’. 

Page 687: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 687/699

Algoritmet dhe strukturat e të dhënave

687

Konstruktori i hash tabelës dhe ‘makeEmpty’ (bëje të zbrazët) 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 }89 // 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 për kërkim është paraqitur në kodin vijues. Ky e përdorë funksionin privat ‘isActive’, të paraqitur në kodin e ardhëshëm. Poashtu e thërret edhefunksionin ‘findPos’, të paraqitur më vonë, për të implementuar kontrolliminkuadradik. Pastaj, është lehtë të implementohet ‘find’: një element është gjeturnëse rezultati i ‘findPos’ është një qelulë aktive (nëse ‘findPos’ ndalet në njëqelulë aktive, duhet të ketë përshtatje).

Shumica e funksioneve janë me vetëm disa rreshta kodi sepse ato e thërrasinfunksionin ‘findPos’ për 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 );78 if( isActive( currentpos ) )9 return Cref<Object>( array[ currentpos ].element );10 else11 return Cref<Object> ( ) ;12 }  Funksioni ‘find’   –  për hash tabelën me kontrollim kuadratik  

1 // Return true if currentpos exists and is active.

Page 688: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 688/699

Avni Rexhepi

688

2 template <class Object>3 boo1 HashTable<Object>::isActive( int currentpos ) const4 {5 return array[ currentpos ].info == ACTIVE;

6 } Funksioni ‘isActive’ –  për hash tabelën me kontrollim kuadratik  

 Ngjashëm, në vijim është treguar funksioni ‘remove’. Verifikojmë nëse funksini‘findPos’ nga dërgon në qelulë aktive; nëse po, ajo markohet me “Deleted”.Përndryshe “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 else10 throw ItemNotFoundException( );11} Funksioni ‘remove’ –  për hash tabelën me kontrollim kuadratik

 Në vijim është dhënë funksioni ‘insert’. Funksioni ‘insert’ e bën ‘rehash’-iminnëse tabela është (gjysëm) e mbushur. Në rreshtin 7 thirret ‘findPos’. Nëse ‘x’është gjetur, “hidhet” gjetja e duplikatit në rreshtin 9, përndryshe, ‘findPos’ jepvendin për të insertuar ‘x’-in. Insertimi kryhet në rreshtin 10. Në rreshtin 12 përshtatet ‘occupied’ dhe kthehet, përveq nëse ‘rehash’ është në rregull; përndryshe thirret metoda private ‘rehash’. 

1 // Insert item x into the hash table. If the item is2 // already present, then throw DuplicateItemException.3 template <class Object>4 void HashTable<Object>::insert( const Object & x )5 {6 // Insert x as active7 int currentpos = findPos( x );8 if( isActive( currentpos ) )9 throw DuplicateItemException( ) ;10 array[ currentpos ] = HashEntry( x, ACTIVE );1112 if( ++occupied > array.size( ) / 2 )13 rehash( ) ;14 } 

Page 689: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 689/699

Algoritmet dhe strukturat e të dhënave

689

 Funksioni ‘insert’ –  për hash tabelën me kontrollim kuadratik

Kodi që implementon rehash-imin është treguar në vijim. Rreshti 5 bën kopjen etabelës origjinale. Në rreshtin 13 krijohet hash tabela e zbrazët me madhësi të

dyfishuar. Pastaj skenohet vargu origjinal dhe elementet aktive insertohen nëtabelën e re. Funksioni ‘insert’ përdorë hash funksionin e ri (pasi që është i bazuar në madhësinë 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’ tjetër (përndryshe mund ta zëvendësojmë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;67 // Create new double-sized, empty table8 array.resize( nextprime( 2 * oldArray.size( ) ) );9 for( int j = 0; j < array.size-1; j++ )10 array[ j ].info = EMPTY;1112 // Copy table over13 makeEmpty( ) ;

14 for( int i = 0; i < oldArray.size( ); i++ )15 if ( oldArray [i].info == ACTIVE )16 insert ( oldArray[i].element ) ;17 } Funksioni ‘rehash’ –  për hash tabelën me kontrollim kuadratik

Të gjitha deri më tani janë të pavarura nga kontrollimi kuadratik. Kodi nëvazhdim implementon ‘findPos’, që përfunsimisht mirret me algoritmin përkontrollimin kuadratik. Vazhdojmë të kërkojmë tabelën deri sa të gjendet nëjqelulë e zbrazët ose përshtatja. Rreshtat 12-14 drejtpëdrejt implementojnëmetodologjinë përkatëse të evitimit të shumëzimeve 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 t5 {6 int i = 0;7 int currentpos = hash( x ) % array.size( );89 while( array[ currentpos ].info ! = EMPTY &&

10 array [ currentpos ].element ! = x )

Page 690: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 690/699

Avni Rexhepi

690

11 {12 currentpos += 2 * ++i - 1; // Compute ith probe13 if( currentpos >= array.size( ) )14 currentpos -= array.size( ) ;

15 }16 return currentpos;17 }  Funksioni që përfundimisht mirret me kontrollim kuadratik

 Në fund, kodi në vijim jep hash funksionin e përgjithshëm. Duke kryerkonvertimi e tipeve në rreshtin 7, ai punon duke trajtuar mostrën e bajtave të‘key’ si string primitiv (por që nuk mund të ketë null terminator) dhe pastajduke përdorur hash funksionin e njëjtë si për stringjet. Ky hash funksion punon për tipet primitive por nuk është i përshtatshëm për objektet e komplikuara sepse

mund të mos kënaqë kërkesën që dy objekte që janë deklaruar të barabarta do tëkenë gjithmonë hash vlera të barabarta. Më shumë është hash funksion përshkrues sepse garancioni i vetëm është që dy objekte me bit mostra të njëjtado të kenë hash vlera të njëjta.

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;

67 const char *keyp = reinterpret-cast<const char *>(&key);8 for( size_t i = 0; i < sizeof( Object ); i++ )9 hashVal = 37 * hashVal + keyp[ i ];1011 return hashVal;12 } Hash funksioni i përgjithshëm.

Page 691: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 691/699

Algoritmet dhe strukturat e të dhënave

691

Page 692: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 692/699

Avni Rexhepi

692

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. 

Page 693: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 693/699

Algoritmet dhe strukturat e të dhënave

693

Përmbajtja:

Parathënie ............................................................................................................ 3 

Hyrje ..................................................................................................................... 5 

Algoritmet ........................................................................................................ 5 

Strukturat e të dhënave ..................................................................................... 9 

Strukturat themelore të të dhënave ............................................................. 11 

Konceptet themelore të programimit të orientuar në objekte ......................... 15 

Strukturat e të dhënave dhe reprezentimi i tyre .......................................... 18 

Lista e lidhur ............................................................................................... 19 

Lista e lidhur njëfish ................................................................................... 20 

Lista e lidhur dyfish .................................................................................... 21 

Steku ........................................................................................................... 22 

Rreshti, radha e pritjes ................................................................................ 23 

1.  Memoria, Tipet Abstrakte të të dhënave dhe Adresat ........................... 27 

Vështrim mbi memorien ................................................................................. 27 

Të dhënat dhe memoria .............................................................................. 29 

Sistemi numerik binar ................................................................................ 30 

Rezervimi i memories ................................................................................ 30 

Grupet e tipeve abstrakte të të dhënave ...................................................... 32 

Adresat e memories .................................................................................... 37 

Adresat reale të memories .......................................................................... 38 

ADT dhe adresat e memories ..................................................................... 38 

Variablat dhe Pointerët ................................................................................... 38 

Deklarimi i variablave dhe objekteve ......................................................... 39 

Tipet primitive të të dhënave dhe tipet e definuara prej shftyrëzuesit ....... 39 

Definimi i tipeve të definuara nga shfrytëzuesi ......................................... 40 

Tipet e të dhënave të definuara prej shftytëzuesit dhe klasat ..................... 43 

Pointerët ..................................................................................................... 45 

Page 694: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 694/699

Avni Rexhepi

694

Vargjet dhe stringjet ........................................................................................ 46 

1.2.1 Objektet “First-Class” dhe “Second-Class” ....................................... 47 

Përdorimi i vector-it ............................................................................ 48 

1.2.3 Ndryshimi i madhësisë së vektorit ..................................................... 50 

1.2.5 Mekanizmat e përcjelljes së parametrave të funksionit ..................... 54 

1.2.6 Vargjet primitive të konstanteve ........................................................ 56 

1.2.8 Tipi ‘string’ nga libraria standarde ..................................................... 57 

Sintaksa e pointerëve 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” Pointerët, fshirja e dyfishtë dhe problemet tjera .................... 64 

1.5 Referencat ................................................................................................. 66 

1.6 Strukturat dhe pointerët ............................................................................. 69 

1.6.1 Të dhënat ekzogjene kundrejt atyre indigjene dhe kopjimi i cekëtkundrejt atij të thellë ................................................................................... 71 

1.6.1 Listat jo të vazhduara –  listat e lidhura .............................................. 73 

1.7.1 Kontejnerët ......................................................................................... 75 

1.7.2 Iteratorët ............................................................................................. 76 

1.7.3 STL Algoritmet .................................................................................. 76 

Aritmetika e pointerëve ............................................................................... 77 

Pointerët në pointerë ................................................................................... 80 

Vargjet primitive ......................................................................................... 82 

Vargu dhe stringu ............................................................................................ 89 

Përparësitë ................................................................................................... 89 

Të metat....................................................................................................... 90 

Vargjet statike dhe dinamike....................................................................... 90 

Madhësia fikse dhe vargjet dinamike .......................................................... 90 

Lidhja me stringjet ...................................................................................... 91 

Page 695: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 695/699

Algoritmet dhe strukturat e të dhënave

695

Alokimi dinamik i vargjeve ........................................................................ 92 

Vargjet dinamike ........................................................................................ 95 

Menaxhimi i kapaciteti: Sigurimi i kapacitetit, Paketimi(ngjeshja,

kompresimi) ............................................................................................... 96 

Funksionet për qasje në të dhëna: Set, Get, InsertAt, RemoveAt .............. 99 

Verifikimi i kufijëve ....................................................................................... 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 epërm/lartë) ................................................................. 111 

Θ-Notation (Rendi i njëjtë) .......................................................................... 112 

Ω-Notation (Kufiri i poshtëm/ulët) .............................................................. 113 

Reduktimi ................................................................................................. 114 

Rishikim i asimptotës ............................................................................... 115 

Efikasiteti i algoritmit –  rastet e ndryshme dhe shembujt ........................ 117 

Rekurrenca ................................................................................................... 121 

2.3. Rritja e funksioneve .......................................................................... 124 

Algoritmet për nga teknika dhe qasja ........................................................... 132 

Copëtimi i problemeve ............................................................................. 132 

Programimi linear –  zgjidhja për kutinë e zezë ....................................... 137 

Algoritmet lakmitare –  kurrë mos shiko prapa ........................................ 137 

Prapaveprimi ............................................................................................ 137 

Kërkimi lokal –  Mendo globalisht, vepro lokalisht.................................. 138 

Hill Climbing ............................................................................................ 138 

Kalitja e simuluar –  të mësuarit nga natyra .............................................. 138 

3.  Implementimi i strukturave themelore –  Stack dhe Queue ................ 141 

Page 696: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 696/699

Avni Rexhepi

696

Stack (Steku) ................................................................................................. 141 

Steku përmes vargjeve .............................................................................. 142 

Stack ADT................................................................................................. 142 

Operacionet ............................................................................................... 143 

Aksiomat për stekun.................................................................................. 143 

Krijimi i stekut në C++ ............................................................................. 146 

Steku në veprim ........................................................................................ 151 

Implementimi i stekut të bazuar në vargje ................................................ 156 

Implementimi ............................................................................................ 156 

Përdorimi i stekut ...................................................................................... 158 

Rreshti - Queue (Kju) .................................................................................... 162 

Queue përmes vargut në C++ ................................................................... 167 

4.  Listat e lidhura ........................................................................................ 174 

Struktura e listës së lidhur ......................................................................... 177 

Lista e lidhur njëfish ndaj listës së lidhur dyfish ...................................... 178 

Listat e lidhura në C++ ................................................................................. 190 

Listat e lidhura një-fish (Singly-linked list) .................................................. 194 

Shembull: ..................................................................................................... 195 

Opearcionet (Veprimet) në listën e lidhur njëfish.................................... 195 

Lista e lidhur njëfish, reprezentimi i brendshëm ....................................... 195 

Përshkimi i listës së lidhur njëfish ............................................................ 197 

Algoritmi i përshkimit ............................................................................... 197 

Lista e lidhur njëfish –  Operacioni i largimit (fshirjes) ............................ 204 

Lista e lidhur në STL .................................................................................... 215 

Steku përmes listës së lidhur ......................................................................... 217 

Steku.......................................................................................................... 217 

Konstruktori dhe destruktori StackLinkedList .......................................... 220 

Steku si listë e lidhur në C++ .................................................................... 224 

Aplikacioni StackLinkedList .................................................................... 228 

Page 697: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 697/699

Algoritmet dhe strukturat e të dhënave

697

Queue përmes listës 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 

Përparësitë dhe të metat e rekursionit .......................................................... 269 

Algoritmet rekurzive ................................................................................ 270 

5.2. Algoritmet përçaj-e-sundo ................................................................. 278 

6.  Pemët ........................................................................................................ 304 

Tiparet matematike të pemëve binare .......................................................... 312 

Përshkimi i pemës ........................................................................................ 315 

Algoritmet rekurzive të pemës binare .......................................................... 325 

Përshkimi i grafit .......................................................................................... 330 

Kërkimi “thellësia së pari” ....................................................................... 333 

Kërkimi “gjerësia së pari” ........................................................................ 336 

Pema binare .................................................................................................. 339 

Elementet e pemës binare ......................................................................... 339 

Thellësia dhe madhësia ............................................................................ 341 

Përse përdoret pema binare? .................................................................... 342 

Vlera çelës ................................................................................................ 344 

Krijimi i pemës binare .............................................................................. 345 

Pema binare në C++ ..................................................................................... 363 

Kërkimi tek pema binare .............................................................................. 368 

Shembull i pemës binare ............................................................................ 368 

Operacionet ................................................................................................. 368 

Page 698: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 698/699

Avni Rexhepi

698

Pema e kërkimit binar - Binary search tree ................................................... 369 

Pema binare e kërkimit –  reprezentimi i brenshëm ................................... 370 

Shembull ................................................................................................... 372 

Pema binare e kërkimit –  operacioni i kërkimit ........................................ 375 

Shembull ................................................................................................... 375 

Shembull ................................................................................................... 384 

Pirgu binar ..................................................................................................... 386 

Pema binare komplete ............................................................................... 386 

Shembull korrekt i pemës binare komplete............................................... 386 

Shembull jo korrekt, niveli i mesëm është i pakompletuar ....................... 386 

Shembull jo korrekt, niveli i fundit ka "vrimë" ........................................ 386 

Tipari Heap ............................................................................................... 387 

Pirgu binar - Reprezentimi i brendshëm 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 

Pemët e balansuara ........................................................................................ 400 

Pemët AVL ............................................................................................... 400 

Pemët kuq e zi ............................................................................................... 414 

Tiparet e pemëve kuq e zi ......................................................................... 416 

Splay Trees .................................................................................................... 423 

Operacionet ............................................................................................... 425 

Lista me kapërcime - Skip List ..................................................................... 433 

Pemët M-are .................................................................................................. 445 

B-Pemët (B-Trees) ........................................................................................ 448 

7.  Grafet ....................................................................................................... 458 

Reprezentimi i grafit të padrejtuar ............................................................ 462 

Page 699: Algoritme Dhe Struktura E Te Dhenave LIBRI

7/23/2019 Algoritme Dhe Struktura E Te Dhenave LIBRI

http://slidepdf.com/reader/full/algoritme-dhe-struktura-e-te-dhenave-libri 699/699

Algoritmet dhe strukturat e të dhënave

Matrica e fqinjësisë .................................................................................. 462 

Lista e fqinjësisë ....................................................................................... 465 

Algoritmet për grafet e padrejtuara .............................................................. 468 

Përshkimi thellësia-së-pari ....................................................................... 469 

Përshkimi gjerësia-së-pari ........................................................................ 476 

Pema e shtrirjes minimale ............................................................................ 478 

Algoritmi Dijkstra-Prim ........................................................................... 479 

Algoritmi i Kruskal-it ............................................................................... 483 

Algoritmi i shtegut më të shkurtër ................................................................ 486 

Algoritmi i Dijkstra’s ................................................................................... 487 

Topologjia ................................................................................................ 491 

8.  Algoritmet e kërkimit .............................................................................. 498 

Kërkimi sekuencial ....................................................................................... 498 

Kërkimi binar ............................................................................................... 500 

Analiza e algoritmit të kërkimit binar .......................................................... 503 

Algoritmi për bashkim të vargjeve të sortuara ............................................. 506 

Algoritmi i bashkimit - Merge algoritmi ...................................................... 506 

Testimi i numrave primar (qasja naive) ....................................................... 508 

Çka janë numrat primar? .......................................................................... 508 

Përmirësimi i mundëshëm 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