Sunteți pe pagina 1din 8

Tehnici de Programare 8.

Liste

Udritoiu tefan Stoica Spahiu Cosmin

Lucrarea 8 Liste dinamice.


Introducere n alocarea dinamic Orice algoritm lucreaz cu date (numere ntregi, reale, iruri de caractere, etc.). Prin tipul unei date se nelege o mulime de elemente numite valori, pe care este posibil s le primeasc o variabil de acel tip. Pe mulimea valorilor unui tip se definesc operaiile asociate tipului (ex: adunare scdere, mprire ntreag, mprire real). Pentru fiecare tip se definete modul n care se memoreaz valorile sale. Exemplu: pentru int, valorile se memoreaz utiliznd codul complementar i se folosesc 2 octei consecutivi. O variabil se caracterizeaz prin: tipul ei, nume i adres (convenim s numim adres numrul de ordine al primului octet din cei p octei consecutivi necesari, din memoria intern). Din punctul de vedere al unui programator, memoria calculatorului se prezint ca o succesiune de octei, fiecare octet avnd o adres binar bine stabilit. Utilizatorul nu are acces direct la adresa variabilei, aceasta fiind alocat n mod automat de compilatorul limbajului de programare. Se cunosc dou forme de alocare a memoriei de ctre programator: static i dinamic. Utiliznd forma de alocare static, va trebui precizat n program exact toate variabilele de care este nevoie. De exemplu, n cazul vectorilor, nu se tie ntotdeauna de la nceput cte componente are. Este nevoie s se declare de o dimensiune acoperitoare (de ex, 50, dac n mod curent este nevoie doar de 10-20 componente). Se observ c restul de variabile, dei nu sunt folosite niciodat, pentru ele se pstreaz spaiu n memoria calculatorului. Dar dac ntr-o anumit situaie este nevoie de 51 de variabile, atunci se va genera eroare deoarece se depete numrul maxim declarat. Aceste probleme sunt rezolvate folosind alocarea dinamic. Utiliznd forma de alocare dinamic, n timpul rulrii programului, n funcie de necesiti, se aloc memorie suplimentar sau se renun la ea. Pentru alocarea dinamic se utilizeaz tipul de date referin. Se consider secvena de program:
struct Lista_numere { int numar; Lista_numere *adr_urm; } Lista_numere *list;

numar (100)

adr_urm (110..10)

Variabila st este o variabil de tip referin. Ea reine adrese de nregistrri. La rndul ei, o nregistrare are dou cmpuri: numr care reine un numr ntreg (informaia util) ; i adr_urm care reprezint adresa unei alte nregistrri (de obicei adresa urmtoarei nregistrri). Astfel o nregistrare conine att informaie util ct i adrese ale altor nregistrri. Pentru a se rezerva spaiu pentru o nregistrare se folosete funcia new.
list = new Lista_numere;

Tehnici de Programare 8. Liste

Udritoiu tefan Stoica Spahiu Cosmin

Practic atunci cnd avem nevoie de o nou variabil, vom aloca spaiu pentru ea. Nu este necesar s avem declarate *list1, *list2 *listn pentru fiecare variabil de care avem nevoie, deoarece avem posibilitatea ca fiecare nregistrare s rein adresa unei alte nregistrri. Dac am avea o lista de 5 variabile, practic este necesar doar reinerea primei adrese deoarece adresa variabilei 2 o reinem n adr_urm din prima variabil, adresa variabilei 3 o reinem n adr_urm din cea de-a doua variabil etc. Atunci cnd nu ne mai este necesar o variabil, ea se terge (se elibereaz memoria rezervat pentru ea) folosind funcia delete.
delete list;

Observaii: list se refer la adresa la care se gsete variabila list list>numar se refer la cmpul numeric (informaia util) care are informaia memorat n variabila list list>adr_urm se refer la adresa unei alte nregistrri list>adr_urm>numar semnific variabila numar care se gsete n nregistrarea care are adresa adr_urm al nregistrrii cu adresa list.

Lista liniar simplu nlnuit


O list liniar simplu nlnuit este o structur de forma:
nr 1 adr_urm2 adr1 nr 2 adr_urm3 adr2

.
adr n

nrn

NULL

