7.structuri Dinamice de Date - Liste

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

Sunteți pe pagina 1din 12

7 Structuri dinamice de date.

Liste

Organizarea de tip list corespunde unei structurri lineare a datelor, n


sensul c la nivelul fiecrei componente exist suficient informaie pentru
identificarea urmtoarei componente a coleciei. Datele unei mulimi structurate
prin intermediul listelor sunt referite de obicei prin termenii de noduri, celule,
componente etc.

7.1 Reprezentarea listelor


Reprezentarea unei liste poate fi realizat static prin intermediul structurii de
date vector. n acest caz ordinea componentelor este dat de ordinea pe domeniul de
valori corespunztor indexrii i, n consecin, urmtoarea component este implicit
specificat. Memorarea unei mulimi de date {d1, d2,, dn} prin intermediul unei
structuri statice poate fi realizat n limbajul C utiliznd un masiv unidimensional.
Principalele dezavantaje ale utilizrii reprezentrii statice rezid din
volumul de calcule necesare efecturii operaiilor de inserare/eliminare de noduri i
din necesitatea pstrrii unei zone de memorie alocat, indiferent de lungimea
efectiv a listei.
Aceste dezavantaje pot fi eliminate prin opiunea de utilizare a structurilor
dinamice. Componentele unei liste dinamice sunt omogene, de tip articol. Fiecare
nod, considerat separat, este o structur eterogen, coninnd o parte de informaie
i cmpuri de legtur care permit identificarea celulelor vecine. Cmpurile de
legtur sunt reprezentate de date de tip referin (adres).
n cazul listelor cu un singur cmp de legtur (simplu nlnuite), valoarea
cmpului indic adresa nodului urmtor, n timp ce n cazul listelor cu dubl
legtur (dublu nlnuite), valorile memorate n cmpurile de legtur sunt
adresele componentelor care preced i, respectiv, urmeaz celulei. n ambele
situaii, cmpul de legtur pentru indicarea celulei urmtoare corespunztor
ultimei componente a listei are valoarea NULL n cazul listelor deschise (lineare)

Structuri dinamice de date. Liste

i respectiv indic adresa primei componente din list n cazul listelor nchise
(circulare).
Declararea tipurilor de date C pentru definirea structurilor de liste dinamice
simplu i respectiv dublu nlnuite este:
a)

List simplu nlnuit

typedef struct nod{


tip_informatie inf;
struct nod *leg;
} list, *lista;

b)

List dublu nlnuit

typedef struct nod{


tip_informatie inf;
struct nod *ls, *ld;
} list, *lista;

unde tip_informatie este numele tipului de date C utilizat pentru memorarea


fiecrei date din mulimea{d1, d2,, dn}.
n cele ce urmeaz vom considera c tip_informatie este tipul C int.

7.2 Operaii primitive asupra listelor


Accesul la informaia stocat ntr-o variabil de tip list revine la
efectuarea urmtoarelor operaii primitive: regsirea nodului (dac exist) care
corespunde unei chei date (condiie impus asupra valorii cmpului de informaie),
inserarea unei noi componente n list, eliminarea componentei (componentelor) cu
proprietatea c valorile cmpurilor de informaie satisfac o anumit cerin i
nlocuirea cmpului de informaie corespunztor unei componente printr-o
informaie dat (modificat).
Accesarea componentelor unei liste reprezentat printr-o structur static
poate fi realizat att secvenial, ct i direct, utiliznd valorile indicelui considerat
pentru indexare, n timp ce accesarea componentelor unei liste dinamice se
realizeaz de regul numai secvenial, ncepnd cu prima component i
continund cu urmtoarele, pe baza valorilor cmpurilor de legtur.
Convenional, numim cap al listei dinamice pointerul a crui valoare este adresa
primei componente a listei. n continuare ne vom referi exclusiv la liste dinamice, studiul
listelor reprezentate prin intermediul vectorilor fiind propus cititorului.
1. Parcurgerea datelor memorate ntr-o list
Funcia C parc implementeaz parcurgerea unei liste dinamice n varianta
simplu nlnuit i n cazul listelor dublu nlnuite. Se presupune c declaraiile
de tip pentru definirea structurilor de liste menionate anterior sunt globale, relativ
la procedurile descrise n continuare.
a) Lista reprezentat prin structur dinamic simplu nlnuit
void parc(lista cap)
{ if(cap)
{ printf("%i ",cap->inf);
parc(cap->leg);
}
}

