Sunteți pe pagina 1din 27

1

Colegiul National Carol I


Craiova



Elev : Profesor coordonator :
Tudosie Remus Octavian Marciana Procop

2




1. Liste simplu inlantuite 3
1.1. Structura listei 4
1.2. Operatii cu liste 5
2. Liste Dublu Inlantuite 11
2.1. Structura listei 11
2.2. Operatii cu liste duble 12
3.Lista circulara simplu-nlantuita 19
4. Lista circulara dublu-nlantuita 20
5. Structura de tip Stiva 20
6. Bibliografie 26




3

Liste simplu inlantuite


Definitie.
O list liniar este o colecie de n0 noduri, x
1
,x
2
,...,x
n
aflate ntr-
o relaie de ordine.Astfel x
1
este primul nod al listei,x
2
este al doilea
nod al listei,...,x
n
este ultimul nod al listei.Operaiile permise sunt:
accesul la oricare nod al listei n scopul citirii sau modificrii
informaiei coninute de acesta;
adugarea unui nod,indiferent de poziia pe care o ocup n list;
tergerea unui nod,indiferent de poziia pe care o ocup n list;
schimbarea poziiei un nod n cadrul listei

Listele simplu inlantuite sunt structuri de date dinamice
omogene. Spre deosebire de masive, listele nu sunt alocate ca blocuri
omogene de memorie, ci ca elemente separate de memorie. Fiecare nod
al listei contine, in afara ce informatia utila, adresa urmatorului
element. Aceasta organizare permite numai acces secvential la
elementele listei.
Pentru accesarea listei trebuie cunoscuta adresa primului element
(numita capul listei); elementele urmatoare sunt accesate parcurgand
lista.
Lista simplu inlantuita poate fi reprezentata grafic astfel:




4

1. Structura listei
Pentru a asigura un grad mai mare de generalitate listei a fost creat un
alias pentru datele utile (in cazul nostru un intreg):
// Datele asociate unui
// element dintr-o lista
typedef int Date;
In cazul in care se doreste memorarea unui alt tip de date, trebuie
schimbata doar declaratia aliasului Date.
Pentru memorarea listei se foloseste o structura autoreferita. Acesta
structura va avea forma:
// Structura unui element
// dintr-o lista simplu inlantuita
struct Element
{
// datele efective memorate
Date valoare;
// legatura catre nodul urmator
Element* urmator;
};
In cazul in care elemenul este ultimul din lista, pointerul urmator va
avea valoarea NULL.
Declararea listei se face sub forma:
// declarare lista vida
Element* cap = NULL;
5


Principalele operatii cu liste sunt:
1.Parcurgerea si afisarea listei
Lista este parcursa pornind de la pointerul spre primul element si
avansand folosind pointerii din structura pana la sfarsitul listei (pointer
NULL).
// Parcurgere si afisare lista simpla
void Afisare(Element* cap)
{
// cat timp mai avem elemente
// in lista
while (cap != NULL)
{
// afiseaza elementul curent
cout << cap->valoare << endl;
// avanseaza la elementul urmator
cap = cap->urmator;
}
}
2.Inserare elemente
Inserarea unui element se poate face la inceputul sau la sfarsitul
listei.
a) Inserare la inceput
Acesta este cazul cel mai simplu: trebuie doar alocat elementul, legat de
primul element din lista si repozitionarea capului listei:
// Inserare element la inceputul unei
// liste simplu inlantuite
void InserareInceput(Element* &cap, Date val)
{
// Alocare nod si initializare valoare
Element *elem = new Element;
6

elem->valoare = val;
// legare nod in lista
elem->urmator = cap;
// mutarea capului listei
cap = elem;
}
b) Inserare la sfarsitul listei
In acest caz trebuie intai parcursa lista si dupa aceea adaugat
elementul si legat de restul listei. De asemenea, trebuie avut in vedere
cazul in care lista este vida.
// Inserare element la sfarsitul unei
// liste simplu inlantuite
void InserareSfarsit(Element* &cap, Date val)
{
// Alocare si initializare nod
Element *elem = new Element;
elem->valoare = val;
elem->urmator = NULL;
// daca avem lista vida
if (cap == NULL)
// doar modificam capul listei
cap = elem;
else
{
// parcurgem lista pana la ultimul nod
Element *nod = cap;
while (nod->urmator != NULL)
nod = nod->urmator;
// adaugam elementul nou in lista
nod->urmator = elem;
}
}