Semnificaia notaiilor folosite este urmtoarea: adr1, adr2, . adrn reprezint adresele de memorie ale celor n nregistrri adr_urm2, adr_urm3, adr_urmn reprezint cmpurile din nregistrrile st, n care se reine adresa urmtoarei nregistrri. Practic adr_urm2 = adr2 ; adr_urm3 = adr3 ; . ; adr_urmn = adrn NULL semnific faptul c n cmpul respectiv nu exist nici o adres, deci dup aceast nregistrare nu mai exist nici o alta Exemplu de aezare n memoria calculatorului a unei liste: Pentru cazul nostru avem: adr1 = 15 (poziia la care se gsete primul octet din nregistrare: cmpul numar. De exemplu 100) adr_urm2 = adr2 = 43 (poziia n memorie a .. urmtoarei variabile) adrn = 362 La poziia 363 se gsete valoarea NULL Observm c spaiul alocat pentru aceste nregistrri nu este consecutiv. El este alocat acolo unde se gsete memorie liber, neocupat cu alte date. Denumirea de simplu nlnuit provine de la faptul c fiecare element al listei conine o singur adres, i anume adresa urmtorului element din list. 2

Tehnici de Programare 8. Liste

Udritoiu tefan Stoica Spahiu Cosmin

Operaiile pe care le putem face cu aceast structur de date sunt urmtoarele: creare, listare, adugare, tergere. Crearea listei Se va cere numrul n de nregistrri. Se creeaz o prim nregistrare avnd ca informaie util nr.1 Variabila prim, va reine adresa primei nregistrri din list. Pentru fiecare i cuprins ntre 2 i n se adaug cte o nou nregistrare listei. Variabila ultim reine adresa ultimei nregistrri din list, pentru a-i completa cmpul adres. Se procedeaz astfel deoarece atunci cnd am creat o nregistrare, nu se cunoate adresa nregistrrii care urmeaz.
void function creare() { int i; Lista_numere *temp; prim = new Lista_numere; //se aloc spaiu pentru prima nregistrare prim->numar = 1; prim->adr_urm = NULL; //iniial dup prima nregistrare nu mai urmeaz nici o alt nregistrare. Ea este i prima i ultima ultim = prim; //se pstreaz n ultim ultima nregistrare din list for (i=2; i<=n; i++) { temp = new Lista_numere; //alocarea de spaiu pentru nregistrarea nr. i temp->numar = i; //reinerea informaiei utile (numrul propriuzis) temp->adr_urm = NULL; //aceasta nregistrare se adaug ultima, la sfritul listei ultim->adr_urm = temp; ultim = temp; //Acum s-a refcut legtura dintre vechea ultim nregistrare i nregistrarea nou creat. Variabila temp devine ultima nregistrare. iniial: nr 1 adr_urm2 nri-1 nri NULL . NULL prim dup refacerea legturii: nr 1 adr_urm2 . prim }//end for ultim temp

nri-1

adr_urmi-1

nri

NULL ultim

Listarea Am precizat faptul c prim reine adresa primei nregistrri. Pentru a nu deteriora aceast valoare, o vom memora n variabila temp. Ct timp nu am ajuns la sfritul listei, vom tipri informaia util i ncrcm n temp adresa nregistrrii urmtoare
void function Listare() { Lista_numere *temp; temp = prim; while (temp != NULL) //cat timp nu s-a ajuns la sfrit

Tehnici de Programare 8. Liste


printf(numarul este: %d temp = temp->adr_urm; //temp devine nregistrarea urmtoare }//end while } {

Udritoiu tefan Stoica Spahiu Cosmin


,temp->numar;

Adugarea n list Se consider c dorim s adugm o nou nregistrare dup nregistrarea nr 2. Pentru aceasta va trebui s ne poziionm pe nregistrarea dup care dorim s facem adugarea, alocarea spaiului pentru noua nregistrare, completarea informaiei utile pentru ea, completarea adresei urmtoarei nregistrri dup cea nou creat care va fi adresa urmtoarei nregistrri dup nregistrarea pe care suntem poziionai, cmpul adres al nregistrrii pe care suntem poziionai va lua valoarea noii nregistrri. adr_urm3 nr 1 adr_urm2 nr2 nr3 adr_urm4 .
prim adr2 adr3 nrx nreg. nou adr_x adr_urmx nrx adr_x void function Adaugare( ) { int nr; Lista_numere *temp, *nou; scanf(%d, &nr); temp = prim; //ne vom poziiona pe cea de-a doua nregistrare (temp devin nregistrarea 2) temp = temp->adr_urm; //se aloca spaiu pentru noua nregistrare nou = new Lista_numere; nou->numar = nr; //se creeaz legtura verde (din desenul 2) care este de fapt fosta legtur ctre elementul 3 (legtura verde din desenul 1) nou->adr_urm = temp->adr_urm; //se creeaz i legtura roie temp->adr_urm = nou; } adr_urm3

nr 1 adr_urm2 prim

nr2 adr2

nr3 adr3 adr_urm3

adr_urm4

nreg. nou

tergerea din list Pentru tergere se procedeaz n mod diferit dac se terge prima nregistrare sau una diferit de aceasta. Dac dorim s tergem prima nregistrare, se salveaz coninutul acesteia ntr-o variabil temp, variabila prim va deveni cea de-a doua nregistrare, se terge variabila temp.

Tehnici de Programare 8. Liste

Udritoiu tefan Stoica Spahiu Cosmin

Dac dorim s tergem o alt nregistrare dect prima, se face poziionarea pe nregistrarea care urmeaz a se terge, cmpul de adres al nregistrrii precedente capt valoarea cmpului de adres al nregistrrii curente, eliberm spaiul rezervat nregistrrii curente.
nr 1 adr_urm2 prim nr2 adr2 adr_urm3 nr3 adr_urm4 adr3 nr3 adr_urm5 adr4

nr 1 adr_urm2 prim

nr2 adr2

adr_urm4

nr3

adr_urm4 adr3

nr3 adr_urm5 adr4

Observm c este necesar s reinem i nregistrarea dinaintea nregistrrii de ters pentru a-i putea modifica cmpul adres
void function Stergere(int nr) //primete ca parametru nregistrarea de ters { Lista_numere *temp, *parinte; int i; //cazul cnd se terge prima nregistrare if (i == 1) { temp = prim; prim = prim->adr_urm; delete temp; } //end if else { temp = prim; //ne poziionm pe nregistrarea numrul nr (cea care se terge). n variabila printe se retine nregistrarea anterioar celei care se terge. for (i=0; i<nr; i++) { parinte = temp; temp = temp->adr_urm; } //end for //crem legtura verde(din desenul 2) parinte->adr_urm = temp->adr_urm; delete temp; } //end else }

Lista liniar dublu nlnuit


O list dublu nlnuit este o structur de forma: NULL nr 1 adr_urm2
adr1 adr_spate1 nr 2 adr_urm3 adr2

adr_spaten-1 adr n

nrn

NULL

Fiecare nregistrare va reine att adresa urmtoarei nregistrri, ct i adresa nregistrrii de dinaintea ei.

Tehnici de Programare 8. Liste

Udritoiu tefan Stoica Spahiu Cosmin

Operaiile uzuale care se pot face cu o list dublu nlnuit sunt: adugare la dreapta, adugare la stnga, tergere la stnga, tergere la dreapta, listare de la dreapta la stnga, listare de la stnga la dreapta. Structura unei nregistrri are urmtoarea form:
struct Lista_numere { int numar; Lista_numere *next_adr; Lista_numere *last_adr; } Lista_numere *list;

a) Crearea listei Se va cere numrul n de nregistrri. Se creeaz o prim nregistrare avnd ca informaie util nr.1 Variabila prim, va reine adresa primei nregistrri din list. Pentru fiecare i cuprins ntre 2 i n se adaug cte o nou nregistrare listei. Variabila ultim reine adresa ultimei nregistrri din list, pentru a-i completa cmpul adres.
void function creare() { int i; Lista_numere *temp; prim = new Lista_numere; //se aloc spaiu pentru prima nregistrare prim->numar = 1; prim->next_adr = NULL; prim->last_adr = NULL; //iniial dup prima nregistrare nu mai urmeaz nici o alt nregistrare. Ea este i prima i ultima ultim = prim; //se pstreaz n ultim ultima nregistrare din list for (i=2; i<=n; i++) { temp = new Lista_numere; //alocarea de spaiu pentru nregistrarea nr. i temp->numar = i; //reinerea informaiei utile (numrul propriuzis) temp->next_adr = NULL; //se creaz legtura dintre noua nregistrare si ultima (sgeata verde din desen) temp->last_adr = ultim; //aceasta nregistrare se adaug ultima, la sfritul listei, deci se face i legtura roie ultim->next_adr = temp; ultim = temp; //Acum s-a refcut legtura dintre vechea ultim nregistrare i nregistrarea nou creat. Variabila temp devine ultima nregistrare. iniial:

NULL nr 1 adr_urm2
prim

adr_spaten nr 2 ultim

NULL temp

nrn

NULL

dup refacerea legturii:

NULL nr 1 adr_urm2
prim

adr_spaten-1 nr 2 adr_urmn

adr_spaten-1 ultim

nrn

NULL

}//end for }

Tehnici de Programare 8. Liste

Udritoiu tefan Stoica Spahiu Cosmin

In funcia de creare s-a implementat adugarea la stnga. Funciile de tergere i listare se fac analog ca la listarea simplu nlnuit, inndu-se cont de faptul c n cazul de fa trebuie s se fac actualizarea a dou legturi n loc de una singur. Existena a dou legturi are avantajul ca permite deplasarea n list att spre dreapta (temp = temp->next_adr), ct i spre stnga (temp = temp->last_adr). n felul acesta la tergere nu mai este nevoie s se rein printele, ci pur i simplu se scrie temp->last_adr.

Stiva
O stiv se definete ca o list liniar simplu nlnuit n care toate intrrile i ieirile se fac pe la un singur capt al ei. Ea este o structur tip adugare n stiv LIFO: last in, first aut (ultimul sosit, primul plecat) In acest caz nregistrarea de pe nivelul k reine nregistrarea de pe nivelul k-1. In cazul stivei se reine doar elementul din vrful stivei nr 5 adr_urm4 (elementul 5 din exemplul nostru). adr 5 De exemplu elementul 4 nu se poate extrage pn nu s-a extras elementul 5. De asemenea nu se poate insera nici un nr 4 adr_urm3 alt element ntre elementul 4 i 5, n mod direct. Dac totui adr 4 dorim s facem acest lucru va trebui s extragem elementul 5, s adugm elementul care dorim s l inserm, i abia nr 3 adr_urm2 apoi s adugm din nou elementul 5. adr 3 Operaia de adugare n list se numete PUSH, iar cea de extragere POP.

Parcurgerea stivei: de la ultimul element )(din vrf), ctre primul (ctre baz

adr 2
nr 1

nr 2 adr_urm1

NULL

Adugarea i extragerea din stiv Elementul vrf semnific ultimul element din list (n exemplul nostru elementul cu adresa 5).

adr1
void function Push( ) { Lista_numere *temp; int nr; temp = new Lista_numere; //ce se face aici? scanf(%d,&nr); //se citeste numarul care se adaug //se va crea legtura cu elementul vrf (dup elementul inserat, n stiv va urma vrful temp->adr_urm = varf; // temp va deveni noul vrf al stivei varf = temp; } void function Pop ( ) { Lista_numere *temp; temp = vrf->next; delete vrf; varf = temp;

Tehnici de Programare 8. Liste


} // explicai de ce s-a procedat astfel la tergere

Udritoiu tefan Stoica Spahiu Cosmin

Probleme propuse
1. S se realizeze o list simplu nlnuit, unde funciile de creare, adugare, listare i tergere s fie implementate recursiv. 2. S se implementeze pentru lista dublu nlnuite operaiile de adugare(stnga, dreapta), tergere i listare 3. S se implementeze o list circular, dublu nlnuit. O list se numete circular, dac ultima nregistrare va avea o legtur spre prima, iar prima spre ultima, ca n figura:
adr_spat next_adr nr1 en 2
ne xt_ n ad r r 2

e
4

ad r _s pa t
5

nex t_a d

adr5

rn a r dr _ sp 4 e at
3

adr4

t spa dr_ a r adr3 e 2 r _ad n t nex 3


4

4. Scriei un program care s implementeze o stiv dublu nlnuit 5. S se scrie un program care sorteaz elementele dintr-o stiv, prin una din metodele nvate, folosind ca structur intermediar o list dublu nlnuit 6. Se consider o structur liniar simplu nlnuit la care toate intrrile se fac la captul din stnga, iar ieirile prin captul din dreapta. O astfel de structur se numete coad. Ea este de tipul FIFO (first in, first out)

intrri n coad

nr 1 adr_urm2 adr1

nr 2 adr_urm3 adr2

ne xt_ r d anr

3 2

adr1

r ad _s t pa e1

adr2

nrn adr n

NULL ieiri din coad