Programarea calculatoarelor

b) Lista reprezentat prin structur dinamic dublu nlnuit


void parc(lista cap)
{ if(cap)
{ printf("%i ",cap->inf);
parc(cap->ls);
}
}

2. Regsirea unei date ntr-o colecie memorat ntr-o list


Funcia C cauta calculeaz adresa nodului n care este gsit elementul
cutat. Dac valoarea cutat nu se regsete printre elementele listei, funcia
returneaz valoarea NULL.
a) Lista reprezentat prin structur dinamic simplu nlnuit
lista cauta(lista cap,int info)
{ if(cap==NULL)return NULL;
else if(cap->inf==info) return cap;
else return cauta(cap->leg,info);
}

b) Lista reprezentat prin structur dinamic dublu nlnuit


lista cauta(lista cap,int info)
{ if(cap==NULL)return NULL;
else if(cap->inf==info) return cap;
else return cauta(cap->ls,info);
}

3. Inserarea unei date ntr-o list


Includerea unei noi componente ntr-o list poate fi realizat, n funcie de
cerinele problemei particulare, la nceputul listei, dup ultima component din
list, naintea/dup o component cu proprietatea c valoarea cmpului de
informaie ndeplinete o anumit condiie.
Deoarece prin inserarea unei componente se poate ajunge la depirea
spaiului disponibil de memorie, este necesar verificarea n prealabil dac este
posibil inserarea sau nu (dac se poate aloca spaiu de memorie pentru
componenta de inserat). n continuare ne vom referi la liste dinamice simplu
nlnuite. Operaiile de inserare n cazul listelor dinamice dublu nlnuite pot fi
realizate similar cazului listelor simplu nlnuite, cu specificarea ambelor cmpuri
de adres ale nodurilor.
Pentru exemplificarea operaiei de inserare sunt prezentate funciile de
inserare la nceputul listei, inserare dup ultimul element al listei i inserarea unei
celule dup un nod cu informaie dat.
Inserarea la nceputul listei
Funcia C inserare_la_inceput returneaz valoarea 1 dac adugarea unui
nou element este posibil (spaiul de memorie este suficient pentru o nou alocare),
altfel returneaz 0. n cazul n care inserarea este posibil, prin apelul funcii este
realizat adugarea unui nou nod la nceputul listei.

Structuri dinamice de date. Liste


int inserare_la_inceput(lista *cap,int info)
{ lista nou;
if(nou=(lista)malloc(sizeof(list)))
{ nou->inf=info;
nou->leg=*cap;
*cap=nou;
return 1;
}
return 0;
}

Inserarea dup ultima component a unei liste


Funcia C inserare_la_sfarsit returneaz 1 dac i numai dac este posibil
o inserare, altfel calculeaz 0. Pentru inserarea unui nou nod n list dup ultima
celul este necesar calculul ultimului nod al listei, notat p.
int inserare_la_sfarsit(lista *cap,int info)
{ lista nou;
if(nou=(lista)malloc(sizeof(list)))
{ nou->leg=NULL;nou->inf=info;
if(cap==NULL)*cap=nou;
else
{ for(lista p=*cap;p->leg;p=p->leg);
p->leg=nou;
}
return 1;
}
return 0;
}

Inserarea unei informaii dup o celul cu informaie cunoscut


Inserarea unui nou nod ntr-o list identificat prin variabila cap dup o
celul p cu informaie cunoscut, infod, poate fi realizat astfel. Este apelat funcia
de cutare cauta, care calculeaz nodul p cu proprietatea c informaia memorat n
p este infodat. Dac p este adresa vid sau dac spaiul de memorie disponibil nu
este suficient, inserarea nu poate fi realizat. n caz contrar, este inserat un nou nod
ntre celulele p i p->leg.
int inserare_dupa_informatie(lista cap,int info,int infod)
{
lista nou,p;
if(nou=(lista)malloc(sizeof(list)))
if(p=cauta(cap,infod)){
nou->inf=info;
nou->leg=p->leg;
p->leg=nou;
return 1;
}
return 0;
}

Programarea calculatoarelor

4. Eliminarea unei date dintr-o list