c) Inserare dupa un element dat

7

void InserareInterior(Element* &cap, Element* p, Date val)
{
// Alocare si initializare nod
Element *elem = new Element;
elem->valoare = val;
elem->urmator = NULL;
// lista vida
if (cap == NULL)
{
cap = elem;
return;
}
// inserare la inceputul listei
if (cap == p)
{
elem->urmator = cap;
cap = elem;
return;
}
// inserare in interior
elem->urmator = p->urmator;
p->urmator = elem;
}

3.Cautare element

Cautarea unui element dintr-o lista presupune parcurgerea listei pentru
identificarea nodului in functie de un criteriu. Cele mai uzuale criterii
sunt cele legate de pozitia in cadrul listei si de informatiile utile
continute de nod. Rezultatul operatiei este adresa primului element gasit
sau NULL.

a) Cautarea dupa pozitie
8

Se avanseaza pointerul cu numarul de pozitii specificat:
// Cautare element dupa pozitie
Element* CautarePozitie(Element* cap, int pozitie)
{
int i = 0; // pozitia curenta
// parcurge lista pana la
// pozitia ceruta sau pana la
// sfarsitul listei
while (cap != NULL && i < pozitie)
{
cap = cap->urmator;
i++;
}
// daca lista contine elementul
if (i == pozitie)
return cap;
else
return NULL;
}
b) Cautarea dupa valoare

Se parcurge lista pana la epuizarea acesteia sau identificarea
elementului:
// Cautare element dupa valoare
Element* CautareValoare(Element* cap, Date val)
{
// parcurge lista pana la gasirea
// elementului sau epuizarea listei
while (cap != NULL && cap->valoare != val)
cap = cap->urmator;
return cap;
}


9

Stergere element

a) Stergerea unui element din interiorul listei (diferit de capul listei)
In acest caz avem nevoie de adresa predecesorului elementului de sters.
Se modifica legaturile in sensul scurtcircuitarii elementului de sters,
dupa care se elibereaza memoria corespunzatoare elementului de sters:

// sterge un element din interiorul listei
// primind ca parametru adresa predecesorului
void StergereElementInterior(Element* predecesor)
{
// salvam referinta la elementul de sters
Element* deSters = predecesor->urmator;
// scurcircuitam elementul
predecesor->urmator = predecesor->urmator->urmator;
// si il stergem
delete deSters; }


b) Stergerea unui element de pe o anumita pozitie
Daca elementul este primul din lista, atunci se modifica capul listei,
altfel se cauta elemental si se sterge folosind functia definite anterior:
void StergerePozitie(Element* &cap, int pozitie)
{
// daca lista e vida nu facem nimic
if (cap == NULL)
return;
// daca este primul element, atunci
// il stergem si mutam capul
if (pozitie == 0)
{
Element* deSters = cap;
cap = cap->urmator;
delete deSters;
10

return;
}
// daca este in interor, atunci folosim
// functia de stergere
Element* predecesor = CautarePozitie(cap, pozitie-1);
StergereElementInterior(predecesor);
}
c) stergerea dupa o valoare

Se cauta predecesorul elementului si se foloseste functia de stergere
element:
void StergereValoare(Element* &cap, Date val)
{
// daca lista e vida nu facem nimic
if (cap == NULL)
return;
// daca este primul element, atunci
// il stergem si mutam capul
if (cap->valoare == val)
{
Element* deSters = cap;
cap = cap->urmator;
delete deSters;
return;
}
// cautam predecesorul
Element* elem = cap;
while (elem->urmator != NULL && elem->urmator->valoare != val)
elem = elem->urmator;
// daca a fost gasit, atunci il stergem
if (elem->urmator != NULL)
StergereElementInterior(elem);
}

11


