Sunteți pe pagina 1din 7

1.

Structuri dinamice de date


1.1. Liste înlănţuite
Lista este o structură de date abstractă constituită dintr-o succesiune de elemente de acelaşi
tip. Fiecare element din listă are un succesor (cu excepţia ultimului element al listei) şi un
predecesor (cu excepţia primului element al listei). De exemplu, lista elevilor din catalocul
unei clase.
Cel mai simplu mod (dar nu întotdeauna şi cel mai eficient) este de a memora elementele
listei într–un vector. Succesiunea elementelor este în acest caz implicită (ordinea indicilor
elementelor vectorului).
În cazul în care structura de date este dinamică (suportă frecvent operaţii de inserare,
respectiv ştergere a unor elemente din structură), implementarea prin vectori a listelor nu este
eficientă. O altă implementare posibilă, eficientă pentru structuri de date liniare dinamice, sunt
listele înlănţuite. În funcţie de numărul de legături existente între elementele consecutive ale
listei, lista este simplu sau dublu înlănţuită.

1.2. Liste simplu înlănţuite


O listă simplu înlănţuită este o structură dinamică de date constituită dintr-o succesiune de
elemente omogene (de acelaşi tip) denumite noduri.
Spre deosebire de tablouri, listele nu sunt alocate ca blocuri omogene ce ocupă poziţii
succesive în memorie, ci ca elemente separate de memorie. Fiecare nod al listei conţine două
părţi: o parte de informaţie utilă ( în care sunt memorate informaţiile corespunzătoare
nodului, specifice problemei) şi o parte de legătură (în care este memorată adresa următorului
element din listă). Deci un element al structurii în studiu are forma:
Informaţia utilă Informaţia de legătură
Astfel, această organizare a datelor permite numai acces secvenţial la elementele listei.
Pentru simplitate vom considera, fără a restrânge generalitatea, că informaţiile memorate în
nodurile listei sunt numere întregi.

Lista simplu inlănţuită poate fi reprezentată grafic astfel:

Pointer către începutul listei Pointer către elementul următor


din listă

NULL
1 3 9 2
Prim

Informaţia Pointer NULL pentru


utilă marcarea sfârşitului de listă

Observăm că adresa fiecărui nod din listă este conţinută într-un alt nod, cu o singură excepţie:
primul nod, a cărui adresă nu este conţinută de nici un nod al listei. Prin urmare, pentru a
identifica o listă simplu înlănţuită este suficient să reţinem adresa primului element al listei într-
un pointer separat, denumit Prim.
Exceptând primul nod, într-o listă simplu înlănţuită mai există un element special: ultimul
nod. Acest nod va conţine în partea de legătură pointerul NULL.
Precizăm de asemenea, că în figura de mai sus, legăturile dintre noduri le-am reprezentat prin
săgeţi.
În lmbajul C++, declaraţia unei structuri autoreferite, care reprezintă un nod din listă, va fi:
struct nod
{ <tip_de_data> inf; // Date efective memorate
nod *urm; // Legătura către nodul următor
};
nod *Prim; // Definirea pointerului Prim către un nod
Pentru comoditate în aplicaţiile ulterioare, vom defini tipul de date Lista care este adresa unui
nod din listă: typedef struct nod * Lista; // Lista –alias, pseudonim pentru expresia struct nod
*

Operaţiile fundamentale pe care trebuie să le implementăm sunt:


1. inserarea unui nod în listă;
2. Parcurgerea unei liste;
3. Ştergerea unui nod din listă.

1.2.1. Inserarea unui nod într-o listă simplu înlănţuită


Pentru inserare vom implementa o funcţie Adaug cu trei parametri:
1. Prim –pointer care conţine adresa primului nod din lista în care se va insera; acest
parametru va fi transmis prin referinţă, deoarece în urma inserării începutul listei se poate
modifica;
2. p –pointer care conţine adresa nodului din listă după care se face inserarea; dacă p este
NULL, atunci inserarea noului nod se va face la începutul listei;
3. x –informaţia utilă a nodului care urmează să fie inserat în listă.

Vom aloca dinamic memorie pentru un nou nod, a cărui adresă o vom reţine în variabila
pointer q. În câmpul inf al noului nod vom memora valoarea x (q->inf=x sau (*q).inf=x).
*q
q x
q->inf q->urm
În cazul aplicării
funcţiei Adaug la inserare apar două cazuri distincte. Dacă p=NULL, noul nod va fi inserat la
începutul listei; dacă p!=NULL, inserarea se va face în interiorul listei.>

Exemplul_1 (Adăugarea unui nou nod la începutul listei).


Presupunem că la momentul dat lista este cea de mai jos, iar variabila Prim reţine adresa
primului element:
3 9 2 NULL
Prim