Modificarea coninutului unei liste prin eliminarea uneia sau mai multor
componente poate fi descris secvenial, astfel nct este suficient s dispunem de o
procedur care realizeaz eliminarea unei singure componente.
Criteriile de eliminare pot fi formulate diferit, cele mai uzuale fiind: prima
component, ultima component, prima component care ndeplinete o anumit
condiie, respectiv componenta care precede/urmeaz primei componente care
ndeplinete o condiie dat.
n aceste cazuri este necesar verificarea existenei n lista considerat a
componentei ce trebuie eliminat. Verificarea asigur i testarea faptului c lista
prelucrat este vid sau nu.
Informaia i ataat nodului eliminat din list reprezint dat de ieire
pentru orice modul de eliminare, n cazul n care i nu este cunoscut naintea
eliminrii (de exemplu, atunci cnd este solicitat eliminarea unei celule care
conine o informaie dat) .
Eliminarea unui nod p poate fi realizat logic sau fizic. Eliminarea logic a
celulei p este efectuat excluznd p din lista dinamic prin setarea legturii nodului
care precede p pe adresa succesorului lui p, dac p nu este adresa primului element
al listei, cap, respectiv prin atribuirea adresei primului element al listei cu
cap->leg, n caz contrar.
Eliminarea cu tergere fizic unui nod p presupune redenumirea acelui nod
n scopul eliberrii memoriei ocupate de p i efectuarea operaiilor descrise n
cadrul procesului de eliminare logic.
n continuare sunt prezentate urmtoarele tipuri de eliminri, cu tergere
fizic.
Eliminarea primei componente a unei liste
Funcia C elimina_de_la_inceput returneaz 1 dac lista nu este vid, deci
eliminarea primului nod este posibil, altfel returneaz 0. Dac lista conine mcar
un nod, este eliminat prima celul.
Int elimina_de_la_inceput(lista *cap,int *info)
{ if(*cap)
{ lista aux=*cap;
*info=aux->inf;
*cap=(*cap)->leg;
free(aux);
return 1;
}
return 0;
}

Eliminarea ultimei componente a unei liste


Similar operaiei de inserare a unui nod dup ultima celul a unei liste,
eliminarea ultimului nod presupune determinarea acelei celule p cu proprietatea c
p->leg este NULL. Funcia C elimina_ultim returneaz 1 dac lista nu este vid,

Structuri dinamice de date. Liste

caz n care este eliminat cu tergere ultimul nod al listei. Dac lista este vid,
funcia calculeaz valoarea 0.
Int elimina_ultim(lista *cap,int *info)
{ if (*cap)
{ if((*cap)->leg)
{ for(lista p=*cap;p->leg->leg;p=p->leg);
*info=p->leg->inf;
free(p->leg);
p->leg=NULL;
}
else
{ *info=(*cap)->inf;
free(*cap);
*cap=NULL;
}
return 1;
}
return 0;
}

Eliminarea primei celule a unei liste care are informaia egal


cu o informaie dat
Pentru realizarea acestei operaii se poate proceda astfel. Sunt calculate aux
i p, unde aux este nodul care precede celulei cu informaie dat n lista din care
este efectuat eliminarea (funcia C cautaprecedent) i p=aux->leg.. Dac p este
NULL, atunci eliminarea este imposibil. Dac aux este NULL, atunci eliminarea
revine la extragerea cu tergere a primului nod din list, altfel este eliminat celula
p, succesoare a lui aux n list. Funcia C elimina_informatie implementeaz
operaia de eliminare a unui nod cu informaie dat, info, din lista identificat prin
parametrul cap.
lista cautaprecedent(lista cap,int info, lista *aux)
{ lista p;
if(cap==NULL)return NULL;
else { for(p=NULL,*aux=cap;(*aux)&&
((*aux)->inf-info);p=*aux,*aux=(*aux)->leg);
if((*aux)==NULL)return NULL;
return p;
}
}
int elimina_informatie(lista *cap,int info)
{ lista aux,p;
p=cautaprecedent(*cap,info,&aux);
if(aux==*cap)
{ *cap=(*cap)->leg;
free(aux);
return 1;
}
else
if(p)
{ p->leg=aux->leg;

Programarea calculatoarelor
free(aux);
return 1;
}
return 0;
}

Eliminarea nodului care succede primei componente al crei cmp