Listele dublu inlantuite sunt structuri de date dinamice omogene. Ele
au aceleasi caracteristici de baza ca si listele simplu inlantuite.
Diferenta fata de acestea consta in faptul ca, pentru fiecare nod, se
retine si adresa elementului anterior, ceea ce permite traversarea listei
in ambele directii.
Lista dublu inlantuita poate fi reprezentata grafic astfel:


1. Structura listei
Structura folosita este similara cu cea de la liste simple.
Pentru a asigura un grad mai mare de generalitate listei a fost creat un
alias pentru datele utile (in cazul nostru un intreg):
// Datele asociate unui
// element dintr-o lista
typedef int Date;

In cazul in care se doreste memorarea unui alt tip de date, trebuie
schimbata doar declaratia aliasului Date.
Pentru memorarea listei se foloseste o structura autoreferita. Acesta
structura va avea forma:
// Structura unui element
// dintr-o lista dublu inlantuita
struct Element
{
// datele efective memorate
12

Date valoare;
// legatura catre nodul urmator
Element* urmator, * anterior;
};

In cazul in care elemenul este ultimul din lista, pointerul urmator va
avea valoarea NULL. Pentru primul element, pointerul anterior va avea
valoarea NULL.
Declararea listei se face sub forma:
// declarare lista vida
Element* cap = NULL;
In cazul in care se doreste crearea unei liste circulare, pointerul urmator
al ultimului element va referi primul element al listei, iar pointerul
anterior al primului element va referi ultimul element.
2. Operatii cu liste duble
Principalele operatii cu liste sunt:
1.Parcurgere si afisare lista
Lista este parcursa pornind de la pointerul spre primul element si
avansand folosind pointerii din structura pana la sfarsitul listei (pointer
NULL).
// Parcurgere si afisare lista dubla
void Afisare(Element* cap)
{
// cat timp mai avem elemente
13

// in lista
while (cap != NULL)
{
// afiseaza elementul curent
cout << cap->valoare << endl;
// avanseaza la elementul urmator
cap = cap->urmator;
}
}
Pentru parcurgerea inversa a listei se va porni cu un pointer catre
ultimul element al listei, iar avansarea se va face folosind secventa cap
= cap->anterior; .
2.Inserare element
Inserarea unui element se poate face la inceputul sau la sfarsitul listei.

a) Inserare la inceput

Acesta este cazul cel mai simplu: trebuie doar alocat elementul,
legat de primul element din lista si repozitionarea capului listei:
// Inserare element la inceputul unei
// liste dublu inlantuite
void InserareInceput(Element* &cap, Date val)
{
// Alocare nod si initializare valoare
Element *elem = new Element;
elem->valoare = val;
elem->anterior = NULL;
// legare nod in lista
14

elem->urmator = cap;
if (cap != NULL)
cap->anterior = elem;
// mutarea capului listei
cap = elem;
}

b) Inserare la sfarsitul listei
In acest caz trebuie intai parcursa lista si dupa aceea adaugat elementul
si legat de restul listei. De asemenea, trebuie avut in vedere cazul in care
lista este vida.
// Inserare element la sfarsitul unei
// liste dublu inlantuite
void InserareSfarsit(Element* &cap, Date val)
{
// Alocare si initializare nod
Element *elem = new Element;
elem->valoare = val;
elem->urmator = NULL;
elem->anterior = NULL;
// daca avem lista vida
if (cap == NULL)
// doar modificam capul listei
cap = elem;
else
{
// parcurgem lista pana la ultimul nod
Element *nod = cap;
while (nod->urmator != NULL)
nod = nod->urmator;
// adaugam elementul nou in lista
nod->urmator = elem;
elem->anterior = nod;
}
}
15

b) Inserare dupa un element dat

void InserareInterior(Element* &cap, Element* p, Date val)
{
// Alocare si initializare nod
Element *elem = new Element;
elem->valoare = val;
elem->urmator = NULL;
elem->anterior = NULL;
// lista vida
if (cap == NULL)
{
cap = elem;
return;

}
// inserare la inceputul listei
if (cap == p)
{
elem->urmator = cap;
cap->anterior = elem;
cap = elem;
return;
}
// inserare in interior
elem->urmator = p->urmator;
elem->anterior = p;
p->urmator->anterior = elem;
p->urmator = elem;
}
3.Cautare element
Cautarea unui element dintr-o lista presupune parcurgerea listei pentru
identificarea nodului in functie de un criteriu. Cele mai uzuale criterii
sunt cele legate de pozitia in cadrul listei si de informatiile utile
16

