Professional Documents
Culture Documents
Kapitulli 6 - Listat Si ADT
Kapitulli 6 - Listat Si ADT
Kapitulli 6
Lista është një strukturë fleksibël sepse ajo mund të shtohet dhe pakësohet sipas kërkesës,
dhe elementët mund të kapen, futen, ose fshihen në çdo pozicion brenda saj. Listat
gjithashtu mund të lidhen së bashku ose të zbërthehen në nën-lista.
Listat përbëhen nga objekte që krijohen gjatë ekzekutimit të programit, pra ato janë
struktura dinamike. Ky është tipari thelbësor që i dallon ato nga strukturat e tjera si array,
record-i, etj., që janë struktura statike të krijuara në kohën e kompilimit të programit.
Matematikisht, një listë është një varg me zero ose më tepër elementësh të një tipi të dhëne
(të cilin në përgjithësi e quajmë elementtype). Në këtë varg dallohet koka e listës (një
element) dhe pjesa tjetër e vargut (mbetja e listës) që në vetvete është gjithashtu një listë.
Shpesh paraqesim një listë të tillë si një varg elementësh të ndarë me presje:
Numri n i elementeve quhet gjatësia e listës. Duke supozuar qe n >= 1, themi që a1 është
elementi i parë dhe an është elementi i fundit. Në qoftë se n = 0, do të thotë që kemi një listë
boshe, e cila nuk ka elemente.
Një veçori e rëndësishme e një liste është që elementët e saj mund të organizohen në formë
lineare, që radhiten sipas pozicionit tyre në listë. Themi që ai paraprin ai +1 për i = 1, 2, …,
n-1, dhe ai ndjek ai-1 për i = 2, 3, ..., n. Gjithashtu, themi që elementi ai është në pozicionin
i. Është gjithashtu e volitshme të pranohet ekzistenca e një pozicioni që ndjek elementin e
fundit në një listë. Funksioni END(L) do të kthejë pozicionin që vjen pas pozicionit n në një
liste L prej n-elementësh. Shënojmë që pozicioni END(L) ka një distancë nga fillimi i listës
që ndryshon ndërkohë që lista shtohet ose pakësohet (shkurtohet), ndërkohë që të gjithë
pozicionet e tjera kanë një distancë fikse nga fillimi i listës.
Shembull 6.1 Le të shkruajmë, duke përdorur këto veprime, një procedurë PURGE (Pastro)
që merr një listë si argument, të dhënë input dhe eliminon përsëritjet – dublikatat nga lista.
Elementët e listës janë të tipit elementtype. Një listë e përbërë nga të tillë elemente ka tipin
LIST, një marrëveshje që do ta përdorim në vazhdim në këtë kapitull. Supozojmë që kemi
një funksion:
Që merr vlerën True nëse x dhe y janë “të njëjtë apo identikë” dhe False në të kundërt.
Nocioni i të qenit të njëjtë qëllimisht është lënë i pacaktuar. Nëse elementtype është Real,
për shembull, mund të kërkojmë që same(x, y) të jetë True nëse dhe vetëm nëse është i
vërtetë kushti x = y. Megjithatë, nëse elementtype është një record që përmban numrin e
llogarisë, emrin dhe adresën e një nënshkruesi si në deklarimin më poshtë:
Type elementtype
Declare acct_no: Integer
Në këtë rast, ne mund të duam që same(x, y) të jetë True nëse x.acct_no = y.acct_no.
Më poshtë paraqitet kodi për procedurën PURGE. Variablat p dhe q, të tipit position, janë
përdorur për të treguar dy pozicione në listë. Ndërsa programi ekzekutohet, çdo element që
ndodhet majtas pozicionit p dhe është dublikatë e elementin që ndodhet në p fshihet nga
lista. Në një përsëritje të ciklit nga instruksioni (2) deri në atë (11), q përdoret për të skanuar
listën që nga pozicioni pasues i p-së, për të fshirë të gjitha kopjet e njëjta të elementit në
pozicionin p. Më pas p zhvendoset në pozicionin pasardhës dhe procesi përsëritet.
Siç është shkruar algoritmi është i pavarur nga mënyra në të cilën janë implementuar listat.
Një çështje me vlerë për t’u evidentuar lidhet me trupin e ciklit të brendshëm, rreshtat (4)
deri te (10) në skemën e mësipërme. Kur ne fshijmë elementin e pozicionit q në rreshtin (6),
elementët që ishin në pozicionet q+1, q+2, ... , e me radhë, zhvendosen një pozicion më
përpara në listë. Në veçanti, mund të ndodh që q të jetë pozicioni i fundit në L, vlera e q-të
do të bëhet END(L). Në rastin kur ekzekutohet rreshti (8), NEXT(END(L)) do të prodhojë
një rezultat të papërcaktuar. Për këtë arsye, është e rëndësishme që brenda ciklit me kushtin
q = END(L) në rreshtin (4), të ekzekutohet ose (6) ose (8), por kurrë të dy instruksionet
njëra pas tjetrës.
Mënyra e paraqitjes së listave është e lidhur në një farë mase me trajtimin që do t’i bëhet
kësaj strukture në një situatë konkrete: në rastet kur do të ketë shumë heqje ose shtime
elementësh të rinj, atëherë do të përdoret një strukturë dinamike për paraqitjen e tyre
nëpërmjet pointerave. Në rastin tjetër, kur lista qëndron në kujtesë dhe përdoret vetëm si
burim informacioni, pa hequr ose shtuar elementë të rinj, rezervimi dinamik i saj nuk është
i nevojshëm, prandaj që mënyra më e mirë për ta paraqitur listën është me anë të tabelës.
Në vijim të këtij seksioni do të ilustrojmë karakteristikat për dy mënyrat e paraqitjes së
listës.
Elementët e
listë në një
moment të
caktuar
last Elem.i fundit
Hapsirë e
lirë që mund
gjmaks
të përdoret
elements për listën
Figura 6.1: Vizualizimi i paraqitjes së listes me tabele
Më poshtë jepen implementimet për veprimet INSERT, DELETE dhe LOCATE mbështetur
në paraqitjen e listës nëpërmjet tabelës.
b) DELETE fshin elementin në pozicionin p duke lëvizur elementët nga pozicionet p+1,
p+2, …, last në pozicionet p, p+1, …, last-1.
c) LOCATE në mënyrë të njëpasnjëshme skanon tabelën për të parë për një element të
dhënë. Në rast se elementi nuk gjendet, LOCATE kthen last+1.
Function LOCATE ( x:elementtype, L:LIST) Returns position;
//kthen pozicionin e x në listën L
For q 1 To L.last
If L.element[q] = x Then
Return(q)
EndIf
Next q
Return (L.last+1) //nese nuk gjendet
EndFunction //LOCATE
Të shihet si një ushtrim shtimi i një elementi x: elementtype në fund të listës ekzistuese L.
B. Vika & Dh. Tole
6 Strukturë të Dhënash |7
Kjo lloj paraqitjeje na shpëton nga përdorimi i vazhdueshëm i memories për ruajtjen e një
liste dhe si pasojë edhe nga zhvendosja e elementëve për të liruar hapësirë për elementët e
rinj ose për të mbyllur boshllëqet e krijuara nga fshirja e elementëve. Megjithatë, ne e
paguajmë një çmim, atë të hapësirës plotësuese për pointer-in.
Në këtë paraqitje, një listë është e përbërë nga qeliza, ku secila prej tyre përbëhet nga një
element i listës dhe një pointer që shënon tek qeliza tjetër fqinjë në listë. Në qoftë se lista
është a1, a2, …, an qeliza që mban ai ka një pointer që shënjon qelizën që mban ai+1, për
i=1, 2, …, n-1. Qeliza që mban an ka një pointer vlera e së cilës është NULL (hiç, zero).
Ka gjithashtu një qelizë header (koka apo kreu), kjo për paraqitjen me qelizë Dummy, që
shënon tek qeliza që mban a1; koka nuk mban element. Në rastin e një liste boshe, pointer-
i i qelizës header-it është NULL, dhe nuk ka qeliza të tjera.
header
a1 a2 an NULL
Type tipqelize
Declare element : elementype
Declare pas : ^ tipqelize
endType
Ky funksion punon duke lëvizur shënjuesin q nëpër listë nga koka, derisa të arrijë fundi, i
cili dallohet nga fakti që q tregon tek një qelizë që ka një shënjues NULL. Vëmë re që kjo
paraqitje e funksionit END është jo i efektshëm, pasi kërkon që të kontrollohet e gjithë lista
sa herë që ne na duhet të llogarisim funksionin END(L).
Nëse është e nevojshme të bëhet kjo gjë shpesh, atëherë ne ose 1) mund të përdorim një
paraqitje të listës që përfshin një pointer tek qeliza e fundit, ose 2) të zëvendësojmë
përdorimet e END(L) aty ku është e mundur. P.sh., kushti: p <> END(L) mund të
zëvendësohet me: p^.pas <> NULL.
p
a b …
(a)
p
a b
(2)
x …
(3) .(4)
p
(1) (b)
Figura 6.Error! No text of specified style in document..3: Diagrama e INSERT
Në rreshtin (1) , temp vendoset për të treguar qelizën që përmban b. Në rreshtin (2) krijohet
një qelizë e re e listës dhe fusha pas e qelizës që përmban a bëhet që të tregojë mbi këtë
qelizë të re. Në rreshtin (3) fusha element e qelizës së re të krijuar merr vlerën x , dhe në
rreshtin (4) fusha pas merr vlerën temp, pra shënjon mbi qelizën që përmban b. (b) tregon
rezultatin e ekzekutimit të INSERT. Shënjuesit e rinj janë paraqitur me vija të ndërprera
dhe të markuar nga numri që tregon hapin në të cilin ato janë krijuar.
…
p a b c
4. Ndërtimi i një funksioni logjik që tregon nëse në listën L gjendet elementi me vlerë VL:
Ne mund të bëjmë pyetjen nëse është më mirë të përdorim një paraqitje me pointer apo një
paraqitje me tabelë të listës në një situatë të caktuar. Shpesh përgjigja varet se cilat veprime
kemi ndërmend të kryejmë, ose cilat do të kryhen më shpesh. Në raste të tjera, vendimi varet
nga fakti se sa gjatë duhet të qëndrojë lista.
• Disa veprime zgjasin më tepër në një paraqitje se në tjetrën. P.sh., INSERT dhe
DELETE ndjekin një numër konstant, të pandryshueshëm, hapash për një listë të
lidhur, por kërkojnë një kohë në proporcion me numrin e elementëve të
njëpasnjëshëm kur përdoret paraqitje me tabela. Përkundrazi, ekzekutimi i
PREVIOUS dhe END kërkon kohë konstante në paraqitjen me tabelë, por kohë
proporcionale me gjatësinë e listës nëse përdoret paraqitja me pointer.
• Në qoftë se një program thërret për shtim ose fshirje që prek elementin e një
pozicioni të shënuar nga disa variabla të tipit position, dhe vlera e këtij variabli do
të përdoret më vonë, atëherë paraqitja me shënjues nuk mund të përdoret sikurse
ne e kemi përshkruar atë këtu. Si një parim kryesor, pointer-at duhet të përdoren
me kujdes dhe kufizim të madh.
• Paraqitja me tabelë mund të shpenzojë hapësirë, duke qenë se përdorë maksimumin
e hapësirës pavarësisht nga numri i elementëve aktuale në listë në çdo kohë.
Paraqitja me pointer përdor vetëm aq hapësirë sa është e nevojshme për elementët
që ndodhen aktualisht në listë, por kërkon hapësirë për pointer-at në çdo qelizë.
Në një numër aplikimesh ne mund të duam të përshkojmë një listë në mënyrë të efecientë
në të dy drejtimet: nga koka tek fundi dhe anasjelltas. Ose, për një element të dhënë, ne
mund të duam të përcaktojmë shumë shpejt elementët paraardhës dhe pasardhës. Në situata
të tilla ne mund të duam t’i japim çdo qelize në një listë një pointer si për qelizën
paraardhëse ashtu edhe për atë pasardhëse, duke sugjeruar kështu listat lineare me dy-
drejtime.
… …
… …
Figura 6.Error! No text of specified style in document..5: Vizualizimi i paraqitjes së listës me dy-drejtime
Type tipqelize
Declare element : elementtype;
Declare pas, para : ^ tipqelize
EndType
Type position = ^ tipqelize;
p
Figura Error! No text of specified style in document.Error! No text of specified style in
document..6: Veprimi DELETE në listën e lidhur me dy-drejtime
Në figurën e mësipërme shigjetat me vijë të plotë paraqesin pointer-at e vjetër, ndërsa ato
me vijë të ndërprerë pointer-at e rinj.
Së pari, ne përcaktojmë qelizën paraardhëse duke përdorur fushën para. Bëjmë që fusha
pas e kësaj qelize të tregojë tek qeliza që është një pozicion pas elementit të referuar nga
pointer-i p. Më pas bëjmë që fusha para e kësaj qelize pasardhëse të tregojë tek qeliza që
është një pozicion përpara elementit të referuar nga pointer-i p. Qeliza e treguar nga p bëhet
e papërdorshme dhe mund të ripërdoret automatikisht nëse është e nevojshme.
Matricat e rralla janë matricat ku pjesa dërmuese e elementëve kanë të njëjtën vlerë,
zakonisht zero; kurse elementët e tjerë, të pakët në numër, kane vlera të ndryshme.
Matricat e rralla janë një dukuri e zakonshme në fusha të ndryshme të matematikës, fizikës,
etj.
0 0 0 -3 0
0 0 0 0 0
0 0 5 0 7
A = 0 0 0 0 0
0 0 10 0 0
7 9 0 1 0
0 0 0 0 3
Matrica e rrallë Anxm ka n x m elementë, por vetëm pak prej tyre kanë nevojë për rezervim
të mirëfillte. Në rastin e mësipërm, vetëm 8 prej 35 elementëve kanë nevojë për rezervim.
Nëse matrica e mësipërme do të deklarohej si një tabelë, atëherë për të do të rezervoheshin
35 vende në kujtesë, gjë që do të sillte një shpërdorim të saj. Do të ishte më e efektshme që
në kujtesë të rezervoheshin vetëm elementët që janë të ndryshëm nga zero së bashku me
vendndodhjen e tyre në matricë. Këtë mund ta bëjmë duke përdorur listat.
Elementët e listës do të përmbajnë vlerën e një elementi të matricës si dhe rreshtin dhe
kolonën ku ndodhet ky element në matricë.
Le të shohim më konkretisht teknikën e paraqitjes së një matrice të rrallë nëpërmjet listave
të lidhura.
Së pari ndërtohet një array V me n elementë (aq sa është numri i rreshtave të matricës). Çdo
element në këtë array do të ketë adresën e kokës së listës që mban të gjithë elementët e
ndryshëm nga zero në rreshtin përkatës të matricës, në qoftë se në ketë rresht gjendet të
paktën një element i ndryshëm nga zero. Në rast se ky rresht i ka të gjithë elementët e
barabartë me zero, atëherë në këtë pozicion të array-it vendoset vlera NULL.
Figura 6.Error! No text of specified style in document..7: paraqitja e matricës së rallë A me listë të lidhur
Type list = ^ el
Type vshenjues = array[1..n] Of list
Për të paraqitur një polinom të plotë duhen rezervuar koeficientet dhe fuqitë e x-it.
Paraqitja me matricë do të sillte një shpërdorim të kujtesës, sepse polinomi mund të jetë i
shkallës së lartë dhe numri i koeficienteve të ndryshëm nga zero mund të jetë shume i vogël.
P.sh, polinomi x100 – 35x47 + 34x7 - x2 mund të paraqitej nga një matricë me dy rreshta dhe
101 shtylla. Në këtë matricë rreshti i parë përmban koeficientet e termave kurse rreshti i
dytë fuqitë përkatëse të x-it. Meqenëse polinomi i mësipërm ka shumicën e koeficienteve të
barabartë me zero, matrica përfaqësuese do të ishte një matricë e rrallë. Në këtë mënyrë
edhe polinomi do të paraqitet me një listë. Çdo element i listës do të jetë një regjistrim me
tri fusha: në fushën e parë do të vendoset koeficienti i termit, në të dytën fuqia përkatëse e
x-it, kurse në fushën e tretë adresa e elementit pasardhës.
Type el
Declare k : Integer
Declare f : Integer
Declare pas : ^el
EndType
Type list = ^ el
Procedure Polinomi()
Declare i, f1, f2 :Integer; elr : ^el
krijoP(P1)
krijoP(P2)
i0
While ((P1 <> NULL) and (P2 <> NULL)) do
AllocateMemory (elr)
f1 P1^.f
B. Vika & Dh. Tole
6 Strukturë të Dhënash | 18
f2 P2^.f
If f1 > f2 Then
elr^.k P1^.k
elr^.f P1^.f
P1 P1^.pas
Else If f2 > f1 Then
elr^.k P2^.k
elr^.f P2^.f
P2 P2^.pas
Else If f2 = f1 Then
elr^.k P1^.k + P2^.k
elr^.f P1^.f
P1 P1^.pas
P2 P2^.pas
EndIf
If elr^.k <> 0 Then
elr^.pas NULL
If i < 1 Then
P elr
bishti elr
Else
bishti^.pas elr
bishti=elr
EndIf
EndIf
i i+1
EndWhile