Sunteți pe pagina 1din 147

Algoritmi i structuri de date

CUPRINS

1. MODELE DE CALCUL.........................................................................................................3
1.1. Legtura dintre analiza algoritmilor i dezvoltarea software...........................................4
2.1. Algoritmi recursivi i relaii de recuren......................................................................10
2.2 Analiza spaiului de memorie consumat ntr-un algoritm...............................................15
2.3. Arbori.............................................................................................................................17
2.3.1. Reprezentarea arborilor...........................................................................................18
2.3.2. Arbori binari............................................................................................................20
3. GRAFURI. TRAVERSRI PE GRAFURI..........................................................................35
4. STRUCTURI DE DATE ELEMENTARE...........................................................................38
4.1. Pointeri i adrese............................................................................................................38
4.1.1. Pointeri i argumente de funcii..................................................................................40
4.1.2. Pointeri i tablouri.......................................................................................................42
4.1.3. Aritmetica adreselor....................................................................................................44
4.1.4. Pointeri pe caractere i funcii.....................................................................................47
4.2. Tablouri..........................................................................................................................49
4.3. Liste................................................................................................................................50
4.3.1. Implementri de liste...............................................................................................51
4.3.2. Alte consideraii asupra operaiei de inserare ntr-o list nlnuit........................55
4.3.3. Stive ordonate.........................................................................................................56
4.3.4. Stive nlnuite........................................................................................................58
4.3.5. Cozi ordonate.........................................................................................................59
4.3.6. Cozi nlnuite.........................................................................................................62
4.3.7. Liste generalizate...................................................................................................64
4.4. Fiiere.............................................................................................................................74
4.4.1. Fiiere text i fiiere binare....................................................................................74
4.4.2 Accesul la fiiere.....................................................................................................75
4.4.3. Algoritmi de prelucrare a fiierelor secveniale......................................................76
5. IMPLEMENTAREA STRUCTURILOR DE DATE............................................................83
5.1. Structuri simple de date.................................................................................................83
5.2. Liste, pointeri, mulimi, arbori.......................................................................................90
5.3. Fiiere text i fiiere cu tip. Fiiere secveniale...........................................................129

1. MODELE DE CALCUL
Maina este un model de calcul constituit din Unitate Centrala (U.C.), Memorie (M),
I/O.
Exemple de modele de calcul:
Maina Von Newman (fig.1)- presupune execuia pe baz modelului de calcul cu:
U . C.
M.
I/O

Fig. 1 Arhitectura von Neumann


Programarea este n acest caz programare imperativ, procedural.
Maina RAM (Random Acces Memory) cu:
P(U . C.)
M.
S .(sir de instructiuni)
I/O

model bazat pe algebra boolean;

programarea este imperativ, procedural;

evoluia se face printr-un set redus de instruciuni;

vitez foarte mare de execuie.


Modelul RAM const ntr-o mulime finit de regitri ca i dintr-o memorie finit. Memoria
este mprit n cuvinte, fiecare avnd o adres unic, ntreg ntre 1 i n. Coninutul fiecrui
cuvnt poate fi un ntreg, un numr real sau un numr cu o infinitate de zecimale ( de exemplu
pi) i reprezint date sau instruciuni.
Calculul cu adrese este posibil. Ciclul fetch store al itemilor din memorie implic regsirea
adresei corespunztoare din memorie. Operaiile de scoatere sau stocare a unei valori sunt
executate n timp constant, indifferent de locul n care valoarea este stocat n memorie. Toate
operaiile sunt efectuate n regitri.

Maina TURING

Fig. 2 Un exemplu de main Turing


Modelul introdus de Alan Turing este un model operaional, mecanic, al calculabilitii. Este
un dispozitiv format dintr-o band infinit (care conine un ir de simboluri) i o unitate de
control. Un cap de citire/scriere permite citirea/scrierea simbolurilor de pe/pe band. Banda se
poate deplasa spre stnga sau spre dreapta cu o singur poziie. Funcionarea unitii de
control pleac dintr-o stare iniial. Pe baz strii curente i a simbolului curent de pe band,
maina trece ntr-o nou stare, (poate s scrie pe band sau s se deplaseze cu o poziie spre
stnga sau spre dreapta caz n care se spune c maina a executat o tranziie. Datorit faptului
c maina poate s scrie i s citeasca pe/de pe band se poate considera c maina dispune de
o memorie infinit. Turing afirm c funciile calculabile sunt acelea care se pot calcula cu
acest tip de dispozitiv.
1. MODELUL funcional - bazt pe teoria - calcullui.
Limbaje n acest model sunt LISP, ML, MIRANDA, etc., iar programarea este n acest
caz programare funcional.
2. MODELUL logic - bazt pe predicate de ordin I.
Un exemplu de limbaj n acest model este PROLOG, iar programarea se numete
programare logic.
n cele ce urmeaz ne vom limita la modelul Von Newman.

1.1. Legtura dintre analiza algoritmilor i dezvoltarea software


Algoritmul
Un algoritm se definete ca o succesiune de operaii prin care, pentru o anumit clas de
probleme, plecnd de la condiiile iniiale (datele de intrare) permite obinerea soluiei (datele de
ieire).
Un algoritm poate fi dat n limbaj natural (o specificaie), n limbaj matematic
(alta specificaie), sau n limbaj de programare (alt specificaie), .a.m.d. Alte
metode de descriere a algoritmilor includ pseudolimbaje, pseudocod, scheme
logice, diagrame.
Not: un limbaj (C++) se constituie din:

variabile;

identificatori;

constante;

operatori numerici obinuii;

operatori relaionali;
4

structuri de control a execuiei: if/else, while, do/while, for, etc.

Etapele realizrii unui produs software


Modelul waterfall (cascad- fig. 2):
Necesiti

Design

Testare

Implementare

Fig. 3. Modelul cascad


Etapele de realizare ale unui produs software:
O prim faz:

se pleac de la cerine;

se obin specificaii;

se face analiza specificaiilor;


A doufaz:

proiectare de ansamblu (se descompune modulul n submodule, etc);

proiectarea structurilor de date;

proiectarea algoritmilor;

analiza performanelor;

codificarea (scrierea programului);


A treiafaz:

testarea
Ultima faz:

implementarea.
Programul rezultat se compar cu cerinele, i dac nu corespunde, se reia ciclul ori de
cte ori este nevoie.
Analiza performanelor presupune- renunnd la acuratee- estimarea timpului de lucru
i a spaiului de stocare, netiind nc limbajul care va fi folosit i calitatea programului ce se
va obine.
Presupunnd c modelul RAM de main pe care lucrm execut instruciuni
pseudocod, i ca fiecare instruciune pseudocod consum acelai timp de execuie, rezult c
timpul estimat pentru execuia unui algoritm este proporional cu numrul instruciunilor
executate de acel algoritm.
Timpul de execuie al algoritmului depinde de:

dimensiunea datelor de intrare

spaiul de memorie suplimentar ocupat


Dimensiunea datelor de intrare este o funcie f(n) care calculeaz, pentru un n dat,
numrul de instruciuni al algoritmului respeciv.
f ( n ) c log 2 n
Estimarea se face pn la o constant c.
Definiie.
Date dou funcii f, g : N N cu f = O(g) sau f(n) = O(g(n)), f este
ordinul de complexitate a lui g dac N0 N i constanta c > 0 astfel nctn>N0 f(n) < c
g(n).
5

Resurse:
1. ro.wikipedia.org/wiki/Main_Turing
2. http://www.slu.edu/colleges/AS/languages/classical/ram/ram.html - exemplu free de
imlmentare a mainii RAM
3.http://facultate.regielive.ro/cursuri/electronica_calculatoare_limbaje_de_programare/structu
ri_de_date_si_algoritmi-668.html
4.http://www.cs.cmu.edu/~mihaib/articles/spatial/spatial-html.html- un exemplu de
imlpementare n hardware a programelor

2.COMPLEXITATEA ALGORITMILOR
Facem precizarea c parte din algoritmii i structurile prezentate n capitolele
urmtoare- cu precdere cele relative la pointeri i fiiere privesc sintaxa i semantica proprie
limbajului C. Unele consideraii teoretice sau practice sunt descrise n pseudocod, C ori
Pascal, scopul declarat fiind acela de a oferi cititorului o imagine ct mai clar a
mecanismelor de funcionare a algoritmilor i de reprezentare a structurilor prezentate, cu
accent mai puin pe specificitatea limbajului folosit pentru implementare.
La evaluarea (estimarea) algoritmilor se pune n eviden necesarul de timp i de
spaiu de memorare al lui.
Studierea complexitii presupune analiza complet n cadrul algoritmului a
urmtoarelor 3 puncte de vedere:
1. configuraia de date cea mai defavorabil (cazurile degenerate);
2. configuraia de date cea mai favorabil;
3. comportarea medie.
Punctul 3 presupune probabilitatea de apariie a diferitelor configuraii de date la
intrare.
Punctul 1 este cel mai studiat i este folosit, de obicei, pentru compararea algoritmului.
i n ceea ce privete timpul, se studiaz configuraia cea mai defavorabil a algoritmului.
Complexitatea unui algoritm se noteaz cu: O(f(n)).
Definiie. Fie f,g : N N dou funcii. Spunem c f O(g) i notm f = O(g) dac
i numai dac o constant c R i un numr n0 N astfel nct pentru n n0 : f(n)
cg(n).

Observaie:
f : N N este o funcie f(n) cu n - dimensiunea datelor de intrare.
f(n) reprezint timpul de lucru al algoritmului exprimat n "pai".
Lema 1. Dac f este o funcie polinomial de grad k, de forma:
f(n) = ak nk + ak-1nk-1 + ... + a1 n + a0,
atunci
f = O(nk).
Fcndu-se majorri n membrul drept, obinem rezultatul de mai sus:
f(n) < ak nk + ak-1 nk-1 + ... + a1 n + a0 < nk ( ak + ak-1 + a0 ) < nk c
pentru n > 1 f(n) < c nk, cu n0 = 1.
Concluzie: f = O(nk), i ordinul O exprim viteza de variaie a funciei, funcie de
argument.
Exemplu: Calcularea maximului unui ir
maxsir(A,n)
{
6

max = A[1]
for i= 2 to n do
if A[i] > max then
max = A[i]
return (max)
}
Exprimm:
T(n) timpul de execuie n pai al acestui algoritm;
T(n)= 1 + 2(n-1) = numrul de atribuiri i comparaii
Cazul cel mai defavorabil: situaia n care vectorul este ordonat cresctor (pentru ca de
fiecare dat se face i comparaie i atribuire).
Putem spune c T(n) = O(n), este o funcie polinomial de gradul I. Conteaz doar
ordinul polinomului, nu coeficientul termenului de grad maxim. Iar la numrarea pailor ne
concentrm asupra numrului buclelor, nu asupra pailor din interiorul buclei.
Exemplu:
Insertion Sort (algoritmul de sortare prin inserare)
Algoritmul INSERTION SORT consider c n pasul k, elementele A[1k-1] sunt
sortate, iar elementul k va fi inserat, astfel nct, dupa aceasta inserare, primele elemente
A[1k] s fie sortate.
Pentru a realiza inserarea elementului k n secvena A[1k-1], aceasta presupune:

memorarea elementului ntr-o varibil temporar;


deplasarea tuturor elementelor din vectorul A[1k-1] care sunt mai mari dect A[k], cu o
poziie la stnga (presupune o parcurgere de la dreapta la stnga);
plasarea lui A[k] n locul ultimului element deplasat.
Complexitate: O(n)
insertion_sort(A,n)
{
for k= 2 to n do
|temp = A[k]
|i=k-1;
|while (i>=1) and (A[i] > temp) do | A[i+1] = A[i];
|
|_ i=i-1;
|_ A[i+1] = temp;
}
Cazul cel mai dafavorabil: situaia n care deplasarea (la dreapta cu o poziie n
vederea nserrii) se face pn la nceputul vectorului, adic irul este ordonat descresctor.
Exprimarea timpului de lucru:
T(n) = 3(n 1) + (1 + 2 + 3+ ... + n 1) = 3(n 1) + 3n (n 1)/2
Rezult complexitatea:
T(n) = O(n2) - funcie polinomial de gradul II.
Observaie: Cnd avem mai multe bucle imbricate, termenii buclei celei mai interioare dau
gradul polinomului egal cu gradul algoritmului.
Bucla cea mai interioar ne d complexitatea algoritmului.
n

i O ( n2 )
i 1

Exemplu: nmulirea a dou matrici


prod_mat(A,B,C,n)
7

{
for i = 1 to n do
for j = 1 to n do
| C[i,j] = 0
|for k = 1 to n do
|_
C[i,j] = C[i,j] + A[i,k] * B[k,j]
}
Rezult complexitatea O(n3).
Exemplu: Cutarea binar (Binary Search)
Fie A, de ordin n, un vector ordonat cresctor. Se cere s se determine dac o valoare
b se afl prntre elementele vectorului. Limita inferioar se numete low, limita superioar se
numete high, iar mijlocul virtual al vectorului, mid.

low

middle

high

Binary_search(A,n,b)
{
low = 1;
high = n;
while low <= high do
| mid = [(low + high)/2];
//partea ntreaga
| if A[mid]=b then return (mid);
| else if A[mid]>b then high=mid-1; //restrng
|
//cautarea la partea stnga
|_
else low = mid+1;
//restrng cautarea la dreapta
return(0);
}
Calculul complexitii algoritmului const n determinarea numrului de ori pentru care se
execut bucla while.
Se observ c, la fiecare trecere, dimensiunea zonei cutate se njumtete. Cazul cel
mai defavorabil este ca elementul cutat s nu se gseasc n vector. Pentru simplitate se
consider n = 2k unde k este numrul de njumtiri.
Rezult k = log2 n i facnd o majorare, T(n) log2 n + 1 n, a.. 2k n < 2k+1.
Complexitatea acestui algoritm: este O(log2n). Baz logaritmului se poate ignora,
deoarece: logax = logbx logab i logab este o constant, ramne deci O(log n), adic o
funcie logaritmic.
Proprieti:
1) Fie f, g : N N.
Dac f = O(g)
| k f = O(g)
| f = O(k g) , k R constant.
2) Fie f, g, h : N N.
i:
f = O(g)
|
g = O(h)
|
f = O(h)
8

3) Fie f1, f2, g1, g2 : N N.


i:

f1 = O(g1) |

| f1 + f2 = O(g1 + g2)
f2= O(g2) |
| f1 f2 = O(g1g2)
Aceasta proprietate permite ca, atunci cnd avem dou bucle imbricate (de
complexiti diferite), complexitatea total s se obin nmulindu-se cele dou complexiti.
Cele dou complexiti se adun, dac buclele sunt succesive.
Teorema.
Oricare ar fi dou constante c > 0, a > 1, i f : N N, o funcie monoton
strict cresctoare, atunci:
(f(n))c= O(af(n))
Demonstraia se bazeaz pe limita:
xp
x a x

( a , p )

lim

ntre clasa funciilor logaritmice, i cea a funciilor polinomiale exist relaia:


c
O(n ) O(an).
Au loc urmtoarele incluziuni:
O(1) O(log n) O(n) O(n log n) O(n2) O(nk log n) O(nk+1) O(2n)
Pentru calculul complexitii se va ncerca ncadrarea n clasa cea mai mic de
complexitate din acest ir:
O(1)

clasa algoritmilor constani;


O(log n)

clasa algoritmilor logaritmici;


O(n)

clasa algoritmilor liniari;


O(n log n)

clasa algoritmilor polilogaritmici;


2
O(n )

clasa algoritmilor ptratici;


O(nk log n)
O(nk+1)

clasa algoritmilor polilogaritmici;

clasa algoritmilor polinomiali;

O(2n)

clasa algoritmilor exponeniali.

Tehnici de calcul a complexitii


Se folosesc urmtoarele sume:
n

i
i 1
n

n ( n 1)
2

O(n2 )

i2

n ( n 1) ( 2 n 1)
6

i3

n2 ( n 1)2
4

i 1
n
i 1
n

O(n3 )

O(n4 )

21 2n1 - 1
i 0

S calculm, de exemplu, suma:

i 21
i 1

Se noteaz:
G ( n)

i 21
i 1

G(n)=2G(n)-G(n)=i=1n2i2l-i=1ni2l=i=1ni2l+1-i=1ni2l=
n2n+1-2+i=2n(i-1-i)2l=n2n+1-2-i=2n2l=(n-1)2n+1+2
Prin aceeai tehnic se calculeaz suma:
n

(n 1) 21
i 1

Am vzut c:

i 2n1 2n1 2 n
i 1

2.1. Algoritmi recursivi i relaii de recuren


Exemplu:
Problema turnurilor din Hanoi
Se dau n discuri: a1, a2, ... , an de dimensiuni diferite, cu d1 < d2 < ... < dn , di fiind diametrul discului. Discurile respecive sunt stivuite pe o tij:

cazul n=3

Fig. 4. Problema turnurilor din Hanoi pentru n=3


Se cere s se deplaseze aceast stiv pe o alt tij, folosind ca manevra o tij auxiliar,
respectndu-se condiia Un disc nu poate fi plasat dect peste un disc mai mare.
Problema P(n) a deplasrii a n discuri, se rezolv prin deplasri succesive ale
discurilor de pe o tij pe alta. Deplasarea de pe o tij pe alta este echivalent cu deplasarea a
n-1 discuri de pe tij iniala (ti) pe tija de manevr, apoi plasarea celui mai lung disc pe tija
final, pentru ca la sfrit s se aduc de pe tija de manevr (tm), pe tija final (tf), cele n-1
discuri deplasate.
Primele micri s-ar figura astfel:

ti

tf

ti

tf

tm

10

Procedura Hanoi:
Hanoi(n, ti, tf, tm)
{
if(n=1) then muta (ti, tf) // deplaseaz discul superior de pe ti pe tf
else
| Hanoi(n-1, ti, tm, tf)
| muta(ti, tf)
|_ Hanoi(n-1, tm, tf, ti)
}
Pentru o problem P(1), timpul T(1) = 1, pentru o mutare.
Pentru P(n), timpul:
T ( n ) 2 T ( n 1) 1

(1)

Dorim s aflm ordinul de complexitate al lui T(n).

T ( n ) 2 T ( n 1) 1 1 2 ( 2 T ( n 2 ) 1)
Asociem relaiei (1) ecuaia caracteristic: x 2 x 1; x0 1; x0 2 x0 1
T ( n ) x0 2 ( T ( n 1) x0 f ( n ) 2 f ( n 1) f ( n ) T ( n ) x0
f ( n 1) T ( n 1) x0 , cu x0 const.

f ( n ) 2 f ( n 1) 2 2 f ( n 2 ) 2 2 2 f ( n 3) 24 f ( n 4 ) ... 2n 1 f (1)

Fcnd identificarea:
2n 1 2 T ( n ) 1
f (1) x0

T(n) = 2 n 1

ordinul este O(2n), adic o complexitate exponenial.


Relaii de recuren. Clasele relaiilor de recuren
1. f ( n ) a f ( n 1) b
f(n) = af(n - 1) + b
x0 = ax0 + b
Prin scderea celor dou relaii, rezult un algoritm exponenial cu baz a:
f(n) - x0 = a (f(n-1) - x0)
f ( n ) a f ( n 1) b f ( n 2 )
2.
f(n) = tn tn = atn-1 + btn-2
Fcnd n = 2 t2 = at + b,
cu urmtoarele cazuri:
a) t1 , t2 R soluia ecuaiei este de forma:
f ( n ) t1n t2n

iar i se calculeaz din condiiile iniiale:


f(1)=x1
f(2)=x2,
adic:
t1+t2=x1
t12+t22=x2
cu x1 i x 2 constante.
Este rezolvat astfel ecuaia recursiv.
b)
t1 = t2 = t soluia este de forma:
f ( n) ( n) t n

c)

t1, t2 C soluia este de forma:


11

f ( n ) t1n t2n

n care i C, = (conjugat) soluia trigonometric:


f ( n ) r n ( 1 cos nt 1 sin nt )

3.

Clasa de relaii de recuren pentru algoritmi de tip "divide et impera"

Exemplu: Algoritmul Merge Sort (sortare prin interclasare)


Pentru a sorta o secven de n elemente ale unui vector A, se mparte vectorul n 2
segmente de lungime n/2 pe care le sorteaz separat recursiv, dup care urmeaz interclasarea.
Pseudocod: Procedura MERGE_SORT primete ca argumente A - vectorul de sortat,
i doi indici care delimiteaz o poriune din acest vector. Apelul iniial va fi
MERGE_SORT(A, 1, n).
MERGE_SORT(A, low, high)
{
if (low high) return
else
|
| low + high
|
| mid=| ---------------- |
//partea ntreaga
|
|_
2
_|
| MERGE_SORT(A, low, mid)
//sortare separata
| MERGE_SORT(A, mid+1, high) //sortare separata
|_MERGE(A, low, mid, high)
//interclasare
}
Procedura MERGE interclaseaz secvenele sortate A[lowmid] i A[mid+1high].
Pentru aceasta este nevoie de un vector auxiliar B, de aceeai dimensiune cu A.
MERGE(A, low, mid, high)
{
i=low; j=mid+1;
k=low;
while i mid and j high do
| if A[i] <A [j] then B[k]=A[i]; i=i+1
| else B[k] = A[j]; j=j+1
|_ k = k+1
while i mid do
| B[k] = A[i]; i=i+1
|_ k = k+1
while j high do
| B[k] = A[j]; j=j+1
|_ k = k+1
for k=low to high do A[k] = B[k];
}
Artm complexitatea procedurii MERGE_SORT: O(n log n)
Aceast funcie cere pentru o secven c n operaii.
Timpul de execuie al algoritmului este:

n 1
0,
T ( n)
2T (n / 2) n, n 1
12

Considerm: n = 2k;
T(n) = 2T(n/2) + n = 2(2T(n/4) + n/2) + n = ... = 22T(n/22) + 2n
= 22 (2T(n/23) + n/22) + 2n = 23T(n/23) + 3n = ... = 2kT(n/2k) + kn

T(n) = kn = nlog2n ,
pentru c n = 2k , i, deci, k = log2n.

Aadar, complexitatea algoritmului este O(n log n).


Pentru a rezolva problema de dimensiune n, se rezolv pentru a probleme de
dimensiune n/b, iar combinarea rezultatelor celor a probleme, duce la g(n) operaii.
Se demonstreaz, analog, i relaia de recuren:
T ( n) a T ( n / b) C nk

Soluia acestei ecuaii de recuren este:


O ( nlogb a ),

a bk

T ( n ) O ( n k log n ),
O ( n k ),

a bk
a bk

Utiliznd aceste reete putem calcula complexitile pentru:


algoritmul Merge_Sort:
a = 2 , b = 2 , k = 1 , bk = a complexitatea O(nklog n)

algoritmul Binary_Search:
a = 1 , b = 2 , k = 0 complexitatea O(n0log n) = O(log n), (situaia ak = b).

4.

Relaii de recuren cu istorie complet


T ( n) C

Exemplu:

n 1

T (i )
i 1

T ( n ) C T ( n 1) T ( n 2 ) ... T (1)
T ( n 1) C

T (i )

- se face T ( n 1) T ( n )

i 1

Exemplu: Algoritmul Quick_Sort


elemente

pivot

A[k]=pivot

elemente

pivot

Quik_Sort(A, low, high)


{
if(high low) then
| k= Partition(A, low, high) // procedura de partiionare
| Quick_Sort (A, low, k-1)
|_ Quick_Sort(A, k+1, high)
}
Pseudocodul pentru funcia partition:
Partition(A, low, high)
{
13

l= low; h= high;
x= A[l];
while (l <h) do
I | while (A[l] x) and (l >_high)
|
do l= l+1
II | while (A[h] >x) and (h low)
|
do h= h-1
|_ if (l <h) then interchange (A[l], A[h])
interchange (A[h], A[low])
return(h);
}
Algoritmul consider pivotul ca fiind: A[low]. Indicele l parcurge vectorul de la stnga
la dreapta, iar indicele h parcurge vectorul de la dreapta la stnga. Ei se apropie pn se
ntlnesc (l = h). Deci, l las n urma numai elemente A[i] pivot, iar h las n urm numai
elemente A[i] pivot.
Ciclul I while nseamn c nainteaz l ct timp A[l] pivot. Acest ciclu se oprete pe
condiia
A[h] pivot, fixndu-se aici.
Ciclul II while nseamn c nainteaz h ct timp A[h] pivot. Acest ciclu se oprete
pe condiia:
A[h] pivot, fixndu-se aici.
Cele dou poziii se schimb, astfel nct s se permit naintarea indicilor mai
departe.

low

Pentru aflarea complexitii, cercetm cazul cel mai defavorabil. Fie cazul n care
vectorul este ordonat descresctor. Pivotul gsit, la primul pas, este elementul maxim din
vector, rezult c trebuie plasat n ultima poziie. Pivotul va fi maximul dntre elementele
secvenei, deci, va fi plasat n ultima poziie din secven.
Problema se mparte n 2 subprobleme: P(n) P(n-1) , P(0).
Numrul de comparaii pentru funcia Partition este (n-1). Vectorul se parcurge n
dou direcii, dar o singur dat.
Rezult c timpul de funcionare al algoritmului Quick_Sort este:
T ( n ) ( n 1) T ( n 1) .

Rezolvnd aceast ecuaie, avem:

T ( n ) n 1 T ( n 1) n 1 n 2 T ( n 2 ) ... n 1 n 2 n 3... 1 T (1)

unde: T(1) este 0 (nu se partiioneaz). Rezult:


T ( n)

n 1

i n(n 1) / 2
i 1

Aceast sum este de complexitate O(n2) deci algoritmul este ineficient.

Studiul complexitii algoritmului Quick_Sort n caz mediu


Pentru complexitatea medie trebuie considerat probabilitatea tuturor apariiilor
datelor de intrare. Considerm c orice configuraie de date la intrare este egal probabil.
Probabilitatea ca pivotul s fie plasat n poziia k este egal pentru k low, high . Aadar,
14

pivotul va fi plasat n poziia k prin partiionare, cu o probabilitate egal cu 1/n, pentru


k low, high ( k 1, n ) .
Suma tuturor probabilitilor este 1. Evenimentul este plasarea pivotului n poziia k.
Considerm Ti(n) timpul de execuie al algoritmului Quick_Sort atunci cnd pivotul este
plasat n poziia i.
Rezult:
Ti ( n ) n 1 T (i 1) T ( n i )

Aceasta se rezolv astfel: nmulind relaia cu n rezult:


n T ( n ) n ( n 1) 2

n 1

T (i )
i 0

Scriem acum relaia nlocuind pe n cu n+1:


( n 1) T ( n 1) ( n 1) n 2

T (i )
i 0

i sczndu-le acum membru cu membru rezult:

( n 1) T ( n 1) nT ( n ) n ( n 1) n ( n 1) 2 T ( n )
( n 1) T ( n 1) 2 n ( n 2 ) T ( n )
1
care se nmulete cu:
,
( n 1)( n 2 )
T ( n 1)
2n
T (n)

n2
( n 1)( n 2 ) n 1

Notm:
T ( n)
F (n)
,
n 1

F ( n 1)