de informaie este cunoscut
Funcia C elimina_dupa_informatie returneaz valoarea 1 dac eliminarea
este posibil, altfel calculeaz valoarea 0. n situaia n care informaia infodat a
fost gsit n cmpul corespunztor nodului nodul p (p nu este NULL) i p are
succesor n list, este realizat eliminarea nodului p->leg.
int elimin_dupa_informatie(lista cap,int *info, int
infodat)
{ lista aux,p;
p=cauta(cap,infodat);
if((p)&&(p->leg))
{ aux=p->leg;
p->leg=aux->leg;
*info=aux->inf;
free(aux);
return 1;
}
return 0;
}

7.3 Liste circulare


n anumite situaii este preferabil renunarea la structura de tip linear a
listelor i utilizarea unei legturi de la ultima component ctre capul listei,
rezultnd structura de list circular.
Principalul avantaj al utilizrii acestui tip de structur rezid din posibilitatea
de accesare oricrui alt element al listei pornind din orice element. Dac nodul
cutat este situat dup nodul curent, este iniiat un proces de cutare similar listelor
lineare. n caz contrar, nodul poate fi accesat prin parcurgerea listei de la primul
su element, care, n procesul de cutare, este atins dup parcurgerea n ntregime a
listei, ncepnd de la nodul curent.
n continuare sunt prezentate funciile C pentru realizarea unor operaii de
baz n lucrul cu liste circulare.
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
typedef struct nod
{ int inf;
struct nod *leg;
} list, *lista;

Structuri dinamice de date. Liste


int inserare_la_inceput(lista
int stergere_la_inceput(lista
int inserare_la_sfarsit(lista
int stergere_la_sfarsit(lista
void parc(lista);
lista cauta(lista,int);

*,int);
*,int *);
*,int);
*,int *);