Să ilustrăm pas cu pas inserarea la începutul acestei liste a unui nou nod, cu informaţia x
(==7), a cărei adresă va fi memorată în pointerul q:
1. Se alocă memorie din Heap pentru noul element (Lista q=new nod;).
2. Se iniţializează zona de informaţie utilă a acestuia (q->inf=x).
3. Se iniţializează zona de legătură cu adresa nodului care a fost anterior primul în listă,
adresă alocată în variabila Prim (q->urm=Prim).
4. Se reiniţializează variabila Prim cu adresa noului element alocat (Prim=q;).
Situaţia finală va fi următoarea:
3 9 2 NULL
Prim 4. 3.
q 7
1. 2.
Observaţie. Algoritmul prezentat este corect şi dacă lista iniţial este vidă (Prim==NULL).

Exemplul _2 (Inserarea unui nod în interiorul listei).


Să ilustrăm pas cu pas inserarea, în lista înlănţuită iniţială din exemplul precedent, a unui nou
nod cu informaţia x (==7), după nodul a cărui adresă este memorată în parametrul p (p-
>inf==9).
Deci, înainte de inserare lista are structura:

3 9 2 NULL
Pri p
m

Algoritmul:
1. Se alocă memorie pentru noul element (Lista q=new nod;).
2. Se iniţializează zona de informaţie utilă a acestuia (q->inf=x).
3. Se iniţializează zona de legătură a noului nod cu adresa care se află în zona de legătură a
variabila p (q->urm= p->urm;).
4. Se reiniţializează variabila *p cu adresa noului element alocat (p->urm=q;).
Situaţia finală va fi următoarea:

q 1. 7
*p 4. 2. 3.
3 9 2 NULL
Pri
m
P
void Adaug(Lista & Prim, Lista p, int x) // Funcţia Adaug îmbină ambele cazuri descrise
anterior
{ Lista q=new nod;
q->inf=x;
if(!p) {q->urm=Prim; Prim=q;}
else {q->urm=p->urm; p->urm=q;}

Observaţii
1. Crearea unei liste se realizează prin inserări succesive de elemente.
2. Funcţia de inserare creată (Adaug) este generală, fucţionează pentru toate cazurile posibile:
inserare la începutul listei, inserare în interiorul listei şi inserare la sfârşitul listei.

1.2.2. Parcurgerea unei liste


Această operaţie presupune vizitarea fiecărui nod din listă, în scopul prelucrării informaţiilor
asociate nodurilor. În acest scop vom utiliza un pointer v căruia îi vom atribui iniţial adresa
primului element din listă (v=Prim). Cât timp lista nu s-a terminat (v! =NULL), se vizitează
nodul curent, apoi se trece la nodul următor (v=v->urm). Funcţia următoare realizează
parcurgerea unei liste simplu înlănţuite cu afişarea informaţiilor din noduri:

void Afisare(Lista Prim)


{ Lista v;
for (v=Prim; v; v=v->urm)
cout<<v->inf<<" ";
cout<<endl;
}
Aplicaţie. În programul ce urmează, şirul de numere întregi, citite de la tastatură, se alocă în
memoria HEAP prin intermediul unei liste simplu înlănţuite, creată prin inserarea succesivă a
nodurilor la începutul listei (aplicând funcţia Adaug). Apoi informaţiile din câmpurile inf ale
nodurilor listei create, se afişează pe ecran în ordinea inversă înregistrării lor (aplicând funcţia
Afisare). În continuare, în lista creată iniţial, cu ajutorul funcţiei Insert200 se inserează după
fiecare nod, ce conţine valoarea 100, un nou nod cu valoarea respectivă egală cu 200.
Informaţiile din lista modificată se afişează din nou pe ecran.

//Codul de realizare a aplicaţiei descries mai sus


#include<iostream>
#include<conio.h>
using namespace std;

struct nod // Declararea structurii unui nod al listei


{ int inf;
nod *urm;
};
typedef struct nod *Lista;
Lista Prim, p;
int x;

void Adaug(Lista & Prim, Lista p, int x) // Inserarea unui nou nod
{ Lista q=new nod;
q->inf=x;
if(!p) {q->urm=Prim; Prim=q;}
else {q->urm=p->urm; p->urm=q;}
}

void Afisare(Lista Prim) // Parcurgerea listei cu începutul indicat de pointerul Prim


{ Lista v;
for (v=Prim; v; v=v->urm)
cout<<v->inf<<" ";
cout<<endl;
}
void Insert200(Lista Prim) // Modificarea listei iniţiale, pointată de Prim
{ Lista v;
for (v=Prim; v; v=v->urm)
if (v->inf==100) Adaug( Prim, v, 200);
}

int main ()
{cout<<"Crearea unei liste pana la citirea valorii 0"<<endl;
cout<<"Dati un numar:";cin>>x;
while (x) // Depunerea numerelor citite de la tastatură în lista nou
creată
{
Adaug( Prim, NULL,x);
cout<<"dati un numar:";cin>>x;
}
cout<<endl<<"Lista creata initial:"<<endl;
Afisare(Prim); // Afişarea datelor din lista iniţială
cout<<endl;
Insert200(Prim); // Modificarea lista iniţiale
cout<<"Lista modificata:"<<endl;
Afisare(Prim); // Afişarea datelor din lista modificată
cout<<endl;
getch();
return 0;
}

1.2.3. Ştergerea unui nod dintr-o listă simplu înlănţuită

Pentru ştergere vom implementa o funcţie denumită Elimin cu doi parametri:


1. Prim –pointer care conţine adresa primului nod al listei din care se face ştergerea; acest
parametru va fi transmis prin referinţă, deoarece în urma ştergerii, începutul listei se poate
modifica;
2. p –pointer care conţine adresa nodului (din listă) care precedă nodul ce urmează a fi şters
(se transmite adresa nodului precedent şi nu adresa nodului de şters pentru a putea restaura corect
legăturile în listă); dacă p este NULL deducem că va fi şters primul nod din listă.
Şi la ştergere apar două cazuri distincte. Dacă p==NULL, va fi şters primul nod din listă; dacă
p!=NULL, va fi şters un nod din interiorul listei.

Exemplul_1 (Ştergerea primului nod din listă).


Să ilustrăm pas cu pas ştergerea primului nod al listei din exemplul precedent, adică din lista:

3 9 2 NULL
Prim
1. Memorăm în variabila q adresa nodului ce urmează a fi şters (q=Prim).
2. Adresa primului element din listă se schimbă (Prim=Prim->urm).
3. La sfârşit eliberăm zona alocată nodului ce a fost eliminat din listă (delete q).
2. *q
3 9 2 NULL
Prim 1. 3.
q

Exemplul_2 (Ştergerea unui nod din interiorul listei).


Să ilustrăm pas cu pas ştergerea din lista din exemplul precedent a nodului cu informaţia 9,
adică din lista:
3 9 2 NULL
Prim
p

Algoritmul:
1. Memorăm în variabila q adresa nodului ce urmează a fi şters (q=p->urm;).
2. Modificăm legătura care există de la *p la *q (elementul de şters) şi o transformăm într-o
legătură la nodul care urmează după cel ce va fi şters (p->urm = q->urm).
3. Eliberăm zona de memorie alocată nodului ce a fost eliminat din listă (delete q).

Situaţia finală va fi următoarea:


*p 2. *q
3 9 2 NULL
Prim 1. 3.
p q

void Elimin(Lista & Prim, Lista p)


{ Lista q;
if (p) { q =p->urm;
if (q) { p->urm =q->urm; delete q; }
}
else { q=Prim; if (q) {Prim=Prim->urm; delete q; }
}
Aplicaţie. În programul ce urmează, şirul de numere întregi, citite de la tastatură, se alocă în
memoria HEAP prin intermediul unei liste simplu înlănţuite, creată prin inserarea succesivă a
nodurilor la începutul listei (aplicând funcţia Adaug). Apoi informaţiile din câmpurile inf ale
nodurilor listei create, se afişează pe ecran în ordinea inversă înregistrării lor (aplicând funcţia
Afisare). În continuare, din lista creată iniţial, cu ajutorul funcţiilor DelPar şi Elimin se şterg
din listă toate nodurile, ce conţin valori numerice pare. Informaţiile din liata modificată se
afişează din nou pe ecran.

// Codul de realizare a aplicaţiei formulate mai sus


#include<iostream>
#include<conio.h>
using namespace std;
struct nod // Declararea structurii unui nod al listei
{ int inf;
nod *urm;};
typedef struct nod *Lista;
Lista Prim, p; int x;

void Adaug(Lista & Prim, Lista p, int x) // Inserarea unui nou nod
{ Lista q=new nod;
q->inf=x;
if(!p) {q->urm=Prim; Prim=q;}
else {q->urm=p->urm; p->urm=q;}
}
void Afisare(Lista Prim) // Parcurgerea listei cu începutul indicat de pointerul Prim
{ Lista v;
for (v=Prim; v; v=v->urm) cout<<v->inf<<" ";
cout<<endl;
}
void Elimin(Lista & Prim, Lista p) // Stergerea unui nod din lista
{ Lista q;
if (p) { q =p->urm;
if (q) { p->urm =q->urm; delete q; }
}
else { q=Prim;
if (q) { Prim=Prim->urm; delete q; }
}
}
void DelPar(Lista Prim) // Determinarea numerelor pare din câmpurile inf ale nodurilir
{ Lista v;
v=Prim;
while(v->urm)
{ if (v->urm->inf %2==0 && v!=NULL)
Elimin( Prim, v); else v=v->urm;
}
}
int main ()
{ cout<<"Crearea unei liste pana la citirea valorii 0"<<endl;
cout<<"dati un numar:";cin>>x;
while (x) // Depunerea numerelor citite de la tastatură în lista nou creată
{ Adaug( Prim, NULL, x);
cout<<"dati un numar:";cin>>x; }
cout<<"Lista initiala:"<<endl;
Afisare(Prim); cout<<endl; //Afisarea datelor din lista initiala
DelPar(Prim);
Elimin(Prim, NULL); // Modificatea listei iniţiale
cout<<"Lista modificata:"<<endl;
Afisare(Prim); cout<<endl; //Afisarea datelor din lista modificata
getch();
return 0; }

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