continute de nod. Rezultatul operatiei este adresa primului element gasit
sau NULL.

a) Cautarea dupa pozitie

Se avanseaza pointerul cu numarul de pozitii specificat:
// Cautare element dupa pozitie
Element* CautarePozitie(Element* cap, int pozitie)
{
int i = 0; // pozitia curenta
// parcurge lista pana la
// pozitia ceruta sau pana la
// sfarsitul listei
while (cap != NULL && i < pozitie)
{
cap = cap->urmator;
i++;
}
// daca lista contine elementul
if (i == pozitie)
return cap;
else
return NULL;
}
b) Cautarea dupa valoare

Se parcurge lista pana la epuizarea acesteia sau identificarea
elementului:
17

// Cautare element dupa valoare
Element* CautareValoare(Element* cap, Date val)
{
// parcurge lista pana la gasirea
// elementului sau epuizarea listei
while (cap != NULL && cap->valoare != val)
cap = cap->urmator;
return cap;
}
4.Stergere element
a) Stergerea unui element din interiorul listei (diferit de capul listei)

// sterge un element din interiorul listei
// primind ca parametru adresa elementului
void StergereElementInterior(Element* elem)
{
// scurcircuitam elementul
elem->anterior->urmator = elem->urmator;
elem->urmator->anterior = elem->anterior;
// si il stergem
delete elem;
}
b)Stergerea unui element de pe o anumita pozitie

Daca elementul este primul din lista, atunci se modifica capul listei,
altfel se cauta elementul si se sterge folosind functia definita anterior:
void StergerePozitie(Element* &cap, int pozitie)
{
// daca lista e vida nu facem nimic
if (cap == NULL)
return;
// daca este primul element, atunci
// il stergem si mutam capul
if (pozitie == 0)
18

{
Element* deSters = cap;
cap = cap->urmator;
cap->anterior = NULL;
delete deSters;
return;
}
// daca este in interor, atunci folosim
// functia de stergere
Element* elem = CautarePozitie(cap, pozitie);

StergereElementInterior(elem);
}
c) stergerea dupa o valoare

Se cauta elementul si se foloseste functia de stergere element:
void StergereValoare(Element* &cap, Date val)
{
// daca lista e vida nu facem nimic
if (cap == NULL)
return;
// daca este primul element, atunci
// il stergem si mutam capul
if (cap->valoare == val)
{
Element* deSters = cap;
cap = cap->urmator;
cap->anterior = NULL;
delete deSters;
return;
}
// cautam elementul
Element* elem = CautareValoare(cap, val);
// daca a fost gasit, atunci il stergem
if (elem->urmator != NULL)
19

StergereElementInterior(elem);
}

Primul i ultimul nod sunt legate mpreun. Pentru a parcurge o list
circular nlnuit se ncepe de la oricare nod i se urmrete lista prin
aceasta direcie aleas pn cnd se ajunge la nodul de unde s-a pornit
parcurgerea (lucru valabil i pentru listele circulare dublu-nlnuite).
Fiecare nod are o singur legatur, similar cu listele liniare simplu-
nlnuite, ns, diferena const n legtura aflat dup ultimul nod ce l
leag pe acesta de primul nod. La fel ca i n listele liniare simplu-
nlnuite, nodurile noi pot fi inserate eficient numai dac acestea se afl
dup un nod care are referine la acesta. Din acest motiv, este necesar s
se menin numai o referin ctre ultimul element dintr-o list circular
simplu-nlnuita, cci aceasta permite o inserie rapid la nodul de
nceput al listei, i de asemenea, permite accesul la primul nod prin
legatura dintre acesta i ultimul nod.




20