void main()
{
clrscr();
int n,info;
lista cap=NULL;
printf("Numarul de noduri:");
scanf("%i",&n);
printf("Introduceti informatiile\n");
for(int i=0;i<n;i++){
scanf("%i",&info);
if(inserare_la_inceput(&cap,info));
else
{printf("\n Spatiu insuficient \n");
return;
}
}
printf("\nLista rezultata\n");
parc(cap);
printf("\n\nLista dupa extragerea primului
element:\n");
if(stergere_la_inceput(&cap,&info)) parc(cap);
else printf("\nEroare: lista vida");
printf("\n\nInformatia nodului de introdus la
sfarsit:");
scanf("%i",&info);
if(inserare_la_sfarsit(&cap,info)){
printf("Lista rezultata\n");
parc(cap);
}
else
printf("\n Spatiu insuficient \n");
printf("\n\nLista dupa extragerea ultimului
element:");
if(stergere_la_sfarsit(&cap,&info)){
printf("\nInformatia extrasa %i\nLista
rezultata:",info);
parc(cap);
}
else printf("\nEroare:Lista vida");
getch();
}
void parc(lista cap)
{
lista p=cap;
if(cap){
printf("%i ",cap->inf);
for(p=p->leg;p-cap;p=p->leg)

Programarea calculatoarelor
printf("%i ",p->inf);
}
else printf("\nLista vida");
}
lista cauta(lista cap,int info)
{
if(cap==NULL)return NULL;
if(cap->inf==info) return cap;
for(lista p=cap->leg;p!=cap;p=p->leg)
if(p->inf==info) return p;
return NULL;}
int inserare_la_inceput(lista *cap,int info)
{
lista nou,ultim;
if(nou=(lista)malloc(sizeof(list))){
nou->inf=info;
nou->leg=*cap;
if(*cap){
for(ultim=*cap;ultim->leg!=(*cap);ultim=ultim->leg);
ultim->leg=nou;
}
else nou->leg=nou;
*cap=nou;
return 1;
}
return 0;
}
int stergere_la_inceput(lista *cap,int *info)
{
if(*cap){
lista aux=*cap;
*info=aux->inf;
for(lista ultim=*cap;
ultim->leg!=(*cap);ultim=ultim->leg);
if(ultim==(*cap)) *cap=NULL;
else{
*cap=(*cap)->leg;
ultim->leg=*cap;
}
free(aux);
return 1;
}
return 0;
}
int inserare_la_sfarsit(lista *cap,int info)
{
lista nou,ultim;
if(nou=(lista)malloc(sizeof(list))){
nou->leg=*cap;nou->inf=info;
if(*cap==NULL){
*cap=nou;
(*cap)->leg=*cap;
}

Structuri dinamice de date. Liste


else{
for(ultim=*cap;ultim->leg!=(*cap); ultim=ultim->leg);
ultim->leg=nou;
}
return 1;
}
return 0;
}
int stergere_la_sfarsit(lista *cap,int *info)
{
if (*cap){
if((*cap)->leg!=(*cap)){
for(lista pultim=*cap;
pultim->leg->leg!=(*cap);pultim=pultim->leg);
*info=pultim->leg->inf;
free(pultim->leg);
pultim->leg=(*cap);
}
else{
*info=(*cap)->inf;
free(*cap);
*cap=NULL;
}
return 1;
}
return 0;
}

7.4 Stive i cozi


Aa cum a rezultat din subcapitolele precedente, operaiile de inserare i
eliminare sunt permise la oricare dintre componentele unei liste. O serie de aplicaii
pot fi modelate utiliznd liste lineare n care introducerea i respectiv eliminarea
informaiilor este permis numai la capete. n acest scop au fost introduse tipurile
de list stiv i coad prin impunerea unui tip de organizare a aplicrii operaiilor
de inserare i eliminare.
7.4.1 Stiva
Se numete stiv o list organizat astfel nct operaiile de inserare i
eliminare sunt permise numai la prima component. Acest mod de organizare
corespunde unei gestiuni LIFO (Last In First Out) a informaiei stocate.
Operaiile de baz efectuate asupra unei stive pot fi realizate similar
cazului listelor dinamice lineare, innd cont c inserarea/extragerea unui element
sunt posibile numai n prima poziie (vezi modulul de inserare la nceputul unei
liste i respectiv funcia de extragere a primului nod dintr-o list, prezentate
n 7.2).

Programarea calculatoarelor

7.4.2 Coada
Se numete coad o list organizat astfel nct operaia de inserare este
permis la ultima component, iar operaia de eliminare este permis numai la
prima component. Acest mod de organizare corespunde unei gestiuni FIFO (First
In First Out) a informaiei stocate.
Implementarea unei liste de tip coad poate fi efectuat att printr-o
structur static (masiv unidimensional), ct i printr-o structur dinamic de tip
list. Pentru optimizarea operaiilor de inserare/extragere, n cazul implementrii
cozilor prin structuri dinamice lineare, este necesar utilizarea a dou informaii:
adresa primei componente i adresa ultimei componente. Aceste informaii pot fi
meninute explicit prin utilizarea a doi pointeri sau prin utilizarea unui pointer i a
unei structuri de list circular.
O variant alternativ de implementare a unei liste de tip coad dinamic
este obinut prin considerarea unei liste circulare, cu memorarea adresei ultimului
element.
n continuare sunt prezentate operaiile de inserare i extragere a unei
informaii dintr-o list liniar de tip coad.
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
typedef struct nod{
int inf;
struct nod *leg;
} list, *lista;
int inserare(lista *,lista *,int);
int extragere(lista *,lista *,int *);
void parc(lista);
void main()
{
clrscr();
int n,info;
lista cap=NULL,ultim=NULL;
printf("Numarul de noduri:");
scanf("%i",&n);
printf("Introduceti informatiile\n");
for(int i=0;i<n;i++){
scanf("%i",&info);
if(inserare(&cap,&ultim,info));
else
{printf("\n Spatiu insuficient \n");
return;
}
}

Structuri dinamice de date. Liste


printf("\nCoada rezultata\n");
parc(cap);
printf("\n\nCoada dupa o extragere:\n");
if(extragere(&cap,&ultim,&info)) parc(cap);
else printf("\nEroare: Coada vida");
getch();
}
void parc(lista cap)
{
if(cap){
printf("%i ",cap->inf);
parc(cap->leg);
}
}
int extragere(lista *cap,lista *ultim,int *info)
{
if(*cap){
lista aux=*cap;
*info=aux->inf;
if((*ultim)==(*cap)) *cap=*ultim=NULL;
else *cap=(*cap)->leg;
free(aux);
return 1;
}
return 0;
}
int inserare(lista *cap,lista *ultim,int info)
{
lista nou;
if(nou=(lista)malloc(sizeof(list))){
nou->inf=info;nou->leg=NULL;
if(*cap==NULL) *cap=*ultim=nou;
else{
(*ultim)->leg=nou;
(*ultim)=nou;
}
return 1;
}

return 0;}

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