Sunteți pe pagina 1din 9

Structuri de date (I) Liste

In foarte multe situaii programatorul trebuie s organizeze lucrul cu diverse serii de date i s decid asupra modalitii de memorare a acestora. S presupunem, de exemplu, c dorim s lucrm, ntr!un program, cu linii poligonale din spaiul tridimensional "#. $ste clar c o linie poligonal este bine definit de v%rfurile sale, i deci vom memora numai coordonatele acestor puncte. & astfel de serie de puncte succesive o vom numi traseu n "# i, n continuare, ne propunem s inventariem posibilitile de memorare ale unui traseu pe care le avem la ndem%n p%n n acest moment. 'rima soluie( memorm fiecare dintre cele trei coordonate, x, ) i z, ale punctelor traseului n c%te un vector de dimensiune egal cu numrul de puncte, de exemplu(
double x[100],y[100],z[100]; for(i=0;i<100;i++){ x[i]=sqrt(i); y[i]=1.5*i; z[i]=i*i;

*ceast separare pe componente nu reflect suficient de bine ideea de +punct n "#,, o rezolvare mai bun este dat de soluia a doua( organizarea coordonatelor ntr!o matrice cu trei coloane(
double tr!s[100]["]; for(i#t i=0;i<100;i++){ tr!s[i][0]=sqrt(i); tr!s[i][1]=1.5*i; tr!s[i][$]=i*i;

*cum e mai clar( fiecare linie definete un punct, totui s!a pierdut ceva( nu mai apar nicieri denumirile coordonatelor. Soluia a treia( pentru a pune n eviden complet faptul c un traseu este o secven de puncte, vom defini o structur, numit Punct, cu trei c%mpuri membre, x, y i z, iar un traseu va fi un vector de astfel de structuri(
stru%t &u#%t{ double x,y,z; ; &u#%t tr!s[100]; for(i=0;i<100;i++){ tr!s[i].x=sqrt(i); tr!s[i].y=1.5*i; tr!s[i].z=i*i;

In urmtoarea soluie, a patra, ncercm s nu ncrcam prea mult stiva( alocm pe stiv doar un tablou de poineri ctre puncte memorate n heap(
&u#%t* tr!s'[100]; for(i=0;i<100;i++){ tr!s'[i]=#e( &u#%t; tr!s'[i])*x=sqrt(i); tr!s'[i])*y=1.5*i; tr!s'[i])*z=i*i;

*tenie( punctele traseului nu formeaz un vector, pointeri care le intesc sunt memorai ntr!un vector. * cincea soluie, ultima i cea mai elegant( alocm n heap direct un tablou de puncte(
i#t #=100; &u#%t* 'tr!s; 'tr!s=#e( &u#%t[#]; for(i=0;i<100;i++){ 'tr!s[i].x=sqrt(i); 'tr!s[i].y=1.5*i; 'tr!s[i].z=i*i;

*ceast soluie are i avanta-ul utilizrii unui vector de dimensiune variabil. .am acestea ar fi toate posibilitile nt%lnite de noi p%n acum de memorare a unei liste. In general, vom numi nod o structur care nmagazineaz, n c%mpurile sale membre, date des! pre un anumit obiect, i vom numi list o mulime de noduri. *stfel, mai sus, soluiile #, / i 0 exemplific trei modaliti de organizare n memorie a unei liste care s conin v%rfurile unei linii poligonale n "#. 1iecare nod al unei astfel de liste este o structur Punct, iar fiecare list este organizat cu a-utorul tablourilor( tablori de noduri sau tablouri de pointeri ctre noduri. & list organizat cu a-utorul unui tablou o numim list tabelat. *vanta-ul ma-or al listelor tabelate const n accesul direct( dac tim indicele nodului cutat, nodul poate fi accesat cu operatorul de indexare 2 3. 4ea-unsul ma-or( aceste liste sunt greu de actualizat. 5ac vrem s eliminm un nod din list, de exemplu, trebuie s rescriem o bun parte din list( mutm cu un pas n fa toate nodurile care urmeaz dup cel eliminat. 5ac dorim s inserm un nod nou undeva n mi-locul listei, vom deran-a iari o -umtate din list. 6n alt nea-uns( alocarea unui tablou se efectueaz, n mod obligatoriu, ntr!o zon continu de memorie ! aritmetica pointerilor impune ca elementele succesive ale unui tablou s aibe adrese succesive (cu pasul adecvat mrimii elementelor) 7 i astfel se poate nt%mpla ca n memorie s existe suficient spaiu pentru toat lista dar s fie fragmentat n zone prea mici pentru a putea cuprinde, n bloc, un tablou. 5ificultile semnalate mai sus sunt depite prin utilizarea de liste nlnuite. 8om renuna la accesul direct dat de tablouri i vom parcurge lista secvenial, din aproape n aproape. In acest scop fiecare nod are prevzut, pe l%ng c%mpurile obinuite de date, unul sau mai muli pointeri care intesc ctre alte noduri ale listei. *ceti pointeri definesc diverse relaii de ordine ntre nodurile listei. & list simplu nlnuit are o singur ordine de parcurgere, una dublu
2

nlnuit are dou ordini de parcurgere, .a.m.d. & list simplu nlnuit poate fi liniar (dac are un prim i un ultim element, capetele listei) sau circular (obinut dintr!o list liniar, fc%nd ca pointerul de legtur al ultimului nod s inteasc spre primul nod). Se pot defini i liste liniare sau circulare dublu nlnuite, precum i liste care s memoreze grafuri. S observm c, pentru a asigura accesul la nodurile sale, trebuie s dispunem de cel puin un pointer care s indice ctre un nod al listei. In figura urmtoare este sc9iat o list liniar simplu nlnuit, prevzut cu doi pointeri de acces, pointerii prim i ultim(

&bservm c fiecare nod are c%te un singur pointer de legtur, pointerul next. 8om respecta cu strictee urmtoarea convenie( vom atribui valoarea zero (sau 46LL) oricrui pointer care nu are inta bine definit. *stfel pointerul next al ultimului nod din lista de mai sus trebuie s fie nul. Iat i o list liniar dublu nlnuit

n care nodurile au c%te doi pointeri de legtur( next i prev. In sf%rit, sc9ia unei liste circulare simplu nlnuite este urmtoarea(

&bservm c avem un singur pointer de acces la list, pointerul act, care intete ctre nodul actual, cel care este inspectat la momentul dat. .u liste nlnuite se pot efectua urmtoarele operaii de interes general( crearea unei liste, parcurgerea listei, cutarea, inspectarea i modificarea unui nod n list, adugarea unui nod n faa listei sau la sf%ritul listei, inserarea unui nod n list, tergerea unui nod, tergerea unei liste. :oate aceste operaii vor fi exemplificate n urmtorul exemplu de lucru cu o list liniar simplu nlnuit.

Incepem cu urmtoarele directive de precomilare(


+i#%lude<iostre!,.-* +defi#e #ull 0

&bservaie( pentru a se face distincie ntre valoarea ntreag ; i adresa +nicieri,, n . este utilizat macroul 46LL definit ca ; sau ca (void<);. In .== se recomand utilizarea lui ;, dar poate fi utilizat i macroul 46LL. 5up cum se vede, noi preferm aici s ne definim propria substituie( null, cu litere mici. In continuare definim forma unui nod al listei, pentru simplificarea expunerii am redus c%mpurile de date ale unui nod la unul singur, ntregul info. 'ointerul next va inti, dup cum i spune i numele, ctre nodul urmtor din list(
stru%t .od{ i#t i#fo; .od *#ext; ;

6rmeaz c%teva funcii de lucru cu noduri( o funcie de iniializare a unui nod nou, o funcie care citete de la tastatur un ntreg i care, dac ntregul citit nu este egal cu un cod de oprire, returneaz adresa unui nod iniializat cu informaia citit, altfel returneaz null, o subru! tin care afieaz un nod indicat printr!un pointer i o funcie care decide dac dou noduri conin aceleai informaii(
.od* i#it.od(i#t #/#fo){ .od* #ou; #ou=#e( .od; #ou)*i#fo=#/#fo; #ou)*#ext=#ull; retur# #ou;

00!lo%!, u# .od #ou 00si il u,'le,

.od* %iteste.od(i#t sto'){ i#t i#fo; %i#**i#fo; if(i#fo==sto') retur# #ull; else retur# i#it.od(i#fo); 1oid !fis.od(.od* '){ %out<<')*i#fo<<e#dl; retur#; i#t %o,'.od(.od* ', .od* q){ retur# (')*i#fo)==(q)*i#fo);

1unciile de mai sus, dei foarte simple, au fost definite pentru a ncapsula conceptul de nod( dac vom utiliza acest program pentru a prelucra liste cu un alt tip de noduri (cu mai multe c%mpuri de date, de exemplu), va trebui s modificm numai aceste funcii, restul funciilor 7 cele care urmeaz 7 fiind folosite fr nici o modificare. In acest scop, vom avea gri- ca aceste funcii de prelucrare a listelor s nu acceseze n mod direct nici un c%mp de date al vreunui nod, s acceseze cel mult pointerul next al acestora. 5efinim acum structura Lista, format numai din doi pointeri ctre noduri(
stru%t 2ist!{ .od* 'ri,; .od* ulti,; ;

5e fapt ar fi fost suficient un singur pointer, pointerul prim, care s rein adresa primului nod din list, ultimul nod este uor de recunoscut( are pointerul next nul, am preferat s utilizm i pointerul ultim (indicator ctre ultimul nod) pentru a face eficient completarea unei liste prin adugarea nodului nou la sf%ritul listei. 1acem observaia c, pentru o tratare unitar, toate structurile pe care le vom utiliza n program (noduri i liste) vor fi alocate dinamic cu operatorul new ( i dealocate cu delete), n consecin ele vor fi referite numai prin pointeri. Iat funcia de iniializare a unei liste(
1oid i#it2ist!(2ist!* li)*'ri,=#ull; li)*ulti,=#ull; li){

Iniial vom avea o list vid prim>ultim>null. 6rmeaz funcia care adaug un nod la sf%ritul unei liste(
1oid !d!u32ist!(2ist!* li, .od* ') { if(li)*'ri,==#ull) li)*'ri,=li)*ulti,='; else li)*ulti,=li)*ulti,)*#ext='; retur#;

*tenie, n momentul apelului, nodul intit de p trebuie s fie de-a iniializat cu initNod(). Listele sunt create prin citirea de la tastatur( utilizatorul introduce un cod de oprire (de tip int) i apoi o serie de numere ntregi 7 informaiile din nodurile listei. .rearea listei este sto! pat de introducerea codului de oprire.
1oid %iteste2ist!(2ist!* { i#t 4, sto'; li)

.od* '; %out<<56od de o'rire=5; %i#**sto'; %out<<57!ti i#for,!tiile 'e#tru list!5<<e#dl; for(4=1;;4++){ %out<<54=5<<4<<5 8 5; '=%iteste.od(sto'); if('==#ull) bre!4; !d!u32ist!(li,'); retur# ;

6rmeaz funcia care parcurge i afieaz o list(


1oid !r!t!2ist!(2ist!* li, %-!r ,es!9[]){ .od* '=li)*'ri,; %out<<:;#:<<,es!9<<:;#:<<e#dl; if('==#ull) %out<<52ist! 1id!5<<e#dl; else for( ; '<=#ull; '=')*#ext) !fis.od('); retur#;

5up cum tim, spaiul de memorie alocat cu operatorul new trebuie eliberat cu delete, iat funcia care +terge, o list ntreag(
1oid ster3e2ist!(2ist!* li) { 00'!r%ur3e, list! %u li.'ri, si ster3e, i# ur,! .od *'; if(li)*'ri,==#ull) retur#; do{ '=li)*'ri,)*#ext; delete li)*'ri,; li)*'ri,='; (-ile(li)*'ri,<=#ull); retur#;

In acest moment avem la dispoziie un set minimal de funcii de prelucrare a listelor liniare simplu nlnuite, aa c le putem testa cu urmtoarea funcie main()(
i#t ,!i#(1oid){ 2ist!* li1=#e( 2ist!; i#it2ist!(li1); %iteste2ist!(li1); !r!t!2ist!(li1, 52ist! i#iti!l!85); 6

ster3e2ist!(li1); !r!t!2ist!(li1,52ist! du'! ster3ere85); retur# 0;

"ezultat(

.ontinum cu celelalte operaii necesare n manipularea listelor. 6rmtoarea funcie caut n list un nod pentru care info este egal cu cod i l terge din list pe primul gsit. Sunt tratate diferit cazurile( trebuie ters primul nod, ultimul nod sau unul intermediar.
1oid ster3e.od(2ist!* li, .od* %od) { .od *',*q; 00d!%! list!)i 1id!8 if(li)*'ri,==#ull) retur#; '=li)*'ri,; q=li)*'ri,)*#ext; 00d!%!)i 'ri,ul .od8 if(%o,'.od(',%od)){ delete '; li)*'ri,=q; retur#;

00d!%! #u, %!ut!, %odul %u ' si q for( ;q<=#ull == <%o,'.od(q,%od); '=q, q=q)*#ext); 00d!%! #u)l 3!si,, #)!1e, %e ster3e if(q==#ull) retur#; 00!1e, ')*#ext==q==#odul %!re trebuie sters 00d!%!)i ulti,ul if(q==li)*ulti,){ delete q; ')*#ext=#ull; li)*ulti,='; retur#;

00!ltfel; il dezle3!, si)l ster3e, ')*#ext=q)*#ext; delete q; retur#; 00il s%o!te, di# list! 00il de!lo%!,

6rmtoarele dou funcii insereaz un nod nou n list, dup sau n faa unuia determinat dup valoarea variabilei cod.
1oid i#sert7u'!(2ist!* li, .od* %od, .od* #ou){ .od *'; for('=li)*'ri,;'<=#ull == <%o,'.od(',%od);'=')*#ext); if('==#ull >> '==li)*ulti,) !d!u32ist!(li, #ou); else{ #ou)*#ext=')*#ext; ')*#ext=#ou; retur#;

1oid i#sert?!tz!(2ist!* li, .od* %od, .od* #ou){ .od *',*q; 00d!%! list!)i 1id!8 if(li)*'ri,==#ull) { !d!u32ist!(li, #ou); retur#;

'=li)*'ri,;

q=li)*'ri,)*#ext; 00d!%! trebuie 'us i# f!t! 'ri,ului #od8 if(%o,'.od(',%od)){ #ou)*#ext=li)*'ri,; li)*'ri,=#ou; retur#;

00d!%! #u, %!ut!, %odul %u ' si q for( ;q<=#ull == <%o,'.od(q,%od); '=q, q=q)*#ext); 00d!%! #u)l 3!si,, 'u#e, #odul #ou tot i# f!t! 'ri,ului8 if(q==#ull) { #ou)*#ext=li)*'ri,; li)*'ri,=#ou; retur#;

00@1e, ')*#ext==q==#odul i# f!t! %!rui! 'u#e, #oul .od #ou)*#ext=q; ')*#ext=#ou; retur#;

*ceste funcii pot fi testate cu urmtoarea funcie main()(


i#t ,!i#(1oid){ 2ist!* li1=#e( 2ist!; i#it2ist!(li1); %iteste2ist!(li1); !r!t!2ist!(li1, 52ist! i#iti!l!5); 00ster3e2ist!(li1); 00!r!t!2ist!(li1,52ist! du'! ster3ere85); .od* %od=i#it.od(15); .od* #ou=i#it.od(150); i#sert?!tz!(li1, %od, #ou); !r!t!2ist!(li1, 52ist! 'refi#!l!5); ster3e.od(li1, i#it.od("0)); !r!t!2ist!(li1, 52ist! fi#!l!5); retur# 0;

Lsm cititorului ca exerciiu s scrie o variant a funciei adaugLista() care s adauge de fiecare dat nodul cel nou n fa, pe primul loc n list. Iat i o tem pentru avansai( scriei o funcie care sc9imbe sensul de parcurs al listei astfel nc%t primul nod s devin ultimul.

S-ar putea să vă placă și