Fiecare nod are dou legturi, asemanator ca i la listele liniare simplu-
nlnuite, ns diferena este c listele circulare dublu-nlnuite legatura
dinaintea primului nod l leag pe acesta de ultimul nod, i legatura de
dinaintea ultimului nod catre l leag pe acesta de primul nod. La fel ca
i la listele liniare dublu-nlnuite, operaiile de inserie i tergere pot fi
fcute n orice punct din list, cu acces la oricare nod apropiat.


21


Aceasta structura de date este un caz particular de lista care functioneaza pe
principiul LIFO (last in first out, ultimul intrat este primul servit, puteti sa va ganditi
la o stiva de materiale pentru care un nou material se va adauga intotdeauna deasupra
si se va extrage tot de deasupra). Prin urmare principalele prelucrari care se refera la
aceasta structura de date vor fi:
- creare stiva
- listare stiva (parcurgere in ordine inversa creerii)
- adaugare la sfarsit ( peste varful stivei, operatie numita push ( ) )
- stergere element din varful stivei (operatie numita pop( ) )
- prelucrarea varfului stivei

Specific acestei structuri de date este faptul ca prelucrarile se fac intotdeauna la
elementul de la acelasi capat, element pe care il vom numi varf.
Functia push( ) , creaza stiva cand aceasta este vida sau adauga un nou element in
caz contrar.
Functia pop( ), elimina elementul din varful stivei.

Fie o stiva de numere intregi pentru care elementele sunt adaugate in ordinea:
10,20,30,40

La primul pas se creaza stiva caz in care primul element creat va fi varful stivei:


vf

Apoi se adauga prin operatia push ( ) un nou element, 20:








vf
vf






10






22

In continuare se adauga 30 care va deveni noul varf:
vf





La sfarsit se adauga 40:




vf





Afisarea se va face in ordine inversa generarii si prin urmare se va afisa: 40,30,20, 10.










23


Pentru stergere, prin operatia pop( ) se va sterge elementul din varful stivei, 40, dupa
care noul varf va deveni precedentul sau, 30:



pop( ) vf


rezulta: vf



Pentru a prelucra aceasta structura de date va fi suficient un singur pointer, pentru
varf. Iata o solutie de implementare:

//prelucrari stiva : (LIFO)
#include<iostream.h>



24

#include<conio.h>

struct nod{int info;
nod *back;};

nod *varf;

void push(nod* &v,int x)
{nod *c;
if(!v)
{v=new nod;
v->info=x;
v->back=0;}
else
{c=new nod;
c->back=v;
c->info=x;
v=c;}
}

void afisare(nod *v)
{nod *c;
c=v;
while(c)
{cout<<c->info<<" ";
c=c->back;}
}

void pop(nod* &v)
{nod* c;
if(!v)
cout<<"stiva este vida si nu mai ai ce elimina!!!";
else
{c=v;
v=v->back;
delete c;}
}


void main()
{int n,a;
25

cout<<"numarul initial de noduri ";
cin>>n;
for(int i=1;i<=n;i++)
{cout<<"valoarea de adaugat in stiva ";
cin>>a;
push(varf,a);
}
cout<<endl;
afisare(varf);
int nre,nra;
cout<<endl<<"cate adaugari ?";
cin>>nra;
for(i=1;i<=nra;i++)
{cout<<"valoarea de adaugat ";
cin>>a;
push(varf,a);}
cout<<endl<<"dupa adaugare"<<endl;
n=n+nra;
cout<<"stiva are "<<n<<" elemente"<<endl;
afisare(varf);
cout<<endl<<"cate eliminari ?";
cin>>nre;
for(i=1;i<=nre;i++)
pop(varf);
cout<<endl<<"dupa eliminare"<<endl;
n=n-nre;
cout<<"stiva are "<<n<<" elemente"<<endl;
afisare(varf);
//prelucrez varful stivei: de exemplu se poate dubla continutul:
varf->info=2*varf->info;
cout<<endl<<"dupa dublarea valorii varfului "<<endl;
afisare(varf);
getch();
}



26


http://infoscience.3x.ro/

http://users.cs.tuiasi.ro/

http://info64.ro/

http://wiki-proiecte.wikispaces.com/

http://informaticasite.ro/

http://www.referat.ro/

http://alocaredinamica.eu5.org/
27


https://www.worldit.info

http://ro.wikipedia.org/