Documente Academic
Documente Profesional
Documente Cultură
Pentru rezolvarea unei probleme, în multe cazuri se pot folosi mai mulţi algoritmi, din care trebuie
ales cel mai eficient:
- la nivel conceptual, alegerea algoritmului se face în funcţie de complexitatea algoritmilor
descoperiţi pentru rezolvarea problemei, fiind preferat algoritmul care are complexitate mai mică –
algoritmul care consumă cel mai puţin resursele calculatorului;
- la nivel logic, alegerea se face în funcţie de modul în care pot fi implementaţi algoritmii în limbajul
de programare ales, fiind preferat algoritmul care are o implementare mai uşoară
– algoritmul care consumă cel mai puţin resursele programatorului.
Nu întotdeauna cei doi algoritmi coincid, programatorul urmând să decidă criteriul folosit pentru
alegerea algoritmului. Un rol important în implementarea eficientă a unui algoritm îl joacă modul în
care au fost organizate datele în colecţia de date.
Pe lângă criteriile pentru clasificarea datelor studiate, mai există şi următoarele criterii:
Structurile statice au un mare dezavantaj, deoarece alocarea zonei de memorie se face la compilarea
programului (în funcţie de modul în care a fost declarată structura), iar în timpul execuţiei
programului pot să apară următoarele cazuri:
- spaţiul alocat structurii este insuficient;
-spaţiul alocat structurii este mult mai mare decât este necesar.
Acest dezavantaj este eliminat prin folosirea structurilor dinamice, la care dimensiunea zonei de
memorie alocate nu mai este fixă, deoarece alocarea memoriei se face în timpul execuţiei
programului, în funcţie de numărul de componente ale structurii la acel moment
Exemplificam modul în care identificaţi problemele în care puteţi folosi structura de date de tip listă
pentru a le rezolva.
Enunţul problemelor pentru care trebuie aleasă o structură de date şi conceput
1
algoritmul pentru prelucrarea lor:
1. Într-o bibliotecă, există o colecţie de cărţi organizate în ordinea alfabetică a autorilor. Un cititor
poate împrumuta o carte (se extrage o carte din colecţie) sau poate înapoia o carte(se inserează o carte
în colecţie). Toate aceste operaţii trebuie executate astfel încât să se păstreze organizarea în ordine
alfabetică a autorilor.
2. La o benzinărie s-a format o coadă de aşteptare. Maşinile sunt servite în ordinea venirii: prima
maşină sosită este servită, iar ultima maşină venită se aşază la sfârşitul cozii. Toate aceste operaţii
trebuie executate astfel încât să se păstreze ordinea în care au sosit maşinile şi s-au aşezat la coadă.
3. Într-o stivă de cărţi, volumele sunt aşezate în ordinea alfabetică a titlurilor. Trebuie extrasă o carte
din stivă fără a deranja modul în care sunt ordonate cărţile în stivă.
La nivel conceptual, toate aceste colecţii de date reprezintă un şir de date de acelaşi tip, care trebuie
prelucrate prin inserarea şi extragerea de elemente, păstrându-se o anumită ordine de aranjare a
elementelor. Dacă la nivel logic s-ar alege soluţia de a grupa aceste elemente într-o structură de date
de tip vector, algoritmii de prelucrare – care sunt algoritmi de actualizare a vectorilor – vor necesita
multe deplasări de elemente care consumă timp de prelucrare.
Caracteristicile operaţiilor de prelucrare a vectorilor sunt:
- Vectorul este o structură de date care are lungimea fizică fixă, iar implementarea lui la nivel fizic
este un proces prin care se face conversia locaţiei conceptuale a unui element, în locaţia reală, din
memoria internă.
-Orice operaţie de adăugare sau extragere a unui element din colecţie modifică lungimea logică a
vectorului.
-Pentru a insera un element în colecţie, trebuie deplasate toate elementele spre
dreapta, începând cu poziţia inserării.
-Pentru a extrage un element din colecţie, trebuie deplasate toate elementele spre stânga, de la sfârşit,
până în poziţia din care se extrage.
În cazul structurilor care trebuie să-şi păstreze în timpul exploatării ordonarea după un anumit
criteriu, mecanismul vectorilor este greoi. În aceste cazuri, se poate alege ca soluţie de implementare
a structurii de date lista – care nu este o structură fizică de organizare a datelor, ci o structură logică,
ce degrevează programatorul de ordonarea după indice a structurii, impusă de vectori. Listele sunt
structuri de date care, spre deosebire de vectori, au marele avantaj că permit şi implementare prin
alocarea dinamică a memoriei.
Lista este o structură de date logică, liniară, cu date omogene, în care fiecare
element are un succesor şi un predecesor, exceptând primul element, care nu are
decât succesor, şi ultimul element, care nu are decât predecesor.
Lista este o colectie de elemente intre care este specificata cel putin o relatie de ordine.
Daca relatia de ordine "<" este totala se poate construi o secventializare a
elementelor listei astfel incit daca a si b sunt doua elemente consecutive in secventa atunci a<b si nu
exista nici un alt element c in lista asfel incit a<b<c.
Aparent, vectorul şi lista par să fie aceeaşi structură de date: sunt structuri omogene şi secvenţiale
(liniare) în care fiecare element are un succesor şi un predecesor, cu excepţia primului şi a ultimului
element, care au numai succesor, respectiv numai predecesor. Între cele două structuri există însă
următoarele deosebiri:
Criteriul Tabloul de memorie Lista
Dispunerea Sunt structuri contigue. Pot fi structuri dispersate.
elementelor Predecesorul, elementul şi Predecesorul,
în memorie succesorul se găsesc în locaţii elementul şi succesorul nu
contigue prin mecanismul de sunt în locaţii de
implementare a structurii de memorie contigue.
2
date la Programatorul trebuie să
nivel fizic. definească mecanismul prin
care elementele
structurii vor fi legate unele
de altele pentru
a putea fi regăsite în
memorie.
Implementarea Sunt structuri implicite. Fiind Sunt structuri explicite. Fiind
în limbaj o o structură
structură fizică, este logică, trebuie aleasă o
implementată în metodă de
limbajul de programare. Nu implementare folosind
necesită structurile fizice
informaţii suplimentare existente. Necesită informaţii
pentru localizarea suplimentare
elementelor structurii în pentru localizarea
memoria elementelor structurii în
internă, deoarece memoria internă, deoarece
mecanismul mecanismul
prin care este implementată prin care este implementată
fizic fizic nu asigură
asigură identificarea identificarea elementelor.
elementelor
Alocarea Sunt structuri statice. Sunt structuri statice sau
memoriei dinamice, în
funcţie de implementarea
aleasă.
Se defineşte lungimea listei (n) ca fiind numărul de elemente ale listei. Lista vidă este lista care are
lungimea 0 (nu are nici un element). Elementele listei se mai numesc şi noduri.
În funcţie de modul în care sunt aranjate elementele în listă, se pot folosi metodele:
3
Nodurile listei sunt stocate într-un bloc contiguu de locaţii de memorie cu adrese
consecutive. De exemplu, dacă avem o listă formată din cuvinte de maximum 4
caractere, acestea vor fi scrise într-un bloc contiguu de locaţii de 5 octeţi.
Această implementare este cea mai simplă – şi se face folosind un vector de şiruri de caractere:
typedef char nod[5];
nod lista[100];
S-a declarat o listă care poate avea maxim 100 de noduri. Informaţia dintr-un nod este de tip şir de
caractere cu lungimea de 5 caractere. Acest tip de listă se mai numeşte şi listă contiguă.
În implementarea prin alocare secvenţială, algoritmii pentru operaţiile de prelucrare a listei sunt cei
folosiţi pentru operaţiile de prelucrare a vectorilor şi necesită foarte multe operaţii de deplasare a
elementelor în vector.
4
Deoarece în ambele cazuri de implementare nodurile nu sunt aranjate succesiv, ci aleatoriu, în
memorie, trebuie implementat un mecanism prin care să se precizeze ordinea reală a acestor noduri
(ordinea în care se înlănţuiesc în listă). Aceasta înseamnă că:
-trebuie cunoscut locul din care începe lista (lanţul de noduri), adică poziţia primului nod din listă
(nodul prim) – în exemplu, nodul "alfa";
-trebuie cunoscut locul în care se termină lista, adică poziţia ultimului nod din listă (nodul ultim) – în
exemplu, nodul "zeta";
-pentru fiecare nod din listă, cu excepţia ultimului nod, trebuie cunoscut nodul care este succesorul
lui – de exemplu, pentru nodul "gama" trebuie cunoscut că
succesorul său este nodul "teta".
Metoda folosită pentru implementare este ca un nod al listei să conţină două tipuri de informaţii:
informaţia propriu-zisă şi informaţia de legătură (informaţia prin care se identifică nodul care
urmează în structură, adică
adresa acestui nod). Informaţia pentru legătură va fi:
- în cazul implementării statice – indicele din vector al succesorului;
- în cazul implementării dinamice – adresa succesorului exprimată printr-un pointer.
În ambele cazuri, informaţia de legătură memorată în ultimul nod este constanta NULL sau 0 (care
semnifică faptul că ultimul nod nu se leagă de nimic).
5
adresa urm;}; //informaţia pentru legătură
nod lista[100];
Câmpurile <info ij> sunt câmpurile cu informaţii, iar câmpul urm este câmpul care conţine
informaţia de legătură (adresa succesorului). Se defineşte constanta NMAX pentru a putea folosi în
expresii lungimea fizică a vectorului. Deoarece indicele 0 se foloseşte pentru adresa NULL, în
vectorul care implementează lista, elementul cu indicele 0 nu se foloseşte.
În liste, algoritmii de inserare şi eliminare a unor noduri din structură se simplifică foarte mult:
- Inserarea unui nod constă în alocarea zonei de memorie în care se scrie nodul şi legarea lui la
structură (stabilirea legăturii cu predecesorul şi cu succesorul lui).
-Eliminarea unui nod constă în ruperea nodului din structură, prin legarea predecesorului său cu
succesorul lui, şi eliberarea zonei de memorie pe care a ocupat-o.
Implementarea structurilor de date cu ajutorul listelor are următoarele avantaje:
- alocarea dinamică a memoriei – care o gestionează mult mai eficient;
- algoritmii de prelucrare – care sunt mult mai eficienţi (se execută mult mai puţine operaţii pentru
inserarea şi ştergerea unui element).
statică secvenţială Nu Nu
statică înlănţuită nu da
dinamică înlănţuită da da
Se observă că implementarea statică secvenţială nu aduce niciunul dintre avantajele listelor, fiind o
implementare în care lista este de fapt un vector.
Algoritmii pentru prelucrarea unei liste înlănţuite sunt aceiaşi, atât în cazul implementării statice, cât
şi în cazul implementării dinamice. Deoarece informaţiile pe care le aveţi nu vă permit încă
abordarea alocării dinamice a memoriei, pentru prezentarea algoritmilor se va folosi implementarea
statică.
Aşadar, lista este o structură logică de date, parcursă liniar, care are două extremităţi (început şi
sfârşit), în care fiecărui element i se asociază o informaţie suplimentară referitoare la locul
elementului următor, din punct de vedere logic
6
Clasificarea listelor
7
Algoritmi pentru prelucrarea listelor generale
lista[prim].info = val;
lista[prim].urm = NULL;
ultim = prim;
Există următoarele stări ale listei, care trebuie cunoscute în algoritmii de prelucrare:
-Lista vidă. Această stare trebuie cunoscută, atunci când se elimină noduri din listă, deoarece în lista
vidă nu există noduri care să fie eliminate. Pentru testarea unei liste dacă este vidă, se poate
implementa funcţia operand este vida(), care va furniza valoarea 1 („adevărat“), dacă lista este vidă,
şi valoarea 0 („fals“) dacă lista nu este vidă.
8
-Lista plină. Această stare poate să apară din cauza implementării statice a listei. Ea trebuie
cunoscută atunci când se adaugă noduri la listă, deoarece se poate depăşi lungimea fizică a
vectorului. În lista plină nu mai există locaţii libere în vector, în care să se adauge noduri. Pentru
testarea unei liste dacă este vidă, se poate implementa funcţia operand este_plina(), care va furniza
valoarea 1 („adevărat“), dacă lista este plină, şi valoarea 0 („fals“) dacă lista nu este plină.
int este_plina()
{return nr_el == NMAX;}
Iniţializarea listei
Prin acest algoritm se creează lista vidă, în care atât nodul prim cât şi nodul ultim nu există, şi vor
avea valoarea NULL. În vectorul liber, toate elementele au valoarea 1 (toate elementele din vectorul
listei sunt libere).
Implementarea algoritmului. Se foloseşte funcţia procedurală init() ai cărei parametri prim şi ultim
de tip adresa se transmit prin referinţă, deoarece sunt parametri de ieşire.
Alocarea memoriei
Pentru a putea adăuga un nod la listă – trebuie mai întâi să i se aloce o zonă de
memorie. Prin acest algoritm se găseşte prima poziţie liberă în vectorul lista.
Implementarea algoritmului. Se foloseşte funcţia operand aloc_mem() care furnizează adresa
(indicele) primei poziţii libere în vector – variabila locală p de tip adresă.
adresa aloc_mem()
{adresa p;
for (adresa p=1; !liber[p]; p++);
liber[p]=0; nr_el++;
return p;}
Crearea listei
Deoarece în algoritmii de prelucrare trebuie să se cunoască adresa primului nod, este importantă
adăugarea primului nod la lista vidă. Paşii algoritmului de creare a unei liste sunt:
PAS1. Se adaugă primul nod la listă (nodul prim).
PAS2. Cât timp mai există informaţie execută: se adaugă un nod la listă.
În lista cu un singur nod, adresa de legătură a nodului prim are valoarea NULL şi atât nodul prim
cât şi nodul ultim au aceeaşi adresă. Paşii executaţi în acest algoritm sunt:
PAS1. Se cere alocarea de memorie pentru nodul prim.
PAS2. Se scrie informaţia în nodul prim.
PAS3. Adresei de legătură a nodului prim i se atribuie valoarea NULL.
PAS4. Nodului ultim i se atribuie adresa nodului prim.
9
Implementarea algoritmului. Se foloseşte funcţia procedurală adauga primul nod() ai cărei parametri
se transmit astfel: prim şi ultim de tip adresa prin referinţă, deoarece sunt parametri de ieşire, iar n,
care conţine informaţia utilă, prin valoare, deoarece este parametru de intrare.
10
Implementarea algoritmului. Se foloseşte funcţia procedurală
adauga_dupa_ ultim() ai cărei parametri se transmit astfel: prim şi ultim de tip adresa prin referinţă,
deoarece sunt parametri de intrare-ieşire şi n – care conţine informaţia utilă – prin valoare, deoarece
este parametru de intrare.
11
listă a informaţiei pe care o conţine, între alte două informaţii, şi anume: informaţia din predecesorul
nodului q trebuie să fie anterioară ei, iar informaţia din nodul q trebuie să o urmeze. Astfel, în listă
nu se va insera nodul p înainte de nodul q, ci după el, interschimbând apoi informaţiile între cele
două noduri. Paşii executaţi în acest algoritm sunt:
PAS1. Se cere alocarea de memorie pentru nodul p.
PAS2. Se copiază informaţia din nodul q în nodul p.
PAS3. Se scrie în nodul q informaţia care trebuie adăugată la listă.
PAS4. Nodul p se leagă de succesorul nodului q.
PAS5. Nodul q se leagă de nodul p adăugat.
PAS6. Dacă nodul q a fost ultimul nod, nodul p adăugat devine nodul ultim.
Parcurgerea listei
Prin acest algoritm se vizitează fiecare nod al listei, pornind de la primul nod, până la ultimul nod,
în ordinea de înlănţuire a nodurilor – furnizată de câmpul urm din nodul vizitat. Lista se parcurge
pentru a prelucra informaţia stocată în noduri.
Implementarea algoritmului. Se foloseşte funcţia procedurală parcurge() al cărei parametru prim de
tip adresa se transmite prin valoare, deoarece este parametru de intrare.
12
2. Nodul care se găseşte într-o anumită poziţie în listă, pe care o notăm cu poz.
Algoritmul este: se parcurge lista începând de la primul nod, în ordinea de înlănţuire a nodurilor,
până când s-au parcurs poz noduri sau până s-a ajuns la sfârşitul listei.
Implementarea algoritmului. Se foloseşte o funcţie operand cu tipul adresa care va returna adresa
nodului găsit. În cazul în care nodul căutat nu există în listă, funcţia va returna valoarea NULL. În
ambele variante:
-parametrul funcţiei este prim de tip adresa şi se transmite prin valoare, deoarece este parametru de
intrare;
-variabila locală p de tip adresa se foloseşte pentru parcurgerea listei – este iniţializată cu adresa
primului nod;
-adresa nodului găsit se memorează în variabila p care va fi returnată de funcţie.
Varianta 1
Observaţie. Prin găsirea predecesorului unui nod, se poate simplifica algoritmul pentru ştergerea
ultimului nod.
void elibereaza(adresa p)
{liber[p]=1; nr_el--;}
13
Pentru eliminarea unui nod din listă, în funcţie de cerinţele problemei, se poate folosi unul dintre
algoritmii următori:
1. eliminarea primului nod;
2. eliminarea ultimului nod;
3. eliminarea unui nod din interiorul listei.
14
Eliminarea unui nod din interiorul listei
Pentru a elimina nodul p aflat în interiorul listei, trebuie să legăm predecesorul nodului p de
succesorul lui. Dar, predecesorul unui nod nu este cunoscut, ci numai succesorul lui.
Eliminarea unui nod din listă înseamnă de fapt eliminarea din listă a informaţiei pe care o conţine.
Astfel, din listă nu se va elimina nodul p, ci succesorul său (care este cunoscut), după ce informaţia
din el a fost copiată în nodul p. Paşii executaţi în acest algoritm sunt:
PAS1. Se salvează adresa succesorului nodului p în variabila q de tip adresa.
PAS2. Se copiază în nodul p toată informaţia din succesorul lui (informaţia propriu-zisă şi informaţia
de legătură).
PAS3. Se cere eliberarea zonei de memorie de la adresa memorată în variabila q.
PAS4. Dacă succesorul nodului p era nodul ultim, atunci nodul p devine nodul ultim.
Implementarea algoritmului. Se foloseşte funcţia procedurală elimina() ai cărei parametri sunt de tip
nod: p (adresa nodului care se elimină), care se transmite prin valoare, deoarece este parametru de
intrare, şi ultim, care se transmite prin referinţă, deoarece este parametru de intrare-ieşire.
Prelucrarea listelor
În cazul listelor ordonate, dacă informaţia din nodul listei este o dată elementară, cheia de ordonare
va fi data elementară. Dacă informaţia din nodul listei este o înregistrare, cheia de ordonare va fi
unul dintre câmpurile înregistrării.
În general, rezolvarea problemelor în care organizaţi datele sub
formă de liste presupune folosirea algoritmilor prezentaţi,astfel:
1. Se creează lista prin folosirea următorilor algoritmi:
- Se creează lista vidă – algoritmul pentru crearea listei vide.
- Se adaugă câte un nod la listă. Poziţia în care se adaugă nodul depinde de cerinţele problemei. Dacă
lista nu este ordonată, adăugarea se poate face la sfârşitul sau la începutul listei – algoritmul pentru
adăugare la începutul sau la sfârşitul listei. Dacă lista este ordonată, se parcurge mai întâi lista pentru
a găsi poziţia de inserare – algoritmul pentru căutarea unui nod în listă – după care se inserează noul
nod în poziţia găsită – algoritmul pentru adăugare în interiorul listei.
2. Se întreţine lista prin folosirea următorilor algoritmi:
-Se adaugă noi noduri la listă. Ca şi la crearea listei, în funcţie de cerinţele problemei se va folosi unul
dintre algoritmii de adăugare.
-Se elimină noduri din listă. Mai întâi se parcurge lista pentru a găsi nodul care se elimină –
algoritmul pentru căutarea unui nod în listă – după care se elimină
nodul din poziţia găsită folosind unul dintre algoritmii pentru eliminare – în
funcţie de poziţia în care a fost găsit nodul.
-Se modifică informaţia dintr-un nod al listei. Mai întâi se parcurge lista pentru a găsi nodul în care
se va modifica informaţia – algoritmul pentru căutarea unui
nod în listă. Dacă lista nu este ordonată sau dacă informaţia care se modifică nu
15
face parte dintr-un câmp cheie dintr-o listă ordonată, se modifică valoarea câmpului respectiv. Dacă
lista este ordonată şi, prin modificarea valorii câmpului, se poate distruge această ordonare, se
modifică valoarea câmpului, se salvează nodul listei într-o variabilă intermediară, se elimină din
vechea poziţie – algoritmul pentru eliminarea unui nod din listă –, se parcurge lista pentru a găsi
noua poziţie – algoritmul pentru căutarea unui nod în listă – şi se inserează nodul în poziţia găsită –
algoritmul pentru adăugarea unui nod la listă.
3. Se obţin informaţii din listă. Se parcurge lista – algoritmul de parcurgere a listei şi se vizitează
fiecare nod al listei pentru a extrage din el informaţiile necesare.
În exempul următor se urmărește exemplificarea modului în care, pentru rezolvarea problemei,
folosiţi algoritmii de prelucrare a listelor simplu înlănţuite şi implementarea lor cu ajutorul
subprogramelor.
Exemplul 1:
Se citesc, într-o listă, cifrele unui număr care are o valoare foarte mare. Citirea se va face până când
numărul citit nu este o cifră. Să se afişeze inversul acestui număr.
Nodurile listei nu sunt ordonate conform unui criteriu. Cifrele numărului se vor citi de la tastatură şi
se vor scrie în listă prin adăugare în faţa primului nod. Pentru a afişa inversul numărului se va
parcurge lista de la primul nod până la ultimul, şi se va afişa informaţia din fiecare nod. Deoarece în
aceşti algoritmi nu este necesară informaţia despre adresa ultimului nod, din subprograme au fost
eliminate prelucrările care se referă la acesta.
Folosind tehnica top-down, problema poate fi împărţită în subprobleme, iar algoritmul de rezolvare
a unei subprobleme este implementat cu ajutorul unui subprogram.
P1 Se creează lista vidă – subprogramul init().
P2 Se creează lista cu cifre – subprogramul creare()– astfel: se citeşte primul număr şi cât timp
numărul citit este cifră şi lista nu este plină – subprogramul este_plina()
– execută: se adaugă un nod cu cifra respectivă înaintea primului nod din listă –
subprogramul adaug_inainte_prim() – şi se citeşte un număr.
P3 Dacă lista nu este vidă – subprogramul este_vida()–, atunci se parcurge lista de la primul nod
până la sfârşitul ei şi se afişează informaţia din fiecare nod – subprogramul afiseaza().
Structura modulelor este următoarea:
16
_ Date de intrare: prim – adresa primului nod al listei;
_ Date de ieşire: valoarea de adevărat sau fals a stării de listă vidă;
_ Funcţia modulului: obţinerea informaţiei despre starea de listă vidă.
4. Modulul 4(aloc mem):
_ Date de intrare: niciuna;
_ Date de ieşire: p – adresa alocată pentru nod;
_ Funcţia modulului: alocarea unei zone de memorie (un element din vector) pentru un nod nou.
5. Modulul 5 (creare):
_ Date de intrare: prim – adresa primului nod al listei;
_ Date de ieşire: prim – adresa primului nod al listei;
_ Funcţia modulului: se creează lista prin citirea cifrelor de la tastatură.
6. Modulul 6 (adaug inainte prim):
_ Date de intrare: prim şi n – adresa primului nod al listei şi cifra din nodul care se adaugă;
_ Date de ieşire: prim – adresa primului nod al listei;
_ Funcţia modulului: se adaugă la listă un nod cu informaţie înaintea primului nod.
7. Modulul 7 (afişează):
_ Date de intrare: prim – adresa primului nod al listei;
_ Date de ieşire: niciuna;
_ Funcţia modulului: vizitarea nodurilor listei de la primul până la ultimul, pentru a
afişa informaţia din nod.
Programul obţinut este:
#include <iostream.h>
const unsigned NMAX=100;
typedef unsigned adresa;
struct nod {unsigned cifra;
adresa urm;};
nod lista[NMAX+1];
int nr_el,liber[NMAX];
int este_plina()
{return nr_el == NMAX;}
adresa aloc_mem()
{adresa p;
for (p=1; !liber[p]; p++);
liber[p]=0; nr_el++;
return p;}
17
void afiseaza(adresa prim)
{for (adresa p=prim; p!=NULL; p=lista[p].urm) cout<<lista[p].cifra;}
void main()
{adresa prim; init(prim); creare(prim);
if (!este vida(prim)) {cout<<"Numarul invers este: "; afiseaza(prim);}
else cout<<"Nu exista numar";}
TEMA
1. Refaceţi programul folosind prototipurile funcţiilor.
2. Scrieţi următoarele programe în care numerele din nodurile listei se
citesc dintr-un fişier text în care sunt scrise pe acelaşi rând, separate
prin spaţiu:
a. Se creează o listă cu aceste numere şi se afişează în ordinea inversă citirii din fişier numai numerele
pare.
b. Se creează o listă în care ordinea de acces este inversă celei în care sunt citite numerele. Să se
elimine din listă cel mai mic număr şi cel mai mare număr.
Exemplul 2:
Se introduc de la tastatură mai multe numere naturale, până când se introduce numărul 0. Să se
afişeze mai întâi numerele pare şi apoi numerele impare.
Vom folosi pentru memorarea numerelor o listă înlănţuită, în care vom scrie mai întâi numerele pare
şi apoi numerele impare. Numerele pare vor fi scrise în listă prin adăugarea în faţa primului nod, iar
numerele impare prin adăugare după ultimul nod. Problema poate fi descompusă în subprobleme,
care vor fi rezolvate cu ajutorul subprogramelor.
P1 Se creează lista vidă – subprogramul init().
P2 Se creează lista cu numere – subprogramul creare()– astfel: se citeşte primul
număr şi se adaugă primul nod la istă – subprogramul adaug_prim() –, se citeşte
următorul număr şi cât timp numărul citit este diferit de zero şi lista nu este plină –subprogramul
este_plina() – execută: dacă numărul este par, atunci se adaugă înaintea primului nod –
subprogramul adaug_inainte_prim(); altfel, se adaugă după ultimul nod – subprogramul
adaug_dupa_ultim() – şi se citeşte un nou număr.
P3 Dacă lista nu este vidă – subprogramul este_vida()–, atunci se parcurge lista de la primul nod
până la sfârşitul ei şi se afişează informaţia din fiecare nod – subprogramul afiseaza()
nod lista[NMAX+1];
int nr_el,liber[NMAX];
int este_plina()
{return nr_el == NMAX;}
void init()
{nr_el=0;
for (adresa p=1;p<=NMAX;p++) liber[p]=1;}
18
adresa aloc mem()
{adresa p;
for (p=1; !liber[p]; p++);
liber[p]=0; nr el++;
return p;}
void afisare(adresa p)
{while (p!=NULL) {cout<<lista[p].nr<<" "; p=lista[p].urm;}
void main()
{adresa prim; init(); creare(prim);
if (!este vida(prim)) {cout<<"Numerele sunt: ";
afisare(prim);}
else cout<<"Nu exista numere";}
TEMA
Refaceţi programul folosind prototipurile funcţiilor. Explicaţi modul în care a fost descompusă
problema în subprobleme şi precizaţi specificatiile fiecarui modul
Exemplul 3:
Se citesc de la tastatură mai multe numere, până se citeşte
valoarea 0. Numerele se scriu într-o listă, ordonate crescător. Să se afişeze numerele din nodurile
listei.
Numerele care trebuie memorate în nodurile listei se citesc de la tastatură, până se citeşte valoarea 0
(are semnificaţia că nu mai există informaţie de adăugat). Nodurile acestei liste fiind ordonate,
inserarea unui nou număr citit de la tastatură trebuie să se facă într-o poziţie care să păstreze ordinea
elementelor. Acest algoritm se numeşte algoritmul de sortare prin inserare. Se porneşte de la lista
care conţine primul nod cu informaţie. Paşii executaţi în acest algoritm sunt:
PAS1. Se adaugă primul nod cu informaţie la listă.
PAS2. Cât timp mai există informaţie de adăugat execută
PAS3. Se caută nodul înaintea căruia trebuie adăugat noul nod.
PAS4. Dacă s-a ajuns la sfârşitul listei, atunci se adaugă noul nod ca ultim nod din
listă; altfel, se adaugă noul nod în faţa nodului găsit. Se revine la Pasul 2.
19
Implementarea algoritmului.
#include <iostream.h>
const unsigned NMAX=100;
typedef unsigned adresa;
struct nod {int info;
adresa urm;};
nod lista[NMAX+1];
int nr_el,liber[NMAX];
int este_plina()
{return nr_el==NMAX;}
void init()
{nr_el=0;
for (adresa p=1;p<=NMAX; p++) liber[p]=1;}
adresa aloc_mem()
{adresa p; for (p=1; !liber[p]; p++);
liber[p]=0; nr_el++;
return p;}
void main()
{adresa prim; creare(prim);
afiseaza(prim);
20
getch();
clrscr();}
if (!este_vida) afiseaza(prim);//se parcurge lista pentru afişare
else cout<<"Nu exista elemente";}
TEMA
Refaceţi programul de la problema 2 astfel încât numerele din listă să fie
afişate astfel: mai întâi numerele pare, ordonate crescător, şi apoi
numerele impare, ordonate descrescator
Exemplul 4
Calcularea rezistenţei echivalente. Să se calculeze rezistenţa echivalentă între punctele A şi B pentru
circuitul electric din figură.
Pentru calcularea rezistenţei echivalente se porneşte de la ultimele rezistenţe – Rn şi Rn-1 – care sunt
legate în serie. Se calculează rezistenţa lor echivalentă Re1, care va fi legată în paralel cu rezistenţa
Rn-2. Prin calcularea rezistenţei echivalente a celor două rezistenţe legate în paralel Re1 şi Rn-2, se va
obţine o nouă rezistenţă echivalentă Re2 care este legată în serie cu rezistenţa Rn-3. Calcularea
rezistenţei echivalente a circuitului electric este un proces repetitiv în care alternează calcularea unei
rezistenţe echivalente a două rezistenţe legate în serie cu calcularea unei rezistenţe echivalente a
două rezistenţe legate
în paralel. Pentru a şti care dintre variantele de calcul se alege, se foloseşte variabila s, care are
valoarea 1 dacă rezistenţele sunt legate în serie şi valoarea 0 dacă sunt legate în paralel. Valorile
pentru rezistenţe se citesc dintr-un fişier text în care sunt memorate pe acelaşi rând, separate prin
spaţiu. Se creează o listă simplu înlănţuită în care ordinea de acces este inversă faţă de cea în care
sunt citite numerele din fişier. Pentru calcularea rezistenţei echivalente, lista se parcurge de la primul
nod până la ultimul nod.
#include<fstream.h>
const unsigned NMAX=100;
typedef unsigned adresa;
struct nod {float info;
adresa urm;};
nod lista[NMAX+1];
int nr_el,liber[NMAX];
fstream f("lista_r.txt",ios::in);
int este_plina()
{return nr_el == NMAX;}
adresa aloc_mem()
{adresa p;
for (p=1; !liber[p]; p++);
liber[p]=0; nr_el++;
return p;}
21
void adaug_inainte_prim(adresa &prim, float x)
{adresa p; p=aloc_mem();
lista[p].info=x; lista[p].urm=prim; prim=p;}
void main()
{adresa prim; creare(prim); f.close();
cout<<"Rezistenta echivalenta= "<<R(prim);}
O lista liniara simplu inlantuita este caracterizata prin faptul ca relatia de ordine definita pe
multimea elementelor este unica si totala.
Ordinea elementelor pentru o astfel de lista este specificata explicit printr-un cimp de informatie care
este parte componenta a fiecarui element si indica elementul urmator, conform cu relatia de ordine
definita pe multimea elementelor listei.
Deci fiecare element de lista simplu inlantuita are urmatoarea structura:
Pe baza informatiei de inlantuire (pastrata in cimpul leg) trebuie sa poata fi identificat urmatorul
element din lista.
Daca implementarea structurii de lista inlantuita se face prin tablouri, aceasta este o lista inlantuita
alocata static sau simplu o lista inlantuita statica.
Consideram urmatoarele declaratii:
struct Element {
char data[100];
int leg;
};
Element V[8];
Pentru elementele vectorului V exista o ordine naturala data de aranjarea in memorie a elemetelor
sale: V[0], V[1], ... V[7]. Vom reperezenta memoria ocupata de vectorul V astfel incit fiecare element
sa fie reprezentat vertical, in felul urmator:
22
Completind cimpul leg pentru fiecare element al vectorului putem obtine o lista liniara simplu
inlantuita. Valoarea cimpului leg va fi indexul in vector al urmatorului element din lista.
Vectorul V:
Pe baza inlantuirii stabilita de valorile din figura de mai sus se obtine ordinea:
V[3], V[6], V[7], V[0], V[1], V[2], V[4], V[5].
Obs. Ultimul element din lista are in cimpul leg valoarea -1. Este necesar sa cunoastem care este
primul element din inlantuire, pentru aceasta retinem intr-o variabila:
Sageata care porneste din cimpul leg arata faptul ca valoarea memorata aici indica elementul la care
duce sageata.
Parcurgerea listei
Consideram: cap - contine adresa primului element din lista.
O parcurgere inseamna prelucrarea pe rind a tuturor elementelor listei, in ordinea in care acestea
apar in lista. Vom avea o variabila pointer crt care va indica pe rind fiecare element al listei:
Parcurgerea in ordine a elemntelor listei se face in felul urmator:
int crt;
.................
crt = cap;
while (crt!=-1) {
// Prelucreaza V[crt]
crt = V[crt].leg;
}
Un caz special apare atunci cind dorim sa facem o parcurgere care sa se opreasca in fata unui element
care sa indeplineasca o conditie (ca in cazul cind inseram un element intr-o pozitie data printr-o
conditie, sau stergem un elemen care indeplineste o conditie).
Presupunem ca lista are cel putin un element.
23
p = cap;
while (V[p].leg!=-1 && !conditie(V[p].leg))
p = V[p]leg;
Bucla while se poate opri pe condifia "V[p].leg==-1", ceea ce insemna ca nici un element din lista nu
indeplineste conditia iar variabila p indica ultimul element din lista, sau pe conditia
"conditie(V[p]leg)", ceea ce insemna ca p va contine adresa elementului din fata primului element
care indeplineste conditia.
Fiecare sageata nou creeata insemna o atribuire: se atribuie variabilei in care sageata nou creata isi
are originea, valoarea luata dintr-o variabila in care se afla originea unei sageti cu aceeasi destinatie.
In cazul nostru avem atribuirile (fiecare atribuire corespunde sagetii cu acelasi numar din figura):
Observatie:
Daca variabila cap este initial -1, (ceea ce inseamna inserarea intr-o lista vida)
atribuirile de mai sus functioneaza corect rezultind o lista cu un singur element.
V[p].leg = cap; // de fapt V[p].leg = -1;
cap = p;
24
(1) V[p].leg = V[q].leg;
(2) V[q].leg = p;
Observatii:
Atunci cind q indica ultimul element dintr-o lista, atribuirile de mai sus functioneaza corect si
adauga elementul indicat de p la sfirsitul listei.
Nu se poate face inserarea in fata unui element dat (prin q) fara a parcurge lista de la capat.
Stergerea unui element din lista
Consideram: cap - contine adresa primului element din lista.
Stergerea la inceputul listei
Prin operatia de stergere se intelege scoaterea unui element din inlantuire.
(1) p = cap;
(2) cap = V[cap].leg;
25
(1) p = V[q].leg;
(2) V[q].leg =V[p].leg; // sau V[q].leg =V[V[q].leg].leg;
Observatii:
Atunci cind q indica penultimul element dintr-o lista, atribuirile de mai sus
functioneaza corect si sterg ultimul element din lista.
Nu se poate face stergerea elementului indicat de q fara parcurgerea listei de la capat.
Exemplul 5
a) Sa se creeze o lista liniara simplu inlantuita care sa memoreze urmatoarele informatii despre elevii
unei clase formata din n elevi:
- numele (sir de maxim 20 de caractere)
- prenumele (sir de maxim 20 de caractere)
- 3 note intr-un vector cu 3 componente reale
b) Sa se afiseze numele, prenumele si media fiecarui elev.
c) Sa se scrie o functie care calculeaza si returneaza media clasei.
#include <iostream>
#include<fstream>
ifstream f("elevi.in");
struct elev{char nume[20];
char prenume[20];
float note[3];
float media;
};
struct nod{elev e;
int leg;
};
nod V[100];
int nrelv=0;
26
f>>n;
for(int i=1;i<=n;i++)
{f>>x.nume>>x.prenume>>x.note[0]>>x.note[1]>>x.note[2];
x.media=(x.note[0]+x.note[1]+x.note[2])/3;
adaugf(cap,x);
}
}
int main()
{ int cap=-1;
citire(cap);
cout<<cap;
afis(cap);
cout<<mediagen(cap);
return 0;
}
Exemplul 6
Sa se scrie o functie care primeste ca parametru adresa primului nod al unei LLSI cu cel putin 3
noduri si sterge primul si ultimul nod al listei.Lista este memorata intr-un vector V. Elementele listei
sunt date de inretgistrarea
struct elev
{ char nume[30];
int note[3];
int leg;}
O lista liniara dublu inlantuita este caracterizata prin faptul ca pe multimea elementelor sunt definite
doua relatii de ordine totala inverse una alteia (inainte si inapoi). Rezulta doua secventializari ale
listei Ordinea elementelor pentru o astfel de lista este specificata prin doua cimpuri de informatie
care sunt parte componenta a fiecarui element si indica elementul urmator si respectiv elementul
precedent, conform cu relatiile de ordine definite pe multimea elementelor listei.
27
Deci fiecare element de lista dublu inlantuita are urmatoarea structura:
Pe baza informatiilor de inlantuire (pastrate in cimpurile urm si prec) trebuie sa poata fi identificate
urmatorul element din lista respectiv elementul precedent.
struct Element {
char data[50];
int urm,prec;
};
Element V[8];
Pentru elementele vectorului V exista o ordine naturala data de aranjarea in memorie a elemetelor
sale: V[0], V[1], ... V[7]. Vom reperezenta memoria ocupata de vectorul V astfel incit fiecare element
sa fie reprezentat vertical, in felul urmator:
Completand cimpurile urm si prec pentru fiecare element al vectorului se obtine o lista liniara dublu
inlantuita. Valoarea cimpului urm va fi indexul in vector al urmatorului element din lista iar valoarea
cimpului prec va fi indexul in vector al elementului precedent din lista.
Vectorul V:
28
Pe baza inlantuirii stabilita de valorile din figura de mai sus se obtin:
secventa inainte: V[3], V[6], V[7], V[0], V[1], V[2], V[4], V[5] si
secventa inapoi : V[5], V[4], V[2], V[1], V[0], V[7], V[6], V[3].
Observatie.
Ultimul element din lista are in cimpul de legatura valoarea -1.
Este necesar sa cunoastem care este primul element in cele doua inlantuiri. Pentru aceasta retinem in
doua variabile:
int cap1,cap2;
indexul primului element din fiecare inlantuire:
cap1=3.
cap2=5.
Parcurgerea in ordinea inainte a elemntelor listei se face in felul urmator:
int crt;
.................
crt = cap1;
while (crt!=-1) {
// Prelucreaza V[crt]
crt = V[crt].urm;
}
O lista liniara dublu inlantuiata poate fi privita ca o lista liniara simplu inlantuiata daca se face
abstractie de una din inlantuiri. Astfel operatiile cu astfel de liste au implementari similare celor
corespunzatoare de la listele liniare simplu inlantuite. Operatiile de insertie sau stergere la sfarsitul
listei pot fi simplificate utilizand cealalta inlantuire pentru a accesa ultimul element (ultimul element
intr-o inlantuire este primul element in cealalta inlantuire). Operatiile de insertie sau stergere in
interiorul listei pot fi optimizate alegand inlantuirea cea mai potrivita situatiei respective.
Exemplul 7
a) Sa se creeze o LLDI care sa memoreze numere intregi citite dintr-un fisier text.
b) Sa se scrie o functie care primeste ca parametru adresa primului nod al listei si o afiseaza in
ambele sensuri.
c) Sa se scrie o functie care primeste ca parametru adresa p a unui nod si un numar natural x si
adauga dupa nodul indicat de p un nod care sa contina valoarea x.
d) Sa se scrie o functie care primeste ca parametru adresa p a unui nod si sterge nodul indicat de p.
e) Folosind functiile de punctele b), c) si d) sa se adauge dupa nodul al doilea un nod cu informatia 7,
sa se stearga al treilea nod si apoi primul nod si sa se afiseze lista in ambele sensuri dupa fiecare
dintre aceste operatii.
#include <iostream>
#include<fstream>
29
using namespace std;
ifstream f("date.in");
}
else {prim=nrelem;
nou.urm=-1;
nou.prec=-1;
}
V[nrelem]=nou;
}
cout<<V[p].info<<" ";
cout<<endl;
while(V[p].prec!=-1) {cout<<V[p].info<<" ";
p=V[p].prec;
}
cout<<V[p].info<<" ";
cout<<endl;
}
30
nou.prec=p;
V[p].urm=nrelem;
nou.urm=q;
if(q!=-1) V[q].prec=nrelem;
V[nrelem]=nou;
}
int main()
{ int prim=-1;
creare(prim);
afis(prim);
inseraredupa(V[prim].urm,7);
afis(prim);
sterg(V[V[prim].urm].urm,prim);
afis(prim);
sterg(prim,prim);
afis(prim);
f.close();
}
Exemplul 8
Se considera o lista liniara dublu inlantuita. Sa se scrie o functie care primeste ca parametru adresa
primului nod al listei si muta ultimul nod in fata primului. Elementele listei sunt date de
inretgistrarea
struct nod
{ char nume[30];
int prec,urm;}
Liste circulare
O lista circulara simplu inlantuita este o lista liniara simplu inlantuita modificata astfel incat ultimul
element arata spre primul element din lista.
O lista circulara dublu inlantuita este o lista liniara dublu inlantuita modificata astfel incat ultimele
elemente pointeaza respectiv spre primele elemente din lista.
In continuare ne vor referi la liste circulare simplu inlantuite pe care le vom numi simplu: liste
circulare.
31
Deci fiecare element de lista circulara are urmatoarea structura:
Pe baza informatiei de inlantuire (pastrata in cimpul leg) trebuie sa poata fi identificat urmatorul
element din lista.
struct Element {
char data[100];
int leg;
};
Element V[8];
Pentru elementele vectorului V exista o ordine naturala data de aranjarea in memorie a elemetelor
sale: V[0], V[1], ... V[7]. Vom reperezenta memoria ocupata de vectorul V astfel incit fiecare element
sa fie reprezentat vertical, in felul urmator:
Completind cimpul leg pentru fiecare element al vectorului putem obtine o lista liniara simplu
inlantuita. Valoarea cimpului leg va fi indexul in vector al urmatorului elementdin lista.
Vectorul V:
Observatie
Nu mai exista un ultim element in lista ca la listele liniare.
Este necesar sa cunoastem care este primul element din inlantuire, pentru aceasta retinem intr-o
variabila:
int cap;
indexul primului element
cap=3.
Parcurgerea in ordine a elementelor listei se face in felul urmator:
32
int crt;
.................
if (cap!=-1){
crt = cap;
// Prelucreaza V[crt]
while (V[crt].leg!=cap) {
crt = V[crt].leg;
// Prelucreaza V[crt]
}
}
Indiferent de modul cum se materializeaza informatiile de legatura pentru a reprezenta o lista
inlantuita vom folosi urmatoarea reprezentare:
Sageata care porneste din cimpul leg arata faptul valoarea memorata aici indica elementul la care
duce sageata.
-Parcurgerea listei
Consideram: cap - contine adresa primului element din lista.
O parcurgere inseamna prelucrarea pe rind a tuturor elementelor listei, in ordinea in care acestea
apar in lista. Vom avea o variabila pointer p care va indica pe rind fiecare element al listei:
if (cap!=-1){
p = cap;
// Prelucreaza V[p].data
while (pV[p].leg>!=cap){
p = V[p].leg;
// Prelucreaza V[p].data
}
}
Un caz special apare atunci cind dorim sa facem o parcurgere care sa se opreasca in fata unui element
care sa indeplineasca o conditie (ca in cazul cind inseram un element intr-o pozitie data printr-o
conditie, sau stergem un element care indeplineste o conditie).
Presupunem ca lista are cel putin un element.
p = cap;
while (V[p].leg!=cap && !conditie(V[p].leg))
p = V[p].leg;
Bucla while se poate opri pe condifia "V[p].leg==cap", ceea ce insemna ca nici un element din lista nu
indeplineste conditia iar pointerul p indica ultimul element din lista, sau pe conditia
"conditie(V[p].leg)", ceea ce insemna ca l p va contine adresa elementului din fata primului element
care indeplineste conditia.
33
Fiecare sageata nou creata insemna o atribuire: se atribuie varibilei in care sageata nou creata isi are
originea, valoarea luata dintr-o variabila in care se afla originea unei sageti cu aceeasi destinatie.
In cazul nostru avem atribuirile (fiecare atribuire corespunde sagetii cu acelasi numar din figura):
(1) parcurge lista (p = adresa elemenului ce contine in cimpul leg
adresa continuta de variabila cap)
(2) V[p].leg = q;
(3) V[q].leg = cap;
(4) cap=q
Sa detaliem:
Prima operatie:
Parcugerea listei are rolul de a depista elementul care indica capul listei (contine in cimpul leg adresa
continuta de variabila cap). Adresa acestui element va fi continuta de variabila p.
A doua operatie:
V[p].leg=q
Modifica elementul care contine in cimpul leg adresa din cap a primului element din lista pentru a
contine adresa continuta de q a elementului inserat element care va fi de altfel si capul listei
A treia operatie:
V[q].leg = cap;
leaga elementul de inserat de restul listei. In urma acestei atribuiri, cap si V[q].leg vor indica ambii
inceputul listei initiale (vezi figura de mai jos).
34
A patra operatie:
cap = q;
pune in pointerul cap adresa elementului inserat in fata listei.
Observatie:
Daca lista in care se face insertia este vida atunci trebuie efectuate citeva modificari in secventa 1- 4
pentru ca rezultatul sa fie corect.
Exercitiu: Care sunt aceste modificari?
Inserarea in interior
Varibila q va indica elementul dupa care se face inserarea.
Observatii:
1. Nu se poate face inserarea in fata unui element dat (prin q) fara a parcurge lista de la capat.
2. Nu exista nici o diferenta intre inserarea in interior in cazul listelor liniare simplu inlantuite si cel al
listelor circulare.
35
-Stergerea la inceputul listei
Prin operatia de stergere se intelege scoaterea unui element din inlantuire.
(1) parcurge lista (variabila p va contine adresa elementului care indica prin leg la elementul adresat
de cap)
(2) V[p].leg=V[cap].leg
(3) p = cap;
(4) cap = V[cap].leg;
-Stergerea in interior
Varibila q va indica elementul din fata celui care va fi sters.
(1) p = V[q].leg;
(2) V[q].leg = V[p].leg; // sau V[q].leg = V[V[q].leg].leg;
Observatii:
1. Atunci cind q indica penultimul element dintr-o lista, atribuirile de mai sus
functioneaza corect si sterg ultimul element din lista.
2. Nu se poate face stergerea in fata unui element dat (prin q) fara a parcurge lista de la capat.
3. Nu exista nici o diferenta intre stergera in interior in cazul listelor liniare simplu inlantuite si cel al
listelor circulare.
Exemplul 9
Sa se genereze o lista circulara care prelucreaza numere intregi:
a) sa se parcurga lista incepand de la primul nod si sa se afiseze informatia
b) sa se parcurga n elemente pornind de la primul din p in p elemente. De exemplu, daca lista
contine valorile: 1,2,3,4,5,6,7,8 si n=10 iar pasul este 3 atunci se va afisa: 1,4,7,2,5,8,3,6,1,4
c) sa se afiseze toate permutarile circulare ale listei de numere
36
d) sa se stearga elementul de pe pozitia k si sa se afiseze lista ramasa
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("numere.in");
struct nod
{
int info,next;
};
nod V[100];
int prim=-1,nrelem=-1;
void insert(int x)
{
nrelem++;
nod k;
int crt;
k.info=x;
if(prim==-1)
{
prim=0;
k.next=prim;
}
else
{
crt=prim;
while(V[crt].next!=prim) crt=V[crt].next;
k.next=prim;
V[crt].next=nrelem;
}
V[nrelem]=k;
void afis()
{
int crt;
crt=prim;
while(V[crt].next!=prim) { cout<<V[crt].info<<" "; crt=V[crt].next;}
cout<<V[crt].info<<endl;
}
37
void afiscirc()
{
int crt,cap;
cap=prim;
do {
crt=cap; while(V[crt].next!=cap) {cout<<V[crt].info<<" ";
crt=V[crt].next;}
cout<<V[crt].info<<endl;
cap=V[cap].next;
}while(cap!=prim);
}
void sterg(int k)
{ int crt=prim,i;
for(i=1;i<k-1;i++)
crt=V[crt].next;
if(V[crt].next==prim)
prim=V[prim].next;
V[crt].next=V[V[crt].next].next;
int main()
{
int x,n,p,k;
Exemplul 10
Un numar de n copii se gasesc in cerc urmand ca unul dintre ei sa iasa din cerc. N si cele n nume se
citesc din fisierul copii.in. In fisierul cantec.in este scris un cantecel despartit in silabe. De la tastatura
se citeste numele unui copil de la care se incepe “numaratoarea”. Sa se determine numele copilului
care iese din cerc.
Exemplu:
copii.in
5
adi
dan
cristi
ana
alexandru
38
cantec.in
a la ba la por to ca la
Daca se incepe de la dan iese ana.
#include <iostream>
#include<string.h>
#include <fstream>
ifstream f("copii.in");
ifstream g("cantec.in");
struct copil
{
char nume[30];
int next;};
copil V[100];
int prim=-1,nrelem=-1;
}
V[nrelem]=nou;}
void afis()
{
int crt=prim;
do{
cout<<V[crt].nume<<" ";
crt=V[crt].next;
}while(crt!=prim);}
for(i=1;i<=k-1;i++)
crt=V[crt].next;
cout<<V[crt].nume<<" iesi afara";
}
int main()
39
{ char x[100],nume[30];
int k=0;
while(f>>x)
adaug(x);
afis();
f.close();
while(g>>x) k++;
cout<<k;
cout<<endl<<"numele primului elev=";
cin>>nume;
numaratoare(nume,k);
return 0;
}
Exemplul 11
Fie o problemã a unei firme de turism care se enunțã astfel: dintr-o listã de n localitãți, n≤20, așezate
în ordine alfabeticã, se vor alcãtui anumite voiaje. Costul zilnic de cazare în fiecare localitate este
cunoscut dintr-un fișier de date locuri.txt. Un turist dorește sã afle costul total al unui voiaj pe care îl
alcãtuiește din lista de localitãți prezentatã de firmã. Nu se iau în considerare cheltuielile de
transport.
De exemplu, fie lista localitãților însoțite de costul de cazare pe zi:
și se dorește afișarea unui itinerariu care pornește din București. Acesta poate fi cel din figura care
pleacã din București și se încheie la Horezu.
#include<fstream.h>
#include<conio.h>
#include<string.h>
void main()
{typedef struct loc
{char nume[15];float pret;int urm;};
int prim,ultim,curent,ant,p,L[20],i,
v[20],plina=0;
loc lista[20];
prim=ultim=-1;
char den[15]; float cost, cost_total;
ifstream f(.locuri.txt.);
for(i=0;i<20;i++) L[i]=1;
clrscr();
while(!f.eof()&& !plina)//creare lista
{f>>den;if(f.eof())break;
40
f>>cost;
curent=-1;
for(i=0;i<20&&curent==-1;i++)
if(L[i]) {L[i]=0;curent=i;}
if(curent==-1)plina=1;
else
{strcpy(lista[curent].nume,den);
lista[curent].pret=cost;
lista[curent].urm=-1;
ant=-1;
p=prim;
while(p!=-1 &&
strcmp(den,lista[p].nume)>=0)
{ant=p;p=lista[p].urm;}
if(ant==-1)
{ lista[curent].urm=prim;
prim=curent;
if(ultim==-1)ultim=curent;
}
else
if(p==-1)
{ lista[ultim].urm=curent;
ultim=curent;
}
else
{ lista[ant].urm=curent;
lista[curent].urm=p;
}
}
}
if(plina) cout<<„\nLista e plina”<<endl;
f.close();
//Afisare lista
curent=prim;
while(curent!=-1)
{ cout<<lista[curent].nume<<„ „;
curent=lista[curent].urm;
}
//construire voiaj
cout<<endl;
cost_total=0;i=-1;
cout<<„Dati localitatea de pornire „;
cin>>den;
do
{curent=prim;
while(curent!=-1 &&
strcmp(lista[curent].nume,den)!=0)
curent=lista[curent].urm;
if(curent==-1)
cout<<„Nu este o localitate din
lista”<<endl;
else
{v[++i]=curent;
cost_total+=lista[curent].pret;
}
cout<<„Dati localitatea urmatoare „;
cin>>den;
}while(strcmp(den,”stop”)!=0);
cout<<„\n Ati ales voiajul „<<endl;
41
for(int k=0;k<=i;k++)
cout<<lista[v[k]].nume<<endl;
cout<<„Cu costul total „<<cost_total;
getch();
}
in program este folosit vectorul v în care sunt înregistraţi indicii localitãţilor alese de client din lista.
Tema Modificați programul aplicației de mai sus pentru a extinde dialogul cu clientul, astfel încât
acesta sã poatã propune mai mult de un voiaj; pentru fiecare nouã propunere se afișeazã lista
localitãților pe un rând-ecran, iar pe rândul urmãtor, costul acelui voiaj.
Exemplul 12
Un caz interesant de listã este lista circularã, adicã lanțul închis. Pentru acest tip de listã
extremitatea de început coincide cu cea de sfârșit. Simularea unei astfel de liste cu ajutorul
structurilor fixe va folosi tot un tablou unidimensional. În prelucrãrile informațiilor din acest tip de
listã se va avea grijã ca elementul cu indicele de sfârșit al listei, ultim, sã aibã ca succesor elementul
de indice inițial al listei, prim. În cazul eliminãrilor unor elemente dintr-o listã circularã apare
necesarã determinarea situației de listã cu un singur element, adicã momentul în care prim=ultim,
sau, altfel exprimat, lista[prim].urm=prim.
Ca aplicație, se va considera lista grup utilizatã în exemplele paragrafului. Numele persoanelor din
lista grup vor fi ale unor copii care sunt așezați în cerc în ordinea venirii. Copiii pregãtesc un joc prin
alegerea celui care sã porneascã jocul. Pentru aceasta, începând cu primul așezat în cerc, este eliminat
fiecare al n-lea copil, pînã ce mai rãmâne un singur copil care va începe jocul. Se cere organizarea
acestor prelucrãri într-un program care sã afișeze numele copilului care este ales în acest mod,
numele copiilor citindu-se dintr-un fișier, copii.txt, iar valoarea lui n de la tastaturã.
#include<fstream.h>
#include<conio.h>
#include<string.h>
void main()
{typedef struct persoana
{char nume[15];int urm;};
int prim,ultim,ant,curent,L[10],i,
plina=0,n;
persoana grup[10]; char den[15];
prim=ultim=-1;
for(i=0;i<10;i++) L[i]=1;
ifstream f(„copii.txt”);
while(!f.eof()&&!plina)//creare lista
{ f>>den;
if(f.eof()) break;
curent=-1;
for(i=0;i<10&&curent==-1;i++)
if(L[i]) {L[i]=0;curent=i;}
if(curent==-1)plina=1;
else
{strcpy(grup[curent].nume,den);
grup[curent].urm=-1;
if(prim==-1) prim=curent;
else grup[ultim].urm=curent;
ultim=curent;
}
}if(plina) cout<<„\nLista e plina”<<endl;
f.close();
42
//afisarea listei
cout<<„lista creata este”<<endl;
curent=prim;
while(curent!=-1)
{cout<<grup[curent].nume<<„ „;
curent=grup[curent].urm;
}
//inchiderea circulara
grup[ultim].urm=prim;
//eliminare cu pasul n
cout<<endl;
cout<<”Dati pasul de elmiminare”;
cin>>n;
curent=prim;
while(grup[curent].urm!=curent)
{for(i=1;i<n;i++)
{ant=curent;
curent=grup[curent].urm;
}
grup[ant].urm=grup[curent].urm;
L[curent]=1;
curent=grup[ant].urm;
}
cout<<endl;
cout<<”A ramas copilul”;
cout<<grup[curent].nume;
}
43