2n
F ( n ),
( n 1)( n 2 )

F ( n)

2 ( n 1)
+ F ( n - 1)
n ( n 1)

Fcnd o majorare:
F (n)
F (n)

2
F ( n 1)
n

2
2
2
2
2
2
2
2
2
2
F ( n 1)
F (n 2)

F ( n 3) ...

... F (i )
n
n n 1
n n 1 n 2
n n 1 n 2
2

F (i ) 0 (un element nu se ordoneaz).

F ( n ) 2 ln n 2 ln 1 F ( n ) 2 ln n
T ( n)
F ( n ) T ( n ) ( n 1) ln n 2 n ln n T ( n ) O ( n ln n )
n 1

unde O(nln n) este complexitatea acestei funcii.

2.2 Analiza spaiului de memorie consumat ntr-un algoritm


Algoritmi recursivi
Algoritmii recursivi consum memorie suplimentar pentru simularea recursivitii.
Fie urmtoarea procedur recursiv:
parcurgere(l) // l - lista nlnuit (pointer la primul element)
{
if(l 0)
| parcurgere(link(l))
|_ prelucrare(data(l)) // exemplu: afisare
}
Funcia afiseaz o list invers, de la coad la cap.
15

Apelul funciei se face astfel:


se creeaz n stiva programului o "nregistrare de acivare" n care sunt memorate:
- parametrii de apel;
- adresa instruciunii de retur (cu care va coninua programul dup terminarea
execuiei funciei);
se rezerv spaiu pentru variabilele locale.
se execut instruciunile funciei care folosesc parametri i variabile locale din
"nregistrarea de acivare";
se scoate din stiv "nregistrarea de acivare" (decrementarea vrfului stivei), stiva fiind
ordonat;
se coninu cu instruciunea dat de adresa de retur memorat n "nregistrarea de
acivare".
Aadar, variabilele globale (statice) sunt memorate ntr-o zon de memorie fix, mai
exact n segmentele de date. Variabilele automate (locale) se memoreaz n stiv, iar
variabilele dinamice n "heap"-uri (cu malloc n C, i cu new n C++).
Consumul de memorie al algoritmului recursiv este proporional cu numrul de apeluri
recursive ce se fac. Variabilele recursive consum mai mult memorie dect cele iterative. La
prelucrarea unei liste, dac primul element nu este vid, se prelucreaz acesta, urmnd apoi ca
restul listei s fie considerat ca o nou lista mai mic, etc.
De exemplu, algoritmul Quick_Sort:
Quick_Sort(A, low, high)
{
if(low < high)
| k = Partition(A, low, high)
| Quick_Sort(A, low, k-1)
|_Quick_Sort(A, k+1, high)
}

low

high

Avem n acest algoritm dou apeluri recursive.


Cazul cel mai defavorabil:

k
high

low

Considerm consumul de memorie n stiv : M(n) = c + M (n - 1) M(n) = O(n)


un ordin de complexitate mare.
Pentru reducerea consumului de memorie, se concepe un alt algoritm la Quick_Sort,
astfel nct un apel s fie rezolvat recursiv, iar cellalt apel iterativ.

k
secventa mica rezolvata recursiv

secventa mare rezolvata iterativ

Quick_Sort(A, low, high)


{
16

while (low < high)


| k = Partition(A, low, high)
| if( k-low > high-k)
|
| Quick_Sort(A, k+1, high)
|
|_high = k-1
| else
|
| Quick_Sort(A, low, k-1)
|_
|_low = k-1
}
Necesarul de memorie pentru aceasta este M(n) c + M(n/2), nsemnnd c oricare ar
fi secvena mai mic, ea este dect jumtatea M(n) = O(log n) am redus ordinul de
complexitate.

2.3. Arbori
Exemplu de structur de arbore:
a
/ \
b
/ \
d
e
/
h

c
/ \
f
g
\
\
i
j

Exemple de arbori:
poligoane
/
\
triunghi patrulatere
/ \ \
dreptunghi romb oarecare
Definiie
Se numete arbore cuplul format din V i E : T= (V,E) cu V - o mulime de noduri i
E VxV - o mulime de arce, cu proprietile:
1) nodul r V (nodul rdcin) astfel nct j V, (j, r) E
2) x V\{r} , y V unic, astfel nct (y, x) E (Cu alte cuvinte, pentru toate nodurile
care nu-s rdcin, un singur arc ce intr n nodul respeciv)
3) y V, un drum { r = x0, x1, x2, ... ,xn= y} , cu xi V i (xi, xi+1) E (sau arborele
trebuie s fie un graf conex: nu exist noduri izolate sau grupuri de noduri izolate).
Proprietate
Dac T, T= (V, E) este un arbore i r V este rdcina arborelui, atunci mulimea T\
{r}=(V',E'), cu V' = V -{r} i E' = E -{ (r, x)/(r, x) E } poate fi partiionat astfel nct s
avem mai muli arbori, a cror reuniune s fie T\{r}, i oricare ar fi doi arbori intersectai, s
dea mulimea vid:
T\{r}= T1 T2 ... Tk , Ti Tj = .
17

Definiii
1)
Dac avem (x, y) E , x

predecesorul lui y (tata), y succesorul lui x (fiu)


x
/
y
2)
Fie E(x) = { y, (x, y) E } mulimea succesorilor lui x.
Definim gradul lui x:
degree(x) = E(x) = numrul de succesori.
Definim gradul arborelui :
degree(T) = max{ degree(x), x V},
y se mai numete nod terminal, sau frunz, dac degree(y) = 0, adic dac nu are
descendeni.
Strmoii unui nod sunt aa-numiii ancestors(x) :
ancestors(x) = {r = x0, x1, x2, ..., xk}
cu proprietatea c (xi, xi+1) E , i= {0,k - 1} i (xk, x) E .
Nivelul unui nod :
level(x) = ancestors(x) + 1.
Adncimea arborelui :
depth(T) = max { level (x) pentru x V}.
Exemplu:
A
|
B
C
/ \ |
E F G
/ \
K L
/

\
D
/ | \
H I J
|
M

predecesor(E) = B
succesor(C) = G
E(D) = {H, I, J}
degree(D) = 3
degree(B) = 2
degree(F) = 0
degree(T) = 3
ancestors(L) = {A, B, E}
level(L) = 4 , level(B) = 2 , level(A) = 1
depth(T) = 4
2.3.1. Reprezentarea arborilor
Reprezentarea prin liste generalizate
Se consider c nodurile terminale sunt elemente atomice, iar nodurile de grad 1
sunt subliste. Deci, fie arborele de mai sus scris sub forma:
A( B (E (K, L), F), C (G), D (H (M), I, J))
cu reprezentarea:

18

G 0

0 M

Reprezentarea prin structuri nlnuite


link1

data

......

link2

k=degree(T)

link k

B
C

F
0

K
0

L
0

M
0

Aceast reprezentare are calitatea c, atunci cnd conteaz ordinea descendenilor, ea


poate surprinde structura diferit.
De exemplu:
structura
x
este diferit de structura
x
/ | \
/ | \
vid y vid
y vid vid
Metodele de reprezentare expuse permit s putem identifica legtura nod-descendent
(succesor). Dar, exist aplicaii n care este nevoie de legtura nod-predecesor. Aadar, pare
util reprezentarea arborelui sub forma nodului (data, parent):

19

A 0

Avnd adresa unui nod, se gsesc toi predecesorii, obinndu-se o list nlanuit:
(Reprezentarea TATA):
M

2.3.2. Arbori binari


Un arbore binar este un arbore de grad maxim 2. n cazul acestor arbori, se pot defini
aplicaii, instrumente n plus de operare. Arborii binari pot avea deci gradele 2, 1, 0:
A
/ \
B
/ \
D

A
/
B

C
\
E

A
/ \
B C
/ \ / \
D E F G

/
C

Observaie:
Arborele A este diferit de A
/ \
/ \
B vid
vid B
Structura de baz a unui arbore binar:
rad
/
/

\
\

/ \
/ SAS \
/______ \

/ \
/ SAD \
/_______\

SAS - subarborele stng (binar)


SAD - subarborele drept (binar)
Definiii
1)
Se numete arbore binar strict arborele pentru care oricare ar fi un nod
x V degree(x) = 2, sau degree(x) = 0.
20

Exemplu:
a
/ \
b
c
/ \ / \
d e f g
/ \
h i
2)

Se numete arbore binar complet un arbore binar strict pentru care y cu:
degree(y) = 0 (frunza) level(y) = depth(T)
Cu alte cuvinte, nodurile terminale aparin ultimului nivel din arbore.

Exemplu:
a
/
b
/ \

c
/ \
d
e f
g
/ \ / \ / \ / \
h i j kl mn o
2.3.2.1. Relaii ntre numrul de noduri i structura unui arbore binar
Lema 1
Numrul maxim de noduri de pe nivelul i al unui arbore binar este egal cu 2i-1.
Demonstraia se face prin inducie:
La nivelul 1 avem 20 = 1 nod = rad (rdcina A). Presupunem conform metodei P(n):
pe nivelul n avem 2n-1 noduri. Demonstrm pentru P(n+1): se observ c toate nodurile de
pe nivelul n+1 sunt noduri descendente de pe nivelul n. Notnd niv(i) numrul de noduri de
pe nivelul i rezult:
niv(n+1) 2niv(n) 22n-1 = 2n.
Lema 2
Numrul maxim de noduri ale arborelui binar de adncime h este egal cu 2h -1.
Demonstraie:
Numrul total de noduri este egal cu:
h

i 1

niv (i )

i 1

2i 1 20 21 ... 2h 1

2h 1
2h 1 (progresie geometric).
2 1

Observaie: Numrul maxim de noduri pentru arborele binar se atinge n situaia unui
arbore binar complet.
2h -1 = numrul de noduri n arborele binar complet de adncime h
Lema 3
Notm cu:
n2 - numrul de noduri de grad 2 din arborele binar;
n1 - numrul de noduri de grad 1 din arborele binar;
21

n0 - numrul de noduri terminale (frunze) din arborele binar;


n orice arbore binar, n0 = n2 +1 (nu depinde de n1).
Demonstraie: fie n = n0 + n1 + n2 (numrul total de noduri); conform definiiei,

fiecare nod are un singur predecesor numrul de muchii E = n - 1. Acelai numr de


muchii E = 2 n2 + n1.
Deci, n - 1 = 2n2 + n1 , nlocuind, n0 + n1 +n2 -1 = 2n2 + n1 n0 = n2 + 1 ceea ce
trebuia de demonstrat.
Rezult c ntr-o expresie numrul de operatori binari i unari este egal cu numrul de
operanzi +1.
Lemele se folosesc pentru calcule de complexitate.
Operaii asupra arborilor binari
Operaii curente:
selecia cmpului de date dintr-un nod i selecia descendenilor;
inserarea unui nod;
stergerea unui nod.
2.3.2.2. Traversarea arborilor binari (A.B.)
Traversarea const n "vizitarea" tuturor nodurilor unui arbore ntr-un scop anume, de
exemplu, listare, testarea unei condiii pentru fiecare nod, sau alt prelucrare. O traversare
realizeaz o ordonare a nodurilor arborelui (un nod se prelucreaz o singur dat).
Strategii de traversare:
traversare n preordine: prelucrare n ordinea: rad, SAS, SAD;
traversare n inordine: prelucrare n ordinea: SAS, rad, SAD;
traversare n postordine: prelucrare n ordinea: SAS, SAD, rad.
rad
/ \
SAS SAD

Exemplu de traversare:
a
/
b

c
\

\
e

/ \
g

preordine : A B D E G H C F
inordine : D B G E H A C F
postordine : D G H E B F C A

Funcii de parcurgere (n pseudocod)


Facem notaiile:
p pointer la un nod
lchild(p) pointer la succesorul stng (p stg)
22

rchild(p) pointer la succesorul drept (p drt)


data(p) informaia memorat n nodul respeciv (p data)
n C++ avem:
struct Nod
{
Atom data;
Nod* stg;
Nod* dr;
};
Nod* p;
Procedurile de parcurgere sunt:
preorder(t)
{
if(t 0)

| print (data(t))
| preorder (lchild(t))
|_ preorder (rchild(t))

if(t 0)

| inorder (lchild(t))
| print (data(t))
|_ inorder (rchild(t))

if(t 0)

| postorder (lchild(t))
| postorder (rchild(t))
|_ print(data(t))

// vizitarea uni nod


// parcurgerea subarborilor

}
inorder(t)
{

}
postorder(t)
{

}
Binarizarea arborilor oarecare
Lema 1
Dac T este un arbore de grad k cu noduri de dimensiuni egale (k pointeri n fiecare
nod), arborele avnd n noduri reprezentarea va conine n (k - 1) + 1 pointeri cu valoare zero
(nuli).
Demonstraie: numrul total de pointeri utilizati n reprezentare este nk. Numrul total
de pointeri nenuli este egal cu numrul de arce nk - (n - 1) = n (k - 1) + 1
nr. pointeri nuli n ( k 1) 1
n 1

1
nr. total pointeri
nk
nk

raportul este maxim pentru k = 2.


Rezult c cea mai eficient reprezentare (n structura nlanuit) este reprezentarea n
arbori binari.
2.3.2.3. Arborele binar de cutare (bst)

23

Arborele binar de cutare reprezint o soluie eficient de implementare a structurii de


date numite "dicionar". Vom considera o mulime "atomi". Pentru fiecare element din aceasta
mulime avem: a atomi, este definit o funcie numit cheie de cutare: key(a) k cu
proprietatea c doi atomi distinci au chei diferite de cutare: a1 a2 key(a1) key(a2).
Exemplu: (abreviere, definitie) ("BST","Binary Search Tree")
("LIFO","Last In First Out") key(a) = a.abreviere
Un dicionar este o colecie S de atomi pentru care se definesc operaiile:
insert(S,a)
insereaz atomul a n S dac nu exist deja;
delete(S,k)
terge atomul cu cheia k din S dac exist;
search(S,k)
caut atomul cu cheia k n S i-l returneaz sau determin dac nu este.
O soluie imediat ar fi reinerea elementelor din S ntr-o list nlnuit, iar operaiile
vor avea complexitatea O(n).
Tabelele Hashing
Acestea sunt o alt soluie pentru a reine elementele din S. Complexitatea pentru
arborele binar de cutare n cazurile:
cel mai defavorabil:
O(n);
mediu:
O(log n).
Un arbore binar de cutare este un arbore T ale crui noduri sunt etichetate cu atomii
coninui la un moment dat n dicionar.
T = (V, E) , V = n. (n atomi n dicionar)
Considernd r V (rdcina arborelui), Ts subarborele stng al rdcinii i Td
subarborele drept al rdcinii, atunci structura acestui arbore este definit de urmtoarele
proprieti:
1)
un nod x Ts atunci key(data(x)) < key(data(r));
2)
x Td atunci key(data(x)) > key(data(r));
3)
Ts i Td sunt BST.
Observaii:
1)
Considerm c pe mulimea k este definit o relaie de ordine;
2)
Pentru oricare nod din BST toate nodurile din subarborele stng sunt mai mici dect
rdcina i toate nodurile din subarborele drept sunt mai mari dect rdcina.
Exemple:
15
/ \
7 25
/ \ / \
2 13 17 40
/
/ \
9
27 99
este BST
3)

15
/ \
10 25
/\ /
2 17 13
nu este BST.

n ordine: viziteaz nodurile n ordine cresctoare a cheilor:


2 7 9 13 15 17 25 27 40 99

Funcii:
1)
Search:
search(rad,k) // rad

pointer la rdcina arborelui


24

// k cheia de cutare a arborelui cautat


if (rad = 0) then return NULL
else
if key (data (rad)) > k
then return search (lchild (rad))
else
if key (data (rad)) < k
then return search (rchild (rad))
else return rad

}
Insert:
Se va crea un nod n arbore care va fi plasat la un nou nod terminal. Poziia n care
trebuie plasat acesta este unic determinat n funcie de valoarea cheii de cutare.
2)

Exemplu: vom insera 19 n arborele nostru:


15
/ \
7
25
/ \ / \
2 13 17 40
/
\
/ \
9
19 27 99
insert(rad,a)
{

// rad

referinta la pointerul la rdcina arborelui

if (rad= 0) then rad= make_nod(a)


else if key (data (rad)) > key(a)
then insert(lchild(rad),a)
else if key (data(rad)) < key(a)
then insert (rchild (rad),a)
}
Funcia make_nod creaz un nou nod:
make_nod(a)
{
p= get_sp()
data(p)= a
lchild(p)= 0
rchild(p)= 0
return(p)

// alocare de memorie pentru un nod nou

}
Observaie:
1)
La inserarea unui atom deja existent n arbore, funcia insert nu modific structura
arborelui. Exist probleme n care este util contorizarea numrului de inserri a unui atom n
arbore.
2)
Funcia insert poate returna pointer la rdcina fcnd apeluri de forma p= insert(p,a).
3)
Delete:
delete(rad,k) // rad

referinta la pointer la rdcina


25

// k cheia de cutare a atomului care trebuie sters


if rad = 0 then return // nodul cu cheia k nu se afla n arbore
else if key(data(rad)) > k then
delete(lchild(rad),k)
else if key(data(rad)) < k then

delete (rchild(rad),k)
else delete_root(rad)
}
tergerea rdcinii unui BST.:
1)
rad arbore vid
2)
a) rad
sau
b) rad a) SAS
/
\
SAS
SAD

sau

b) SAD

delete_root(rad)
// rad referinta la pointer la rdcina
{
if lchild(rad)=0 then
| p= rchild(rad)
| ret_sp(rad)
|_ rad= p
else if rchild(rad)= 0 then
| p= lchild(rad)
| ret_sp(rad)
|_ rad= p
else |
p=
remove_greatest(lchild(rad))
| lchild(p)= lchild(rad)
| rchild(p)= rchild(rad)
| ret_sp(rad)
|_ rad= p
}
15
/ \
7
25
/ \ / \
2 13 17 40
/
/
9
27
/ \
26 33
Detaarea din structura arborelui BST a celui mai mare nod- (remove_greatest):
Pentru a gsi cel mai mare nod dintr-un arbore binar de cutare, se nainteaz n
adncime pe ramura dreapt pn se gsete primul nod care nu are descendent dreapta.
Acesta va fi cel mai mare.
Vom trata aceast procedur recursiv:
Caz1: rad se returneaz pointer la rdcin i arborele rezultat va fi vid.

26

Caz2: rad se returneaz pointer la rdcin i arborele rezultat va fi format doar


din SAS subarborele stng (SAS).
Caz3: rad funcia returneaz pointer la cel mai mare nod din SAD, iar rezultatul va
fi SAS arborele care este fomat din rdcina, SAS i SAD cel mai mare nod.
remove_greatest(rad) // rad -referinta la pointer la rdcin: un pointer la rdcin ce
poate fi modificat de catre funcie
{
if rchild (rad)= 0 then | p= rad
| rad= lchild (rad)
|_ return(p)
else return (remove_greatest (rchild(rad)))
}
Observaie:
Funcia remove_greatest modific arborele indicat de parametru, n sensul eliminarii
nodului cel mai mare, i ntoarce pointer la nodul eliminat.
Demonstraia eficienei (complexitate)
Complexitatea tuturor funciilor scrise depinde de adncimea arborelui. n cazul cel
mai defavorabil, fiecare funcie parcurge lantul cel mai lung din arbore. Funcia de cutare
are, n acest caz, complexitatea O(n).
Structura arborelui BST este determinat de ordinea inserrii.
De exemplu, ordinea 15 13 12 11 este alta dect 15 12 11 13 .
Studiem un caz de complexitate medie:
Crearea unui BST pornind de la secvena de atomi (a1 a2 ... an)
gen_BST
(va fi n programul principal)
| rad= 0
| for i= 1 to n
|
insert (rad, ai)
Calculm complexitatea medie a generrii BST:
Complexitatea n cazul cel mai defavorabil este:
n

( i ) O( n

i 1

Notm cu T(k) numrul de comparaii mediu pentru crearea unui BST pornind de
la o secven de k elemente la intrare. Ordinea celor k elemente se consider aleatoare.
Pentru problema T(n) avem de creat secvena (a1 a2 ... an) cu observaia ca a1 este
rdcina arborelui. Ca rezultat, n urma primei operaii de inserare pe care o facem, rezult:
a1
/ \
a1
ai
(ai<a1) (ai>a1)

27

Nu vom considera numrarea operaiilor n ordinea n care apar ele, ci considerm


numrul de operatii globale. Dupa ce am inserat a1, pentru inserarea fiecarui element n SAS
sau SAD a lui a1, se face o comparaie cu a1. Deci:
T(n)= (n - 1) + val.med.SAS + val.med.SAD
val.med.SAS = valoarea medie a numrului de comparatii necesar pentru a construi
subarborele stng SAS;
val.med.SAD = valoarea medie a numrului de comparatii necesar pentru a construi
subarborele drept SAD;
n

i 1

i 1

T ( n ) ( n 1) (1 / n ) T ( i 1) (1 / n ) T ( n i )

Notm:
Ti(n) = numrul mediu de comparaii necesar pentru construirea unui BST cu n noduri
atunci cnd prima valoare inserat (a1) este mai mare dect i-1 dntre cele n valori de inserat.
Putem scrie:
Ti ( n ) ( n 1) T ( i 1) T ( n i )
n

T ( n ) (1 / n ) Ti ( n )
i 1

Deci:
n

T ( n ) (1 / n ) (( n 1) T ( i 1) T ( n i ))
i 1

i 1

i 1

( n 1) (1 / n ) T ( i 1) (1 / n ) T ( n i )
n

( n 1) ( 2 / n ) T ( i 1)
i 1

Astfel:

n 1

T ( n) n 1 (2 / n) T (i)
i 0

Complexitatea acestei funcii este: O(nln n) (vezi - complexitatea medie a


algoritmului Quick-Sort)
2.3.2.4. Arbori binari de cutare dinamic echilibrai (AVL)
Definiie
Un arbore binar este echilibrat dac i numai dac, pentru fiecare nod din arbore,
diferena dntre adncimile SAS i SAD n modul este 1.
Exemple:
a

a
/ \
b
c
b
c
/ \ / \
/ \
\
d
e f
g
d e
f
/ \
g
h
arbore binar
arbore binar
complet echilibrat echilibrat
/

28

Adncimea unui arbore echilibrat cu n noduri este O(ln n).


Se completeaz operaiile insert i delete cu nite prelucrri care s pstreze
proprietile de arbore binar echilibrat pentru arborele binar de cutare. Arborele binar
echilibrat este un BST echilibrat, proprietatea de echilibrare fiind conservat de insert i
delete. Efortul, n plus, pentru completarea operaiilor insert i delete nu schimb
complexitatea arborelui binar echilibrat.
Transformarea structurii arborelui dup inserare pentru a conserva proprietatea de
arbore binar echilibrat
Modificrile care se vor face se vor numi rotaii.
Caz 1:

Fie arborele echilibrat


A

h = depth(T1) = depth(T2) = depth(T3)

/ \
B T3
/ \
T1 T2

Considerm arborii T1, T2, T3 echilibrai. Insernd un nod prin rotaie simpl, rezult
structurile rotit simplu la dreapta i rotit simplu la stnga
dreapta:
A
/ \
B T3
/ \
T1 T2

Caz 2:

imaginea oglind a rotaiei

A
/ \
T3 B
/ \
T2 T1

Inserarea se face prin rotaii duble:


A
/ \
B T3
/ \
T1 T2

rotaie dubl
la dreapta

A
/ \
T3 B
/ \
T2 T1

rotaie dubl
la stnga

Fie primul caz:


A
/ \
B T3
/ \
T1 T2
este BST: T1 < B < T2 < A < T3

29

Toi arborii care respect n coninuare aceast condiie vor fi BST.


Ridicnd pe B n sus, i notnd cu // legturile neschimbate, rezult:

B
// \
// A
T1
/ \\
____________T2_T3_________ pe aceeai linie
care este un BST, deci este arbore echilibrat, i are aceeai adncime (!!!) cu arborele iniial
(de dinainte de inserare). Nodurile superioare nu sunt afectate. Rationament analog pentru
imaginea oglind.
Fie cazul 2: Pentru rotaia dubla se detaliaz structura arborelui T2. Nu se poate
sparge arborele iniial ca n cazul 1.
A
/ \\
B
\\
// \
T3
// C
//
/ \
T1 T2S T2D

depth(T1) = depth(T3) = h
depth(T2S) = depth(T2D) = h - 1
n urma inserrii, unul dntre arborii T2S i T2D si mrete adncimea. Aplicam
aceeai
tehnic:
T1 < B < T2S < C < T2D < A < T3
ncepem cu C:
C
/
\
B
A
// \
/ \\
//
T2S T2D \\
____T1_________ T3_____________________
la acelasi nivel
Rezult un BST echilibrat, de aceeai adncime cu arborele iniial. Rotatiile sunt
duble, n sensul c s-a facut o rotaie simpl B la stnga cu o rotaie simpla A la dreapta.
Operaiile care trebuiesc fcute n cazul 1 (rotaie simpl la dreapta):
r pointer la nodul rdcina (A)
a pointer la rdcina
30

p = lchild(r)
b = lchild(a)
lchild(r) = rchild(p)
lchild(a) = rchild(b)
rchild(p) = r
rchild(b) = a
r=p
a=b
Operaiile care trebuiesc fcute n cazul 2 (rotaie dubl)
b = lchild(a)
c = rchild(b)
lchild(a) = rchild(c)
rchild(b) = lchild(c)
rchild(c) = a
lchild(c) = b
a = c // se schimba rdcina arborelui.
Aadar, n inserarea prin rotaie se obine un arbore echilibrat cu adncimea egal cu
adncimea arborelui de dinainte de inserare. La inserarea unui nod terminal ntr-un arbore
AVL este necesar aplicarea a cel mult o rotaie asupra unui nod. Trebuie, deci s gasim nodul
asupra cruia trebuie aplicat rotaia. Reprezentm ramura parcurs de la rdcin la nodul
inserat:
bf = 1

x
/

bf = 0

y
\

bf = - 1 (bf = -2 dup inserare)

z
\
w

bf = 0 (bf = 1 dup inserare)

/
bf = 0 (bf = -1 dup inserare)

v
\
nodul
inserat

S-a notat pentru fiecare nod bf balance factor (factor de dezechilibrare):


bf(nod) = depth (lchild (nod)) depth (rchild (nod))
adic este diferena dntre adncimea subarborelui stng i adncimea subarborelui drept.
Calculm factorii de balansare dup inserare.
Observaie: Pentru nodul terminal s-a schimbat adncimea i factorul de balansare; bf = -2
dup inserare devine nod dezechilibrat. Trebuie aplicat, deci, echilibrarea.
Definiie:
Se numete nod critic primul nod cu bf 0 ntlnit la o parcurgere de jos n sus a
ramurii care leag nodul inserat de rdcin.
Observaie: Toate nodurile din ramur care sunt pe nivele inferioare nodului critic vor cpta
bf = 1 sau bf = -1.
La nodul critic exist dou situaii:

31

1.
Nodul critic va fi perfect balansat (bf = 0), dac dezechilibrul creat de nodul inserat
anuleaz dezechilibrul iniial al nodului. n acest caz nu este nevoie de rotaie (el completeaz
un gol n arbore).
2.
Factorul de balansare devine bf = 2 sau bf = -2 atunci cnd nodul inserat mareste
dezechilibrul arborelui (s-a inserat nodul n subarborele cel mai mare). n acest caz, se aplica
o rotaie n urma creia se schimb strucutra subarborelui, astfel nct noua rdcin capat bf
= 0, conservndu-se adncimea.
Concluzie: Problema conservrii proprietii de echilibrare a arborelui se rezolv aplicnd o
rotaie asupra nodului critic numai atunci cnd inserarea dezechilibreaz acest nod.
Costul suplimentar care trebuie suportat se materializeaz prin necesitatea ca n fiecare
nod s se memoreze factorul de dezechilibrare bf. Aceti factori de dezechilibrare vor fi
actualizai n urma operaiilor de rotaie i inserare. Operaia de tergere ntr-un AVL implic
mai multe rotaii.
Exemplu: Se d arborele cu urmtoarea structur:
55
/
20
/ \
10 35
/
/
5
30

\
80
\
90

S se insereze nodurile 15, apoi 7 i s se echilibreze arborele.


Inserm prima valoare 15. Comparm mai nti cu 55 : e n stnga lui, .a.m.d. pn
cnd gsim locul ei n poziia de mai jos. Pentru a douvaloare de inserat, 7, nodul critic este
55. El este dezechilibrat stnga. Deci, va fi echilibrat la valoarea 2. Este necesar aplicarea
unei rotaii asupra rdcinii.
55
/
20
/ \
10
35
/ \
/
5
15 30
\
7

\
80
\
90

Facem o identificare cu unul din desenele de echilibrare prezentate anterior. Se


asociaz nodurile:
55 A
20 B etc.

20 ----------> noduri implicate


/ \
n
10
55 ------> rotaie
/ \ / \
32

15 35 80
\
/
\
7 30
90

n urma unei rotaii simple, factorii de dezechilibru implicai n rotaie devin zero.
Fie o a treia valoare de inserat 3, apoi a patra 9:
Nodul critic pentru 3 este 5, iar pentru 9 este este 10. Dup rotaia aplicat, rezult
arborele:

20
/
\
7
55
/ \
/ \
5 10 35 80
/ / \
/
\
3 9 15 30
90
La rotaia dubla, dac nodul 9 a fost inserat n subarborele T2S,
B are bf = 0 |
A are bf = -1 |
excepie fcnd nodul C nodul de inserat
La rotaia dubla, dac nodul 9 a fost inserat n subarborele T2D,
B are bf = 1 |
A are bf = 0 |
excepie fcnd nodul C nodul de inserat
2.3.2.5. Reprezentarea implicit a arborilor binari
n acest mod de reprezentare se reprezinta arborele printr-un tablou. Fie un arbore
binar complet:
a
/

/ \
d
e
/ \ / \ /
h i j
k l

/ \
f
g
\ / \
m n o

care are 4 nivele, deci 24 - 1 = 15 noduri.


Asociem acestui arbore un vector V:
V:
a

1
indici

10

11

12

13

14

15

structurat astfel: rdcina, nivelul 1 de la stnga la dreapta, nodurile nivelului 2 de la stnga la


dreapta, etc, despartite de linii duble.
Lema
33

Dac n vectorul V un nod x este reprezentat prin elementul de vector V(i), atunci:
1. left_child(x) este reprezentat n elementul de vector V [2i];
2. right_child(x) este reprezentat n elementul de vector V [2i + 1];
3. parent(x) este reprezentat n elementul de vector V [[i/2]]
cu observaia c paranteza ptrat interioar este partea ntreg.
Demonstratie:
Se face inducie dup i:
Pentru i = 1 V[1] rdcina
V[2] left_child(rad)
V[3] right_child(rad)
Presupunem adevrat lema pentru elementul
V[i] V[2i] left_child
V[2i + 1] right_child
Elementul V[i + 1] este nodul urmto*r (de pe acelasi nivel sau de pe nivelul
urmto*r) ntr-o parcurgere binar.
V[i + 1] left_child n V[2i + 2] = V[2(i + 1)]
right_child n V[2i + 3] = V[2(i + 1) + 1]
Dac avem un arbore binar care nu este complet, reprezentarea lui implicit se obine
completndu-se structura arborelui cu noduri ficive pn la obinerea unui arbore binar
complet.
2.3.2.6. Arbori heap (heap

grmad)

Definiie:
Se numete arbore heap un arbore binar T = (V, E) cu urmtoarele proprieti:
1)
funcia key : V R care asociaz fiecarui nod o cheie.
2)
un nod v V cu degree(v) > 0 (nu este nod terminal), atunci:
key(v) > key(left_child(v)), dac left_child(v)
key(v) > key(right_child(v)), dac right_child(v)
(pentru fiecare nod din arbore, cheia nodului este mai mare dect cheile descendentilor).
Exemplu:
99
/ \
50
30
/ \ / \
45 20 25 23
Observaie: De obicei funcia cheie reprezint selecia unui subcmp din cmpul de date
memorate n nod.
Aplicaii ale arborilor heap

Coada cu prioritate;

Algoritmul Heap_Sort
Resurse:
1. http://www.cs.cmu.edu/~mihaib/articles/algoritmi/algoritmi-html.html - complexitatea
algoritmilor i noiuni conexe
2.http://thor.info.uaic.ro/~adiftene/Scoala/PA/PA%20-%20Seminar01%20-%20Complexitatea
%20Algoritmilor.pdf seminarii de proiectarea algoritmilor, cu exemple de calcul i exerciii
3. http://www.cs.utt.ro/%7Emarius/curs/upc/curs16.pdf - despre algoritmii de sortare
34

4. E. Horowitz, S. Sahni, "Fundamentals of Computer Algorithms" 1985 ca i


"Fundamentals of Data Structures".
5. T. H. Cormen, C.E. Leiserson, R.R. Rivest, Introducere n algoritmi, Computer Libris
Agora, 2000,
6. R. Viinescu, V. Viinscu- Algoritmi i structuri de date- Teorie i aplicaii. Probleme de
concurs, Editura Edusoft, 2006
7. http://software.ucv.ro/~buricea_mihail/luc6p10.html - liste, stive...(C/C++)
8. http://www.upg-ploiesti.ro/curs_multimedia/conv12/5.htm- arbori, parcurgere (Pascal)

35

3. GRAFURI. TRAVERSRI PE GRAFURI


Definiie:
Fie G = (V, E) o mulime, n care V este o mulime de noduri finita, V = n , i E o
mulime de arce, E = {(i, j), i, j N }. G se numete graf, E VV.
Avem: grafuri orientate n care (i, j) (j, i)
grafuri neorientate n care (i, j) E (j, i) E
Observaie: Un arbore este un caz particular de graf.
Definiii:

i se numete predecesor al lui j;

j se numete succesor al lui i;

(i, j)- este adiacent cu i i j;

i, j - adiacente;

Ei = { k, (k, i) E} mulimea de vecini la intrare;

E'i = { j, (i, j) E} mulimea de vecini la iesire;


Ei E'i mulimea vecinilor.
grad_intrare(i) = in_degree(i) = Ei
grad_ie_ire(i) = out_degree(i) = E'i
Pentru un graf neorientat (G - neorientat), Ei E'i i degree(i)= Ei.

Definiie
Se numete drum orientat ntr-un graf de la x la y secvena de noduri D = (i1 = x,
i2, ... , in = y),
(ik, ik+1) E, k 1, n .
Drumul D este neorientat, dac (ik, ik+1) E sau (ik+1, ik) E
Definiie
Se numete graf conex graful pentru care dounoduri (x, y) V, D un drum de la
x la y.
Un graf este complet dac fiecare nod este conectat cu oricare din celelalte noduri: E
= VV \ {(i, i), i V}
Definiie
Fie G = (V, E) un graf. Se numete subgraf al grafului G, un graf G' = (V', E'), astfel
nct V' V, E' (V'V') E
Reprezentri
1)
Matrice de adiacen
Fie G = (V, E) ,V = n. Se determin matricea de adiacen
astfel:
M n n , M (i , j )

1 (i , j ) E
0 (i , j ) E

36

Un graf este etichetat dac o funcie definit pe E R, adic fiecarui arc i se


asociaz o valoare. Astfel de grafuri se reprezint prin matrici de colecivitate:
C (i , j )

0 (i , j ), (i , j ) E
,
(i , j ) E

Complexitate:

1)

O(1), (i, j) E
O(n), Ei, E'i, indegree(i), outdegree(i)
O(n2), spaiu

Liste de adiacen direct

Definiie
Fie G = (V, E) un graf, V = n , V = {1, 2, ... , n}. Se numete list de adiacen
asociat acestui graf o colecie de n pointeri (1, 2, ... , n), fiecare pointer coninnd adresa
unei liste dup regula urmtoare: L(i) da adresa listei nlanuite care conine toti succesorii lui
i.
L(i) Ei
Exemplu:
2

2)

Liste de adiacene inverse


(i,j) E
O(E) cardinal de E
L'(1, 2, ..., n), L'(i) E'i
Ei: O(E) sau O(maxout_degree)
E'i: O(E)
spatiu: O(E + n)

3)

Liste de adiacen dubl


(i,j) , E, (i,j, e(i,j))

Operaii pe grafuri, traversri


La grafuri se folosesc operaii ntre elemente ((i,j) adiacente, etc), operaii de
traversare pe grafuri.
37

Se numete traversare pe graf o procedur de traversare, pentru un scop bine definit,


prin toate nodurile.
Exist: traversri n adncime depth first
traversri n latime bmedth first
Proceduri:
dfs(G, i)
{

// mark(1... n) initializat cu (00 ... 0) ext


// procesare()
procesare(i)
mark(i) = 1
for fiecare k V cu (i,k) E
| if mark(k) = 0
|_dfs(k)

}
bfs(G, i)
{

// mark(1... n) initializat cu (0 ... 0)

ext o coada q

isempty,
// a= pop(p), put(q) ext
procesare(i)
mark(i) = 1
put(q, i)
while(is Empty(q) = N0)) do
| k = pop(q)
| for fiecare j V , (k,j) E
|
| if(mark(j)=0)
|
|
| procesare(j)
|
|
| mark(j) = 1
|_
|_
|_ put(q,j)
}
1)

Matrice adiacen
dfs(M, i)
{
procesare(i)
mark(i) = 1
for k = 1 to n
| if (M(i,k) = 1)
|
| if mark(k) = 0
|_
|_ dfs(k)
}

2)

Liste de adiacen direct


dfs(L, i)
{
procesare(i)
38

mark(i) = 1
p = P(i)
while p 0
| if mark(data(p)) 0
| dfs(data(p))
|_ p = link(p)
}
Arbori de acoperire pe grafuri
Fie G(V, E) un graf.
Se numete arbore de acoperire un arbore T = (V, E') , cu E' E
Observaie: Algoritmii dfs, bfs produc arbori de acoperire.
Resurse:
1. www.cwu.edu/~andonie/Cartea%20de%20algoritmi/cap9.htm - 87k- explorri n grafuri
2. http://inf.ucv.ro/~bazavan/courses/CB1103/tutorial3.pdf- similar ca mai sus, inclusiv arbori

4. STRUCTURI DE DATE ELEMENTARE


O structur de date presupune un mod de organizare a datelor (n tablouri, structuri,
etc), i definirea operaiilor acceptate. Deci, o structur de date reprezint o colecie
organizata de date i un set de operaii definite.
i noiunea de tip de date presupune: |--- reprezentare
|--- operaii acceptate
De exemplu, pentru tipul int: |--- reprezentare: pe doi octeti: cod
|
complementar
|---operaii acceptate: +, -, *, /, &, |, etc.
Dac pentru un tip de date nu intereseaz reprezentarea, ci doar operaiile acceptate,
nseamn c tipul de date este abstract.

4.1. Pointeri i adrese


Definiie
Un pointer este o variabil care conine adresa unei alte variabile. Pointerii sunt
foarte mult utilizai n C pentru c uneori sunt singura cale de rezolvare a unei anumite
probleme, iar alteori folosirea lor duce la alctuirea unui cod mai compact i mai eficient
dect altul obinut n alt mod.
Lucrul cu pointeri presupune atenie ntruct, folosii neglijent, se pot crea uor
pointeri care s pointeze n locuri cu totul neateptate, fapt ce poate conduce la deteriorarea
anumitor programe ori date. Cu metod, pointerii se utilizeaz pentru un plus de simplitate.
Acesta este aspectul pe care vom ncerca s-l ilustrm.
Din moment ce un pointer reine adresa unui obiect, este posibil adresarea acelui
obiect "indirect" prin intermediul pointerului.
S presupunem c x este o variabil, s spunem de tip int i c px este un pointer creat
ntr-un mod neprecizat. Operatorul & d adresa unui obiect, astfel nct instruciunea
39

px=&x
asigneaz variabilei px adresa lui x acum, px nseamn "pointeaz pe x". Operatorul & poate
fi aplicat numai variabilelor i elementelor unui tablou, construcii ca &(x+1) i &3 sunt
interzise.
Este deasemenea interzis pstrarea adresei unei variabile registru.
Operatorul unar * testeaz operandul su ca adresa ultimului semnal i acceseaz
aceast adresa pentru a aduce coninutul locaiei de la adresa respeciv. Astfel, dac y este tot
un int
y = *px
asigneaz lui y, ori de cte ori este cazul coninutul locaiei unde pointeaz px. Astfel,
secvena
px = &x;
y = *px;
asigneaz lui y aceai valoare ca i
y=x
Totodat este necesar declararea variabilelor care apar n secven:
int x, y;
int *px;
Declararea lui x si y este deja cunoscuta. Nou este declararea pointerului px
int *px
aceasta este interpretat ca o mnemonic: nseamn c *px este un int, adic n momentul n
care px apare n context sub forma *px, este echivalent cu a ntlni o variabil de tip int. De
fapt, sintaxa declarrii unei variabile imit sintaxa expresiilor n care ar putea s apar
respeciva variabil. Acest raionament este util n toate cazurile care implic declaraii
complicate. De exemplu:
double atof(), *dp;
spune c ntr-o expresie atof() i *dp au valoare de tip double.
De notat declaraia implicit, ceea ce vrea s nsemne c un pointer este constrns s pointeze
o anumita categorie de obiecte. (Funcie de tipul obiectului pointat).
Pointerii pot aparea n expresii. De exemplu, dac px pointeaz pe ntregul x atunci *px poate
aprea n orice context n care ar putea apare x.
y = *px + 1
d lui y o valoare egala cu x plus 1.
printf("%d\n", *px)
40

imprim o valoare curent a lui x i


d = sqrt((double) *px)
face ca d = radical din x, care este forat de tipul double nainte de a fi transmis lui sqrt.
n expresii ca:
y = *px + 1
operatorii unari * i & au prioritate mai mare dect cei aritmetici, astfel aceast expresie ori de
cte ori pointerul px avanseaz, adun 1 i asigneaz valoarea lui y. Vom reveni pe seama
asupra a ceea ce nseamn
y = *(px + 1)
Referiri prin pointer pot apare i n partea stnga asignrilor.
Dac px pointeaz pe x atunci
*px = 0
l pune pe x pe zero i
*px += 1
l incrementeaz pe x, ca i
(*px)++
n acest ultim exemplu parantezele sunt necesare; nepunndu-le, expresia va incrementa pe px
n loc s incrementeze ceea ce pointeaz px deoarece operatorii unari * i + sunt evaluai de la
dreapta la stnga.
n sfrit, dac pointerii sunt variabile, ei pot fi manipulai ca orice alta variabil. Dac py este
un alt pointer pe int, atunci
py = px
copiaz coninutul lui px n py fcnd astfel ca py s varieze odat cu px.

4.1.1. Pointeri i argumente de funcii


Datorit faptului c n C este posibil transmiterea de argumente unei funcii prin "apel
prin valoare" nu exista o modalitate direct pentru funcia apelat de a altera o variabil n
funcia apelant. Ce este de fcut atunci cnd se intenioneaz schimbarea unui argument
obinuit? De exemplu, o rutin de sortare trebuie s inverseze dou elemente neordonate cu o
funcie schimba. Nu este suficient s se scrie:
schimba(a, b);
unde funcia schimba este definit ca

41

schimba(x, y) /* GRESIT */
int x, y;
{
int temp;
temp = x;
x = y;
y = temp;
}
Din cauza apelului prin valoare, schimba nu poate afecta argumentele a i b n rutina
care o apeleaz.
Din fericire, exist o modalitate de a obine efectul dorit. Programul apelant trasmite
pointeri pe valorile care trebuie schimbate.
swap(&a, &b);
Din moment ce operatorul & d adresa unei variabile, &a este un pointer pe a. n swap nsi,
argumentele sunt declarate ca fiind pointeri iar adevraii operanzi sunt accesai prin ei ( prin
pointeri).
swap(px, py) /* interschimba *px i *py */
int *px, *py;
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
O utilizare comun a argumentelor de tip pointer se ntlnete n cadrul funciilor care
trebuie s returneze mai mult dect o singura valoare. (Vei putea obiecta c swap returneaz
dou valori, noile valori ale argumentelor sale.) Ca un exemplu s lum o funcie getint care
realizeaz inversia la intrare prin transformarea unui ir de caractere n valori ntregi, un ntreg
la fiecare apel, getint trebuie s returneze valoarea gsit sau semnul de sfrit de fiier atunci
cnd s-a terminat irul de caractere de la intrare. Aceste valori trebuie s fie returnate ca
obiecte separate, pentru indiferent ce valoare este utilizat pentru EOF aceasta putnd fi
deasemenea valoarea unui ntreg-input.
O soluie, care este bazat pe funcia input scanf, este de a folosi getint care s
returneze ca valoare o funcie EOF, atunci cnd se ntlnete sfritul de fiier; orice alt
valoare returnat nseamn c a fost prelucrat un ntreg obinuit. Valoarea numeric a
ntregului gsit este returnat printr-un argument care trebuie s fie pointer pe un ntreg.
Aceast organizare separ starea de sfrit de fiier de valorile numerice.
Urmtoarea bucl completeaz un tablou cu ntregi prin apeluri la get int.
int n, v, array[SIZE]
for (n = 0; n < SIZE && getint(&v) != EOF; n++)
array[n] = v;
Fiecare apel pune pe y pe urmtorul ntreg gsit la intrare. De notat faptul c este esenial a
scrie &y n loc de y, ca arg al lui getint. A scrie doar y constituie eroare de adresare, getint
susinnd c are de a face cu un pointer propriu zis.
nsi getint este o modificare evident a lui atoi.
42

getint(pn) /* ia numarul intreg -input */


int *pn;
{
int c, sign;
while ((c = getch() == ' ' || c == '\n' || c == '\t');
/*sare blancuri*/
sign = 1;
if (c == 't' || c == '-') { /* semnul */
sign = (c == '+' ? 1 : -1);c = getch();}
for (*pn = 0; c >= '0' && c <= '9'; c = getch())
*pn = 10 * *pn + c - '0';
*pn *= sign;
if (c != EOF)
ungetch(c);
return(c);
}
Peste tot n getint, *pn este utilizat ca o variabil int obinuit. Deasemenea, am utilizat getch
i ungetch n aa fel nct caracterul special (semnalul EOF) care trebuie citit s poat fi
restocat la intrare.

4.1.2. Pointeri i tablouri


n C, exist o relaie strns ntre pointeri i tablouri, atit de strinsa nct pointerii i
tablourile pot fi tratate simultan. Orice operaie care poate fi rezolvat prin indicierea
tablourilor poate fi rezolvat i cu ajutorul pointerilor. Versiunea cu pointeri va fi n general,
mai rapid.
Declaraia
int a[10]
definete un tablou de dimensiunea 10, care este un bloc de 10 obiecte consecutive numite
a[0], a[1], ..., a[9] (vezi mai jos vectori) notaia a[i] desemneaz elementul deci poziiile, ale
tabloului, numrate de la nceputul acestuia. Dac pa este un pointer pe un intreg, decalarat ca
int *pa
atunci asignarea
pa = &a[0]
face ca pa s pointeze pe al "zero-ulea" element al tabloului a; aceasta nseamn c pa conine
adresa lui a[0]. Acum asignarea
x = *pa
va copia coninutul lui a[0] n x.

43

Dac pa pointeaz pe un element oarecare al lui a, atunci prin definitie pa+1 pointeaz
pe elementul urmtor i n general pa-i pointeaz cu i elemente naintea elementului pointat de
pa iar pa+i pointeaz cu i elemente dup elementul pointat de pa. Astfel, dac pa pointeaz pe
a[0]
*(pa + 1)
refer coninutul lui a[1], pa + i este adresa lui a[i] i *(pa+i) este coninutul lui a[i].
Aceste remarci sunt adevarate indiferent de tipul varaiabilelor din tabelul a. Definiia
"adunrii unitii la un pointer " i prin extensie, toata aritmetica pointerilor este de fapt
calcularea prin lungimea n memorie a obiectului pointat. Astfel, n pa+i, i este nmulit cu
lungimea obiectelor pe care pointeaz pa nainte de a fi adunate la pa.
Corespondena ntre indexare i aritmetica pointerilor este evident foarte strns. De
fapt, referina la un tablou este convertit de ctre compilator ntr-un pointer pe nceputul
tabloului. Efectul este c numele unui tablou este o expresie pointer.
Aceasta are cteva implicaii utile. Din moment ce numele unui tablou este sinonim cu locaia
elementului su zero, asignarea
pa = &a[0]
poate fi scris i,
pa = a
nca i mai surprinztor la prima vedere este faptul c o referin la a[i] poate fi scris
i ca *(a+i). Evalund pe a[i], C l convertete n *(a+i); cele dou forme sunt echivalente.
Aplicnd operatorul & ambilor termeni ai acestei echivalene, rezult c &a[i] este identic cu
a+i: a+i adresa elementului al i-lea n tabloul a. Reciproc: dac pa este un pointer i el poate fi
utilizat n expresii cu un indice, pa[i] este identic cu *(pa+i).
Trebuie inut seama de o diferen ce exist ntre numele tablou i un pointer. Un
pointer este o variabil, astfel c pa=a i pa++ sunt operaii. Dar, un nume de tablou este o
constant, nu o variabil: construcii ca a=pa sau a++ sau p=&a sunt interzise. Atunci cnd se
transmite un nume de tablou unei funcii, ceea ce se transmite este locaia de nceput a
tabloului. n cadrul funciei apelate acest fapt argument este o variabil ca oricare alta astfel
nct un argument nume de tablou este un veritabil pointer, adic o variabil coninnd o
adres. Putem folosi acest lucru pentru a scrie o nou versiune a lui strlen, care calculeaz
lungimea unui ir.
strlen(s) /* returneaz lungimea sirului s */
char *s
{
int n;
for (n = 0; *s != '0'; s++)
n++;
return(n);
}
Incrementarea lui s este perfect legal deoarece el este o variabil pointer; s++ nu are efect pe
irul de caractere n funca care a apelat-o pe strlen, dar incrementeaz doar copia adresei. Ca
parametri formali n definirea unei funcii
char s[]
44

i
char *s;
sunt echivaleni; alegerea celui care trebuie scris este determinat n mare parte de expresiile
ce vor fi scrise n cadrul funciei. Atunci cnd un nume de tablou este transmis unei funcii,
aceasta poate, dup necesiti s-o interpreteze ca tablou sau ca pointer i s-l manipuleze n
consecin. Funcia poate efectua chiar ambele tipuri de operaii dac i se pare potrivit i
corect.
Este posibil i transmiterea catre o funcie doar a unei pari dintr-un tablou prin transmiterea
unui pointer pe nceputul subtabloului. De exemplu, dac a este un tablou;
f(&a[2])
i
f(a + 2)
ambele transmit funciei f adresa elementului a[2] deoarece &a[2] i a+2 sunt expresii pointer
care refer al treilea element al lui a. n cadrul lui f, declarea argumentului poate citi
f(arr)
int arr[];
{
...
}
sau
f(arr)
int *arr;
{
...
}
Astfel, dup cum a fost conceput funcia f faptul c argumentul refer de fapt o parte a unui
tablou mai mare nu are consecine.

4.1.3. Aritmetica adreselor


Dac p este un pointer, atunci p++ incrementeaz pe p n aa fel nct acesta s
pointeze pe elementul urmtor indiferent de tipul obiectelor pointate, iar p+=i incrementeaz
pe p pentru a pointa peste i elemente din locul unde p pointeaz curent.
C este consistent i constant cu aritmetica pointerilor; pointerii, tablourile i aritmetica
adresrii constitue punctul forte al limbajului (iar dup alii, un major dezavantaj). S ilustrm
cteva dintre proprietile lui scriind un program pentru alocare de memorie rudimentar (dar
util n ciuda simplitatii sale). Exist dou rutine:alloc(n) returneaz un pointer p pe n poziii
caracter consecutive care poate fi utilizat de ctre apelantul lui alloc pentru alocarea de
caractere; free(p) elibereaz memoria fcnd-o astfel refolosibil mai trziu. Rutinele sunt
"rudimentare" deoarece apelurile la free trebuie fcute n ordine invers apelurilor la alloc.

45

Aceasta nseamn c memoria gestionat de alloc i free este o stiv sau o list prelucrabil n
regim LIFO.
Biblioteca standard C este prevzut cu funcii analoage care nu au att de multe
restricii. Exist multe aplicatii care au realmente nevoie de canalul alloc pentru a dispensa
mici poriuni de memorie, de lungimi neprevzute la momente neprevzute.
Cea mai simpl implementare este de a scrie alloc pentru declararea de pri ale unui
tablou mare pe care l vom numi allocbuf. Acest tablou este propriu lui alloc i free. Lucrnd
cu pointeri, nu cu indici, n tablou nu este necesar ca vreo alt rutinas cunoasca numele
tabloului, care poate fi declarat static, adic local fiierului surs care susine pe alloc i free
numele tabloului fiind invizibil n afara acestui fiier.n implementrile practice tabloul poate
chiar s nu aib nici un nume, el putnd fi obinut prin cererea ctre sistemul de operare a unui
pointer pe un bloc de memorie fr nume.
O alt informaie necesar este legat de ct anume din allocbuf a fost folosit. Vom
utiliza un pointer pe urmtorul element liber, numit allocp. Cnd este apelat alloc pentru n
caractere, el verific dac exist suficient loc eliberat n allocbuf. Dac astfel alloc returneaz
valoarea curent a lui allocp (adic nceputul blocului liber) atunci aceast valoare este
incrementat cu n n asa fel nct allocp s pointeze pe nceputul urmtoarei zone libere.
Free(p) pune pur i simplu pe allocp pe p dac p este n interiorul lui allocbuf.
#define NULL 0 /* val pointerului n caz de eroare */
#define ALLOCSIZE 1000 /* lung spatiului disponibil */
static char allocbuf[ALLOCSIZE]; /* memorie pentru alloc*/
static char *allocp = allocbuf; /*memorarea parti libere*/
char *alloc(n) /* pointer de return pe n caractere */
int n;
{
if (allocp + n <= allocbuf + ALLOCSIZE)
{
allocp += n;
return(allocp - n); /* vechiul p */
}
else /* nu-i destul loc */
return(NULL)
}
free(p) /* zona de memorie libera pointata de p */
char *p;
{
if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
allocp = p;
}
Cteva explicaii. n general un pointer poate fi iniializat ca orice alt variabil, dei n mod
normal singurele valori semnificative sunt NULL sau o expresie care opereaz adrese ale unor
date n prealabil definite, de tip specificat. Declaraia
static char *allocp = allocbuf;
definete pe allocp ca fiind un pointer pe caractere i l iniializeaz pentru a-l pointa pe
allocbuf care este urmtoarea poziie liber atunci cnd ncepe programul. Aceasta stare ar
putea fi scrisa i astfel
static char *allocp = &allocbuf[0];
46

deoararece numele tabloului este adresa elementului zero.

Testul
if (allocp + n <= allocbuf + ALLOCSIZE)
verific dac este suficient loc pentru a satisface cererea pentru n caractere. Dac exist loc,
noua valoare a lui allocp va fi cel mult mai departe de sfritul lui allocbuf. Dac cererea
poate fi satisfacut, alloc returneaz un pointer normal (observai declaraia funciei). Dac
nu, alloc trebuie s returneze un semnal care s semnifice c nu exist spaiu liber. Limbajul C
garanteaz c nici un pointer care pointeaz o dat valid nu va conine zero, asa c valoarea
zero returnat poate fi utilizat ca semnal de eveniment anormal, nu exist spaiu liber. Se
scrie NULL n loc de zero pentru a indica mai clar c aceasta este o valoare special pentru un
pointer. n general ntregii nu pot fi asignai pointerilor; zero este un caz special.
Teste ca
if (allocp + n <= allocbuf + ALLOCSIZE)
i
if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
relev cteva faete importante ale aritmeticii pointerilor.
Mai nti c n unele situaii pointerii pot fi separai. Dac p i q pointeaz pe elemente ale
aceluiai tablou, relaii ca <, >, =, etc. lucreaz exact.
p<q
este adevarat, de exemplu, n cazul n care p pointeaz pe un element anterior elementului pe
care pointeaz q. Relaiile c= i != sunt i ele permise. Orice pointer poate fi testat cu NULL.
Nu exist nicio ans n a compara pointeri n tablouri diferite. n cazul fericit se va obine un
evident nonsens, indiferent de maina pe care se lucreaz. Mai poate s apar situaia
nefericit n care codul va merge pe vreo maina eund "misterios" pe altele.
n al doilea rnd, tocmai s-a observat c un pointer i un intreg pot fi adunai sau sczui.
Instruciunea:
p+n
desemneaz al n-lea obiect dup cel pointat curent de p. Acest lucru este adevrat indiferent
de tipul obiectelor pe care p a fost declarat ca pointer. Compilatorul atunci cnd l ntlnete
pe n, l decaleaz n funcie de lungimea obiectelor pe care pointeaz p, lungime determinat
prin declaraia lui p.
Este valid i scderea pointerilor: dac p i q pointeaz pe elementele aceluiai
tablou, p-q este numrul de elemente dintre p i q. Acest fapt poate fi utilizat pentru a scrie o
nou versiune a lui strlen (evident, deosebit de cea de mai sus).
strlen(s) /* returneaz lungimea sirului */
char *s;
47

{
char *p = s;
while (*p != '\0')
p++;
return(p-s);
}
Prin declarare, p este iniializat pe s, adic s pointeze pe primul caracter din s. n
cadrul buclei while este examinat pe care caracter pn se ntlnete /0 care semnific
sfritul, iar dac while testeaz numai dac expresia este zero, este posibil omiterea testului
explicit, caz n care astfel de bucle sunt scrise adesea
while (*p)
p++;
Deoarece p pointeaz pe caractere, p++ face ca p s avanseze de fiecare dat pe
caracterul urmtor, iar p-v d numrul de caractere parcurse, adic lungimea irului.
Aritmetica pointerilor este consistent: dac am fi lucrat cu float care ocupa mai mult
memorie dect char, i dac p ar fi un pointer pe float, p++ ar avansa pe urmtorul float.
Astfel, vom putea scrie o alt versiune a lui alloc care pstreaz s zicem, float n loc de char,
pur i simplu prin schimbarea lui char n float. n cadrul lui alloc i free. Toate manipularile de
pointeri iau automat n considerare lungimea obiectului pointat n aa fel nct trebuie s nu
fie alterat.
Alte operaii n afara celor mentionate deja (adunarea sau scderea unui pointer cu
un ntreg, scderea sau compararea a doi pointeri): toate celelalte operatii aritmetice cu
pointeri sunt ilegale. Nu este permis adunarea, mprirea, deplasarea logic, sau adunarea
unui float sau double la pointer.

4.1.4. Pointeri pe caractere i funcii


Un ir constant scris astfel
"Eu sunt un sir "
este un tablou de caractere. n reprezentare intern, compilatorul termin un tablou cu
caracterul \0 n aa fel nct programele s poat detecta sfritul. Lungimea n memorie este
astfel mai mare cu 1 dect numarul de caractere cuprinse ntre ghilimele.
Poate cea mai comun apariie a unui ir de constante este ca argument al funciei, cum ar fi n
char *mesaj;
atunci instruciunea
message = "acum e timpul";
asigneaz lui mesaj un pointer n funcie de caracterele reale. Aceasta nu este o copie a iruluinu sunt implicati dect pointerii. C nu este nzestrat cu ali operatori care s trateze un ir de
caractere ca o unitate.
Vom ilustra mai multe aspecte n legatur cu pointerii i cu tablourile folosind dou
funcii cu adevrat utile, din biblioteca standard de I/O.

48

Prima funcie este strcpy(s, t) care copiaz irul t n irul s. Argumentele sunt scrise n
aceast ordine prin analogie cu aranjarea, unde cineva ar putea spune
s=t
pentru a asigna pe t lui s. Versiunea cu tablouri este prima:
strcpy(s, t) /*copiaz t n s */
char s[], t[];
{
int i;
i = 0;
while ((s[i] = t[i] != '\0')
i++;
}
Iat o versiune a lui strcpy cu pointeri:
strcpy(s, t) /* copiaz t n s, versiunea pointeri 1*/
char *s, *t;
{
while ((*s = *t) != '\0') {
s++;
t++;
}
}
Deoarece argumentele sunt transmise prin valoare, strcpy poate utiliza s i t n orice fel se
dorete. Aici ei sunt conventional utilizati ca pointeri, care parcurg tablourile pn n
momentul n care s-a copiat \0 sfrit ul lui t, n s.
n practic, strcpy nu va fi scris uzual ca mai sus. O a doua posilitate ar fi:
strcpy(s, t) /* copiaz t n s, versiunea 2*/
char *s, *t;
{
while ((*s++ = *t++) != '\0')
;
}
n aceasta ultim versiune se imit incrementarea lui s i t n partea de test. Valoarea
lui *t++ este caracterul pe care a pointat nainte ca t s fi fost incrementat; prefixul ++ nu-l
schimb pe t nainte ca acest caracter s fi fost adus. n acelasi fel, caracterul este stocat n
vechea poziie s nainte ca s s fie incrementat. Acest caracter este deasemenea valoarea care
se grupeaz cu \0 pentru simbolul buclei. Efectul net este c, caracterele sunt copiate din t n
s, inclusiv sfritul lui \0.
Ca o ultim abreviere vom observa c i gruparea cu \0 este redundant, astfel c
funcia este adesea scris ca
strcpy(s, t) /* copiaz t n s; versiunea pointeri 3 */
char *s, *t;
{
while (*s++ = *t++)
49

;
}
Dei aceast versiune poate prea complicata la prima vedere, aranjamentul ca notaie
este considerat suveran dac nu exist alte raiuni de a-l schimba astfel c l vei ntlni
frecvent n programele C.
A doua rutin este strcmp(s, t) care compar irurile de caractere s i t i returneaz
negativ, zero sau pozitiv n funcie de relaia dintre s i t, care poate fi: s<t, s=t sau s>t.
Valoarea returnat este obinut prin scderea caracterului de pe prima poziie unde s difer de
t.
strcmp(s, t)
/* returneaz <0 dac s<t, 0 dac s==t, >0 dac s>t */
char s[], t[];
{
int i;
i = 0;
while (s[i] == t[i])
if (s[i++] == '\0')
return(0);
return(s[i] - t[i]);
}
Versiunea cu pointeri a lui strcmp:
strcmp(s, t) /* returneaz <0 dac s<t, 0 dac s==t,
>0 dac s>t */
char *s, *t;
{
for ( ; *s == *t; s++, t++)
if (*s == '\0')
return(0);
return(*s - *t);
}
Dac ++ i -- sunt folosii altfel dect operatori prefix sau postfix pot apare alte combinaii de
* i ++ i --, dei mai puin frecvente. De exemplu:
*++p
incrementeaz pe p nainte de a aduce caracterul pe care pointeaz p.
*--p
decrementeaz pe p n acelai condiii.

4.2. Tablouri
Tabloul este o colecie de date n care fiecare element poate fi identificat pe baz unui
index, colecia asigurnd timp de acces constant pentru fiecare element. Prin reprezentarea
50

tabloului t se nelege plasarea elementelor n locaii succesive de memorie, indexarea uzual


a elementelor tabloului n majoritatea limbajelor de programare ncepnd cu 0:

t[0]

t[1]

t[2]

t[n-1]

unde n este dimensiunea tabloului (n sensul numrului de componente)


Locaiile de memorie pot fi numerotate, putnd accesa direct orice element. Timpul de
accesare al elementului 1, de exemplu, fiind acelai cu timpul de accesare al elementului n.
Funcie de dimensiunea tablourilor (n sensul numrului de indici folosii pentru
indexarea unui element) o clasificare a acestora ar cuprinde:
tablouri unidimensionale: vectori
tablouri bidimensionale: matrice
tablouri tridimensionale, multidensionale: masive.
Observaie:
Adresa tabloului coincide cu adresa primei sale componente (vezi corelaia cu aritmetica
pointerilor 4.1.3.).

4.3. Liste
O list este o mulime de obiecte, numite atomi, pentru care este definit o ordine:
a1 a 2 a 3 a 4 a 5 ... a n
Operaiile principale care se pot se face n cadrul listei:
inserare: introducerea unui nou element ntr-o anumit poziie;
tergere: scoaterea unui element dintr-o anumit poziie;
consultare: accesul asupra fiecarui element din lista;
parcurgere.
Tipurile speciale de liste sunt stivele i cozile
Stive
O stiva este o list n care operaiile de inserare, tergere i consultare se efectueaz
asupra unui capt al listei. Stiva se poate asemna cu un recipient n care se pune i scoate
diferite obiecte. Operaia care pune obiectele n stiv se numete push, iar cea care scoate
obiecte din stiv se numete pop. Captul accesibil pentru stiv se numete vrful stivei:
Aadar:
push insereaz un element n vrful stivei;
pop terge un element din vrful stivei;
top consulta (citete) elementul din vrful stivei;
top(S) citete vrful stivei.
push

pop

51

Cozi
O coad este o list n care inserarea se face la un capt (la sfrit), iar tergerea se
face de la cellalt capt al cozii (de la nceput). Partea din fa a cozii (a primului element) se
numete front, iar partea din spate (a ultimului element) se numete end.
Operaia de inserare n coada add (put)
Operaia de tergere din coada del (get)

add

del

4.3.1. Implementri de liste


O list poate fi realizat ca:- list ordonat sau
- list nlnuit
List ordonat ine cont de o ordine a poziiilor elementelor listei, nu de coninutul
elementelor.
Inserarea ntr-o list de forma:

a1

a 2 a3

... an //////

se face cu deplasare de o poziie la stnga din punctul n care dorim s inserm (pentru a face
astfel loc noului element).
Deplasarea se face nspre zona de memorie liber (cea haurat) presupunem c
dorim s inserm pe a n poziia i):

a1

a2

... ai-1

ai+1 ... //////

Presupunnd acum haurat corpul de elemente din list i nehaurat zona de memorie
liber, inserarea s-ar putea figura astfel:

/////////////////////////////
//////////

/////////////////

///////////////////////////////////
tergerea: deplasarea cu o poziie la stnga din acel punct.

////////////// ////////// //////////


/////////////////////////////
Liste nlnuite
ntr-o list nlnuit, ordinea din memorie nu mai corespunde cu ordinea din list.
Fiecare element al listei nlantuite va avea urmtoarea structur:
(a(i) , succesor(a(i)))
unde a(i) este atomul listei nlnuite, iar informaia succesor(a(i)) ne permite s identificm
un nou element de list nlnuit. Ordinea din memorie a elementelor listei nu conteaz.
Informaia care indic primul element al listei se numete "capul" listei. Informaiei
succesor(a(i)) i se potrivete noiunea de pointer (identificator), pointer-ul fiind o variabil
care pastreaz o adres din memorie. El indic poziia elementului urmtor. Cnd informaiile
de nlanuire sunt pointeri, putem utiliza urmtoarea reprezentare:
52

a1

a2

a3

cap de lista
Capul de list este un pointer separat care duce la primul element din list, iar 0 este
pointer-ul nul (NULL) cu valoare zero. La implementarea listei nlnuite concentrarea se face
la fluxul instruciunilor, nu la declaraiile de variabile.
n programe vom utiliza urmtoarele notaii:
x adresa unui element din list, deci un pointer;
data(x) atomul memorat n elementul de list indicat de x;
link(x) informaia de legatura memorata n elementul de list indicat de x, adic
adresa elementului urmtor;
y = get_sp() y (de acelai tip cu x) primete adresa unei zone de memorie n care se
poate memora un element din list (get space sau alocare de memorie cnd este vorba de
pointer);
ret_sp(x)
eliberez memoria ocupat de elementul de list indicat de x (din
momentul respeciv acolo se poate memora altceva).
Un element de list va fi o astfel de structura:
struct Element {
Atom data;
Element* link;
};
Se va scrie:
tipul lui x
data(x)
link(x)
y = get_sp()
ret_sp()

---------------- Element* x
---------------- x data
---------------- x link
---------------- y = new Element
---------------- delete x

Deoarece lucrm cu conceptul de list vom face declaraia :


typedef Element* List;
Un pointer la un element de list considerat aproximeaz lista ce pornete cu
elementul indicat.
Operaii primitive pentru liste nlnuite
1.

Inserarea
Inserarea se face:
- n fa, sau
- n interior (la mijloc ori la sfrit)

a)

Inserarea n fa
53

a1

a2
1

1
2
a0
x
1 - Prima atribuire: link(x) = l
2 - A douatribuire: l = x
Observaie: Dac lista este vid, l are valoarea 0 (capatul listei) iar atribuirile de mai
sus ramn valabile:
1

a0

a0

x
1
x

b)

Inserarea la mijloc
ai-1

ai+1

ai
2

x
Analog pentru inserarea la sfrit.
1 - Prima atribuire: link(x) = link(y)
2 - A douatribuire: link(y) = x
2.a)

tergerea (tergerea din fa):

54

1
a1

a2

2
1 - Prima atribuire: p = l
2 - A douatribuire: l = link(l)
3 - ret_sp(p)
Sau, tergerea din fa s-ar mai putea reprezenta astfel:
Situaia iniial:
cap

Situaia final:
P
1
cap

(1)
p = cap;
(2)
cap = cap link;
delete p ;
// Elibereaz zona de memorie
Elementul care a fost izolat de list trebuie s fie procesat n coninuare, cel puin
pentru a fi eliberat zona de memorie pe care o ocup, de aceea adresa lui trebuie salvat (s
zicem n variabil pointer p).
2.b)

tergerea de la mijloc sau de la sfrit


Varibila q va indica elementul din fa celui care va fi ters.
Situaia iniial:

Situaia final:

55

(1)
(2)

p = q link;
q link = p link;
delete p;

// sau q link = q link link;

Observaii:
Atunci cnd q indic penultimul element dintr-o list, atribuirile de mai sus
funcioneaz corect i terg ultimul element din list.
Nu se poate face tergerea elementului indicat de q fr parcurgerea listei de la capt.
4.3.2. Alte consideraii asupra operaiei de inserare ntr-o list nlnuit
Inserarea presupune deci adugarea unui element ntr-o poziie specificat n list. Exist
posibiliti diferite de a specifica poziia n care vrem s inserm elementul:
Situaia n care poziia de inserat este dat printr-un numr care s indice al ctelea
element trebuie s fie n list elementul inserat;
Situaia n care poziia de inserat este dat prin valoarea atomului dup care sau nainte de
care se face inserarea;
Situaia n care poziia de inserat poate fi dat implicit prin valoarea atomului de inserat.
Inserarea n faa unui element specificat
Funcia nscrie un element n fa altui element dintr-o list:
insert (l, a, b)
// l list (pointer la primul element)
// a valoarea atomului de inserat
// b valoarea atomului n fa cruia se insereaz
{
p=get_sp();
data(p)=a;
if (l==0) or (data(l)==b) then
{
link(p)=l;
l=p;
}
else
{
q=l;
while
data(link(q)!=b))

((link(q)!=0)and

do q=link(q);
link(p)=link(q);
56

link(q)=p;
}
}
Operaia de tergere dintr-o list nlnuit
Operaia delete terge un atom dintr-o list. Deci vom avea n pseudocod, o funcie de
forma:
delete(l, a)
// l
// a

list
valoarea atomului care trebuie ters
{
if l=0 then eroare ("Atomul nu se afla n list")
else if data(l)=a then | p=1
| l=link(l)
|_ ret_sp(p)
else | q=l
|
while(link(q)!
=0)and(data(link(q))!=a) do
|
q=link(q)
| if link(q=0) then eroare("S-a
ajuns la sfrsitul listei i atomul nu a fost gsit")
| else | p=link(q)
|
| link(q)=link(p)
|_
|_ ret_sp(p)
}
Operaia de parcurgere a listei nlnuite
Operaia de parcurgere a listei nlnuite const dintr-o secven de instruciuni care se
folosete de fiecare dat cnd dorim s prelucram elementele listei ntr-un anumit scop.
O parcurgere a listei presupune o prelucrare efectuat asupra fiecarui element din list
(aadar nu o funcie, ci o secven de instruciuni):
Fie p pointer-ul care indic pe rnd fiecare element al listei, i considerm ca p ncepe
cu l:
while (p!=0) do

| prelucrare (data(p)) // ex:afisarea atomului


|_ p=link(p)
// trece la urmto*rul

4.3.3. Stive ordonate


O stiv este o structur de date de tip "container" (depoziteaz obiecte de un anumit
tip) organizat dup principiul LIFO (Last n First Out).
Operaiile de acces la stiv (push - adaug un element n stiva i pop - scoate un
element din stiva) sunt create astfel nct pop scoate din stiv elementul introdus cel mai
recent.
O stiv este un caz particular de list, i anume este o list pentru care operaiile de
acces (inserare, tergere, accesare element) se efectueaz la un singur capt al listei.

57

Dac STACK este de tip stiv i ATOM tipul obiectelor coninute n stiv atunci
operaiile care definesc tipul structura de stiv pentru tipul STACK sunt:
CREATE() STACK
Operaia CREATE nu primete parametri, creeaz o stiv care pentru nceput este vid
(nu conine nici un obiect).
PUSH(STACK, ATOM) STACK
Operaia PUSH primete ca parametri o stiv i un obiect i produce stiv modificat
prin adugarea obiectului n stiv.
POP(STACK) STACK, ATOM
Operaia POP primete ca parametri o stiv pe care o modific scond un obiect. De
asemenea produce ca rezultat obiectul scos din stiv.
TOP(STACK) ATOM
Operaia TOP ntoarce ca rezultat obiectul din vrful stivei pe care o primete ca
parametru.
ISEMPTY(STACK) boolean
Operaia ISEMPTY este folosit pentru a testa dac stiv este vid.
Facem notaiile:
S
stiv
S.vect vectorul n care se reprezint elementele stivei S
S.sp indicele vrfului stivei
Elementele sunt memorate aadar unul dup altul n vectori, nefiind neaparat n ordine
cresctoare. Zona de memorat trebuie s conin aceste dou informaii: S.vect i S.sp grupate
ntr-o structur:
struct Stack {
int sp;
Atom vect [DIMMAX]
};
Condiia de stiv vid este: S.sp=0
Se scrie:
push(S,a)
{
if S.sp >=DIMMAX then eroare("Stiv plina")
else | S.sp=S.sp+1
|_ S.vect[S.sp]=a

//atomul este pus pe prima

poziie
}
Funcia pop scoate un element din stiv:
pop(S)
{
if S.sp=0

then eroare ("Stiv vid")


else S.sp=S.sp-1

}
Observaie: Se obinuiete ca pe lng tergerea elementului, funcia pop s returneze
elementul scos din list.
58

top(S)
{
if S.sp=0

then eroare("Stiv vid")


else return(S.vect[S.sp])

}
Funcia isEmpty(S) testeaz condiia stiv vid:
isEmpty(S)
{
return(S.sp==0)
}

4.3.4. Stive nlnuite


O stiv poate fi implementat ca o list nlnuit pentru care operaiile de acces se fac
numai asupra primului element din list. Deci, operaia PUSH va nsemna inserare n prima
poziie din list (n fa) iar POP va nsemna tergerea primului element din list. Pentru a
manevra o stiv vom avea nevoie de un pointer la primul element din nlanuire, deci, vom
echivala tipul Stack cu tipul "pointer la element de list", iar funciile care implementeaz
operaiile de acces vor avea aceleai prototipuri cu cele date mai sus.
struct Element
{
Atom data;
Element* link;

//legatura

};
typedef Element* Stack;
Fie S pointer-ul la primul element din nlnuire, se echivaleaz tipul Stack cu typedef
Element* Stack, iar condiia de stiv vid este S=0 :
push(S,a)
{
p=get_sp()
data(p)=a
link(p)=S
S=p
}
pop(S)
{
if S=0 then eroare("Stiv vid")
else | p=S;
| S=link(S)
|_ ret_sp(p)
}
top(S)
{
59

if S=0 then eroare("Stiv vid")


else return(data(S))
}
isEmpty(S)
{
return(S==0)
}
Stivele sunt cele mai simple structuri de date, ele avnd i operaiile imediate.
4.3.5. Cozi ordonate
O coad este o list n care operaiile de acces sunt restricionate la inserarea la un
capt i tergerea de la cellat capt.
coada
PUT

GET

ultimul

primul

Pricipalele operaii de acces sunt:


PUT(Coada, Atom) Coada
Adaug un element la coad.
GET(Coada) Coada, Atom
Scoate un Atom din coad.
Returneaz atomul scos.

head

tail

O coad poate fi organizat pe un spaiu de memorare de tip tablou (vector).


Sunt necesari doi indicatori:
head indic: primul element care urmeaz s fie scos.
tail
indic: locul unde va fi pus urmtorul element adugat la coad.
Condiia "coad vid" este echivalent cu: head = tail. Iniial indicatorii vor fi
iniializai astfel nct s indice ambii primul element din vector.
Operaia PUT nseamn:
- V[tail] primete Atomul adugat;
- incrementeaz tail.
Operaia GET nseamn:
- ntoarce V[head];
- incrementeaz head.
Se observ c adugri i stergeri repetate n coad deplaseaz coninutul cozii la
dreapta, fa de nceputul vectorului. Pentru a evita acest lucru ar trebui ca operaia GET s
deplaseze la stnga coninutul cozii cu o poziie. Primul element care urmeaz s fie scos va fi
60

ntotdeauna n prima poziie, indicatorul head pierzndu-i utilitatea. Dezavantajul acestei


soluii const n faptul ca operaia GET necesit o parcurgere a coninutului cozii.
Facem notaiile:
C
coad
C.vect vectorul n care sunt memorate elementele cozii
C.head indicele elementului ce va fi scos din coad la urmtoarea operaie get
C.tail indicele (poziia) n care va fi memorat urmtorul element adugat la coad.
Condiia coad vid este C.head=C.tail.
Funcia put pune n coad C un atom a:
put(C,a)
{
if C.tail>DIMMAX

then eroare("Coad plina")


else | C.vect [C.tail]=a
|_ C.tail=C.tail+1

}
Funcia get scoate un element din coad i-l returneaz:
get(C)
{
if C.head=C.tail

then eroare("Coad vid")


else | C.head=C.head+1
|_ return C.vect [C.head-1];

}
isEmpty(C)
{
return(C.head==C.tail)
}
Cozi ordonate circulare
Pentru a obine o coad circular vom porni de la o coad liniar simpl (cu doi
indicatori) i vom face n aa fel nct la incrementarea indicatorilor head i tail, cnd acetia
ating ultima poziie din vector s se coninue cu prima poziie din vector.
Funcia urmtoare poate realiza aceasta cerin:
int nextPoz(int index)
{
if (index<DIMVECTOR-1) return index+1;
else return 0;
}
unde DIMVECTOR este dimensiunea vectorului n care se memoreaz elementele cozii.
Coninutul cozii va arta aa:

61

head

tail

sau aa:
5

tail

head

Condiia "coad vid" rmne echivalent cu: head = tail


Coada va fi plin dac: head=1 i tail=DIMVECTOR

tail

head

sau dac: tail+1 = head

head

tail

Ambele situaii sunt coninute n condiia:


nextPoz(tail) = head
// Condiia "coad plin"
Coad circular ordonat asigur reutilizarea spaiului eliberat de get la urmtoarele
inserari n coad.
Observaie:
elemente.

n coada circular de dimensiune DIMMAX pot fi memorate DIMMAX

"Coad plina" se realizeaz n 2 situaii:


a)
C.head=1 i C.tail=DIMMAX
b)
C.tail+1=C.head
iar, condiia C.head=inc(C.tail) le conine pe amndou.

n cazul cozilor circulare se modific doar operaiile put i get:


62

put(C,a)
{
if C.head=inc(C.tail) then eroare("Coad plina")
else | C.vect[C.tail]=a
|_ C.tail=inc(C.tail)
}
get(C)
{
if C.head=C.tail

then eroare("Coad vid")


else | a=C.vect [C.head]
| C.head= inc (C.head)
|_ return(a)

4.3.6. Cozi nlnuite


O coad poate fi implementat printr-o list nlnuit la care operaiile de acces sunt
restricionate corespunztor.
nil
tail

head

Este nevoie de doi indicatori (pointeri):


head indic primul element din coad (primul care va fi scos);
tail indic ultimul element din coad (ultimul introdus).

O coad vid va avea: head=tail=nil.


n mod obinuit, adugarea unui element n coad modifica numai tail iar tergerea
unui element numai head. ntr-un mod special trebuie s fie tratate cazurile:
adugare ntr-o coad vid:
Iniial: head=tail=nil
Final: Coad cu un element:
nil
head

tail

tergere dintr-o coad cu un element:


Iniial: Coad cu un element
Final: head=tail=nil
n aceste cazuri se modific att head ct i tail.
Facem notaiile :
63

C.head pointer la primul element din coad;


C.tail pointer la ultimul element din coad;
C
coad.
Condiia de coad vid este head=0.
Operaiile cozii nlnuite
Funcia put insereaz un element n coad, n poziia fa:
put(C,a)
{
p= get_sp();
data(p); link(p)= 0;
if C.head=0 then
else

| C.head= p
|_ C.tail= p
| link(C.tail)= p
|_ C.tail= p

}
Funcia get scoate un element din poziia fa:
get(C,a)
{
if C.head= 0

then eroare("Coad plina")


else | a= data(C.head)
| p= C.head
| C.head= link(C.head)
| ret_sp(p)
|_ return(a)

}
Funcia front returneaz elementul din fa cozii, fr a-l scoate din coad.
front(C)
{
if C.head=0

then eroare("Coad vid")


else return data(C.head)

}
isEmpty(C)
{
return(C.head=0)
}
Exist aici un element de redundan: ar fi convenabil s nu mai avem spaiu
suplimentar de memorare, ci, s avem un singur pointer ca s putem manevra coada. De aceea
apar utile cozile nlnuite circulare.
Cozi nlnuite circulare
Dac reprezentm coad printr-o structura nlnuit circulara va fi nevoie de un
singur pointer prin intermediul cruia se pot face ambele operaii de adugare i tergere din
coad:
64

ultimul

primul
.....
tail

Fie:
C
pointer la ultimul element din coad
link(C) pointer la primul element din coad
Operaiile de plasare i de scoatere din coad, sunt:
put(C,a)
{
p= get_sp()
data(p)=a
if C=0 then
else

| C= p
|_ link(C)= p
| link(p)= link(C)
| link(C)= p
|_ C= p

}
get(C)
{
if C= 0 then eroare("Coad vid")
else if C=link(C) then| a= data(C)
| ret_sp(C)
| C= 0
|_ return(a)
else | {p= link(C)
| link(C)= link(p)
| a= data(p)
| ret_sp(p)
|_ return(a)
}
front(C)
isEmpty(C)

returneaz data(link(C))
retuneaz condiia C=0.

4.3.7. Liste generalizate


Definiie
Dat o mulime de elemente (atomi), se numete list generalizata o secven finit
(1, 2, ... , n), n care i sunt atomi.
Exemplu:
A = (a, b, c)
65

B = (x, A, (a, c), ( ))


| |
|
\
atom list list list vid
Observaie:
recursive.

Listele generalizate pot s aib elemente comune. Se permite definirea de liste

Reprezentarea listelor generalizate


Presupunem o list de forma: (tag, data, link) n care tag este o etichet {0,1}.
Dac tag = 0 nodul va corespunde unui element atomic cmpul data va conine
atomul respeciv.
Dac tag = 1 nodul va corespunde unei subliste cmpul data va semnifica
legtura la primul element al sublistei; link este legtura pentru urmtorul nod din list.
Fie urmtoarele primitive de selecie pentru un nod de adres p:
p adres unui nod;
link(p) cmpul "link" din nodul indicat de p;
Notm:
tag(p) cmpul "tag" din nodul indicat de p
data(p) cmpul "data" din nodul indicat de p
Fie urmtoarele liste:
D=()
A = (a, (b, c))
B = (A, A, ( ))
C = (a, C)
cu urmtoarele reprezentri:
D : o list vid nseamn un pointer nul
A:
0

0
B:
C:

0
0

Ne propunem s dm nume unei subliste, deci dac tag = 1, adic tag(p) = 1


data(p) va fi adres unei structuri ce conine:
nume lista

pointer la primul element

Aadar, obinem urmtoarea reprezentare:

66

D:

A:

0
B:

_
C

C:

Operaii la liste generalizate:

Funcia insert, este asemntoare cu cea de la liste nlnuite. Elementul ce se


insereaz poate fi un atom sau o sublist;

Funcia del ( ) trebuie s in seama de existena unor liste comune. Deci, este necesar
pstrarea n elementul ce conine numele listei A i a unui indicator care s contorizeze
numrul de referine ale lui.
Exemplu:
A

2
Numai dac acest indicator este 0, se face tergerea efeciv a listei.

Traversarea listelor generalizate


Traversarea listelor generalizate presupune prelucrarea elementelor listei, i a
elementelor sublistelor componente.
Exemplu:
O funcie de copiere i o funcie de test de egalitate a dou liste generalizate,
realizate recursiv i iterativ. Funcia returneaz o copie a listei. Copie mai nti primul
element, i apoi recursiv restul listei:
Varianta recursiv:
67

Copy (l)
{

// l - list nlnuit
if (l = 0)

}
Copy (l)
{

then return (0)


else | p = get_sp()
| data(p) = data(l)
| link(p) = Copy(link(l))
|_ return(p)

// l - list generalizata
if (l = 0)

then return (0)


else | p = get_sp()
| if (tag(l) = 0) then data(p) = data(l)
| else data(p) = Copy(data(l))
| link(p) = Copy(link(l))
|_ return(p)

}
Funcia pentru testarea egalitii este:
isEqual (l1,l2)
{

// procedura tratata iterativ


p1 = l1; p2 = l2
while(p1 0 and p2 0)
| if (data(p1) data(p2)) then return (FALSE)
| p1 = link(p1)
|_ p2 = link(p2)
return(p1 = p2)

}
isEqual (l1,l2)
{

// procedura tratata recursiv


p1 = l1; p2 = l2

while(p1 0 and p2 0)

| if (tag(p1) tag(p2)) then return (FALSE)

| if (tag(p1) = 0 and data(p1) data(p2)) then return (FALSE)


| if (tag(p1) = 1 and not isEqual (data(p 1),data(p2)) then return
(FALSE)
| p1 = link(p1)
|_ p2 = link(p2)
return (p1 == p2)
}
Coad cu prioriti
Este o structur de date pentru care sunt definite urmtoarele operaii:
insert (S,a)
- insereaz un atom n structur,
68

remove (S)
- extrage din structur atomul cu cheia cea mai mare.
O coad cu prioriti poate fi implementat printr-o list. Implementarea cozii cu
prioriti prin list permite definirea operaiilor insert i remove, n cazul cel mai bun, una este
de complexitate O(1) i cealalt este de complexitate O(n).
Implementarea cozii cu prioriti prin heap face o echilibrare cu complexitatea
urmtoare: una este de complexitate 0(log n) i cealalt de complexitate 0(log n).
Operaia de inserare
/ \
/ \
/ 50 \
/ / \ \
/ 40
30 \
/ / \
/ \ \
/ 33 37 12 2 \
/ / \ / _________\
/ 10 15 7 | 42
-----------------|

heap:

Acelai arbore n reprezentare implicit:


50

40

30

33

37

12

10

15

42

Operaiile insert i remove pentru arbori heap au o forma foarte simpl cnd utilizeaz
reprezentarea implicit. Considerm, n coninuare, arbori heap n reprezentare implicit.
Exemplu:
un arbore cu ultimul nivel avnd toate nodurile aliniate la stnga:
Inserm valoarea 42 se adaug nodul la un nivel incomplet;
n reprezentarea implicit se adaug nodul la sfrit.
insert:

1)

n reprezentarea implicit:
V [N + 1] = a
N=N+1
n coninuare reorganizm structura arborelui astfel nct s-i pstreze structura de

heap.
2)
Se utilizeaz interschimbrile. Comparaii:
Iau 2 indici:
child = N
i
parent = [N/2]
(n cazul nostru N = 11 i [N/2] = 5)
69

3)
4)

Comparm V[child] cu V[parent].


Interschimbare dac V[child] nu este mai mic dect V[parent] (se schimb 42 cu 37)
naintare n sus:
child = parent
parent = [child/2]
Se reia pasul 2) pn cnd nu se mai face interschimbarea.
Structura S este un arbore heap. El se afl n reprezentare implicit n 2 informaii:
V vector
N dimensiune

Operaia insert:
insert(V, N, a) // V
// N
(Funcia
// a

vectorul ce conine reprezentarea implicit a heapu-lui;


Numrul de noduri din heap, ambele sunt plasate prin referinta
insert le poate modifica);
atomul de inserat;

{
N = N+1
V[N] = a
child = N
parent = [N/2]
while | parent 1 do
| if key(V [child]) > key(V [parent]) then
| interchange (V [child],V [parent])
|
| child = parent
|
|_ parent = [child/2]
|_ else break // parasim bucla parent = 0
}
Operaia remove:
50
/ \
45
43
/ \ / \
33 40 40 20
/ \ / \
\
10 15 7 37 39

se scoate elementul cel mai mare care este rdcina heap-ului; se iniializeaz cei 2
indici;

se reorganizeaz structura arborilor: se ia ultimul nod de pe nivelul incomplet i se


aduce n nodul-rdcina, i aceasta valoare va fi retrogradat pn cnd structura heap-ului
este realizat.

70

Condiia de heap:

parent
/ \
lchild rchild

parent = max(parent, lchild, rchild).


Exist trei cazuri:
1.
Condiia este ndeplinit deodat;
2.
max este n stnga retrogradarea se face n stnga;
3.
max este n dreapta retrogradarea se face n dreapta.
remove(V, N)
{
a= V[1]
N[1]= V[N]
N= N-1
parent= 1
child= 2
while child N do
| if child+1 N and key(V[child+1]) >
| > key(V[child]) then
|
child= child+1
| if key(V[parent]) < key(V[child]) then
|
| interchange(V[parent], V[child])
|
| parent= child
|
|_ child= 2*parent
| else break
|_ return(a)
}
Complexitatea celor dou operaii insert i remove:
n cazul cel mai defavorabil se parcurge o ramur care leag rdcina de un nod
terminal. La insert avem o comparaie, iar la remove avem dou comparaii. Rezult,
complexitatea este dat de adncimea arborelui. Dac N este numrul de noduri din arbore, 2k
N 2k+1 , i adncimea arborelui este k+1.
2k - 1 < N 2k+1 - 1 k = [log2N]
|
|
|
|
|
|
nr. de noduri
nr. de noduri
ale arborelui
ale arborelui
complet de
complet de
adncime k
adncime k+1
k log2N < k + 1 adncimea arborelui este k = [log2N].
Deci complexitatea este O(log N).
71

A douaplicaie a heap-urilor este algoritmul Heap_Sort.


Algoritmul Heap_Sort
Heap_Sort(V, n)
{
heap_gen(V, n) N = n
for i = n downto 2 step -1 do
| N = i
|_ V[i] = remove(V, N)
}
Aceast procedur sorteaz un vector V cu N elemente: transform vectorul V ntr-un
heap i sorteaz prin extrageri succesive din acel heap.
partea sortata
i a vectorului

heap

max

min

Procedura Heap_Sort prin inserri repetate


heap_sort
{
N = 1 // se considera pentru nceput un heap cu un singur element,
dup care toate
celelalte elemente vor fi inserate n acest heap
for i = 2 to n do
insert(V, N, V[i])
}
Studiul complexitii
Observaii:

Se fac mai multe operaii insert n heap-uri a cror dimensiune crete de la 1 la N;

Se fac n-1 operaii insert n heap-uri cu dimensiunea N n


Rezult complexitatea acestor operaii nu depete O(nlog n). Facem un studiu
pentru a vedea dac nu cumva ea este mai mic dect O(nlog n).
Cazul cel mai defavorabil este situaia n care la fiecare inserare se parcurge o ramur
complet. De fiecare dat inserarea unui element se face adugnd un nod la ultimul nivel.
Pentru nivelul 2 sunt dou noduri. La inserarea lor se va face cel mult o retrogradare
(comparaie).
Pe ultimul exemplu de arbore, avem:
nivelul 2
: 2 noduri 1 comparaie
nivelul
: 4 noduri 2 comparaii
nivelul 4
: 8 noduri 3 comparaii
-------------------------------------------------nivelul i
: 2i-1 noduri i-1 comparaii
Considernd un arbore complet (nivel complet) N = 2k - 1 numrul total de
comparaii pentru toate nodurile este T(n) de la nivelul 2 la nivelul k. Vom calcula:
T ( n)

(i 1) 2i 1
i 2

72

S artm:
T ( n)

k 1

i 21
i 1

cu tehnica

T ( n) 2 T ( n) T ( n) .

Asadar:
T ( n) 2

k 1

k 1

k 1

i 1

i 1
4

i 1

k 1

i 2i i 2i i 2i 1 i 2i

1 2 2 2 3 2 ... ( k 2 ) 2
2 ( k 1) 2 k

k 1

( k 1) 2 k 1 21 2 22 3 23 ... ( k 1) 2 k 1

2i ( k 1) 2 k 2 20 21

i 2
k

i 1

k 1

k 1

2i
i 0

( k 1) 2 1 ( 2 1) ( k 2 ) 2 2

Rezult:
T ( n ) ( k 2 ) 2 k 2 ( k 2 ) ( 2 k 1) k 2 2 n ( k 2 ) k

iar:

k log2 ( n 1) ,

din
n 2k 1

Rezult:

T ( n ) n (log2 ( n 1) 2 ) log2 ( n 1)

-----------------------termen dominant
Fcndu-se majorri, rezult complexitatea O(nlog n).
Prezentm acum alt strategie de obinere a heap_gen cu o complexitate mai bun:
Construim heap-ul de jos n sus (de la dreapta spre stnga). Cele mai multe noduri sunt
la baz, doar nodurile din vrf parcurg drumul cel mai lung.

II
noduri terminale

Elementele V[i+1,N] ndeplinesc condiia de structura a heap-ului:


j >i avem: V[j] > V[2*j] , dac 2*j N
V[j] > V[2*j +1] , dac 2*j + 1 N

73

Algoritmul const n adugarea elementului V[i] la structura heap-ului. El va fi


retrogradat la baz heap-ului (prelucrare prin retrogradare):
retrogradare(V, N, i)
{
parent = i
child = 2*i
// fiu stnga al lui i
while child N do
| if child+1 N and
|
key(V[child+1]) > key(V[child]) then
|
child = child+1
| if key(V[parent]) < key(V[child]) then
|
| interchange(V[parent], V[child])
|
| parent = child
|
|_ child = 2*parent
|_ else break
}
n aceast situaie, vom avea:
heap_gen
{
for i = [N/2] downto 1 step -1 do retrogradare(V, n, i)
}
Complexitatea acestei operaii
Fie un arbore complet cu n = 2k - 1. Cazul cel mai defavorabil este situaia n care la
fiecare retrogradare se parcurg toate nivelele:
nivel k
: nu se fac operaii
nivel k-1
: 2k-2 noduri
o operatie de comparaie
nivel k-2
: 2k-3 noduri
2 operaii
-----------------------------------------------------------------nivel i
: 2i-1 noduri
k-i operaii
------------------------------------------------------------------nivel 2
: 21 noduri
k-2 operaii
0
nivel 1
: 2 noduri
k-1 operaii
Se adun, i rezult:

T ( n)

k 1

( k i ) 2i 1
i 1

Tehnica de calcul este aceeai:

T ( n) 2 T ( n) T ( n)
T (n)

k 2

( k 1) 2i

i 1

k 2

( k i ) 2i 1 ( k 1) 21 ( k 2) 22 ( k 3) 23 ... 3 2k 3 2 2k 2
i 1

( k 1) 20 ( k 2 ) 21 ( k 3) 22 ... 2 2 k 3 2 2 k 2 ( k 1)

k 3

i 1

k 1

k 2

2 ( k 1) 2

k 2

( 2 1) k 1 3 2

k 2

Rezult:
74

k 1

21 2 k 1 ( k 1) 20

k 3

21
i 0

T ( n ) 3 2 k 2 k 1 3 ( 2 k 1) k 1
T ( n ) 3 n log2 ( n 1) 1

------termen dominant
Rezult: complexitatea este O(n). Comparnd cu varianta anterioar, n aceast
variant (cu heap-ul la baz) am ctigat un ordin de complexitate. Rezult, complexitatea
algoritmului Heap_Sort este determinat de funciile remove ce nu pot fi aduse la mai puin de
complexitate O(log n). Rezult:
Heap_Sort = O(n) + O(nlog n)
--------------termen ce determin complexitatea
Rezult complexitatea algoritmului
Heap_Sort = O(nlog n)

4.4. Fiiere
Un fiier este o colecie ordonat de articole (nregistrri) pstrate pe un suport extern
de memorie i identificate printr-un nume. n C, pentru fiierul standard de intrare, datele
sunt introduse de la tastatur iar pentru fiierul standard de ieire, rezultatele sunt afiate pe
terminalul standard de ieire.
Mesajele de eroare se afieaz n fiierul standard de eroare.
Fiierul are un articol care marcheaz sfritul fiierului.
Not: Pentru fiierul standard de intrare de la tastatur, sfritul de fiier, pentru sistemele de
operare DOS i Windows se genereaz prin Ctrl-Z (pentru Unix prin Ctrl-D).
Operaiile specifice prelucrrii fiierelor sunt.
deschiderea unui fiier

nchiderea unui fiier


creerea unui fiier
citirea de articole din fiier (consultarea fiierului)
actualizarea (sau modificarea) fiierului
adugare de articole la sfritul fiierului
poziionarea n fiier.
tergerea unui fiier
schimbarea numelui unui fiier

Prelucrarea fiierelor se face pe dou niveluri:


nivelul inferior, care apeleaz direct la sistemul de operare.
nivelul superior, care utilizeaz structuri speciale FILE
Funciile de pe nivelul superior nu asigur o independen total fa de sistemul de
operare.
Funciile standard de intrare / ieire au prototipurile n fiierul antet <stdio.h>.
-

4.4.1. Fiiere text i fiiere binare


ntr-un fiier text, toate datele sunt memorate ca iruri de caractere, organizate pe linii,
separate ntre ele prin marcajul sfrit de linie \n .
75

ntr-un fiier text spaiul de memorare pe disc nu este folosit n mod eficient pentru datele
numerice (astfel ntregul 12345 ocup 5 octei).
ntr-un fiier binar, datele sunt pstrate n formatul lor intern (2 octei pentru int, 4 octei
pentru float, etc).
La fiierele text marcajul de sfrit de fiier (caracterul 0X1A) exist fizic n fiier. La
ntlnirea acestui caracter funcia fgetc() ntoarce EOF (-1). Marcajul de sfrit de fiier se
genereaz de la tastatur prin Ctrl-Z.
n cazul fiierelor binare, marcajul de sfrit de fiier nu exist fizic n fiier, ci este generat
de funcia fgetc().
n MSDOS (i n Unix), la nivelul liniei de comand intrrile i ieirile standard pot fi
redirectate n fiiere disc, fr a opera nici o modificare la nivelul programului. Astfel:
< redirecteaz intrarea standard ctre fiierul specificat
> redirecteaz ieirea standard ctre fiierul specificat
Fiierul standard de eroare nu poate fi redirectat.
Fiierul specificat poate fi:
con pentru consola sistem (tastatura, respeciv ecranul)
prn pentru imprimanta paralel
com1 pentru interfaa serial de date
nume_fiier pentru un fiier disc
NUL pentru perifericul nul.
4.4.2 Accesul la fiiere.
Fiierele disc i fiierele standard sunt gestionate prin pointeri la structuri specializate
FILE, care se asociaz fiecrui fiier pe durata prelucrrii.
Fiierele standard au pointerii predefinii: stdin, stdout, stderr, stdprn, stdaux.
Declararea unui pointer la fiier se face prin:
FILE *pf;
Deschiderea:
nainte de a fi prelucrat, un fiier trebuie s fie deschis. Prin deschidere:
- se asociaz unui nume de fiier un pointer la fiier
- se stabilete un mod de acces la fiier
Exemplu: FILE *fopen(char *nume_fiier, char *mod_acces);
-

Prin deschiderea unui fiier se stabilete o conexiune logic ntre fiier i variabila pointer
i se aloc o zon de memorie (buffer) pentru realizarea mai eficient a operaiilor de
intrare / ieire. Funcia ntoarce:
un pointer la fiier, n caz c deschiderea fiierului se face n mod corect
NULL dac fiierul nu poate fi deschis.
Vom considera mai nti dou moduri de acces :
citire (sau consultare) r citirea dintr-un fiier inexistent va genera eroare
scriere (sau creare) w - dac fiierul exist deja, el va fi ters
Fiierele standard nu trebuiesc deschise.
nchiderea:
Dup terminarea prelucrrilor asupra unui fiier, acesta trebuie nchis. Un fiier este
nchis automat la apelarea funciei exit().
Exemplu: int fclose(FILE *pf);
Revenim asupra modurilor de acces la disc. Sunt posibile urmtoarele situaii:
1.
fiierul nu exist; dorim s-l creem i s punem informaii n el
w - deschidere pentru scriere, noile scrieri se fac peste cele vechi
76

2.

fiierul exist deja; dorim s extragem informaii din el


r - deschidere pentru citire, fiierul trebuie s existe deja
r+ - citire i scriere ; fiierul trebuie s existe
3.
fiierul exist deja; dorim s adugm informaii la el, pstrnd informaiile deja
existente
a - deschidere pentru adugare, toate scrierile se adaug la sfritul fiierului
existent sau nou creat
a+ - citire i adugare; dac fiierul nu exist, el va fi creat
4.
fiierul exist deja; dorim s punem alte informaii n el tergnd pe cele existente
w+ - citire i scriere; dac fiierul exist deja el este ters
Modul de acces binar se specific cu sufixul b. Astfel avem: rb, w+b
Modul text este considerat implicit, dar poate fi specificat explicit prin t.
Not:
Fiierele n C++: realizarea funciilor de intrare/ieire n C++ se realizeaz prin intermediul
fluxurilor (streamuri). Un flux este o clas.
Fluxurile pot fi clasificate n:
fluxuri de intrare/ieire standard
fluxuri de intrare/ieire folosind fiiere
fluxuri de intrare/ieire n memorie
4.4.3. Algoritmi de prelucrare a fiierelor secveniale
Din punct de vedere al operaiilor de gestiune solicitate de diverse aplicaii, fiierele
binare se pot grupa n fiiere care nu sunt actualizate (inute la zi) i fiiere care sunt
actualizate. De obicei, fiierele din prima grup se regsesc n aplicaii matematice sau ca
fiiere temporare i de tranzacii n aplicaii de gestiune economic. Fiierele din cea de-a
dougrup sunt, de obicei, fiiere permanente (principale) n aplicaii de gestiune economic
i au particulariti de proiectare, referitoare, n special, la asigurarea tergerii i adugrii de
articole.
Asupra fiierelor binare care nu necesit actualizare se realizeaz, de obicei, operaiile
de creare (populare) i consultare. Dntre operaiile de actualizare pot fi realizate, fr mari
complicaii, modificarea i adugarea dens de articole.
Popularea fiierelor se realizeaz prin preluarea datelor fie din alte fiiere, fie de la
tastatur (popularea interaciv). n ultimul caz, cel mai des ntlnit n pracic, fiierul
conductor corespunde mulimii datelor introduse de la tastatur. Articolele sunt
preluate cmp cu cmp, neexistnd posibilitatea citirii unei variabile de tip articol i, n
plus, introducerea unei date este adesea nsoit de proceduri de validare specifice, cu
reintroducerea ei n cazul unei erori.
Sfritul introducerii datelor de la tastatur (i implicit a procesului de populare a
fiierului poate fi:
-De tip chestionar, prin consultarea utilizatorului, privind coninuarea sau nu a
introduce articolelor. Pentru un volum mare de date, varianta prezint dezavantajul mririi
timpului de prelucrare.
-Convenional, prin introducerea pentru primul cmp din articol a unei valori
prestabilite, cu semnificaie de sfrit de prelucrare.
-Standard, prin tastarea caracterului CTRL-Z, cu rol de sfrit de fiier TEXT, caz n
care variabilei standard CheckEof (definit n unitatea standard CRT) trebuie s i se atribuie
n program valoarea TRUE.
O alt problem a populrii fiierelor binare o reprezint aezarea articolelor pe
suportul extern.Din acest punct de vedere se ntlnesc dou modaliti:
Populare dens, prin care articolele se scriu unul dup altul, n ordinea n care au fost
77

furnizate, fr a se lsa locuri libere (acces secvenial). Pentru fiierele care nu


necesit actualizare acesta este tipul recomandat.
Populare aleatoare, prin care articolele sunt scrise pe poziiile al cror numr relativ
este furnizat explicit de programator (acces direct). Scrierea unui articol se realizeaz
dup poziionarea pe numrul relativ dorit, cu procedura Seek(f,nr_relativ). La
populare, nr_relativ nu este limitat dect de spaiul existent pe suportul extern. Metoda
are dezavantajul c necesit evidena "articolelor vide". n cazul fiierelor care nu
necesit actualizare, popularea aleatoare se recomand numai dac, dup creare,
fiierul este dens.
Consultarea fiierelor are mai multe variante, n funcie de scopul prelucrrii. Dup
modul de regsire a articolelor n cadrul fiierului consultarea poate fi secvenial,
direct sau mixt.
Consultarea secvenial presupune regsirea articolelor n ordinea n care sunt
memorate pe suportul extern. Dup numrul articolelor prelucrate, consultarea
secvenial poate fi: integral, cnd se prelucreaz toate articolele fiierului; cu
selecie, cnd se prelucreaz numai acele articole care au una sau mai multe
caracteristici comune. Dup numrul de caracteristici, selecia poate fi simpl, dubl,
multipl.

Exemplu:
Obinerea unei situaii cu mai multe grade de total.
Pentru aceasta se stabilesc cmpuri, numite caracteristici de grupare sau caracteristici de
control, asociate gradelor de total. O caracteristic de control este un cmp al articolului din
fiierul de date, care are aceeai valoare pentru mai multe nregistrri. Astfel, articolele care
au valoare comun pentru o caracteristic de grupare se pot ordona pe submulimi, formnd o
grup de control.
Fiierul poate constitui, n ansamblul su, caracteristica de grupare de cel mai nalt
nivel, pentru care se poate calcula totalul general. Numrul maxim de grade de total este
superior cu unu numrului de caracteristici de control stabilite.
ntre caracteristicile de grupare se stabilete o relaie de ordine ierarhic. Pentru prelucrarea
fiierului, cu utilizare minim de memorie, articolele trebuie sortate dup caracteristicile de
control. Acest tip de prelucrare intr n categoria consultrilor secveniale integrale i urmeaz
algoritmul de principiu din figurile 1.3 sau 1.4. Prelucrarea unui fiier sortat dup criteriile
enunate, presupune existena unor operaii standard, executate la schimbarea valorii fiecrei
caracteristici de control stabilite:
operaii iniiale ale unei grupe de control prin care se iniializeaz variabila de total
specific grupei; se salveaz valoarea caracteristicii primului articol din grup; alte
operaii iniiale specifice grupei;
operaii finale ale unei grupe de control prin care se afieaz totalul calculat pentru
caracteristica ce se schimb; se cumuleaz totalul grupei curente la totalul grupei
ierarhic superioar;
alte operaii finale specifice grupei;
condiia de prelucrare a unei grupe de control conine, pe lng condiia specific,
toate celelalte condiii din amonte.
Raportul final este listat la imprimant, fie direct, ca fiier de ieire, fie creat pe suport
magnetic, ca fiier text, n vederea imprimrii ulterioare. Structura unei pagini a raportului i
controlul trecerii la o nou pagin trebuie asigurate de programator.
n coninuare se prezint structura de principiu a unui program de obinere a unui raport final,
cu control dup dou caracteristici i trei grade de total, unde cmp_1 i cmp_2 sunt
caracteristicile de control (cmpuri din articol), v1 i v2 sunt variabile de lucru pentru salvarea
caracteristicilor, iar totg, tot1 i tot2 sunt variabile pentru calculul gradelor de total. Analog, se
78

poate extinde pentru oricte caracteristici i grade de total.


..............................................................
procedure inceput;
begin
(* citire nume fisiere externe *)
assign(f,nume_fiier); reset(f);
assign(g,lista); rewrite(g);
sf:=false; totg:=0;
(* ------------------------------------scriere n lista a antetului i a capului de tabel
-------------------------------------- *)
read(f,x);
end;
procedure inceput_1;
begin
tot1:=0; v1:=x.camp_1;
end;
procedure inceput_2;
begin
tot2:=0; v2:=x.camp_2;
end;
procedure sfarsit_1;
begin
writeln('total 1 = ',tot1);
totg:=totg+tot1
end;
procedure sfarsit_2;
begin
writeln('total 2 = ',tot2);
tot1:=tot1+tot2
end;
procedure sfarsit;
begin
writeln('total general = ',totg);
close(f); close(g)
end;
procedure prel_art_x;
begin
(* ----------------------------prelucrarea articolului x
----------------------------- *)
if eof(f) then sf:=true else read(f,x)
end;
procedure prel_2;
begin
inceput_2;
while (v1=x.camp_1) and (v2=x.camp_2) and not sf
do prel_art_x;
sfarsit_2;
end;
79

procedure prel_1;
begin
inceput_1;
while (v1=x.camp_1) and not sf do prel_2;
sfarsit_1;
end;
begin
inceput;
while not sf do prel_1;
sfarsit;
end.
Consultarea n acces direct presupune regsirea articolului dup numrul relativ, prin
secvena Seek(f,nr_relativ); Read(f,art). Numrul relativ este furnizat de utilizator i trebuie s
aparin domeniului 0..FileSize(f)-1. Se remarc faptul c dac nr_relativ depete
dimensiunea fiierului, procedura Seek nu genereaz eroare, dar citirea va determina
ntreruperea execuiei programului. Pentru evitarea acestei situaii se va include n program
validarea apartenenei numrului relativ la intervalul acceptat. Algoritmul de consultare n
acces direct a unui fiier are un alt fiier conductor (de exemplu, tastatura).
Exemplu:
............................
begin
(* citire nume fiier extern *)
checkeof:=true; assign(f,nume_fiier); reset(f);
write('nr. relativ: ');
while not eof do
begin
readln(r); {citirea numarului relativ al articolului}
seek(f,r); {$i-} read(f,art); {$i+}
if ioresult = 0 then
(* -----------------------prelucrare articol
Algoritmi i programe de prelucrare a fisierelor
------------------------ *)
else
writeln('Articol inexistent !');
write('Nr. relativ (sau ctrl-z): ')
end;
close(f)
end.
Consultarea n acces mixt utilizeaz o combinaie ntre accesul direct i cel secvenial,
n vederea prelucrrii unui grup de articole, memorate contiguu n fiier i selectabile
printr-o condiie.
Pentru fiierele binare, metoda poate fi aplicat dac se dorete selectarea articolelor
dntre dou limite ale numerelor relative (limita inferioara - li i limita superioara - ls).
Algoritmul trebuie s verifice relaia 0=li=ls<FileSize(f), dup care parcurgerea fiierului
poate fi realizat prin orice tip de structur repetitiv.
Exemplu:
............................
begin
80

(* citire nume fiier extern *)


assign(f,nume_fiier);
reset(f);
write('limita inferioara: ');
readln(li); { citirea nr. relativ al primului articol din secventa }
write('limita superioara: ');
readln(ls); { citirea nr. relativ al ultimului articol din secventa }
if (0<=li) and (li<=ls) and (ls<filesize(f)) then
begin
seek(f,li);
for i:=li to ls do
begin
read(f,art);
(* ----------------------prelucrare articol
----------------------- *)
end
end
else
writeln('>> Nu este indeplinita conditia de limite');
close(f)
end.
Adugarea de articole se realizeaz, n general, cu tranzacii de la terminal, similar
operaiei de populare. Pentru o corect exploatare ulterioar, adaugarea trebuie s fie dens.
Acest lucru poate fi realizat astfel:
Adaugare la sfrsit (extindere), dupa ultimul articol scris. Operatia se realizeaz
similar populrii n acces secvenial, dup poziionarea pe marcatorul de sfrit de fiier, cu
procedura Seek(f,FileSize(f))
Exemplu:
..................
begin
(* citire nume fiier extern *)
Algoritmi i programe de prelucrare a fisierelor
assign(f,nume_fiier);
reset(f);
seek(f,filesize(f)); { pozitionare dupa ultimul articol scris }
checkeof:=true;
write('camp 1: ');
while not eof do
begin
readln(art.camp_1);
(* ----------------------------------------------preluare de la tastatura a celorlalte
campuri din articol
------------------------------------------------ *)
write(f,art);
write('camp 1 (sau ctrl-z): ')
end;
close(f)
end.
81

Inserarea unor articole. Se aplic n cazul n care articolele sunt scrise n fiier n
ordinea cresctoare (descrescatoare) a valorilor unui anumit cmp. n acest caz, noul articol va
fi inserat ntre dou articole, astfel:
- se caut (cu un algoritm secvenial, binar etc.) pozitia k n care trebuie inserat noul articol;
- se copiaz, glisnd cu o pozitie spre dreapta, toate articolele de la sfritul fiierului pn la
articolul cu numrul relativ k;
- se scrie n acces direct noul articol, n poziia k.
Exemplu:
{ articolele fisierului sunt n ordinea crescatoare a valorii campului 1 }
begin
(* citire nume fiier extern *)
assign(f,nume_fiier);
reset(f);
checkeof:=true;
write('camp 1: ');
while not eof do { se asigura adaugarea mai multor articole }
begin
readln(art_nou.camp_1); { introducerea campului
dupa care sunt sortate articolele }
(* ----------------------------------preluare de la tastatura a celorlalte
campuri din articolul de adaugat
----------------------------------- *)
{ secventa de cautare a pozitiei n care se va insera articolul }
seek(f,0); { pozitionare pe inceput de fiier }
sf:=false;
read(f,art_existent);
while not sf and (art_existent.camp_1<art_nou.camp_1) do
begin
read(f,art_existent);
if ioresult <> 0 then sf:=true
end;
if not sf then
begin
k:=filepos(f)-1; { articolul se va insera n pozitia k }
for i:=filesize(f)-1 downto k do
begin
seek(f,i); read(f,art_existent);
write(f,art_existent)
end
end
else k:=filesize(f); {articolul se adauga la sfarsitul fisierului}
seek(f,k);
write(f,art_nou);
write('camp 1: ')
end;
close(f)
end.
Modificarea valorii unor cmpuri din articol se realizeaz n mai multe etape:
82

- se citete articolul care se modific (Seek i Read);


- se modific (n zona articol din memoria principal) cmpurile cu valorile dorite, introduse,
n general, de la tastatura;
- se repozitioneaz pe articolul respeciv cu Seek(f,FilePos(f)-1);
- se scrie articolul modificat, cu procedura Write.
O problem importanta ramne selectarea cmpurilor care se modific, pentru fiecare
articol n parte. O variant simpl este afiarea vechii valori, urmat de introducerea noii
valori, n variabile independente de tip STRING. n cazul n care irul introdus este vid (s-a
tastat numai <ENTER>), respecivul cmp, prin convenie, nu se modific. Altfel, cmpului
respeciv i se va atribui valoarea citit n variabila independent, eventual prin conversie cu
procedura VAL, pentru cmpurile numerice.
Exemplu:
.....................................
(* -----------------------------cautare articol de modificat
------------------------------ *)
read(f,art);
write('Codul: ',art.cod,' '); { afisare vechea valoare }
readln(cods); {citire noua valoare; cods este de tip string}
if length(cods)<>0 then val(cods,art.cod,err);
{ conversie din ASCII n binar }
write('Denumire: ',art.den,' '); { afisare vechea valoare }
readln (dens); { citire noua valoare }
if length(dens) <> 0 then
art.den:=dens; { atribuire noua valoare }
*( ---------------------------------introducerea celorlalte campuri din articol
----------------------------------- *)
seek(f,filepos(f)-1); { repozitionare pe articol }
write(f,art) { rescriere articol modificat }
O alt variant se poate realiza prin folosirea unei machete de ecran n care se afieaz
valorile actuale ale fiecarui cmp de modificat, se poziioneaz succesiv cursorul la nceputul
fiecarui cmp, cu dou raspunsuri posibile ale utilizatorului: <ENTER>, caz n care se
menine actuala valoare, respeciv tasta diferita de <ENTER>, reprezentnd primul caracter al
noii valori. Pentru aplicaii vedei exerciiile corespunztoare din finalul seciunii urmtoare
(aplicaiile 59- 63).
Resurse:
1. T. H. Cormen, C.E. Leiserson, R.R. Rivest, Introducere n algoritmi, Computer Libris
Agora, 2000,
2. R. Viinescu, V. Viinscu- Algoritmi i structuri de date- Teorie i aplicaii. Probleme de
concurs, Editura Edusoft, 2006
3. http://www.studentie.ro/cursuri_Structuri_de_Date_si_Algoritmi-125.html- diverse cursuri
despre structuri de date i algoritmi
4. http://www.structuri.ase.ro/- curs structuri
5. http://www.danciu.ro/materials/Structuri%20de%20date/Capitolele%201,2,3,4.doc - curs structuri
6.http://www.cwu.edu/~andonie/Cartea%20de%20algoritmi/cap3.htm- structuri, grafuri,
arbori, heap

83

5. IMPLEMENTAREA STRUCTURILOR DE DATE


5.1. Structuri simple de date
1. Calcul cmmdc-varianta iterativ
program cmmdc_iterativ;
var sf:char;
a,b:longint;
funcion cmmdc(a,b:longint):longint;
begin
while a<>b do
if a>b then a:=a-b
else b:=b-a;
cmmdc:=a;
end;
begin
sf := D;
repeat
write('a=');readln(a);
write('b=');readln(b);
write('cmmdc=',cmmdc(a,b));
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
2. Calcul cmmdc-varianta Euclid iterativ
program cmmdc_euclid_iterativ;
var sf:char;
a,b:integer;
funcion cmmdc(a,b:integer):integer;
var r:integer;
begin
if b<>0 then
begin
r:=a mod b;
while r<>0 do
begin
a:=b;
b:=r;
r:=amodb;
end;
end;
cmmdc:=b;
end;
begin
sf := D;
repeat
write('a=');readln(a);
write('b=');readln(b);
84

writeln('cmmdc=',cmmdc(a,b));
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
3. Calcul cmmdc-varianta Euclid recursiv
program cmmdc_euclid_recursiv;
var sf:char;
a,b:integer;
funcion cmmdc(a,b:integer):integer;
begin
if b=0 then cmmdc:=a
else cmmdc:=cmmdc(b,a mod b);
end;
begin
sf := D;
repeat
write('a=');readln(a);
write('b=');readln(b);
write('cmmdc=',cmmdc(a,b));
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
4. Calcul cmmdc-varianta recursiv
program cmmdc_recursiv;
var a,b:longint;
sf:char;
funcion c(a,b:longint):longint;
begin
if a=b then c:=a
else if a>b
then c:=c(a-b,b)
else c:=c(a,b-a);
end;
begin
sf := D;
repeat
write('a=');readln(a);
write('b=');readln(b);
write('cmmdc=',c(a,b));
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
5. Calcul cmmdc pentru n numere
program cmmmdc_n_numere;
const n_max=100;
type vector =array[1..n_max] of word;
var
i,j,c,n:integer;
85

v:vector;
sf:char;
funcion cmmdc(a,b:word):word;
begin
while a<>b do begin
if a>b then a:=a-b
else b:=b-a;
end;
cmmdc:=a;
end;
begin
sf := D;
repeat
write('n='); readln(n);
for i:=1 to n do
begin
write('v[',i,']=');
readln(v[i])
end;
c:=v[1];
for j:=2 to n do c:=cmmdc(c,v[j]);
writeln('cmmdc pt. ',n,' numere=',c);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
6. Calculul combinrilor- varianta iterativ
program combinari_iterativ;
var sf:char;
k,m,n:word;
p:real;
begin
sf := D;
repeat
write('n='); readln(n);
repeat
write('m='); readln(m)
until m<n;
p:=1;
for k:=1 to m do p:=(n-m+k)/k*p;
writeln('comb=',p:7:0);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
7. Calculul combinrilor-varianta recursiv
program combinari_recursiv;
var sf:char;
n,m:integer;
86

funcion comb(n,m:integer):integer;
begin
if (m=0) or (n=m)
then comb:=1
else comb:=comb(n-1,m)+comb(n-1,m-1);
end;
begin
sf := D;
repeat
repeat
write('n='); readln(n);
write('m='); readln(m);
until (0<=m) and (m<=n);
writeln('comb=',comb(n,m));
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
8. Rezolvarea triunghiului
program rezolvarea_triunghiului;
const pi=3.14159;
var
x1,x2,x3,y1,y2,y3:integer;
a,b,c,ab,bc,ac:real;
s,p:real;
sf:char;
begin
sf := D;
repeat
writeln('coordonate primului punct');
readln(x1,y1);
writeln('coordonate al doilea punct');
readln(x2,y2);
writeln('coordonate al treilea punct');
readln(x3,y3);
if (x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y3))<>0 then
begin
writeln( Nu formeaz triunghi');
ab:=sqrt(sqr(x2-x1)+sqr(y2-y1));
bc:=sqrt(sqr(x3-x2)+sqr(y3-y2));
ac:=sqrt(sqr(x1-x3)+sqr(y1-y3));
p:=(ab+bc+ac)/2;
s:=sqrt(p*(p-ab)*(p-bc)*(p-ac));
writeln('Aria=',s:2:2);
if (ab=bc)and(bc=ac) then
writeln('triunghi echilateral')
else if (ab=bc)or(bc=ac)or(ac=ab) then writeln ('triunghi
isoscel')
else writeln('triunghi
oarecare');
if(ab*ab+bc*bc=ac*ac) or (ab*ab+ac*ac=bc*bc) or (bc*bc+ac*ac=ab*ab)
then writeln('triunghi dreptunghic');
end
87

else write('punctele sunt colineare');


writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
9. Numere egale cu suma cuburilor cifrelor
program Cuburi_numere_de_trei_cifre;
var sf:char;
n,n1,a,b,c:integer;
ok:boolean;
answer:char;
begin
sf := D;
repeat
writeln('Numerele de 3 cifre egale cu suma cuburilor cifrelor sunt:');
for n:=100 to 999 do begin
n1:=n;
c:=n1 mod 10;
n1:=n1 div 10;
b:=n1 mod 10;
a:=n1 div 10;
ok:=false;
if n=a*a*a+b*b*b+c*c*c then ok:=true;
if ok then writeln(' ':10,n);
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
10. Descompunere n factori primi
program descompunere_n_factori_primi;
var sf:char;
m,n,d:integer;
begin
sf := D;
repeat
write('n=');readln(n);
writeln(n:4,'are factorii primi:');
writeln('factor',' ':5,'multiplicitate');
d:=2;
while d<=trunc(sqrt(n+0.5)) do
begin
m:=0;
while n mod d=0 do begin
n:=n div d;
m:=m+1;
end;
if m>0 then writeln(d:5,' ':9,m:2);
if d=2 then d:=3

88

else d:=d+2;
end;
if n>1 then write(n:5,' ':9,1:2);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
11. irul lui Fibonacci-varianta recursiv
program fibonacci_recursiv;
var sf:char;
n,i:longint;
funcion fibo(n:longint):longint;
begin
if n<=2 then fibo:=1
else fibo:=fibo(n-1)+fibo(n-2);
end;
begin
sf := D;
repeat
write('n=');
readln(n);
for i:=1 to n do write(fibo(i),' ');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
12. irul lui Fibonacci-varianta iterativ
program fibonacci_iterativ;
var sf:char;
a,b,c,n:longint;
begin
sf := D;
repeat
write('n=');readln(n);
write(0,' ',1);
a:=0;
b:=1;
while a+b<=n do
begin
c:=a+b;
write(' ',c);
a:=b;
b:=c;
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.

89

13. Determinare numere perfecte


program numere_perfecte;
const n_max=100;
var
n,i,j,l:longint;
s:longint;
vec:array[1..n_max]of longint;
sf:char;
begin
sf := D;
repeat
write('introduceti un ntreg:');
readln(n);
for i:=2 to n do begin
s:=1;
l:=1;
vec[1]:=s;
for j:=2 to i div 2 do
if i/j=i div j then begin
s:=s+j;
l:=l+1;
vec[l]:=j
end;
if s=i then
begin
write(i,' este numar perfect=');
for j:=1 to l-1 do
write(vec[j],'+');
writeln(vec[l]);
end
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
14. Numere palindrome-varianta 1-a
program numere_palindrome_varianta1;
type cifra=0..9;
var
n,r,nr:longint;
p:cifra;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
r:=n;
nr:=0;
while n<>0 do begin
p:=n mod 10;
n:=n div 10;
nr:=nr*10+p;
end;
90

if r=nr then write(r,' este palindrom')


else write(r,'nu este palindrom');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
15. Numere palindrome-varianta a-2-a
program numere_palindrome_varianta2;
type cifra=0..9;
var
n,r,putere:longint;
p,q:cifra;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
r:=n;
putere:=1;
while r>=10 do begin
putere:=putere*10;
r:=r div 10
end;
if putere=1 then write('Este')
else begin
repeat
p:=n div putere;
q:=n mod 10;
n:=n mod putere;
n:=n div 10;
putere:=putere div 100
until(p<>q)or(putere<10);
if p=q then write('Este')
else write('Nu este')
end;
write(' palindrom');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.

5.2. Liste, pointeri, mulimi, arbori


16. Ciurul lui Eratostene-varianta mulime
program Eratostene_varianta_mulime;
type ind=0..255;
var
n,i,j,k:ind;
mult:set of ind;
sf:char;
begin
91

sf := D;
repeat
write (n=);
readln (n);
mult:=[2..n];
j:=0;
writeln(Numerele prime pana la ,n);
for i:=2 to n do
if i n mult then
begin
write (i:4);
j:=j+1;
k:=2;
while i*k<=n do
begin
mult:=mult-[i*k];
k:=k+1;
end;
end;
writeln;
writeln(Numrul de elemente prime ,j);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
17. Sume maxime de numere ntregi
program sume_de_numere;
const nmax=100;
type ir=array[1..nmax]of byte;
var
s,a:ir;
i,j,nr,n:byte;
f:file of byte;
l:string[80];
ss:longint;
nume:string[10];
sf:char;
begin
sf := D;
repeat
write('nume fiier input:');
readln(nume);
assign(f,nume);
rewrite(f);
write('dati nr. de linii:');
readln(n);
write(f,n);
for i:=1 to n do
begin
writeln('Linia cu',i:2,' numere');
for j:=1 to i do
begin
92

write(j:3,'. ');
readln(nr);
write(f,nr);
end;
end;
reset(f);
read(f,n);
for i:=1 to nmax do s[i]:=0;
writeln ('sumele partiale sunt:');
for i:=1 to n do
begin
for j:=1 to i+1 do a[j]:=s[j];
read(f,nr);
s[1]:=s[1]+nr;
for j:=2 to i do
begin
read(f,nr);
if a[j-1]<a[j] then s[j]:=a[j]+nr
else s[j]:=a[j-1]+nr;
end;
s[i+1]:=nr;
for j:=1 to i do write(s[j],' ');
writeln;
end;
ss:=s[1];
for j:=1 to i+1 do
if ss<s[j] then ss:=s[j];
writeln('Suma maxima=',ss:5);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
18. Eliminare elemente-varianta vector logic
program cerc1_varianta_vector_boolean;
const nmax=100;
type nr=1..nmax;
var i,k,n:nr;
j,m:integer;
prezent:array[nr] of boolean;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
for i:=1 to n do prezent[i]:=true;
i:=n;
for k:=1 to n do begin
for j:=1 to m do
repeat
93

if i<n then i:=i+1 else i:=1


until prezent[i];
write(i:3);
prezent[i]:=false;
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
19. Eliminare elemente-varianta vector numeric
program cerc2_varianta_vector_ntregi;
const nmax =100;
type nr =1..nmax;
var
i,k,n:nr;
j,m:integer;
next:array[nr] of nr;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
for i:=1 to n-1 do next[i]:=i+1;
next[n]:=1;
i:=n;
while next[i] <> i do begin
for j:=1 to m-1 do i:=next[i];
write(next[i]:3);
next[i]:=next[next[i]];
end;
writeln(i:3);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
20. Eliminare elemente-varianta calcul iterativ
program cerc3_varianta_calcul_iterativ;
const nmax=100;
type nr=1..nmax;
var
i,k,n:nr;
j,m:integer;
copil:array[nr] of nr;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
for i:=1 to n do copil[i]:=i;
94

i:=1;
for k:=n downto 1 do begin
i:=(i+m-2) mod k+1;
write(copil[i]:3);
for j:=i to k-1 do
copil[j]:=copil[j+1];
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
21. Eliminare elemente-varianta mulime
program cerc4_varianta_multime;
const nmax=100;
type numar=1..nmax;
var
i,n:numar;
j,m:integer;
multime:set of numar;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
mulime:=[1..n];
i:=n;
while mulime<>[] do begin
for j:=1 to m do
repeat
if i<n then i:=i+1
else i:=1
until i n mulime;
write(i:3);
mulime:=mulime-[i];
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
22. Eliminare elemente-varianta alocare dinamic
program cerc5_varianta_alocare_dinamica;
const nmax=100;
type list=^term;
term=record
nr:integer;
next:list;
end;
var i,n:1..nmax;
95

j,m:integer;
prim,p:list;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
new(prim);
p:=prim;
for i:=1 to n do begin
p^.nr:=i;
if i<n then begin
new(p^.next);
p:=p^.next;
end;
end;
p^.next:=prim;
i:=n;
while i>0 do begin
for j:=1 to m-1 do p:=p^.next;
write(p^.next^.nr:3);
p^.next:=p^.next^.next;
i:=i-1;
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
23. Diferena mulimilor alocate static
program diferenta_a_doua_multimi;
const n_max=50;
type vector=array[1..n_max] of integer;
header=string[20];
var a,b:vector;
na,nb:integer;
sf:char;
procedure citire_vector(var v:vector; var n:integer;x:char);
var i:integer;
begin
repeat
write('nr de componente:');
readln(n)
until n n [1..n_max];
for i:=1 to n do begin
write(x,'[',i,']=');
readln(v[i])
end;
writeln;
end;
96

procedure afisare_vector(v:vector; n,nr_lin:integer;h:header);


var i:integer;
begin
write(h);
for i:=1 to length(h) do h[i]:=' ';
for i:=1 to n do begin
write(v[i]:3);
if (i mod nr_lin=0)and(i<n)then begin
writeln;
write(h)
end
end
end; {afisare_vector}
funcion exist(v:vector;val:integer; n:integer):boolean;
var i:integer;
begin
i:=1;
while (i<n) and (val<>v[i]) do inc(i);
exist:=(n>0) and (val=v[i])
end; {exist}
procedure insert(var v:vector;val:integer;var n:integer);
begin
if not exist(v,val,n) then begin
n:=n+1;
v[n]:=val
end;
end; {insert}
procedure det_dif (v1,v2:vector;n1,n2:integer; h:header);
var d:vector;
nd,i:integer;
procedure scrie_dif;
var i:integer;
begin
if nd=0 then write ('Diferenta vid')
else afisare_vector(d,nd,5,h)
end; {scrie_dif}
begin {det_dif}
nd:=0;
for i:=1 to n1 do
if not exist (v2,v1[i],n2) then
insert(d,v1[i],nd);
scrie_dif
end; {det_dif}
begin {program principal}
sf := D;
repeat
writeln('First vector:');
citire_vector(a,na,'a');
sf := D; repeat
writeln('Second vector:');
citire_vector(b,nb,'b');
sf := D; repeat
97

afisare_vector(a,na,10,'First vector:');
writeln;
afisare_vector(b,nb,10,'Second vector:');
writeln;
det_dif(a,b,na,nb,'First\Second:');
writeln;
det_dif(b,a,nb,na,'Second\First:');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
24. Operaii cu mulimi generate dinamic
program reuniune_i_intersecie_generate_dinamic;
const nmax=100;
type list=^nod;
nod=record
info:integer;
next:list
end;
var a:array[1..nmax]of integer;
l,l1,l2:list;
n,i:integer;
sf:char;
funcion reun(l1,l2:list):list; forward;
funcion inters(l1,l2:list):list;forward;
funcion creal(i,n:integer):list;
var l:list;
begin
if i>n then creal:=nil
else
begin
new(l);
l^.next:=creal(i+1,n);
l^.info:=a[i];
creal:=l
end;
end;
procedure afisl(l:list);
begin
if l<>nil then begin
write(l^.info,' ');
afisl(l^.next)
end
end;
funcion atasare(x:integer;l1,l2:list):list;
var prim:list;
begin
new(prim);
prim^.info:=x;
prim^.next:=reun(l1,l2);
atasare:=prim;
98

end;
funcion atasare1(x:integer;l1,l2:list):list;
var prim:list;
begin
new(prim);
prim^.info:=x;
prim^.next:=inters(l1,l2);
atasare1:=prim;
end;
funcion reun(l1,l2:list):list;
begin
if (l1=nil)and(l2=nil) then reun:=nil
else if l1=nil then
reun:=atasare(l2^.info,nil,l2^.next)
else if l2=nil then
reun:=atasare(l1^.info, l1^.next,nil)
else if l1^.info<l2^.info then
reun:=atasare(l1^.info,l1^.next,l2)
else if l1^.info>l2^.info then
reun:=atasare(l2^.info,l1,l2^.next)
else
reun:=atasare(l1^.info,l1^.next,l2^.next)
end;
funcion inters(l1,l2:list):list;
begin
if (l1=nil)or(l2=nil) then inters:=nil
else if l1^.info<l2^.info then
inters:=inters(l1^.next,l2)
else if l1^.info>l2^.info then
inters:=inters(l1,l2^.next)
else
inters:=atasare1(l1^.info,l1^.next,l2^.next);
end;
begin
sf := D;
repeat
write('prima list, n='); readln(n);
for i:=1 to n do read (a[i]);
l1:=creal(1,n);
writeln;
afisl(l1);
writeln;
write('A doulist, n='); readln(n);
for i:=1 to n do read (a[i]);
l2:=creal(1,n);
writeln;
afisl(l2);
writeln;
l:=reun(l1,l2);
writeln('reuniunea');
afisl(l);
writeln;
99

l:=inters(l1,l2);
writeln('intersecia');
afisl(l);
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
25. Ordonare dinamic recursiv ir numeric
program sortare_dinamica_recursiva_prin_separare;
const nmax=100;
type list=^nod;
nod=record
info:integer;
next:list
end;
var
a:array[1..nmax]of integer;
k,n:integer;
l:list;
sf:char;
funcion fuzi(l1,l2:list):list;
begin
if l1=nil then fuzi:=l2
else if l2=nil then fuzi:=l1
else if l1^.info<=l2^.info then
begin
l1^.next:=fuzi(l1^.next,l2);
fuzi:=l1
end
else begin
l2^.next:=fuzi(l1,l2^.next);
fuzi:=l2
end
end;
funcion separ(l:list):list;
var l1:list;
begin
if l=nil then separ:=nil
else if l^.next=nil then separ:=nil
else begin
l1:=l^.next;
l^.next:=l1^.next;
l1^.next:=separ(l1^.next);
separ:=l1;
end;
end;
procedure sortfuzi(var l:list);
var l1:list;
begin
if l<>nil then

100

if l^.next <>nil then begin


l1:=separ(l);
sortfuzi(l);
sortfuzi(l1);
l:=fuzi(l,l1);
end;
end;
funcion creal(i,n:integer):list;
var l:list;
begin
if i>n then creal:=nil
else begin
new(l);
l^.next:=creal(i+1,n);
l^.info:=a[i];
creal:=l;
end;
end;
procedure afisl(l:list);
begin
if l<>nil then begin
write(l^.info,' ');
afisl(l^.next);
end;
end;
begin
sf := D;
repeat
write('n=');
readln(n);
for k:=1 to n do begin
write('a[',k,']=');
readln(a[k]);
end;
l:=creal(1,n);
sortfuzi(l);
afisl(l);
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
26. Polinoame de o variabil generate dinamic
program polinoame_generate_dinamic;
type poli=^term;
term=record
cf:real;
ex:integer;
next:poli
end;
var p,q,sum,mult:poli;
101

sf:char;
procedure readp (var p:poli);
var n:0..maxint;
i:integer;
crt:poli;
begin
write('nr. termeni:');
readln(n);
if n=0 then p:=nil
else begin
writeln('introd. coef+exp n ordine descresc:');
new(p);
crt:=p;
for i:=1 to n do begin
with crt^ do read(cf,ex);
if i<n then begin
new(crt^.next);
crt:=crt^.next
end
end;
crt^.next:=nil
end;
end;
procedure adaug(cf:real;ex:integer;var p:poli; var
n:integer);
begin
if n<>1 then p:=p^.next;
n:=n+1;
p^.cf:=cf;
p^.ex:=ex;
new(p^.next)
end;
procedure writep(p:poli);
var crt:poli;
begin
crt:=p;
while crt<>nil do begin
with crt^ do begin
if cf<0 then write('-')
else if cf>0 then write('+');
if (abs(cf)<>1) or (ex=0) then write(abs(cf):2:0);
if ex>0 then write('x');
if ex>1 then write('^',ex:1)
end;
crt:=crt^.next
end;
writeln
end;
procedure sumap(p,q:poli; var sum:poli);
var crt:poli;
n:integer;

102

begin
new(sum);
crt:=sum;
n:=1;
while (p<>nil)and(q<>nil) do
if p^.ex < q^.ex then begin
adaug(q^.cf,q^.ex,crt,n);
q:=q^.next
end
else if p^.ex > q^.ex then begin
adaug(p^.cf,p^.ex,crt,n);
p:=p^.next
end
else begin
if p^.cf+q^.cf<>0 then
adaug(p^.cf+q^.cf,p^.ex,crt,n);
p:=p^.next;
q:=q^.next
end;
while p<>nil do begin
adaug(p^.cf,p^.ex, crt,n);
p:=p^.next
end;
while q<>nil do begin
adaug(q^.cf,q^.ex, crt,n);
q:=q^.next
end;
crt^.next:=nil
end;
procedure prodp(p,q:poli; var mult:poli);
var crt,crtp,crtq:poli;
exc,rex,n:integer;
cfc:real;
begin
if (p=nil) or (q=nil) then mult:=nil
else begin
new(mult);
crt:=mult;
n:=1;
exc:=p^.ex+q^.ex;
while exc>=0 do begin
cfc:=0;
crtp:=p;
repeat
rex:=exc-crtp^.ex;
if rex>0 then begin
crtq:=q;
while
(crtq^.ex>rex)and(crtq^.next<>nil) do
crtq:=crtq^.next;
if crtq^.ex=rex then
cfc:=cfc+crtp^.cf*crtq^.cf;
103

end;
crtp:=crtp^.next;
until crtp=nil;
if cfc<>0 then
adaug(cfc,exc,crt,n);
exc:=exc-1
end;
crt^.next:=nil
end;
end;
begin
sf := D;
repeat
readp(p);
readp(q);
write('p(x)=');
writep(p);
write('q(x)=');
writep(q);
sumap(p,q,sum);
write('p(x)+q(x)');
writep(sum);
prodp(p,q,mult);
write('p(x)*q(x)=');
writep(mult);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
27. Afiare vector n ordine invers
program vector_inversat;
const nmax=100;
type vector=array[1..nmax] of integer;
var v:vector;
n,i:integer;
sf:char;
begin
sf := D;
repeat
repeat
write('Numar elemente [1..100]:');
readln(n)
until n n [1..100];
writeln('Dati cele ',n,' elemente');
for i:=1 to n do begin
write('v[',i,')=');
readln(v[i])
end;
writeln('Elementele n ordine inversa:');
for i:=n downto 1 do begin
write(v[i]:3);
104

if (n-i+1) mod 10=0 then writeln


end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
28. Cutare secvenial n vector neordonat
program cutare_secventiala;
const nmax=100;
type
arr=array[1..nmax] of integer;
var
list:arr;
n,i:1..nmax;
ok:boolean;
elem:integer;
sf:char;
begin
sf := D;
repeat
repeat
write('Dati Numrul elementelor:');
readln(n)
until n n [1..nmax];
for i:=1 to n do begin
write('list[',i,']=');
readln(list[i])
end;
write('Elementul cautat=');
readln(elem);
i:=1;
ok:=false;
while not ok and (i<n) do begin
i:=i+1;
if list[i]=elem then ok:=true
end;
if ok then writeln ('Elem. ',elem,' gsit pe poziia ',i)
else writeln ('Elem. ',elem,' negasit');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
29. Cutare binar n vector ordonat
program cutare_binara;
const nmax=100;
type arr=array[1..nmax] of integer;
var
list:arr;
n,i,linf,lsup,med:1..nmax;
ok:boolean;
elem:integer;
sf:char;
begin
105

sf := D;
repeat
repeat
write('Dati Numrul elementelor:');
readln(n)
until n n [1..nmax];
for i:=1 to n do begin
write('list[',i,']=');
readln(list[i])
end;
write('Elementul cautat=');
readln(elem);
ok:=false;
linf:=1;
lsup:=n;
repeat
med:=(linf+lsup) div 2;
if elem=list[med] then ok:=true
else if elem < list[med] then lsup:=med-1
else linf:=med+1
until ok or (linf>lsup);
if ok then writeln ('Elem. ',elem,' gsit pe poziia ',med)
else writeln ('Elem. ',elem,' negasit');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
30. Proceduri pe iruri de caractere
program proceduri_pe_siruri;
const size=20;
type line=string[size];
var s1,s2:line;
p,c:integer;
option:char;
buflen:word;
sf:char;
begin
sf := D;
repeat
s1:='';
repeat
writeln('String=',s1);
write('Option[Read, Delete, Insert,
Quit]:');
readln(option);
case upcase(option) of 'R':
begin
sf := D; repeat
write('New string: ');
buflen:=size;
readln(s1);
106

end;
'D':
begin
write('Delete starting from: ');
readln (p);
write('Number of deleted
characters: ');
readln(c);
delete(s1,p,c)
end;
'I':
begin
write('Insert substr: ');
buflen:=size;
read(s2);
write('Starting from: ');
readln(p);
insert (s2,s1,p)
end;
'Q':
begin
write ('Are you sure [Y/N]:');
readln(option);
if upcase(option)='Y' then option:='Q'
end
else writeln ('Incorrect')
end;
until option='Q';
writeln('Gata!');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
31. Conversie ir de caractere n numeric
program str_to_val;
const size=10;
type line=string[size];
var
s:line;
n,code:integer;
x:real;
sf:char;
begin
sf := D;
repeat
repeat
write ('Numeric constant: ');
readln (s);
if length(s)>0 then
begin
val (s,n,code);
if code=0 then writeln('Is a
107

integer constant')
else
begin
val (s,x,code);
if code=0 then writeln('Is a real constant')
else
begin
writeln ('Error!');
writeln('Codul de eroare:19',code)
end
end
end
until s='';
writeln('Gata! Press any key');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
32. Ordonare vector prin selecie
program sortare_prin_selecie;
const nmax=100;
var
c:array[1..nmax]of integer;
k,n,min,i,j,aux:integer;
sf:char;
begin
sf := D;
repeat
write('Numrul componentelor:'); readln(n);
for i:=1 to n do begin
write ('c[',i,']='); readln (c[i])
end;
for i:=1 to n do begin
k:=i;
min:=c[i];
for j:=i to n do
if c[j]<min then begin
min:=c[j];
k:=j
end;
aux:=c[i];
c[i]:=c[k];
c[k]:=aux
end;
write('vectorul ordonat:');
for j:=1 to n do write(c[j],' ');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.

108

33. Ordonare vector prin enumerare


program sortare_prin_enumerare_da pozitia_n irulsortat;
const nmax=100;
var
c,rang:array[1..nmax]of integer;
n,i,j:integer;
sf:char;
begin
sf := D;
repeat
write('Numrul componentelor:'); readln(n);
for i:=1 to n do begin
rang[i]:=1;
write ('c[',i,']=');
readln (c[i])
end;
for i:=1 to n do
for j:=1 to i-1 do
if c[j]<c[i] then rang[i]:=rang[i]+1
else rang[j]:=rang[j]+1;
write('vectorul ordonat:');
for j:=1 to n do write(rang[j],' ');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
34. Ordonare vector prin inserare
program sortare_prin_inserare;
const nmax=100;
var
c:array[0..nmax]of integer;
k,n,cheie,i:integer;
sf:char;
begin
sf := D;
repeat
write('Numrul componentelor:'); readln(n);
for i:=1 to n do begin
write ('c[',i,']='); readln (c[i])
end;
c[0]:=-maxint;
for i:=1 to n-1 do begin
cheie:=c[i+1];
k:=i;
while cheie<c[k] do begin
c[k+1]:=c[k];
k:=k-1
end;
c[k+1]:=cheie;
end;
write('vectorul ordonat:');

109

for i:=1 to n do write(c[i],' ');


writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
35. Ordonare vector prin transpoziii
program sortare_prin_transpozitii;
const nmax=100;
var c:array[1..nmax]of integer;
k,n,max,i,aux:integer;
schimb:boolean;
sf:char;
begin
sf := D;
repeat
write('Numrul componentelor:'); readln(n);
for i:=1 to n do begin
write ('c[',i,']='); readln (c[i])
end;
k:=n;
schimb:=true;
while schimb do begin
max:=k;
schimb:=false;
for i:=1 to max-1 do
if c[i]>c[i+1] then begin
aux:=c[i];
c[i]:=c[i+1];
c[i+1]:=aux;
k:=i;
schimb:=true
end;
for i:=1 to n do write(c[i],' ');
writeln
end;
write('vectorul ordonat:');
for i:=1 to n do write(c[i],' ');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
36. Generarea triunghiului lui Pascal
program Triunghiul_lui_Pascal;
const nmax=100;
var i,j,n:integer;
triunghi:array[1..nmax,1..nmax] of integer;
sf:char;
begin
sf := D;

110

repeat
write('n='); readln(n);
triunghi[1,1]:=1;
for j:=2 to n do triunghi[i,j]:=0;
for i:=1 to n do begin
triunghi[i,1]:=1;
write(triunghi[i,1],'');
for j:=2 to i do begin
triunghi[i,j]:=
triunghi[i-1,j]+triunghi[i-1,j-1];
write(triunghi[i,j]:4)
end;
for j:=i+1 to n do triunghi[i,j]:=0;
writeln
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
37. Generare stiv de ntregi
program stiv_de_ntregi;
const nmax=100;
var
data,n:integer;
inalt:0..nmax;
stack:array[1..nmax] of integer;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
inalt:=0;
writeln('introduceti ',n,' ntregi');
while inalt <n do begin
read(data);
inalt:=succ(inalt);
stack[inalt]:=data;
end;
while inalt >0 do begin
data:=stack[inalt];
inalt:=pred(inalt);
write(data,' ')
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.

111

38. Generare coad de ateptare


program fir_de_asteptare_de_ntregi;
const nmax=100;
var
data,n:integer;
lung:0..nmax;
coad:array[1..nmax] of integer;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
lung:=0;
writeln('introduceti ',n,' ntregi');
while lung <n do begin
read(data);
lung:=succ(lung);
coad[lung]:=data;
end;
lung:=1;
while lung<=n do begin
data:=coad[lung];
lung:=succ(lung);
write(data,' ')
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
39. Traversare arbore binar
program traversare_graf;
type ref=^nod;
nod=record
info:'a'..'z';
st,dr:ref
end {record};
var
rad:ref;
c:char;
sf:char;
procedure preordine (pointer:ref);
begin
if pointer<>nil then begin
write (pointer^.info);
preordine (pointer^.st);
preordine(pointer^.dr)
end
end; {preordine}
procedure inordine (pointer:ref);
begin
if pointer<>nil then begin
inordine (pointer^.st);
112

write (pointer^.info);
inordine(pointer^.dr)
end
end; {inordine}
procedure postordine (pointer:ref);
begin
if pointer<>nil then begin
postordine (pointer^.st);
postordine(pointer^.dr);
write (pointer^.info);
end
end; {postordine}
procedure creare (var pointer:ref);
begin
read(c);
if c<>'.' then begin
new(pointer);
pointer^.info:=c;
creare (pointer^.st);
creare (pointer^.dr)
end else pointer:=nil
end;
begin {program principal}
sf := D; repeat
write('Arborele dat :':25);
creare (rad);
writeln;
write('Preordine :':25);
preordine (rad);
writeln;
write('Inordine :':25);
inordine (rad);
writeln;
write('Postordine :':25);
postordine (rad);
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
40. Modelarea matricilor rare
program matrice_rara;
type ind=0..25;
ir=array[ind]of integer;
mat=array[ind,ind]of integer;
var n,m,p:integer;
v:byte;
sf:char;
procedure varianta1;
var
i,j,k:ind;
113

a,b:ir;
c:mat;
begin
writeln('Dati vectorii:'
write('Lungimea vectorilor:');
readln(m);
for i:=1 to m do begin
write('v[',i,']=');
readln(a[i]);
end;
for i:=1 to m do begin
write('o[',i,']=');
readln(b[i]);
end;
for i:= 1 to n do
for j:= 1 to n do
c[i,j]:=0;
for k:= 1 to m do
begin
j:=(b[k] div n)+1;
i:=b[k] mod n;
if i=0 then begin
i:=n;
j:=j-1;
end;
c[i,j]:=a[k];
end;
for i:= 1 to n do begin
for j:=1 to n do write(c[i,j]:4);
writeln
end
end;
procedure varianta2;
var i,j,k:ind;
a,b:ir;
c:mat;
begin
writeln('Dati matricea:');
k:=0;
for i:= 1 to n do
for j:=1 to n do begin
write('a[',i,',',j,']=');
readln(c[i,j]);
if c[i,j]<>0 then begin
k:=k+1;
a[k]:=c[i,j];
b[k]:=(i-1)*n+j;
end;
end;
for i:=1 to k do begin
write('v[',i,']=',a[i],' ');
114

writeln('o[',i,']=',b[i]);
end;
end;
procedure varianta3;
var i,j,k:ind;
a,b,c,d:ir;
e:mat;
begin
write('Dati prima matrice:');
write('Dati lungimea vectorilor:');
readln(m);
for i:=1 to m do begin
write('v[',i,']='); readln(a[i]);
write('o[',i,']='); readln(b[i]);
end;
write('Dati a doumatrice:');
write('Dati lungimea vectorilor:');
readln(p);
for i:=1 to p do begin
write('v[',i,']='); readln(c[i]);
write('o[',i,']='); readln(d[i]);
end;
k:=m;
for j:= 1 to p do
begin
k:=i;
while(d[j]<>b[i]) and(i<=m) do
i:= i+1;
if i>m then begin
k:=k+1;
a[k]:=c[j];
b[k]:=d[j];
end
else a[i]:=a[i]+c[j];
end;
m:=k;
for i:= 1 to n do
for j:= 1 to n do
e[i,j]:=0;
for k:=1 to m do begin
j:=(b[k] div n)+1;
i:=b[k] mod n;
if i=0 then begin
i:=n;
j:=j-1;
end;
e[i,j]:=a[k];
end;
writeln('Matrice suma este:');
for i:= 1 to n do begin
for j:= 1 to n do writeln(e[i,j]:4);
115

writeln;
end;
end;
begin {programul principal}
sf := D;
repeat
writeln('1-Se dau v i o,s se calculeze a');
writeln('2-Se da a,s se calculeze v i o ');
writeln('3-Se dau doumatrici s se calculeze suma ');
write('Alegeti varianta 1,2,3:');
readln(v);
write('Dati dimensiunea matricei:');
readln(n);
case v of
1:
varianta1;
2:
varianta2;
3:
varianta3;
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
41. Polinoame de m variabile generate static
program polinoame_de_mai_multe_variabile;
type adev=0..1;
ind=0..50;
ir=array[ind] of real;
mat=array[ind,ind]of byte;
var
m,n,p,q:ind;
a,c,e:ir;
b,d,f:mat;
sf:char;
procedure citire_p(r:ind;var x:ir;var y:mat);
var i,j:ind;
begin
writeln('Dati coeficienti monoamelor:');
for i:=1 to r do begin
write('a(',i,')=');
readln(x[i]);
end;
writeln('Dati exponenti necunoscute:');
for i:=1 to r do
for j:=1 to m do begin
write('b(',i,',',j,')=');
readln(y[i,j]);
end;
end;
procedure suma_p(x,z:ir;y,t:mat;var r:ind;var u:ir;var v:mat);
var i,j:ind;
begin
116

for i:=1 to n do begin


u[i]:=x[i];
for j:=1 to m do v[i,j]:=y[i,j];
end;
for i:=1 to p do begin
u[n+1]:=z[i];
for j:=1 to m do v[n+1,j]:=t[i,j];
end;
r:=n+p;
end;
procedure produs_p(x,z:ir;y,t:mat;var r:ind;var u:ir;var v:mat);
var i,j,k:ind;
begin
r:=0;
for i:=1 to n do
for j:=1 to p do begin
r:=r+1;
u[r]:=x[i]*z[j];
for k:=1 to m do
v[r,k]:=y[i,k]+t[j,k];
end;
end;
procedure reducere_p(r:ind;var x:ir;var y:mat);
var i,j,k:ind;
v_adev:adev;
begin
for i:=1 to r-1 do begin
j:=i+1;
v_adev:=0;
while (v_adev=0) and (j<=r) do begin
k:=1;
v_adev:=1;
while (v_adev=1) and (k<=m) do
if y[i,k]<>y[j,k] then v_adev:=0
else k:=k+1;
j:=j+1;
end;
if v_adev=1 then begin
x[j-1]:=x[j-1]+x[i];
x[i]:=0;
end;
end;
end;
procedure micsorare_p(var r:ind;var x:ir;var y:mat);
var i,j,k:ind;
begin
i:=1;
while i<=n do begin
if x[i]=0 then begin
for j:=i+1 to r do begin
x[j-1]:=x[j];
117

for k:=1 to m do
y[j-1,k]:=y[j,k];
end;
r:=r-1;
i:=i-1;
end;
i:=i+1;
end;
end;
procedure afisare_p(r:ind;x:ir;y:mat);
var i,j:ind;
begin
write('P(a');
for i:=1 to m-1 do write(',',chr(ord('a')+i));
write(')=');
if x[1]<>0 then begin
write (x[1]:3:1);
for j:=1 to m do
if y[1,j]<>0 then begin
write('*',chr(ord('a')+j-1));
write('^',y[1,j]);
end;
end;
for i:=2 to r do
if x[i]<>0 then begin
if x[i]>0 then write('+')
else write('-');
write(abs(x[i]):3:1);
for j:=1 to m do
if y[i,j]<>0 then begin
write('*',chr(ord('a')+j-1));
write('^',y[i,j]);
end;
end;
writeln;
end;
begin
sf := D;
repeat
writeln('Dati primul polinom:');
write('Dati Numrul de monoame:');
readln(n);
write('Dati Numrul de necunoscute:');
readln(m);
citire_p(n,a,b);
writeln('Primul polinom este:');
afisare_p(n,a,b);
writeln('Dati al doilea polinom:');
write('Dati Numrul de monoame:');
readln(p);
citire_p(p,c,d);
writeln('Al doilea polinom este:');
118

afisare_p(p,c,d);
suma_p(a,c,b,d,q,e,f);
reducere_p(q,e,f);
micsorare_p(q,e,f);
writeln('Polinomul suma este:');
afisare_p(q,e,f);
produs_p(a,c,b,d,q,e,f);
reducere_p(q,e,f);
micsorare_p(q,e,f);
writeln('Polinomul produs este:');
afisare_p(q,e,f);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
42. Cutare drumuri de ieire din labirint
program labirinturi;
const n=8;
maxl=5;
spa= ;
type labirint=array[0..n,0..n]of char;
var l:labirint;
t:array[1..maxl]of labirint;
k,nrd,i,j:integer;
sf:char;
procedure afis_ini;
var i,j,lg,sp:integer;
begin
lg:=maxl*(n+4)-3;
sp:=(lg-n-1)div 2;
writeln;
writeln( :sp,LABIRINTUL:);
writeln;
for i:=0 to n do begin
write( :sp);
for j:=0 to n do write(l[i,j]);
writeln;
end;
writeln;
readln;
end;
procedure titlu;
var i:integer;
begin
for i:=nrd-k+2 to nrd do
write(Drum::n-2,i:3,spa);
writeln;
writeln;
end;
procedure scriet;
var i,j,p:integer;
119

begin
titlu;
for i:=0 to n do begin
for p:=1 to k-1 do begin
for j:=0 to n do
write(t[p][i,j]);
write(spa);
end; {for p}
writeln;
end;{for i}
writeln;
writeln;
writeln( Coninuati (D / N) ? ); read(sf); until sf = N;
end; {scriet}
procedure scriel;
begin
if k>maxl then begin
scriet;
k:=1
end;
t[k] :=l ;
k:=k+1;
nrd:=nrd+1;
end; {scriel}
procedure caut(x,y:integer);
begin
if l[x,y]= then begin
l[x,y] :=+ ;
if (x mod n=0)or(y mod n=0) then scriel
else begin
caut(x+1,y) ; {jos}
caut(x,y+1) ; {dr}
caut(x-1,y) ; {sus}
caut(x,y-1) ; {st}
end;
l[x,y] := ;
end
end; {caut}
begin {program principal}
sf := D;
repeat
for i:=0 to n do begin
for j:=0 to n do read(l[i,j]);
readln
end;
k:=1;
nrd :=0 ;
afis_ini ;
caut(n div 2, n div 2) ;
if k>1 then scriet;
writeln( Coninuati (D / N) ? );
read(sf);
120

until sf = N;
end.
43. Livada cu meri
program Livada_cu_mere;
const nmax=100;
type matr=array[1..nmax,1..nmax]of longint;
lo=record
v:integer;
linia:1..nmax;
coloana:1..nmax;
latura:1..nmax;
end;
var a:matr;
s:array[1..nmax]of lo;
li,co,la,m,n,i,j,k,l,nm,smax:integer;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
write('m='); readln(m);
for i:= 1 to n do
for j:=1 to m do begin
write('a[',i:2,',',j:2,']=');
readln(a[i,j]);
end;
for i:= 1 to n do begin
for j:=1 to m do write(a[i,j]:4);
writeln;
end;
if n>m then nm:=m else nm:=n;
l:=0;
for i:=1 to n do
for j:=1 to m do
for k:=1 to nm do
if (i+k<=n)and(j+k<=m) then begin
l:=l+1;
with s[l] do begin
v:=a[i,j]+a[i+k,j]+
a[i,j+k]+a[i+k,j+k];
linia:=i;
coloana:=j;
latura:=k;
end
end;
smax:=s[1].v;
for i:=2 to l do
if s[i].v>=smax then smax:=s[i].v;
write('S=(');
for i:=1 to l do write(s[i].v:4);
write(')');
121

writeln;
for i:=1 to l do
if smax=s[i].v then
writeln('smax=',s[i].v, ' linia=',
s[i].linia:3,'coloana=',
s[i].coloana:3,' latura=',
s[i].latura:3);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
44. Conversie text n alfabetul Morse
program morse;
const a:array[48..122]of string=
('_____','.____','..___','...__','...._','.....','_
....','__...','___..',
'____.',':',';','<','=','>','?','@',
'._','_...','_._.','_..','.','.._.','__.',
'....','..','.___','_._','._..','__','_.','___','._
_.','__._','_._','...','_','.._',
'..._','.__','_.._','_.__','__..','[','\',']','^','
_','`','._','_...','_._.','_..','.','.._.','__.','.
...','..','.___','_._','._..','__','_.','___','.__.
','__._','_._','...','_','.._','..._','.__','_.._',
'_.__','__..');
var
ir:string[120];
i,j,n:integer;
c:char;
sf:char;
begin
sf := D;
repeat
write('n='); readln(n);
writeln('introduceti cele ',n,' car.:');
for i:=1 to n do write(i:1);
writeln;
for i:=1 to n do read (ir[i]);
writeln;
for i:=1 to n do
if ir[i]=' ' then begin
write(' ':4);
delay(500);
end
else begin
delay(300);
write(a[ord(ir[i])],' ');
for j:=1 to ord(a[ord(ir[i])][0])
do begin
c:=a[ord(ir[i])][j];
sound(512);
if c='_' then delay(300);
122

if c='.' then delay(100);


nosound;
delay(100);
end;
end;
writeln;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
45. Reconstituire cuvnt introdus de la tastatur
program reconstituire_cuvant;
const nmax=100;
var
a,b:array[1..nmax]of char;
i,k,n:byte;
c,co:char;
ok:boolean;
sf:char;
begin
sf:=D;
repeat
repeat
sf := D; repeat
write('n='); readln(n);
write('Dati cuvant de ',n,' caract:');
for i:=1 to n do begin
a[i]:=readkey;
end;
writeln;
for i:=1 to n do begin
b[i]:='-';
write (b[i])
end;
writeln;
write('Numar de incercari=');
readln(k);
repeat
writeln('Mai aveti ',k,' incerc');
write('Introd. un caracter:');
readln(c);
for i:=1 to n do
if a[i]=c then b[i]:=c;
for i:=1 to n do begin
write(b[i]);
if b[i]<>'-' then begin
sound(512);
delay(500);
nosound
end
end;
123

writeln;
k:=k-1;
ok:=true;
i:=1;
while (i<=n) and ok do begin
if a[i]<>b[i] then ok:=false;
i:=i+1
end
until (k=0) or ok;
if ok then writeln ('Ati reusit s reconstituiti cuvantul')
else writeln('Ati esuat: mai
incerati');
write('Coninuati (D/N)?:');
readln(co)
until upcase (co)<>D;
write(Gata: Press any Key!:);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
46. Construire careu magic de ordin impar
program careul_magic_de_ordin_impar;
var n,m1,m2,i,j,k:integer;
a:array[1..19,1..19] of integer;
sf:char;
begin
sf := D;
repeat
writeln('Se construieste un careu magic cu n linii');
repeat
write('n=');
readln(n);
until (n n [1..19]) and (odd(n));
for i:=1 to n do
for j:=1 to n do
a[i,j]:=0;
i:=(n div 2)+2;
j:=(n div 2)+1;
for k:=1 to n*n do begin
a[i,j]:=k;
m1:=i+1;
m2:=j+1;
if m1=n+1 then m1:=1;
if m2=n+1 then m2:=1;
if a[m1,m2]=0 then begin
i:=m1;
j:=m2;
end
else begin
i:=i+2;
124

if i>n then i:=i-n


end;
end;
for i:=1 to n do begin
writeln;
for j:=1 to n do write(a[i,j]:4);
end;
writeln;
writeln('Suma magica=',n*(n*n+1) div 2);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
47. Determinare numr zi din an
program zile_din_an;
{$R+}
type luna=1..12;
zi=1..31;
var a,k,nr_zi:integer;
e:luna;
z:zi;
sf:char;
begin
sf := D;
repeat
write('introduceti anul:'); readln(a);
write('introduceti luna 1..12:');
readln(e);
write('introduceti ziua 1..31:');
readln(z);
k:=e-1;
nr_zi:=30*k;
if k>0 then begin
if k<=7 then nr_zi:=nr_zi+(k+1) div 2;
if k>=2 then
if(a mod 4=0)and(a mod 100<>0)
or (a mod 400=0)then nr_zi:=nr_zi-1
else nr_zi:=nr_zi-2;
end;
nr_zi:=nr_zi+z;
write('Au trecut',nr_zi:4,'
zile din anul ',a:4);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
48. Utilizarea memoriei cache
program memoria_cache;
var sf:char;
n,lin,bpm,a,k,m,p,i:integer;
125

s:array[0..22]of integer;
begin
sf := D;
repeat
write('n='); readln(n);
write('a='); readln(a);
k:=0;
while n>0 do begin
s[k]:=n mod a;
n:=n div a;
k:=k+1
end;
{linia se trece n baz 10}
lin:=0;
p:=1;
for i:=2 to 11 do begin
lin:=lin+s[i]*p;
p:=p*a
end;
{bmp se trece n baz 10}
bpm:=0;
p:=1;
for i:=12 to 22 do begin
bpm:=bpm+s[i]*p;
p:=p*a
end;
writeln('Linia n cache:',lin);
writeln('Bitii de pondere mare:',bpm);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
Conversii ntre sistemele roman i arab
49. Conversie din sistemul roman n sistem arab
program roman_arab;
var sf:char;
cifra:char;
val_prec, val_urm:1..1000;
n:integer;
corect:boolean;
begin
sf := D;
repeat
n:=0;
write('Numrul din cifre romane:');
read(cifra);
corect:=true;
case cifra of
'M':val_prec:=1000;
'D':val_prec:=500;
126

'C':val_prec:=100;
'L':val_prec:=50;
'X':val_prec:=10;
'V':val_prec:=5;
'I':val_prec:=1;
else corect:=false
end;
while corect and not eoln do begin
read(cifra);
case cifra of
'M':val_urm:=1000;
'D':val_urm:=500;
'C':val_urm:=100;
'L':val_urm:=50;
'X':val_urm:=10;
'V':val_urm:=5;
'I':val_urm:=1;
else corect:=false
end;
if corect then begin
if val_prec<val_urm then
n:=n-val_prec
else n:=n+val_prec;
val_prec:=val_urm;
end;
end;
if corect then writeln
('Numrul n scriere araba:',n+val_prec)
else writeln('Eroare: ',cifra,' nu este cifra romana');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
50. Conversie din sistemul arab n sistem roman
program arab_roman;
var sf:char;
x,y:integer;
begin
sf := D;
repeat
y:=1;
while y<=5000 do begin
x:=y;
write(y,':');
while x>1000 do begin
write('M');
x:=x-1000;
end;
if x>=900 then begin
write('CM');
x:=x-900;
127

end;
if x>=500 then begin
write('D');
x:=x-500;
end;
if x>=400 then begin
write('CD');
x:=x-400;
end;
while x>=100 do begin
write('C');
x:=x-100
end;
if x>=90 then begin
write('XC');
x:=x-90;
end;
if x>=50 then begin
write('L');
x:=x-50;
end;
if x>=40 then begin
write('XL');
x:=x-40;
end;
while x>=10 do begin
write('X');
x:=x-10;
end;
if x>=9 then begin
write('IX');
x:=x-9;
end;
if x>=5 then begin
write('V');
x:=x-5;
end;
if x>=4 then begin
write('IV');
x:=x-4;
end;
while x>=1 do begin
write('I');
x:=x-1;
end;
y:=2*y;
writeln;
end;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N; end.

128

51. Eliminare spaii excedentare ir de caractere


program eliminare_spatii_excedentare_ir;
var sf:char;
i,j:-1..80;
a,b:packed array[0..80] of char;
procedure cuvant; forward;
procedure sf;
begin
i:=0;
repeat
write(b[i]); i:=i+1
until i>j;
writeln;
readln;
end;
procedure spinit;
begin
i:=i+1;
if a[i]= ' ' then spinit
else if a[i]='%' then sf
else begin
j:=j+1;
b[j]:=a[i];
cuvant
end
end; {spinit}
procedure spatiu;
begin
i:=i+1;
if a[i]=' ' then spatiu
else if a[i]='%' then sf
else
begin
j:=j+1;
b[j]:=' ';
j:=j+1;
b[j]:=a[i];
cuvant
end
end; {spatiu}
procedure cuvant;
begin
i:=i+1;
if a[i]=' ' then spatiu
else if a[i]='%' then sf
else begin
j:=j+1; b[j]:=a[i];
cuvant
end
end; {cuvant}
begin {program principal}
sf := D;
129

repeat
i:=0;
j:=-1;
repeat
read (a[i]); i:=i+1;
until a[i-1]='%';
i:=-1;
spinit;
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.

5.3. Fiiere text i fiiere cu tip. Fiiere secveniale


52. Prelucrare fiier text-varianta 1-a
program Cea_mai_lunga_linie_text_var_1 ;
type numef=string[14];
linie=string[80] ;
var
f: text ;
l: linie ;
ll: integer ;
sursa: numef ;
sf:char;
begin
sf := D;
repeat
write(nume fiier:);
readln(sursa) ;
assign(f,sursa) ;
reset(f);
ll:=0;
while not eof(f) do begin
readln(f,l);
if length(l)>ll then ll:=length(l)
end;
if ll=0 then writeln (fisierul vid)
else begin
writeln(cea mai lunga linie are ,ll:2, caract.);
reset(f);
while not eof(f) do begin
readln(f,l);
if length(l)=ll then writeln(l)
end
end;
close(f);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N; end.

130

53. Prelucrare fiier text-varianta a-2-a


program Cea_mai_lunga_linie_text_var2 ;
type numef=string[14];
linie=string[80] ;
var f,temp :text;
l :linie ;
ll :integer ;
sursa :numef ;
sf:char;
begin
sf := D;
repeat
write(nume fiier:);
readln(sursa) ;
assign(f,sursa) ;
reset(f);
assign(temp,temp.tmp);
rewrite(temp);
ll:=0;
while not eof(f) do begin
readln(f,l);
if length(l)>ll then begin
ll:=length(l);
rewrite(temp);
writeln(temp,l);
end else if length(l)=ll then
writeln(temp,l)
end;
if ll=0 then writeln (fisierul vid)
else begin
writeln(cea mai lunga linie are,ll:2, caractere);
reset(temp);
while not eof(temp) do begin
readln(temp,l);
writeln(l)
end
end;
close(f);
close(temp);
erase(temp);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
54. Creare fiier (studeni din facultate)
program creare_fiier_studenti_din_facultate;
type numef=string[14];
numeg=string[6];
fac=file of numeg;
var
nf:numef;
f:fac;
131

ng:numeg;
n,i:integer;
sf:char;
begin
sf := D;
repeat
write(numef:);
readln(nf);
assign(f,nf);
rewrite(f);
write(nr.grupe:);
readln(n);
for i:=1 to n do
begin
write(grupa ,i,:);
readln(ng);
write(f,ng);
end;
close(f);
reset(f);
while not eof(f) do
begin
read(f,ng);
writeln(ng);
end;
close(f);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
55. Actualizare n acces direct fiier studeni
program accesare_directa_fisiere;
const nm=15;
type numes=string[25];
note=0..10;
student =record
nume:numes;
m:array[1..nm]of note;
end;
grupa=file of student;
numeg=string[6];
var
g:grupa;
ng:numeg;
ns,nn,no,nr,i,j:integer;
s:student;
sf:char;
procedure modi_nota(var g:grupa;is,im:integer; noua:note);
var s:student;
begin
seek(g,is-1);
read(g,s);
132

s.m[im] :=noua ;
seek(g,is-1);
write(g,s);
end;
procedure list_note(var g:grupa;x,y:integer);
var s:student;
i:integer;
begin
seek(g,x-1);
read(g,s);
write(x:2, .,s.nume);
for i:=1 to nn do write(s.m[i]:3);
writeln;
end;
begin
sf := D;
repeat
write(numeg:); readln(ng);
assign(g,ng);
rewrite(g);
write(numar studenti i note:);
readln(ns,nn);
for i:=1 to ns do
with s do begin
write(nume,i:2, :);
readln(nume);
write(notele:);
for j:=1 to nn do read(m[j]);
readln;
writeln;
write(g,s);
end;
close(g);
reset(g);
write(modif. Nota nr pt stud, ns,:);
readln(nr,ns);
write(noua nota :) ;
readln(no);
writeln(note vechi stud. Cu nr.,ns:2);
list_note(g,ns,nr);
modi_nota(g,ns,nr,no);
writeln(note noi stud. Cu nr.,ns:2);
list_note(g,ns,nr);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;.
end.

133

56. Numrare studeni din facultate


program Numarare_studenti_din_facultate;
const nm=15;
type numes=string[25];
note=0..10;
student = record
nume:numes;
m:array[1..nm]of note;
end;
grupa=file of student;
numeg=string[6];
facultate=file of numeg;
numef=string[14];
var f:facultate;
nf:numef;
g:grupa;
ng:numeg;
s:student;
n:longint;
sf:char;
begin
sf := D;
repeat
write(numefac=);
readln(nf);
assign(f,nf);
reset(f);
n:=0;
while not eof(f) do begin
read(f,ng);
assign(g,ng);
reset(g);
while not eof(g) do begin
read(g,s);
writeln(s.nume);
end;
reset(g);
n:=n+filesize(g);
close(g);
end;
close(f);
writeln(facultatea are , n:4, stud);
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
57. Ordonare fiier studeni-varianta 1-a
program ordonare_studenti_varianta1;
const maxstud=30;
maxnote=15;
type
nume=string[24];
134

note=array[1..maxnote]of 0..10;
student=record
stud_nume:nume;
n:note;
restante:0..maxnote;
absente:0..maxnote;
end;
studenti=array[1..maxstud]of student;
var
s:studenti;
ns,nn,o:integer;
c:char;
sf:char;
procedure read_st;
var i,j:integer;
begin
repeat
write('Numar studenti i note:');
readln(ns,nn)
until (ns n [1..maxstud]) and (nn n [1..maxnote]);
for i:=1 to ns do
with s[i] do begin
write ('Nume student ',i,' :');
readln(stud_nume);
write('note: ');
for j:=1 to nn do
read(n[j]);
readln
end
end; {read_st}
procedure write_st;
var i,j:integer;
begin
sf := D;
repeat
writeln('Nume student Note');
for i:=1 to ns do
with s[i] do begin
write(stud_nume,' ':25-length(stud_nume));
for j:=1 to nn do write(n[j]:3);
writeln
end
end; {write_st}
procedure order (o:integer);
var i,j:integer;
temp:student;
begin
for i:=1 to ns-1 do begin
for j:=i+1 to ns do
if (s[j].n[o]>s[i].n[o]) or
(s[j].n[o]=s[i].n[o]) and
(s[j].stud_nume=s[i].stud_nume)
135

then begin
temp:=s[i];
s[i]:=s[j];
s[j]:=temp
end
end;
write_st
end; {order}
begin {programul principal}
sf := D; repeat
read_st;
repeat
write('Index nota[1..', nn,']:');
o:=0;
readln(o);
if o n [1..nn] then order(o)
else writeln ('Eroare');
write ('Stop? [D/N]:');
readln(c)
until upcase(c)='D';
writeln('Press any key');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
58. Ordonare fiier studeni varianta a 2-a
program ordonare_studenti_varianta2;
const maxstud=30;
maxnote=15;
type
nume=string[24];
note=array[1..maxnote]of 0..10;
student=record
stud_nume:nume;
n:note;
restante:0..maxnote;
absente:0..maxnote;
end;
studenti=array[1..maxstud]of student;
var
s:studenti;
ns,nn,o:integer;
c:char;
o_list:array[1..maxstud]of integer;
sf:char;
procedure read_st;
var i,j:integer;
begin
repeat
write('Numar studenti i note:');
readln(ns,nn)
until (ns n [1..maxstud]) and
136

(nn n [1..maxnote]);
for i:=1 to ns do
with s[i] do begin
write ('Nume student ',i,' :');
readln(stud_nume);
write('note: ');
for j:=1 to nn do
read(n[j]);
readln;
o_list[i]:=i;
end
end; {read_st}
procedure write_st;
var i,j:integer;
begin
sf := D;
repeat
writeln('Nume student Note');
for i:=1 to ns do
with s[o_list[i]] do begin
write(stud_nume,' ':25-length(stud_nume));
for j:=1 to nn do write(n[j]:3);
writeln
end
end; {write_st}
procedure order (o:integer);
var i,j:integer;
temp:student;
begin
for i:=1 to ns-1 do begin
for j:=i+1 to ns do
if
(s[o_list[j]].n[o]>s[o_list[i]].n[o]) or
(s[o_list[j]].n[o]=s[o_list[i]].n[o])and
(s[o_list[j]].stud_nume=s[o_list[i]].stud_nume)
then begin
temp:=s[o_list[i]];
s[o_list[i]]:=s[o_list[j]];
s[o_list[j]]:=temp
end
end;
write_st
end; {order}
begin {programul principal}
sf := D; repeat
read_st;
repeat
write('Index nota [1..', nn,']:');
o:=0;
readln(o);
if o n [1..nn] then order(o)
else writeln ('Eroare');
137

write ('Stop? [D/N]:');


readln(c)
until upcase(c)='D';
writeln('Press any key');
writeln( Coninuati (D / N) ? );
read(sf);
until sf = N;
end.
59. Crearea unui fiier secvenial cu o anumit structur a articolelor. S se realizeze
programul pentru crearea unui fier secvenial Produse.dat, care
memoreaz datele referitoare la produsele fabricate de ctre o
ntreprindere, ntr-un an. Structura articolului este urmtoarea:

Precizri: Datele se introduc de la tastatur, cmp cu cmp, asigurndu-se urmtoarele


validri:
codul produsului s fie numeric;
denumirea produsului s fie alfabetic (format numai din litere mari, litere mici i spaii);
codul depozitului s fie o liter ;
codul produsului s fie numeric;
preul unitar s fie numeric i mai mare sau egal cu 100;
cantitile lunare s fie numerice.
n cazul n care o dat nu ndeplinete condiiile impuse, se semnaleaz eroarea i se reia
introducerea. Sfritul introducerii este standard (se tasteaz <CTRL/Z> n cmpul cod).
program creare_fiier_secvential;
uses crt;
type
produs=record
cod:byte;
den:string[30];
dep:char;
pu:longint;
cant:array[1..12] of word
end;
var art:produs; f:file of produs; er:boolean; i,l:byte; s:string[100];
const alfabet=['A'..'Z','a'..'z',' '];
procedure eroare;
begin
er:=true; gotoxy(10,25);
write(s,' tastati <ENTER>',#7);
repeat until readkey=#13;
gotoxy(10,25); clreol;
end;
(* ------------------------------------- *)
procedure cit_cod;
begin
repeat
er:=false; gotoxy(38,5);
{$i-}; readln(art.cod); {$i+};
138

if ioresult <> 0 then


begin
s:='Valoare nenumerica !';
eroare; gotoxy(38,5);
clreol
end
until not er
end;
(* ------------------------------------- *)
procedure cit_den;
begin
repeat
er:=false; gotoxy(38,6); clreol;
readln(art.den); l:=length(art.den);
for i:=1 to l do
if not (art.den[i] n alfabet) then er:=true;
if er then
begin
s:='Caractere nealfabetice !';
eroare
end
until not er
end;
(* ------------------------------------- *)
procedure cit_dep;
begin
repeat
er:=false; gotoxy(38,7); clreol;
readln(art.dep);
if not (upcase(art.dep)n ['A'..'Z']) then
begin
s:='Cod depozit eronat !';
eroare
end
until not er
end;
(* ------------------------------------- *)
procedure cit_pret;
begin
repeat
er:=false; gotoxy(38,8); clreol;
{$i-} readln(art.pu); {$i+}
if ioresult<>0 then
begin
s:='Caractere nenumerice !';
eroare
end
else if art.pu < 100 then
begin
s:='Pret < 100 !';
eroare
end
139

until not er
end;
(* ------------------------------------- *)
procedure cit_cant;
begin
repeat
er:=false; gotoxy(38,i+8); clreol;
{$i-} readln(art.cant[i]); {$i+}
if ioresult<>0 then
begin
s:='Caractere nenumerice !';
eroare
end
until not er
end;
procedure inceput;
begin
textbackground(magenta); textcolor(yellow); clrscr;
assign(f,'produse.dat'); rewrite(f); checkeof:=true;
gotoxy(20,5); write('Cod (sau ^Z): ');
gotoxy(20,6); write('Denumire : ');
gotoxy(20,7); write('Cod depozit: ');
gotoxy(20,8); write('Pret mediu : ');
for i:=1 to 12 do
begin
gotoxy(20,i+8);
write('Cant. luna ',i:2,' : ');
end;
gotoxy(38,5);
end;
(* ------------------------------------- *)
procedure prelucrare;
begin
cit_cod; cit_den; cit_dep; cit_pret;
for i:=1 to 12 do cit_cant;
write(f,art);
for i:=5 to 20 do begin gotoxy(38,i); clreol; end;
gotoxy(38,5);
end;
(* ------------------------------------- *)
procedure sfarsit;
begin
close(f);
end;
(* ------------ program principal ----------- *)
begin
inceput;
while not eof do prelucrare;
sfarsit;
end.

140

60. S se realizeze programul pentru afarea denumirii i a


produciei valorice anuale pentru produsele existente n fierul
Produse.dat, ale cror coduri se introduc de la tastatur. Sfritul
introducerii de la tastatur se marcheaz standard (^Z n cmpul cod).
Observaii: Deoarece codul produselor are valori unice, cutarea se
realizez pn cnd se regsete un articol al crui cod produs este egal
cu valoarea introdus de la tastatur, caz n care se calculeaz valoarea
anual i se afeaz, sau pn la sfritul fierului, caz n care produsul
cutat nu exist n fier i se afeaz un mesaj corespunztor. Pentru
fecare nou cod, cutarea se realizeaz de la nceputul fierului.
program consultare_dupa_camp_cu_valoare_unica;
uses crt;
type produs=record
cod:byte;
den:string[30];
dep:char;
pu:longint;
cant:array[1..12] of word
end;
var art:produs; f:fle of produs;
vb:boolean;
cod_t,i:byte;
v:real;
begin
assign(f,'produse.dat'); reset(f);
clrscr; checkeof:=true;
gotoxy(27,5);
write('Codul produsului: ');
while not eof do
begin
gotoxy(50,5); readln(cod_t);
seek(f,0);
vb:=false;
while not vb and not eof(f) do
begin
read(f,art);
if art.cod = cod_t then
begin
v:=0;
for i:=1 to 12 do
v:=v+art.cant[i];
v:=v*art.pu;
gotoxy(27,12);
writeln('Denumire',' ':10,'Valoare');
gotoxy(25,15);
writeln(art.den,' ':5,v:10:0,' lei');
readln; vb:=true
end
end;
if not vb then
begin
141

gotoxy(30,15);
writeln('Cod produs eronat !',#7)
end;
clrscr; gotoxy(27,5);
write('Codul produsului (sau ^Z): ')
end;
close(f);
end.
61. Afarea denumirii i a produciei valorice anuale pentru
produsele existente n fierul Produse.dat, aflate n depozitele ale
cror coduri se introduc de la tastatur. Sfritul introducerii de la
tastatur se marcheaz standard (^Z n cmpul dep).
Observaii: Deoarece ntr-un depozit se af mai multe produse (cmpul
dep are valori duplicate), cutarea se realizez de la nceputul pn la
sfritul fierului pentru fecare nou cod de deposit introdus. n cazul n
care nici un produs nu are codul depozitului egal cu valoarea introdus de
la tastatur, se afeaz un mesaj corespunztor i procesul se reia.
Existena sau inexistena a cel puin unui produs n depozitul cutat este
marcat cu o variabil boolean.
program consultare_dupa_camp_cu_valoare_duplicata;
uses crt;
type
produs=record
cod:byte;
den:string[30];
dep:char;
pu:longint;
cant:array[1..12] of word
end;
var
art:produs;
f:fle of produs;
ir:string[30];
dep_t:char;
i,k:byte;
v:real;
begin
assign(f,'produse.dat'); reset(f);
clrscr; checkeof:=true;
gotoxy(27,5);
write('Codul depozitului: ');
while not eof do
begin
gotoxy(50,5); readln(dep_t);
seek(f,0);
k:=0;
while not eof(f) do
begin
fllchar(ir,31,' ');
read(f,art);
142

if art.dep=dep_t then
begin
inc(k); v:=0;
for i:=1 to 12 do v:=v+art.cant[i];
v:=v*art.pu;
gotoxy(10,12);
writeln('Cod',' ':10,'Denumire',' ':25,'Valoare');
gotoxy(10,14+k); ir:=art.den; ir[0]:=#30;
writeln(art.cod:3,' ':10,ir,' ',v:10:0,' lei');
end
end;
if k=0 then
begin
gotoxy(30,15);
writeln('Cod depozit eronat !',#7)
end;
readln; clrscr; gotoxy(27,5);
write('Codul depozitului (sau ^Z): ')
end;
close(f);
end.
62. Afarea produciei valorice anuale pentru fecare depozit i pe
ntreprindere, utiliznd fierul Produse.dat.
Observaii: Problema se rezolv cu ajutorul algoritmului de consultare
secvenial cu control dup o caracteristic. Pentru obinerea situaiei
cerute se creeaz un fier de manevr, care va conine date privind codul
depozitului i stocul valoric anual pentru toate produsele existente n
fierul de date. Fiierul de manevr se sorteaz dup cmpul cod depozit,
apoi este consultat secvenial i integral dup aceast caracteristic,
obinndu-se dou grade de total: unul pentru depozit i unul pentru
ntreprindere.
program consultare_cu_control_dupa_caracteristica;
uses crt;
type artf=record
cod:byte;
den:string[30];
dep:char;
pu:longint;
cant:array[1..12] of word
nd;
artm=record
depm:char;
v:real
end;
var
z:artf; x,y:artm;
f:fle of artf;
m:fle of artm;
i,j:byte;
totgen,totdep:real;
143

vb,sf:boolean;
c:char;
procedure copiere;
begin
assign(f,'produse.dat'); reset(f);
assign(m,'manevra.tmp'); rewrite(m);
with z,x do
begin
while not eof(f) do
begin
read(f,z);
v:=0;
for i:=1 to 12 do v:=v+cant[i];
v:=v*pu;
depm:=dep;
write(m,x);
end;
end;
close(f);
end;
procedure sortare;
begin
repeat
vb:=false;
for i:=1 to flesize(m)-1 do
begin
seek(m,i-1); read(m,x,y);
if x.depm>y.depm then
begin
seek(m,i-1);
write(m,y,x);
vb:=true;
end;
end;
until not vb
end;
procedure afsare;
begin
seek(m,0); sf:=false;
totgen:=0; i:=0;
clrscr; gotoxy(5,3);
writeln('Stocul valoric pe depozite');
gotoxy(9,6);
writeln('Depozit Stoc');
read(m,x);
while not sf do
begin
totdep:=0;
c:=x.depm;
while (x.depm=c) and not sf do
begin
totdep:=totdep+x.v;
144

if eof(m) then sf:=true


else read(m,x)
end;
inc(i);
gotoxy(12,7+i);
writeln(c,' ':5,totdep:10:0,' lei');
totgen:=totgen+totdep
end;
gotoxy(9,9+i);
writeln('Total = ',totgen:10:0,' lei');
close(m)
end;
{ *** programul principal ***}
begin
copiere;
sortare;
afsare
end.
63. Crearea unui tabel cu urmtoarele informaii, despre toate
produsele existente n fierul Produse.dat: cod produs, denumire
produs, pre unitar mediu, stoc anual cantitativ i stoc anual
valoric.
Observaii: Tabelul va f memorat ntr-un fier de tip TEXT, pentru listri
multiple i pentru alte consideraii (de exemplu: evitarea relurii
programului n cazul defectrii imprimantei, procesarea tabelului cu un
editor de texte etc.).
program afsare_integrala;
uses crt;
type art=record
cod:byte;
den:string[30];
dep:char;
pu:longint;
cant:array[1..12] of word
end;
var
z:art; f:fle of art;
l:text;
linie:string[76];
d:string[30];
cant_tot:longint;
valoare:real;
i:byte;
begin
clrscr; assign(f,'produse.dat'); reset(f);
assign(l,'lista.txt'); rewrite(l);
with z do
begin
assign(f,'produse.dat'); reset(f);
fllchar(linie,77,''); linie[0]:=#76;
145

writeln(l,' ':25,'TABEL CU STOCURILE DE PRODUSE');


writeln(l); writeln(l); writeln(l,' ',linie);
write(l,' COD ',' ':12,'DENUMIRE',' ':12,' PRET ');
writeln(l,' CANTITATE VALOARE ');
writeln(l,' ',LINIE);
while not eof(f) do
begin
read(f,z);
cant_tot:=0;
for i:=1 to 12 do cant_tot:=cant_tot+cant[i];
valoare:=cant_tot*pu;
fllchar(d,31,' '); d:=den; d[0]:=#30;
write(l,' ',cod:3,' ',d,' ',pu:8,' ');
writeln(l,cant_tot:9,' ',valoare:10:0,' ');
writeln(l,' ',linie)
end;
end;
close(f); close(l);
end.
Resurse:
1. D. Hrinciuc Logoftu- C++ Probleme rezolvate i algoritmi, Editura Polirom, 2001
2. http://www.programare.org/carti.php- o bogat surs de cri de programare free,
majoritatea n englez
3. http://labs.cs.utt.ro/labs/pc/html/node1.html - ebook free, n romn, programare n C, cu
surse comentate
4. E. Nechita, G. C. Crian, I. M. Furdu, ndrumar de laborator, Programare C/C++,
Univ.Bacu, 2005
5. B. Ptru, Aplicaii n C i C++, Editura Teora, 2003

146

147