Vous êtes sur la page 1sur 171

ALGORITMI SI STRUCTURI DE DATE

Adrian CARABINEANU
2
Contents
1 Algoritmi. Not iuni generale 7
1.1 Exemplu de algoritm.
Sortarea prin insert ie . . . . . . . . . . . . . . . . . . . . . . . 7
1.2 Aspecte care apar la rezolvarea unei
probleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 Timpul de execut ie al algoritmilor (complexitatea algoritmilor) 9
1.4 Notat ia asimptotica . . . . . . . . . . . . . . . . . . . . . . . . 11
1.5 Corectitudinea algoritmilor . . . . . . . . . . . . . . . . . . . . 12
1.6 Optimalitatea algoritmilor . . . . . . . . . . . . . . . . . . . . 12
1.7 Existent a algoritmilor . . . . . . . . . . . . . . . . . . . . . . . 15
2 Recursivitatea 17
2.1 Funct ii (subprograme) recursive . . . . . . . . . . . . . . . . . 17
2.1.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.2 Reguli pentru construirea unui subprogram recursiv . . 18
2.1.3 Vericarea corectitudinii programelor recursive . . . . . 19
2.2 Recursivitatea directa si indirecta . . . . . . . . . . . . . . . . 20
2.2.1 Recursivitatea directa . . . . . . . . . . . . . . . . . . 20
2.2.2 Recursivitatea indirecta . . . . . . . . . . . . . . . . . 20
2.3 Recurent e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.1 Metoda substitut ie . . . . . . . . . . . . . . . . . . . . 23
2.3.2 Metoda iterat iei . . . . . . . . . . . . . . . . . . . . . . 23
2.3.3 Metoda master . . . . . . . . . . . . . . . . . . . . . . 24
2.4 Fractali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.4.1 Covorul lui Sierpinski . . . . . . . . . . . . . . . . . . . 28
2.4.2 Curba lui Koch . . . . . . . . . . . . . . . . . . . . . . 29
2.5 Metoda de programare Divide si Stapaneste (Divide et Impera) 32
3
4 CONTENTS
3 Tipuri de structuri de date 35
3.1 Generalitat i . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.2 Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.2.1 Liste alocate secvent ial . . . . . . . . . . . . . . . . . . 36
3.2.2 Liste alocate nlant uit . . . . . . . . . . . . . . . . . . 36
3.3 Stive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.1 Tehnica elimin@arii recursivit@a@tii cu ajutorul stivei 49
3.4 Liste de tip coada . . . . . . . . . . . . . . . . . . . . . . . 50
4 Tehnici (metode) de programare a algoritmilor 53
4.1 Metoda backtracking (c@autare cu revenire) . . . . . . . . . . 53
4.1.1 Backtracking iterativ . . . . . . . . . . . . . . . . . . . 54
4.1.2 Backtracking recursiv . . . . . . . . . . . . . . . . . . . 56
4.2 Metoda optimului local (greedy) . . . . . . . . . . . . . . . . . 58
4.2.1 Exemplu. Problema rucsacului . . . . . . . . . . . . . 59
5 Grafuri 63
5.1 No@tiuni introductive . . . . . . . . . . . . . . . . . . . . . . 63
6 Arbori binari 69
6.0.1 Parcurgerea arborilor binari . . . . . . . . . . . . . . . 70
6.1 Algoritmul lui Human . . . . . . . . . . . . . . . . . . . . . . 76
6.1.1 Prezentare preliminara . . . . . . . . . . . . . . . . . . 76
6.1.2 Coduri prex. Arbore de codicare . . . . . . . . . . . 77
6.1.3 Construct ia codicarii prex a lui Human . . . . . . . 79
6.1.4 Optimalitatea algoritmului Human . . . . . . . . . . . 82
7 Tehnici de sortare 85
7.1 Heapsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
7.1.1 Reconstituirea proprietat ii de heap . . . . . . . . . . . 86
7.1.2 Construct ia unui heap . . . . . . . . . . . . . . . . . . 88
7.1.3 Algoritmul heapsort . . . . . . . . . . . . . . . . . . . 88
7.2 Cozi de prioritat i . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.3 Sortarea rapida . . . . . . . . . . . . . . . . . . . . . . . . . . 94
7.3.1 Descrierea algoritmului . . . . . . . . . . . . . . . . . . 94
7.3.2 Performant a algoritmului de sortare rapida . . . . . . . 96
7.4 Metoda bulelor (bubble method) . . . . . . . . . . . . . . . . 98
CONTENTS 5
8 Tehnici de cautare 99
8.1 Algoritmi de cautare . . . . . . . . . . . . . . . . . . . . . . . 99
8.1.1 Algoritmi de cautare secvent iala (pas cu pas) . . . . . 100
8.1.2 Cautarea n tabele sortate (ordonate) . . . . . . . . . . 101
8.1.3 Arbori de decizie asociat i cautarii binare . . . . . . . . 105
8.1.4 Optimalitatea cautarii binare . . . . . . . . . . . . . . 106
8.2 Arbori binari de cautare . . . . . . . . . . . . . . . . . . . . . 110
8.3 Arbori de cautare ponderat i (optimali) . . . . . . . . . . . . . 115
8.4 Arbori echilibrat i . . . . . . . . . . . . . . . . . . . . . . . . . 120
8.4.1 Arbori Fibonacci . . . . . . . . . . . . . . . . . . . . . 121
8.4.2 Proprietati ale arborilor echilibrat i . . . . . . . . . . . 123
8.5 Insert ia unui nod ntr-un arbore echilibrat . . . . . . . . . . . 125
8.5.1 Rotat ii n arbori echilibrat i . . . . . . . . . . . . . . . . 125
8.5.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . 129
8.5.3 Algoritmul de insert ie n arbori echilibrat i . . . . . . . 132
8.6 Stergerea unui nod al unui arbore echilibrat . . . . . . . . . . 132
8.6.1 Algoritmul de stergere a unui nod dintr-un arbore echili-
brat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
9 Subiecte pentru laborator 145
9.1 Sortarea prin insert ie si prin interclasare . . . . . . . . . . . . 145
9.2 Fractali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
9.3 Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
9.4 Metoda backtracking . . . . . . . . . . . . . . . . . . . . . . . 159
9.4.1 Backtracking iterativ . . . . . . . . . . . . . . . . . . . 159
9.4.2 Backtracking recursiv . . . . . . . . . . . . . . . . . . . 165
6 CONTENTS
List of Figures
2.1 Covorul lui Sierpinski . . . . . . . . . . . . . . . . . . . . . . . 29
2.2 Curba lui Koch . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.1 Liste simplu si dublu nlant uite . . . . . . . . . . . . . . . . . 37
4.1 Backtracking recursiv . . . . . . . . . . . . . . . . . . . . . . . 57
5.1 Exemple de grafuri . . . . . . . . . . . . . . . . . . . . . . . . 64
6.1 Exemplu de arbore binar . . . . . . . . . . . . . . . . . . . . . 70
6.2 Exemplu de arbore binar cu precizarea legaturilor . . . . . . . 71
6.3 Exemplu de arbore Human . . . . . . . . . . . . . . . . . . . 78
6.4 Construirea arborelui Human . . . . . . . . . . . . . . . . . . 80
7.1 Exemplu de heap reprezentat sub forma unui arbore binar si
sub forma unui vector . . . . . . . . . . . . . . . . . . . . . . 86
7.2 Efectul funct iei ReconstituieHeap . . . . . . . . . . . . . . . . 87
7.3 Model de execut ie a funct iei ConstruiesteHeap . . . . . . . . . 89
8.1 Arbore de cautare binara . . . . . . . . . . . . . . . . . . . . . 105
8.2 Arbori de cautare . . . . . . . . . . . . . . . . . . . . . . . . . 106
8.3 Optimizarea lungimii drumurilor externe . . . . . . . . . . . . 107
8.4 Stergerea radacinii unui arbore binar de cautare . . . . . . . . 114
8.5 Arbore binar de cautare . . . . . . . . . . . . . . . . . . . . . 114
8.6 Arbori posibili de cautare si numarul mediu de comparat ii
pentru o cautare reusita . . . . . . . . . . . . . . . . . . . . . 116
8.7 Arbori Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . 122
8.8 Rotat ie simpla la dreapta pentru re-echilibrare . . . . . . . . . 126
8.9 Rotat ie dubla la dreapta pentru re-echilibrare . . . . . . . . . 127
8.10 Rotat ie dubla la dreapta pentru re-echilibrare . . . . . . . . . 127
7
6 LIST OF FIGURES
8.11 Rotat ie simpla la stanga pentru re-echilibrare . . . . . . . . . 128
8.12 Rotat ie dubla la stanga pentru re-echilibrare . . . . . . . . . . 128
8.13 Rotat ie dubla la stanga pentru re-echilibrare . . . . . . . . . . 129
8.14 Exemplu de insert ie ntr-un arbore echilibrat . . . . . . . . . . 130
8.15 Exemplu de insert ie ntr-un arbore echilibrat . . . . . . . . . . 130
8.16 Exemplu de insert ie ntr-un arbore echilibrat . . . . . . . . . . 131
8.17 Exemplu de insert ie ntr-un arbore echilibrat . . . . . . . . . . 131
8.18 Exemplu de stergere a unui nod dintr-un arbore echilibrat . . 133
8.19 Exemplu de stergere a unui nod dintr-un arbore echilibrat . . 133
8.20 Exemplu de stergere a unui nod dintr-un arbore echilibrat . . 134
8.21 Exemplu de stergere a unui nod dintr-un arbore echilibrat . . 135
9.1 Varianta a covorului lui Sierpinski . . . . . . . . . . . . . . . . 150
9.2 Curba lui Koch modicat@a . . . . . . . . . . . . . . . . . . . 152
9.3 Fulgul lui Koch . . . . . . . . . . . . . . . . . . . . . . . . . . 153
9.4 Varianta a fulgului lui Koch . . . . . . . . . . . . . . . . . . . 154
Capitolul 1
Algoritmi. Not iuni generale
1n. . :|.n.nn:n. Un n|j:.n este o :n:n n n|| h.n nn.n
care primeste o mult ime de valori ca nn n .n:n: si produce o mult ime de
valori ca nn n ..:.
1.1 Exemplu de algoritm.
Sortarea prin insert ie
Vom considera mai ntai problema sortarii (ordonarii) unui sir de n numere
ntregi a [0] , a [1] , ..., a [n 1] (ce reprezinta datele de intrare). Sirul ordonat
(de exemplu crescator) reprezinta datele de iesire. Ca procedura de calcul
vom considera procedura :n:.. :.n .n: . pe care o prezentamn cele ce
urmeaza:

Incepand cu al doilea numar din sir, repetam urmatorul procedeu :
inseram numarul de pe pozit ia j ,ret inut ntr-o cheie, n subsirul deja ordonat
a [0] , ..., a [j 1] astfel ncat sa obt inem subsirul ordonat a [0] , ..., a [j] . Ne
oprim cand obt inem subsirul ordonat de n elemente. De exemplu, pornind de
la sirul de numere ntregi 7, 1, 3, 2, 5, folosind sortarea prin insert ie obt inem
succesiv
7 [ 1 3 2 5
1 7 [ 3 2 5
1 3 7 [ 2 5
1 2 3 7 [ 5
1 2 3 5 7
Linia verticala [ separa subsirul ordonat de restul sirului. Prezentam mai jos
programul scris n C + + pentru sortarea elementelor unui sir de 10 numere
7
8 CAPITOLUL 1. ALGORITMI. NOTIUNI GENERALE
ntregi:
# include <iostream.h
void main(void)
{ // datele de intrare
int a[10];
int i=0;
while(i<10)
{ cout<<a[<<i<<]=; cin*(a+i); i=i+1;}
//procedura de calcul
for(int j=1;j<10;j++)
int key=a[j];
//insereaza a[j] n sirul sortat a[0,...,j-1]
i=j-1;
while((i=0)*(a[i]key))
a[i+1]=a[i];
i=i-1;}
a[i+1]=key;}
//datele de iesire
for(j=0;j<n;j++)
cout<<a[<<j<<]=<<*(a+j)<<;; }
Putem modica programul C++ de sortare a n numere ntregi impunand
sa se citeasca numarul n, alocand dinamic un spat iu de n obiecte de tip int
si t inand cont ca (a +i) = a[i] :
# include <iostream.h
void main(void)
{
// datele de intrare
int n;
int i=0;
cout<<n=;
cinn;
int* a;
a = new int [n];
while(i<n)
{ cout<<a[<<i<<]=; cin*(a+i); i=i+1;}
//procedura de calcul
for(int j=1;j<n;j++)
int key=*(a+j);
1.2. ASPECTE CARE APAR LA REZOLVAREA UNEI PROBLEME 9
//insereaza a[j] in sirul sortat a[0,...,j-1]
i=j-1;
while((i=0)*(*(a+i)key))
*(a+i+1)=*(a+i);
i=i-1;}
*(a+i+1)=key; }
//datele de iesire
for(j=0;j<n;j++)
cout<<a[<<j<<]=<<*(a+j)<<;;}
1.2 Aspecte care apar la rezolvarea unei
probleme
Cand se cere sa se elaboreze un algoritm pentru o problema data, care sa
furnizeze o solut ie (e si aproximativa, cu condit ia ment ionarii acestui lucru),
cel put in teoretic trebuie sa e parcurse urmatoarele etape:
1) Demonstrarea faptului ca este posibila elaborarea unui algoritm pentru
determinarea unei solut ii.
2) Elaborarea unui algoritm (n acest caz etapa anterioara poate deveni
inutila).
3) Demonstrarea corectitudinii.
4) Determinarea timpului de execut ie al algoritmului.
5) Investigarea optimalitat ii algoritmului.
Vom prezenta n cele ce urmeaza, nu neaparat n ordinea indicata mai
sus, aceste aspecte.
1.3 Timpul de execut ie al algoritmilor (com-
plexitatea algoritmilor)
Un algoritm este elaborat nu doar pentru un set de date de intrare, ci pentru
o mult ime de astfel de seturi. De aceea trebuie bine precizata mult imea
(seturilor de date) de intrare. Timpul de execut ie se masoara n funct ie de
lungimea n a setului de date de intrare.
Ideal este sa determinam o formula matematica pentru T(n) = timpul de
executare pentru orice set de date de intrare de lungime n. Din pacate, acest
10 CAPITOLUL 1. ALGORITMI. NOTIUNI GENERALE
lucru nu este n general posibil. Din aceasta cauza ne vom limita la a evalua
:n.n| n nn:.n al timpului de execut ie.
Sa reluam procedura de calcul pentru algoritmul de sortare prin insert ie:
//procedura de calcul .................................cost.............timp
for(int j=1;j<n;j++) ......................................c
1
................n
int key=*(a+j); ..........................................c
2
..............n 1
//insereaza a[j] in sirul sortat a[1,...,j-1]
i=j-1; ............................................................c
3
..............n 1
while((i=0)*(*(a+i)key))..........................c
4
.............

n
j=2
t
j
*(a+i+1)=*(a+i);........................................c
5
..........

n
j=2
(t
j
1)
i=i-1;} ...........................................................c
6
..........

n
j=2
(t
j
1)
*(a+i+1)=key; } ...........................................c
7
...............n 1
c
1
, ..., c
7
sunt costurile de timp pentru ecare instruct iune.

In coloana
timp punem de cate ori este repetata instruct iunea; t
j
reprezinta numarul de
execut ii ale testului while (comparat ia) pentru valoarea xata j. Timpul de
execut ie este
T (n) = nc
1
+ (n 1) (c
2
+c
3
+c
7
c
5
c
6
) + (c
4
+c
5
+c
6
)
n

j=2
t
j
. (1.1)
Timpul de execut ie poate sa depinda de natura datelor de intrare.

In cazul
n care vectorul de intrare este deja sortat crescator, t
j
= 1 (deoarece pentru
ecare j, a[0, ..., j 1] este sortat). Timpul de execut ie n acest caz (cel mai
favorabil) este
T (n) = n(c
1
+c
2
+c
3
+c
4
+c
7
) (c
2
+c
3
+c
4
+c
7
) . (1.2)
Daca vectorul este sortat n sens invers (n ordine descrescatoare) avem cazul
cel mai defavorabil. Fiecare element a [j] este comparat cu ecare element
din a [0, ..., j 1] si astfel t
j
= j pentru j = 2, 3, ..., n. Cum
n

j=2
j =
n(n + 1)
2
1,
n

j=2
(j 1) =
n(n 1)
2
,
deducem ca
T (n) =
_
c
4
2
+
c
5
2
+
c
6
2
_
n
2
+
_
c
1
+c
2
+c
3
+
c
4
2

c
5
2

c
6
2
+c
7
_
n(c
2
+c
3
+c
4
+c
7
) .
(1.3)
1.4. NOTATIA ASIMPTOTIC

A 11
Timpul de execut ie este are ordinul de marime O(n
2
) .

In general spunem
ca timpul de execut ie este de ordinul O(f (n)) daca
lim
n
T (n)
f (n)
= l, l nita.
Cand f (n) = n
k
, k N

spunem ca algoritmul este |.nn.n|. Specicam


faptul ca un algoritm este considerat nnh.| daca este polinomial.
1.4 Notat ia asimptotica
Vom considera n cele ce urmeaza funct ii de forma f : N R. Introducem
mai ntai mult imea
(g (n)) = f (n) : c
1
> 0, c
2
> 0, n
0
N astfel nc at
0 c
1
g (n) f (n) c
2
g (n) , n n
0
.
(1.4)
Vom spune ca g (n) este o margine n.n. n: pentru f (n) .
- notat ia delimiteaza o funct ie asimptotic inferior si superior. Cand
avem numai o nn:j.n n.n.n :.n:n utilizam O - notat ia:
O(g (n)) = f (n) : c > 0, n
0
N astfel nc at
0 f (n) cg (n) , n n
0
.
(1.5)
Pentru o nn:j.n n.n.n .n]:.n:n utilizam - notat ia:
(g (n)) = f (n) : c > 0, , n
0
N astfel nc at
0 cg (n) f (n) , n n
0
.
(1.6)
Vom introduce mai departe o - notat ia :
o (g (n)) = f (n) : c > 0, n
0
> 0 astfel nc at
0 f (n) cg (n) , n n
0

(1.7)
echivalenta cu
lim
n
f (n)
g (n)
= 0 (1.8)
si - notat ia
(g (n)) = f (n) : c > 0, n
0
> 0 astfel nc at
0 cg (n) f (n) , n n
0

(1.9)
12 CAPITOLUL 1. ALGORITMI. NOTIUNI GENERALE
echivalenta cu
lim
n
f (n)
g (n)
= . (1.10)
1.5 Corectitudinea algoritmilor

In demonstrarea corectitudinii algoritmilor exista doua aspecte importante


- :.n.nn n: .n|n: presupunand ca algoritmul se termina ntr-un
numar nit de pasi, trebuie demonstrat ca rezultatul este corect.
- J:n.nn:n :j:nn|.: trebuie demonstrat ca algoritmul se ncheie n
timp nit.
Evident, condit iile enumerate mai sus trebuie ndeplinite pentru orice set
de date de intrare admis de problema.
Modul tipic de lucru constan introducerean anumite locuri din program
a unor .nn:.nn ., care reprezinta relat ii ce sunt ndeplinite la orice trecere
a programului prin acele locuri. De exemplu n cazul sortarii prin insert ie,
invariant ii sunt urmatorii:
- nn n: .n: n .||. while {:n.nn: |. i = j 1)
|nn| .nn.. nn. n.. n jn|. , n ] :n n: .n|.
Ciclul for se termina odata cu ultima executare a ciclului while, cand
j = n si cand toate elementele sunt sortate.
1.6 Optimalitatea algoritmilor
Sa presupunem ca pentru o anumita problema am elaborat un algoritm si
am putut calcula timpul sau de execut ie T (n) . Este natural sa ne punem
problema daca algoritmul este executat n timpul | nn. : posibil sau
exista un alt algoritm cu timpul de execut ie mai mic. Spunem ca un algoritm
este .n daca raportul dintre timpul sau de execut ie si timpul de execut ie
al oricarui alt algoritm care rezolva aceeasi problema are ordinul de marime
O(1) . Problema demonstrarii optimalitat ii este deosebit de dicila, mai ales
datorita faptului ca trebuie sa consideram tot i algoritmii posibili si sa aratam
ca ei au un timp de execut ie superior celui al algoritmului optim.

In cazul algoritmilor de sortare vom arata ntr-un capitol ulterior ca tim-


pul de execut ie pentru un algoritm optim este de ordinul O(n ln n) .
1.6. OPTIMALITATEA ALGORITMILOR 13
Algoritmul de sortare prin insert ie, avand timpul de execut ie de ordinul
O(n
2
) , nu este optimal. Vom da n cele ce urmeaza un exemplu de algoritm
de sortare optim si anume algoritmul de :n: :.n .n:|nn:.
Pentru a avea o imagine intuitiva a procedurii de interclasare, sa con-
sideram ca un pachet cu n cart i de joc este mpart it n alte 2 pachete asezate
pe masa cu fat a n sus. Fiecare din cele 2 pachete este sortat, cartea cu
valoarea cea mai mica ind deasupra. Dorim sa amestecam cele doua sub-
pachete ntr-un singur pachet sortat, care sa ramana pe masa cu fat a n jos.
Pasul principal este acela de a selecta cartea cu valoarea cea mai mica dintre
cele 2 aate deasupra pachetelor (fapt care va face ca o noua carte sa e
deasupra pachetului respectiv) si de a o pune cu fat a n jos pe locul n care
se va forma pachetul sortat nal. Repetam procedeul pana cand unul din
pachete este epuizat.

In aceasta faza este sucient sa luam pachetul ramas si
sa-l punem peste pachetul deja sortat ntorcand toate cart ile cu fat a n jos.
Din punct de vedere al timpului de execut ie, deoarece avem de facut cel mult
n/2 comparat ii, timpul de execut ie pentru procedura de interclasare este de
ordinul O(n) .
Procedura de interclasare este utilizata ca subrutina pentru algoritmul de
sortare prin interclasare care face apel la tehnica n..n . n nn. dupa
cum urmeaza:
1..n:

Imparte sirul de n elemente ce urmeaza a sortat n doua
subsiruri de cate n/2 elemente.
on nn: Sorteaza recursiv cele doua subsiruri utilizand sortarea prin
interclasare.
nh.nn: Interclaseaza cele doua subsiruri sortate pentru a produce
rezultatul nal.
Descompunerea sirului n alte doua siruri ce urmeaza a sortate are loc
pana cand avem de sortat siruri cu unul sau doua componente. Prezentam
mai jos programul scris n C + +.

In program, funct ia sort sorteaza un
vector cu maximum 2 elemente, funct ia intecl interclaseaza 2 siruri sortate
iar dist implementeaza strategia n..n . n nn a metodei studiate.
#include<iostream.h
/****************************/
void sort(int p,int q, int n, int *a) {
int m;
if(*(a+p)*(a+q))
{ m=*(a+p); *(a+p)=*(a+q); *(a+q)=m;} }
/**********************************/
14 CAPITOLUL 1. ALGORITMI. NOTIUNI GENERALE
void intecl(int p, int q, int m, int n, int *a){
int *b,i,j,k;
i=p;j=m+1;k=1;
b=new int[n];
while(i<=m && j<=q)
if(*(a+i)<=*(a+j))
{ *(b+k)=*(a+i);i=i+1;k=k+1;}
else
{ *(b+k)=*(a+j);j=j+1;k=k+1;}
if(i<=m)
for (j=i;j<=m;j++)
{ *(b+k)= *(a+j); k=k+1;}
else for(i=j;i<=q;i++)
{ *(b+k)=*(a+i); k=k+1;}
k=1;
for(i=p;i<=q;i++) { *(a+i)=*(b+k); k=k+1;} }
/*************************************/
void dist(int p, int q, int n, int *a){
int m;
if((q-p)<=1) sort(p,q,n,a);
else
m=(p+q)/2; dist(p,m,n,a); dist(m+1,q,n,a);intecl(p,q,m,n,a);
} }
/**************************************/
void main(void){
int n; *a,i;
cout<<n=; cinn;
a=new int[n];
for(i=1;i<=n;i++)
cout<<a[<<i<<]=; cin*(a+i-1);}
dist(0,n-1,n,a);
for(i=1;i<=n;i++)
cout<<a[<<i-1<<]=<<a[i-1];}

In continuare sa calculam T (n), numarul aproximativ de comparat ii efec-


tuat de algoritm. Succesiv, problema se descompune n alte doua probleme,
ecare referindu-se la n/2 elemente. Urmeaza interclasarea elementelor, care
1.7. EXISTENTA ALGORITMILOR 15
necesita un numar de n/2| comparat ii. Avem deci recurent a
T (n) = 2T
__
n
2
__
+
n
2
, T (0) = 0. (1.11)
Simbolul | reprezinta partea ntreaga (sau partea ntreaga inferioara)
a unui numar real. Introducem si simbolul | (partea ntraga superioara a
unui numar) prin relat ia
a| =
_
a dac a a Z,
a| + 1, dac a a RZ.
Vom da o schit a de rezolvare a acestei recurent e, urmand sa reluam subiec-
tul ntr-o alta sect iune. Sa consideram n = 2
k
, k N. Rezulta (efectuand
implicit un rat ionament prin induct ie):
T
_
2
k
_
= 2T
_
2
k1
_
+ 2
k1
= 2
_
2T
_
2
k2
_
+ 2
k2
_
+ 2
k1
= ...
= 2
p
T
_
2
kp
_
+p2
k1
= 2
p
_
2T
_
2
kp1
_
+ 2
kp1
_
+p2
k1
=
= 2
p+1
T
_
2
kp1
_
+ (p + 1) 2
k1
= T (0) +k2
k1
= k2
k1
.
Pentru un numar natural oarecare n, e k astfel ncat sa avem 2
k
n < 2
k+1
.
Deducem
k2
k1
= T
_
2
k
_
T (n) < T
_
2
k+1
_
= (k + 1) 2
k
. (1.12)
Cum k = O(ln n) , din (1.12) rezulta ca
T (n) = O(nln n) , (1.13)
deci algoritmul de sortare prin interclasare este optim. Vom dantr-o sect iune
ulterioara o demonstrat ie riguroasa a rezultatului de mai sus.
1.7 Existent a algoritmilor
Problema existent ei algoritmilor a statn atent ia matematicienilorncanainte
de aparit ia calculatoarelor. Un rol deosebit n aceasta teorie l-a jucat matem-
aticianul englez Alan Turing (1912-1954), considerat parintele inteligent ei
articiale.
Numim :h|nn nn.nnh.|n o problema pentru care nu poate elab-
orat un algoritm. Denirea matematica a not iunii de algoritm a permis
16 CAPITOLUL 1. ALGORITMI. NOTIUNI GENERALE
detectarea de probleme nedecidabile. Cateva aspecte legate de decidabilitate
sunt urmatoarele:
- 1:h|nn :.:.. :j:nn|:. pentru orice program si orice valori de
intrare sa se decida daca programul se termina.
- 1:h|nn |.n|n |: :j:nn|:: sa se decida pentru oricare doua
programe daca sunt echivalente (produc aceeasi iesire pentru aceleasi date
de intrare).
Capitolul 2
Recursivitatea
2.1 Funct ii (subprograme) recursive
Funct ia dist(int p, int q, int n, int *a) folosita n programul de sortare
prin interclasare este o ]n . ::.n.

In general un obiect sau un fenomen
se deneste n nn ::. daca n denit ia sa exista cel put in o :]:.: |n
| n..
2.1.1 Exemple
Ca exemple elementare de funct ii denite recursiv dam
- funct ia factorial denita pe mult imea numerelor naturale
n! =
_
1, dac a n = 0
n (n 1)! dac a n > 0,
(2.1)
-sirul (F
n
)
n1
de numere Fibonacci, denite prin urmatoarea formula de
recurent a:
F
1
= F
2
= 1,
F
n+2
= F
n+1
+F
n
, n 1,
(2.2)
- funct ia lui Ackerman Ack : NN N :
Ack (n, m) =
_
_
_
n + 1, dac a m = 0
Ack (m1, 1) dac a n = 0
Ack (m1, Ack (m, n 1)) dac a n, m > 0.
(2.3)
17
18 CAPITOLUL 2. RECURSIVITATEA
Vom da si un algoritm recursiv de calcul al c.m.m.d.c. a doua numere
naturale. Introducem numerele naaturale a si b de la tastatura si folosim mai
departe algoritmul lui Euclid:
1) Se mparte a la b si se obt ine restul r (r a mod b) .
2) Se executa operat iile de atribuire a b; b r;
3) Daca b ,= 0 atunci se revine la pasul 1. Daca b = 0, atunci cmmdc a.
Pentru implementarea algoritmului folosim funct ia matematica recursiva
cmmdc (a, b) =
_
_
_
a dac a b = 0
b dac a a = 0
cmmdc (b, a mod b) dac a a ,= 0

si b ,= 0.
(2.4)
Programul care descrie algoritmul recursiv este
#include<iostream.h
/**********************/
int cmmdc(int a, int b)
{ if (a==0) return b;
else if (b==0) return a;
else return cmmdc(b,a%b);}
/***************************/
void main ( )
{ int a,b; cout<<a= ; cina; cout<<b= ; cinb;
if (cmmmdc(a,b)==0)
cout<<cmmdc nu se poate calcula; aambele numere sunt 0;
else cout<<cmmdc(<<a<<,<<b<<)=<<cmmdc(a,b);}
2.1.2 Reguli pentru construirea unui subprogram re-
cursiv
1)

In cadrul unui subprogram recursiv algoritmul este descris algoritmul
folosind doua elemente:
- Cazul general al solut iei. Cont ine toate instruct iunile necesare pen-
tru a reduce dimensiunea problemei n vederea aapropierii de rezultatul nal.
Cea mai importanta operat ie care se executa este autoapelul.
- Cazul de baza al solut iei. Cont ine o operat ie care rezolva un caz
special al problemei, fara a se folosi autoapelul.
2) Recursivitatea este un proces repetitiv si este obligatoriu sa existe o
condit ie de oprire a repetit iei.
2.1. FUNCTII (SUBPROGRAME) RECURSIVE 19
3) Orice denit ie recursiva trebuie sa asigure condit ia de consistent a,
adica valoarea funct iei sa e direct calculabila sau calculabila cu ajutorul
unei valori direct calculabile.
2.1.3 Vericarea corectitudinii programelor recursive
Se verica mai ntai daca toate cazurile particulare (inclusiv condit ia de ter-
minare a apelului recursiv) funct ioneaza corect. Se face apoi o vericare
inductiva a funct iei recursive, pentru restul cazurilor, presupunand ca toate
componentele din codul funct iei funct ioneaza corect.
Fie spre exemplicare urmatorul program de generare a permutarilor nu-
merelor 1, 2, ..., n .
# include <iostream.h
int n, p[10];
/************************/
void aseaza ()
{ for (int i=1; i<=n; i++) cout<<p[i]<< ; cout<<endl;}
/***************************/
void permut(int i)
{ int j, aux;
if (i==n+1) aseaza();
else{ p[i]=i; for(j=1;j<=i;j++)
{ aux=p[i]; p[i]=p[j]; p[j]=aux; permut(i+1);
aux=p[i]; p[i]=p[j]; p[j]=aux;} } }
/******************************/
void main()
{ cout<<n= ; cinn; permut(1);}
Observam ca atunci cand i n sunt asate permutarile si programul se
termina.
Altfel, la momentul i pornind de la permutarile primelor i elemente deja
construite se construiesc permutarile primelor i + 1 elemente inserandu-se
elementul i + 1 pe rand pe pozit ia 1, 2, ..., i + 1.
20 CAPITOLUL 2. RECURSIVITATEA
2.2 Recursivitatea directa si indirecta
2.2.1 Recursivitatea directa

In acest caz avem o funct ie (subprogram) care se apeleaza pe ea nsasi. Ex-


emplele date pana acum sunt exemple de recursivitate directa.
2.2.2 Recursivitatea indirecta

In cazul n care ntalnim funct iile (subprogramele) F


1
, F
2
, ..., F
n
care se ape-
leaza ntre ele printr-o schema de forma
F
i
= f
i
(F
1
, .., F
i1
, F
i+1
, ..., F
n
)
spunem ca avem de-a face cu o recursivitate indirecta.
Exemplu. Forma poloneza (postxata) a unei expresii
Da mai ntai o denit ia a not iunii de .:. n|jh:.n (ntr-un sens mai
restrans adecvat scopurilor noastre):
O expresie algebrica cont ine caractere constand din paranteze rotunde,
operatori aritmetici binari din mult imea +, , , / si operanzi reprezentat i
printr-o litera.
1.:.n este denita .nn.: ::. astfel:
- o .:. este formata din :nn. legat i prin operatori aditivi (

sau

);
-un :nn este format dintr-unul sau mai mult i ]n:. separat i de op-
eratori multiplicativi (

sau

/

);
-un ]n: este e o |.:n, e o .:. cuprinsa ntre paranteze rotunde.
Trecerea de la forma n:nn|n la forma .nn a unei expresii este o
operat ie pe care o introducem e iterativ e recursiv. Algoritmul iterativ se
bazeaza pe urmatoarele relat ii (notam cu E, T, F o expresie, termen, factor
sub forma normala si cu E , T , F expresia, termenul, factorul sub
forma postxata):
- E +T = E T +; E T = E T ;
- T F = T F ; T/F = T F /;
- (E) = E , a = a, b = b, etc.
Exemplu; a +b c d (a +b/c) = a +b c d a +b/c = a b c+
dabc/ + = abc +dabc/ + .
2.2. RECURSIVITATEA DIRECT

A SI INDIRECT

A 21
Un algoritm indirect recursiv de vericare a corectitudinii unei expresii si
de obt inere a formei sale postxate este prezentat n [4], pag. 138.
Se denesc urmatoarele funct ii:
- factor, funct ie recursiva de generare a factorilor. Se verica daca ul-
timul caracter citit a fost alfanumeric. Se verica daca a fost introdusa o
paranteza rotunda deschisa (:
-daca da, se citeste urmatorul caracter si apoi se apeleaza funct ia expresie
de aducere a elementelor subexpresiei la forma postxata;
- daca nu, se apeleaza funct ia cit id care va asa caracterul daca este
litera, n caz contrar se apeleaza la err, funct ie de asare a unui mesaj de
eroare.
Indiferent de rezultat, se mai citeste un caracter.
- termen, funct ie recursiva de grupare a factorilor. Se detecteaza un
factor. Cat timp caracterul urmator factorului citit este un operator mul-
tiplicativ se apeleaza extragerea unui nou factor si apoi se scrie operatorul
multiplicativ.
- expresie, funct ie recursiva de grupare a componentelor n termeni. Se
detecteaza un termen. Cat timp caracterul urmator termenului citit este
un operator aditiv se apeleaza extragerea unui nou termen si apoi se scrie
operatorul aditiv.
Vor luate n considerare toate caracterele ntalnite pana la apasarea
tastei 1n:. Prezentam mai jos programul scris n C.
/*******Expresie.cpp*********/
#include<stdio.h
#include<stdlib.h
#include<ctype.h
#include<conio.h
char c;
void expresie();
/*****************/
void err(char *err)
puts(err);
exit(1);
/******************/
void main()
puts(nIntroduceti expresia :

);
c=getchar(); expresie();
if(c!=n

)err(

nExpresie terminata incorect

);
22 CAPITOLUL 2. RECURSIVITATEA
/*****************************/
void cit id()
if(!isalnum(c))err(nMai trebuie un operand

);
putch(c);
/****************************/
void factor()
if(c==()
c=getchar();
expresie();
if(c!=)) err(nMai trebuie o

)

);

else cit id();


c=getchar();
/***************************/
void termen()
char opm;
factor();
while(c==*[[c ==

)opm = c; c = getchar();
factor();
putch(opm);

/*******************************/
void expresie() char opa;
termen();
while(c==+[[c ==

)opa = c; c = getchar();
termen();
putch(opa);

2.3 Recurent e
Cand un algoritm cont ine o apelare recursiva la el nsusi, timpul sau de
execut ie poate descris printr-o ::n n.
1n. ..O ::n n este o ecuat ie sau o inegalitate care descrie o funct ie
exprimand valoarea sa pentru un anumit argument prin intermediul valorilor
calculate anterior pentru argumente mai mici.
2.3. RECURENTE 23
Relat ia (1.11) este un exemplu de recurent a. A rezolva o recurent a
nseamna a gasi delimitari asimptotice sau O pentru funct ia descrisa
de recurent a. Vom prezentan continuare trei metode de rezolvare a recurent elor:
nnn h. ... nnn .:n .. si nnn nn:.
2.3.1 Metoda substitut ie
Metoda substitut iei pentru rezolvarea recurent elor presupune intuirea formei
solut iei si apoi utilizarea induct iei matematice pentru a gasi constantele si a
arata cum funct ioneaza solut ia. Numele metodei provine de la substituirea
formei intuite a solut iei n ipoteza de induct ie.
Metoda substitut iei poate utilizata pentru a stabili e margini supe-
rioare e margini inferioare pentru recurent e. de exemplu sa determinam o
margine superioara pentru recurent a (1.11).

In sect iunea precedenta s-a pro-
pus drept margine superioara T (n) = O(nln n) si ne propunem sa aratam
prin induct ie ca aceasta solut ie este corecta. Presupunem solut ia corecta
pentru n/2|, adica T (n/2|) c n/2| ln (n/2|). Substituind n recurent a
se obt ine
T (n) 2c n/2| ln (n/2|) +n/2 cnln (n/2) +n/2 =
= cnln n cnln 2 +n/2 = cnln n 0.6931n + 0.5n cnln n,
pentru c 1
2.3.2 Metoda iterat iei

In cadrul metodei iterat iei dezvoltam (iteram) recurent a si o exprimam ca o


suma de termeni care depind numai de n si de condit iile init iale.
De exemplu sa consideram iterat ia
T (n) = 3T (n/4|) +n. (2.5)
O iteram dupa cum urmeaza
T (n) = 3T (n/4|) +n = n + 3 (n/4| + 3T (n/16|)) =
= n + 3 (n/4| + 3 (n/16| + 3T (n/64|))) =
= n + 3 n/4| + 9 n/16| + 27 n/64| .
24 CAPITOLUL 2. RECURSIVITATEA
Dezvoltam mai departe recurent a si observam ca cel de-al i - lea termen din
serie este 3
i
n/4
i
| . Iterat ia ajunge la n = 1 sau 0 cand n/4
i
| 1 si deci
i > log
4
n, primul termen al seriei ind
_
3
log
4
n
_
=
_
n
log
4
3
_
. Avem deci
T (n) n + 3n/4 + 9n/16 + 27n/64 +... +
_
n
log
4
3
_

i=0
_
3
4
_
i
+
_
n
log
4
3
_
= 4n +o (n) = (n) . (2.6)
2.3.3 Metoda master
Aceasta metoda se bazeaza pe urmatoarea teorema:
Teorema master. 1. a 1 . b > 1 nnn. f (n) ]n . .
T (n) nn. n n:j.. nnjn.. :.n ::n n
T (n) = aT (n/b) +f (n) , (2.7)
nn .n::nn n/b n n/b| n n/b|. .n. T (n) n n|.n-
.nn n.n. nn n :nn. n.
1) 1nn f (n) = O
_
n
log
b
a
_
n: nnn.n nnnn > 0. nn.
T (n) =
_
n
log
b
a
_
.
.) 1nn f (n) = O
_
n
log
b
a
_
. nn. T (n) =
_
n
log
b
a
ln n
_
.
) 1nn f (n) = O
_
n
log
b
a+
_
n: nnn.n nnnn > 0. . nnn
af (n/b) cf (n) n: nnn.n nnnn c < 1 . . n .n n
nn:.. nn. T (n) = (f (n)) .
1.n|.

In cazul recurent ei (1.11) avem a = b = 2 si f (n) = n/2.
Suntemn cazul (2) al teoremei master si obt inemT (n) = (nln n) .

In cazul
recurent ei (2.5) avema = 3, b = 4 si f (n) = n = n
log
4
3+
cu = 1log
4
3 > 0.
Suntem n cazul (3) al teoremei master si obt inem T (n) = (n) .
1nn:n .. Vom folosi metoda iterat iei pentru a demonstra teorema
mastern cazul recurent elor de forma T (n) = aT (n/b|)+f (n) (demonstrat ia
n cazul recurent elor de forma T (n) = aT (n/b|)+f (n) este asemanatoare).
Pe masura ce iteram recurent a obt inem un sir de invocari recursive cu argu-
mentele
n, n/b| , n/b| /b| , n/b| /b| /b| , ... = n
0
, n
1
, n
2
, .... (2.8)
Cum
n
i
=
_
n pentru i = 0
n
i1
/b| pentru i > 0,
(2.9)
2.3. RECURENTE 25
plecand de la inegalitat ile x x| < x + 1, obt inem
n
0
= n,
n
b
n
1
<
n
b
+ 1,
n
b
2
n
2
<
n
b
2
+
1
b
+ 1,
n
b
3
n
2
<
n
b
3
+
1
b
2
+
1
b
+ 1,
.............................

In general avem
n
b
i
n
i
<
n
b
i
+
i1

j=0
1
b
j
<
n
b
i
+
b
b 1
. (2.10)
Cand i = log
b
n| avem
n
b
i

n
b
log
b
n1
= b si n
i
b +
b
b1
= (1) .
Iterand recurent ele obt inem
T (n) = f (n
0
) +aT (n
1
) = f (n
0
) +af (n
1
) +a
2
T (n
2
) =
= f (n
0
)+af (n
1
)+a
2
f (n
2
)+...+a
log
b
n1
f
_
n
log
b
n1
_
+a
log
b
n
T
_
n
log
b
n
_
=
=
log
b
n1

j=0
a
j
f (n
j
) +
_
n
log
b
a
_
. (2.11)

In stabilirea relat iei (2.11) am t inut cont ca T


_
n
log
b
n
_
= (1) si ca n
virtutea egalitat ii a
log
b
n
= n
log
b
a
avem
a
log
b
n1
a
log
b
n
a
log
b
n

1
a
n
log
b
a
a
log
b
n
n
log
b
a
.
Rezulta de aici ca a
log
b
n
T
_
n
log
b
n
_
=
_
n
log
b
a
_
.
Sa evaluam mai departe suma g (n) =

log
b
n1
j=0
a
j
f (n
j
) n ecare din
cele trei cazuri din enunt ul teoremei master.
26 CAPITOLUL 2. RECURSIVITATEA
n.| . Daca af (n/b|) cf (n) pentru n > b +
b
b1
cu a 1, b > 1 si
c < 1, atunci a
j
f (n
j
) c
j
f (n) si deci
log
b
n1

j=0
a
j
f (n
j
) f (n)
log
b
n1

j=0
c
j
<
f (n)
1 c
. (2.12)
Cum f (n) = O
_
n
log
b
a+
_
, din (2.11) si (2.12) rezulta ca T (n) = (f (n)) .
n.| .. Daca f =
_
n
log
b
a
_
atunci c
2
n
log
b
a
f (n) c
1
n
log
b
a
.
Sa presupunem mai ntai ca a b (deci log
b
a 1).Tinem de asemenea
cont ca
_
n
b
j
+
b
b 1
_
log
b
a
=
n
log
b
a
a
j
_
1 +
b
j
n

b
b 1
_
log
b
a
.
Tinand cont ca pentru j log
b
n| avem
b
j
n
1, deducem ca
_
1 +
b
j
n

b
b 1
_
log
b
a

_
1 +
b
b 1
_
log
b
a
.
Avem asadar
c
2
n
log
b
a
a
j
f (n
j
) c
1
_
1 +
b
b 1
_
log
b
a
n
log
b
a
a
j
. (2.13)

In cazul n care a < b (deci log


b
a < 1) din c
2
n
log
b
a
j
f (n
j
) c
1
n
log
b
a
j
t inand
cont de (2.10),deducem
c
2
_
n
b
j
+
b
b 1
_
log
b
a
f (n
j
) c
1
_
n
b
j
_
log
b
a
,
si apoi
c
2
_
1 +
b
b 1
_
log
b
a
n
log
b
a
a
j
f (n
j
) c
1
n
log
b
a
a
j
(2.14)
Relat iile, (2.13), (2.14) pot scrise sub forma
c

2
n
log
b
a
a
j
f (n
j
) c

1
n
log
b
a
a
j
. (2.15)
Rezulta ca
c

2
(log
b
n| 1) n
log
b
a

log
b
n1

j=0
a
j
f (n
j
)
2.3. RECURENTE 27
c

1
(log
b
n| 1) n
log
b
a
. (2.16)
Din (2.11) si (2.16) rezulta ca T (n) =
_
n
log
b
a
ln n
_
.
n.| 1. Presupunem mai ntai ca log
b
a > 0.

In acest caz cum
f (n) < cn
log
b
a
, deducem ca
log
b
n1

j=0
a
j
f (n
j
) c
log
b
n1

j=0
a
j
_
n
b
j
+
b
b 1
_
log
b
a
=
= c
log
b
n1

j=0
b
j
n
log
b
a
_
1 +
b
j
n
b
b 1
_
log
b
a

cn
log
b
a
_
1 +
b
b 1
_
log
b
a
log
b
n1

j=0
b
j
=
= cn
log
b
a
_
1 +
b
b 1
_
log
b
a
b
log
b
n1
b

c
b

1
_
1 +
b
b 1
_
n
log
b
a
. (2.17)

In cazul n care log


b
a < 0 avem
log
b
n1

j=0
a
j
f (n
j
) c
log
b
n1

j=0
a
j
_
n
b
j
_
log
b
a
=
= cn
log
b
a
log
b
n1

j=0
b
j
= cn
log
b
a log
b
a
b
log
b
n1
b

c
b

1
_
1 +
b
b 1
_
n
log
b
a
. (2.18)
Din , (2.11), (2.17) si (2.18) rezulta ca T (n) =
_
n
log
b
a
_
.
28 CAPITOLUL 2. RECURSIVITATEA
2.4 Fractali
1:nn|| este o gura geometrica fragmentata sau franta care poate di-
vizata n part i, astfel ncat ecare dintre acestea sa e, cel put in aproximativ,
o copie miniaturala a ntregului.
Fractalul, ca obiect geometric, are urmatoarele caracteristici:
- are o structura na la scari arbitrar de mici;
- este prea neregulat pentru a descris n limbaj geometric euclidian
tradit ional;
- este autosimilar;
- are o denit ie simpla si recursiva.
Vom da n continuare cateva exemple de fractali, reprezentarile lor grace
precum si programele ce conduc la aceste reprezentari. Deoarece are mai
multe facilitat i grace, vom folosi n locul limbajului C, metalimbajul 1n|nh.
2.4.1 Covorul lui Sierpinski
Un patrat colorat n negru este divizat n 9 patrate congruente iar patratului
din mijloc i se schimba culoarea din negrun alb. Se repeta procedura pentru
celelalte 8 patrate ramase colorate cu negru si asa mai departe. Se obt ine
covorul lui Sierpinski (2.1).
function covor(i);
% functia covor(n) deseneaza a n-a iterat ie a covorului lui Sier-
pinski
%daca se apeleaza funct ia fara argument se considera implicit
5 iterat ii
switch nargin
case 0
i=5;
end
tic
M=0
%se creaza recursiv o matrice cu elementele0 si 1
%indicand punctele covorului
for k=1:i
M=[M, M, M;
M, ones(3(k-1)),M;
M,M,M];
2.4. FRACTALI 29
Figura 2.1: Covorul lui Sierpinski
end
% comenzile pentru reprezentarea graca
imagesc(M);
colormap(gray);
axis(equal);
axis o;
toc
2.4.2 Curba lui Koch
Un segment de dreapta este divizat n trei segmente congruente. Cu baza
pe segmentul din mijloc se construieste un triunghi echilateral caruia i se
sterge apoi baza. Se obt ine o linie franta formata din 4 segmente congruente.
Se repeta procedura descrisa pentru ecare din cele 4 segmente si asa mai
departe (2.2).
function koch4(nivel)
% koch4(nivel) reprezinta grac fractalul curba lui Koch.
30 CAPITOLUL 2. RECURSIVITATEA
Figura 2.2: Curba lui Koch
%nivel reprezinta numarul de iteratii care se efectueaza .
%Exemplu:
%koch4(5);
%Programul a fost luat de pe site-ul Universitatii din Stuttgart
%http://matlab.mathematik.uni-stuttgart.de sitatii din Stuttgart
if nargin = 1
error([Se cere un argument si numai unul. Tastati ...
help koch4 pentru ajutor.]);
end
xl = zeros(10,1);
xr = xl;
yl = xl;
yr = yl;
xr(nivel) = 1;
r = sqrt(3)/6;
%setari grace
clf;
2.4. FRACTALI 31
set(gca,FontSize,14);
set(gcf,Color,[1,1,1]);
hold on;
subkoch(xl,xr,yl,yr,nivel,r);
title(Curba lui Koch);
text(0.5,-0.05,([Numar de iteratii: num2str(nivel)]), ...
HorizontalAlign,center,FontSize,12);
hold o;
axis equal; axis tight; axis o;
%
function subkoch(xl,xr,yl,yr,nivel,r)
%se deseneaza un segment de dreapta la ultimul nivel al recur-
siei
if (nivel<2)
plot([xl(1) xr(1)],[-yl(1) -yr(1)],b-)
return
end
%Se ramnica functia odata cu trecerea la un nivel inferior
nivel=nivel-1;
%Se autoapeleaza functia pentru treimea din stanga a segmen-
tului
xl(nivel)=xl(nivel+1);
yl(nivel)=yl(nivel+1);
xr(nivel)=1/3*xr(nivel+1)+2/3*xl(nivel+1);
yr(nivel)=1/3*yr(nivel+1)+2/3*yl(nivel+1);
subkoch(xl,xr,yl,yr,nivel,r);
%Se autoapeleaza functia pentru latura din stanga a triunghiu-
lui
xl(nivel)=xr(nivel);
yl(nivel)=yr(nivel);
xr(nivel)=.5*xr(nivel+1)+.5*xl(nivel+1)-r*(yl(nivel+1)-yr(nivel+1));
yr(nivel)=.5*yr(nivel+1)+.5*yl(nivel+1)+r*(xl(nivel+1)-xr(nivel+1));
subkoch(xl,xr,yl,yr,nivel,r);
%Se autoapeleaza functia pentru latura din dreapta a triunghi-
ului
xl(nivel)=xr(nivel);
32 CAPITOLUL 2. RECURSIVITATEA
yl(nivel)=yr(nivel);
xr(nivel)=2/3*xr(nivel+1)+1/3*xl(nivel+1);
yr(nivel)=2/3*yr(nivel+1)+1/3*yl(nivel+1);
subkoch(xl,xr,yl,yr,nivel,r);
%Se autoapeleaza functia pentru treimea din stanga a segmen-
tului
xl(nivel)=xr(nivel);
yl(nivel)=yr(nivel);
xr(nivel)=xr(nivel+1);
yr(nivel)=yr(nivel+1);
subkoch(xl,xr,yl,yr,nivel,r);
nivel=nivel+1;
return
2.5 Metoda de programare Divide si Stapaneste
(Divide et Impera)
Aceasta metoda consta n mpartirea problemei init iale de dimensiuni [n] n
doua sau mai multe probleme de dimensiuni mai mici.

In general se ex-
ecuta mpart irea n doua subprobleme de dimensiuni aproximativ egale si
anume [n/2] .

Impart irea n subprobleme are loc pana cand dimensiunea
acestora permite rezolvarea directa (cazul de baza). Dupa rezolvarea celor
doua subprobleme se executa faza de combinare a rezultatelor pentru re-
zolvarea intregii probleme . Metoda n..n . n nn se poate aplica n
rezolvarea unei probleme care indeplineste urmatoarele conditii :
- se poate descompune n doua sau mai multe suprobleme ;
- aceste suprobleme sunt independente una fat a de alta (o subproblema
nu se rezolva pe baza alteia si nu i foloseste rezultatele;
- aceste subprobleme sunt similare cu problema init iala;
- la randul lor subproblemele se pot descompune (daca este necesar) n
alte subprobleme mai simple;
- aceste subprobleme simple se pot solut iona imediat prin algoritmul sim-
plicat.
Etapele rezolvarii unei probleme (numita problema init iala) cu metoda
n..n . n nn sunt :
2.5. METODADE PROGRAMARE DIVIDE SI ST

AP

ANESTE (DIVIDE ET IMPERA)33


- descompunerea problemei init ialen subprobleme independente ,similare
problemei de baza, de dimensiuni mai mici ;
- descompunerea treptata a subproblemelor n alte subprobleme din ce n
ce mai simple, pana cand se pot rezolva usor, prin algoritmul simplicat ;
- rezolvarea subproblemelor simple ;
- combinarea solut iilor gasite pentru construirea solut iilor subproblemelor
de dimensiuni din ce n ce mai mari ;
- combinarea ultimelor solut ii determin

A obt inerea solut iei problemei


init iale.
Metoda n..n . n nn admite o implementare recursiva ,deoarece
subproblemele sunt asemanatoare problemei init iale, dar de dimensiuni mai
mici . Principiul ::..n .. consta n autoapelarea unui subprogram cand
acesta este activ; ceea ce se ntampla la un nivel, se ntampla la orice nivel,
avand grija sa asiguram condit ia de terminare a apelurilor repetate .
Asemanator se ntampla si in cazul metodei n..n . n nn; la un
anumit nivel sunt doua posibilitat i : s-a ajuns la o (sub)problema simpla
ce admite o rezolvare imediata, caz n care se rezolva (sub)problema si se
revine din apel (la subproblema anterioara, de dimensiuni mai mari); s-a
ajuns la o (sub)problema care nu admite o rezolvare imediata , caz n care
o descompunem in doua sau mai multe subprobleme si pentru ecare din
ele se continua apelurile recursive (ale procedurii sau funct iei).

In etapa
nala a metodei n..n . n nn se produce combinarea subproblemelor
(rezolvate deja) prin secvent ele de revenire din apelurile recursive.
Etapele metodei n..n . n nn (prezentate anterior)se pot reprezenta
prin urmatorul subprogram general (procedura sau funct ie ) recursiv expri-
mat in limbaj natural:
Subprogram DIST (PROBL);
Daca PROBLEMA PROBL este simpla
Atunci se rezolva si se obt ine solutia SOL
Altfel pentru i=1,k executa DIST(PROBL) si se obtine SOL(i)
Se combina solut iile SOL(1),... ,SOL(k) si se obt ine SOL
Sfarsit subprogram
Deci ,subprogramul DIST se apeleaza pentru problema init iala PROBL;
aceasta admite descompunerea n k subprobleme simple; pentru acestea se
reapeleaza recursiv subprogramul; n nal se combina solut iile acestor k sub-
probleme. De obicei problema init iala se descompune n doua subprobleme
mai simple; n acest caz etapele generale ale metodei n..n . n nn se
34 CAPITOLUL 2. RECURSIVITATEA
pot reprezenta concret, n limbaj pseudocod, printr-o procedura recursiva
astfel :
Subprogram DIST(li,ls,sol)
Daca ((ls-li)<=eps)
Atunci REZOLVA (li,ls,sol)
Altfel ALEGE m cuprinsntre li si ls; DIST(li,m,sol1); DIST(m,ls,sol2);
COMBINA(sol1,sol2,sol);
Sfarsit subprogram
Procedura DIST se apeleaza pentru problema init iala care are dimensi-
unea ntre limita inferioara li si limita inferioara ls; daca (sub)problema este
simpla (ls-li<=eps),atunci procedura REZOLVAi aa solut ia imediat si
se produce intoarcerea din apelul recursiv; daca (sub)problema este (nca)
complexa, atunci procedura DIST o mparte n doua subprobleme, alegand
pozit ia m ntre limitele li si ls, pentru ecare din cele doua subprobleme
se reapeleaza recursiv procedura DIST; n nal, la ntoarcerile din apeluri
se produce combinarea celor doua solut ii sol1 si sol2 prin apelul procedurii
COMBINA.
Capitolul 3
Tipuri de structuri de date
3.1 Generalitat i
Structurile de date reprezinta nnn|.n . n n: nn| n n. n nn-
:.n n||n:|. n n n:n n.| nnjn.. Structurilor de date
sunt utilizate n diferite circumstant e ca de exemplu:
Memorarea unor date din realitate;
Instrumente ale programatorilor;
Modelarea unor situat ii din lumea reala.
Cele mai des utilizate structuri de date sunt tablourile, listele, stivele,
cozile, arborii, tabelele de dispersie si grafurile.
3.2 Liste
O |.n |.n.n:n este o structura de date omogena, secvent iala formata din
|nn ale listei. Un element (numit uneori si nn) din lista cont ine o
.n]:nn . .n si eventual o .n]:nn . n |jn:n. De exemplu, n
lista echipelor de fotbal nscrise ntr-un campionat, un element (ce reprezinta
o echipa) poate cont ine urmatoarele informat iie specice: nume, numar de
puncte, numar de goluri nscrise si numar de goluri primite. Fiecare din aceste
informat ii poate reprezenta o |. care poate utilizata pentru comparat ii
si inspect ii. De exemplu luand drept cheie numarul de puncte si golaverajul
se poate face clasicarea echipelor.
35
36 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
1.. .n elementelor din lista deneste ordinea elementelor (primul ele-
ment, al doilea, etc). Cont inutul listei se poate schimba prin:
- nnnjn:n de noi elemente la sfarsitul listei;
- .n:n:n de noi elemente n orice loc din lista;
- :j:n de elemente din orice pozit ie a listei;
- nn.n:n unui element dintr-o pozit ie data.
Printre operat iile care schimba structura listei vom considera si .n. .n|..n:n
unei liste ca o |.n .nn.
Alte operat ii sunt operat iile de n:n:..n:. Ele nu modica structura
listelor dar furnizeaza informat ii despre ele. Dintre operat iile de caracterizare
vom ment iona n cele ce urmeaza:
- |n|..n:n elementului din lista care satisface un anumit criteriu;
- n:n.nn:n |nj.n.. |...
Pot luate n considerare si operat ii mai complexe, ca de exemplu:
- n:n:n unei listen doua sau mai multe sublisten funct ie dendeplinirea
unor condit ii;
- | .n elementelor dintr-o lista, care ndeplinesc unul sau mai multe
criterii, n:- |.n nn.
- :n:n n. |. :nnn dupa valorile creascatoare sau descrescatoare
ale unei chei;
- nh.nn:n a doua sau mai multor liste prin nnnn: {n|..:) sau
.n:|nn:.
Spat iul din memorie ocupat de lista poate alocat n doua moduri: prin
n|n: n .n|n sau prin n|n: n|nn .n.
3.2.1 Liste alocate secvent ial

In acest caz nodurile ocupa pozit ii succesive n memorie. Acest tip de alo-
care este ntalnit cand se lucreaza cu tablouri (vectori). Avantajul alocarii
secvent iale este dat de faptul ca accesul la oricare din nodurile listei este di-
rect. Dezavantajul consta n faptul ca operat iile de adaugare, eliminare sau
schimbare de pozit ie a unui nod necesita un efort mare de calcul dupa cum
s-a vazut n algoritmii de sortare prezentat i mai nainte.
3.2.2 Liste alocate nlant uit
Exista doua feluri de alocare nlant uita: n|n: .n| n|nn .n si n|n:
nh| n|nn .n (gura 3.1). Alocarea nlant uita poate efectuata static
3.2. LISTE 37
Figura 3.1: Liste simplu si dublu nlant uite
(utilizand vectori) sau dinamic.

In acest din urma caz (de care ne vom ocupa
n cele ce urmeaza), se utilizeaza o zona de memorie numita 11.1 (movila,
gramada).

In C + + variabilele pastrate n HEAP (cum ar de exemplu
nodurile listei), se aloca atunci cand doreste programatorul, cu ajutorul op-
eratorului new, iar zona se elibereaza, tot la dorint a acestuia prin operatorul
delete.

In cazul alocarii dinamice, nodul unei liste este o structura care
cont ine alaturi de informat ie si adresa nodului urmator (pentru liste simplu
nlant uite) sau adresele nodului urmator si a celui precedent n cazul listelor
dublu nlant uite. Dupa cum se va vedea din exemplele ce urmeaza, princi-
pala problema n cazul operat iilor cu liste o reprezinta redenirea legaturilor
(adreselor) cand se sterge sau se insereaza un element.
Liste simplu nlant uite
Mai jos prezentam proceduri (funct ii) de creare (prin insert ie repetata) si
parcurgere a listelor precum si procedurile de stergere (eliminare) a unui
nod si de inserare a unui nod imediat dupa alt nod ce cont ine o anumita
informat ie.
#include<iostream.h
//introducem structura de nod al unei liste
38 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
struct nod { int inf; nod *adr ;} ;
//declararea funct iilor de creare, parcurgere, eliminare si inserare
nod *creare(void);
void parcurge(nod *prim);
nod *elimina(nod *prim, int info);
nod *inserare dupa(nod* prim, int info, int info1);
//functia principala
/***********************************************/
void main(void) {
int info, info1; nod *cap;
cap=creare();
parcurge(cap);
cout<<Spuneti ce nod se elimina;
cininfo;
cap= elimina(cap, info);
parcurge(cap);
cout<<Spuneti ce valoare se insereaza si dupa cine<<endl;
cininfo1;
cininfo;
inserare dupa(cap,info,info1);
parcurge(cap);}
//functia de creare a listei
/********************************************/
nod *creare(void)
{ nod *prim,*p,*q; int inf; char a;
//crearea primului element al listei
prim=new nod;
cout<< Introduceti prim-inf<<endl;
cininf;
prim-inf=inf;
prim-adr=NULL;
q=prim;
/*pana cand se decide ca operatia este gata, se creaza elementele urma-
toare*/
cout<<Gata?[d/n]<<endl;
cina;
while (a!=d) {
p=new nod;
3.2. LISTE 39
cout<<p-inf<<endl;
cininf;
/*se creaza un nou nod*/
p-inf=inf ;
p-adr=NULL;
/*se stabileste legatura dintre nodul anterior si nodul nou creat*/
q-adr=p;
q=p;
cout<<Gata?[d/n]<<endl;
cina;}
return prim;}
/********************************************/
/*urmeaza procedura de parcurgere a listei*/
void parcurge(nod *cap)
{ nod *q;
if(!cap) { cout<<Lista vida; return;}
cout<<Urmeaza lista<<endl;
for(q=cap;q;q=q-adr)
cout<<q-inf<< ;}
/****************************/
/*urmeaza procedura de eliminare a nodului ce contine o anumita infor-
matie*/
nod *elimina(nod* prim, int info)
{ nod *q,*r;
/*se studiaza mai intai cazul cand trebuie eliminat primul nod*/
while((*prim).inf==info)
{ q=(*prim).adr; delete prim; prim=q;}
/*se studiaza cazul cand informatia cautata nu este in primul nod*/
q=prim;
while(q-adr)
{ if(q-adr-inf==info)
/*in cazul in care a fost gasit un nod cu informatia ceruta, acesta se
elimina iar nodul anterior este legat de nodul ce urmeaza*/
{ r=q-adr; q-adr=r-adr; delete r;}
else q=q-adr;}
return prim;}
/*************************************/
40 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
/*functia de inserare a unui nod ce contine o anumita informatie dupa
un nod ce contine o informatie data*/
nod *inserare dupa(nod*prim, int info, int info1)
{ nod *c, *d;
c=prim;
while(c-adr&&(c-inf!=info)) c=c-adr;
d=new nod; d-inf=info1;
if(!c-adr)
/*daca nici-un nod nu contine informatia data, noul nod se insereaza
dupa ultimul element al listei*/
{ d-adr=NULL; c-adr=d;}
/* daca au fost depistate noduri ce contin informatia data noul element
se insereaza dupa primul astfel de nod*/
else { d-adr=c-adr; c-adr=d;}
return prim;}
Ca o ilustrare a utilitat ii listelor liniare simplu inlant uite vom prezenta
n cele ce urmeaza o procedura de adunare si nmult ire a doua polinoame
de o variabila. Un polinom va reprezentat printr-o lista, un nod al listei
corespunzand unui monom. Informat ia din nodul listei (monom) va cont ine
gradul monomului si coecientul. Pentru a efectua produsul polinoamelor
vom crea cu funct ia prod o noua lista, n ecare nod al noii liste ind pro-
dusul a doua monoame (ecare dintre aceste monoame apart inand altui poli-
nom). Cu o funct ie numita canonic, daca doua noduri (monoame) din lista
nou creata (lista produs) au acelasi grad, coecientul unuia din monoame
este nlocuit cu suma coecient ilor iar coecientul celuilalt cu 0. Cu funct ia
elimina stergem apoi nodurile (monoamele) care cont in coecientul 0.
Pentru a aduna doua polinoame, vom efectual mai ntai concatenarea lor
(ultimul element din lista ce reprezinta primul polinom va legata de primul
element din lista ce reprezinta al doilea element). Aplicand apoi funct iile
canonic si elimina obt inem polinomul suma. Prezentam programul mai jos:
#include<iostream.h
struct nod{ int grad; int coef; nod *adr ;} ;
/************************/
//declararea functiilor utilizate
nod *creare(void);
void parcurge(nod *prim);
void canonic (nod *prim);
3.2. LISTE 41
nod* concatenare(nod *prim1, nod*prim2);
nod *elimina(nod* prim, int info);
nod* prod(nod *prim1, nod* prim2);
/***********************/
//functia principala
void main(void){
nod *cap, *cap1,*suma,*produs,*prim;
cap=creare();
cout<<Listeaza primul polinom<<endl;
parcurge(cap);
cap1=creare();
cout<<Listeaza al doilea polinom<<endl;
parcurge(cap1);
cout<<Produsul polinoamelor<<endl;
cout<<*****************;
produs=prod(cap,cap1);
canonic(produs);
produs=elimina(produs,0);
parcurge(produs);
prim=concatenare(cap,cap1);
cout<<Polinoamele concatenate<<endl;
parcurge(prim);
canonic(prim);
cout<<Suma polinoamelor<<endl;
suma=elimina(prim,0);
parcurge(suma);}
/**********************/
//functia de creare a unui polinom
nod *creare(void){
nod *prim,*p,*q;int coef,grad;char a;
prim=new nod;
cout<< Introduceti prim-grad<<endl;
cingrad;
prim-grad=grad;
cout<< Introduceti prim-coef<<endl;
cincoef;
prim-coef=coef;
prim-adr=NULL;
42 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
q=prim;
cout<<Gata?[d/n]<<endl;
cina;
while (a!=d){
p=new nod;
cout<<p-grad<<endl;
cingrad;
p-grad=grad;
cout<<p-coef<<endl;
cincoef;
p-coef=coef;
p-adr=NULL;
q-adr=p;
q=p;
cout<<Gata?[d/n]<<endl;
cina;}
return prim;}
/**************************/
//functia de parcurgere a unui polinom
void parcurge(nod *cap){
nod *q;
if(!cap)cout<<Polinom vid;
return;
cout<<Urmeaza polinomul<<endl;
for(q=cap;q;q=q-adr)
cout<<+(<<(*q).coef<<)<<X<<(*q).grad;}
/*********************************/
//functia care efectueaza produsul a doua polinoame
nod* prod(nod* prim1, nod* prim2)
{ nod *p,*q,*r,*cap,*s;
cap=new nod;
cap-grad=1;cap-coef=0;cap-adr=NULL;
s=cap;
for(p=prim1;p;p=p-adr)
for(q=prim2;q;q=q-adr)
{ r=new nod;r-coef=p-coef*q-coef;
r-grad=p-grad+q-grad;r-adr=NULL;
s-adr=r;s=r;}
3.2. LISTE 43
return cap;}
/*************************/
/*functia care aduna coecientii a doua monoame de acelasi grad si
atribuie valoarea 0 coecientului unuia din monoamele adunate*/
void canonic(nod *prim){
nod *p,*q;
for (p=prim;p;p=p-adr)
{ q=p-adr;
while(q) if(p-grad==q-grad)
{ p-coef+=q-coef;q-coef=0;q=q-adr;} } return;}
/******************************/
/*functia care elimina monoamele ale caror coecienti au o valoare data*/
nod *elimina(nod* prim, int info)
{ nod *q,*r;
if((*prim).coef==info)
q=(*prim).adr; delete prim; prim=q;
q=prim;
while(q-adr)
if(q-adr-coef==info)
{ r=q-adr; q-adr=r-adr; delete r;}
else q=q-adr;
return prim;}
/******************************/
//functia de concatenare a doua monoame
nod* concatenare(nod *prim1, nod*prim2)
nod *q,*r;
for(q=prim1;q;q=q-adr) r=q;
r-adr=prim2;
return prim1;
Liste dublu nlant uite

In continuare vom prezenta un program n cadrul caruia se creeaza o lista


dublu nlant uita, se parcurge direct si invers si se elimina nodurile ce cont in
o informat ie data. Structura nod va cont ie trei campuri: unul (inf) este
informat ia nodului, al doilea (urm) este pointerul care indica adresa nodului
urmator iar al treilea (ant) este pointerul care indica adresa nodului anterior.
44 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
Vom introduce un nou tip de variabila, numit lista constand dintr-o struc-
tura formata din doua variabile de tip nod (cele doua noduri reprezentand
primul si ultimul element al listei dublu nlant uite). Funct ia creare per-
mite crearea unei liste dublu nlant uite. Funct ia parcurg d, al carui ar-
gument este primul element al listei, permite parcurgerea directa (plecand
de la primul element si ajungand la ultimul) a listei. Funct ia parcurg i al
carei argument este ultimul nod al listei realizeaza parcurgerea listei n sens
invers. Funct ia elimin d ale carei argumente sunt o variabila de tip lista
si o variabila de tip int, parcurge direct lista dubla si elimina nodurile ce
cont in argumentul de tip ntreg de cate ori le ntalneste. Proprietatea pe
care o au listele duble de a putea parcurse n ambele sensuri este folosita
de funct ia sortin pentru a sorta prin insert ie, dupa valorile din campul inf,
elementele listei, valoarea ntoarsa de funct ie ind lista sortata (mai precis
primul si ultimul element al listei sortate).
#include<iostream.h
struct nod{ int inf; nod *urm; nod *ant;} ;
typedef struct{ nod *p;nod *u;} lista;
//declararea functiilor utilizate
lista creare(void);
void parcurg i(nod*prim);
void parcurg d(nod*prim);
lista elimin d(lista list,int info);
nod*sortin(lista list);
/********************************/
//functia principala
void main(void)
{ int info;
nod *prim;lista list;
list= creare();
parcurg d(list.p);
parcurg i(list.u);
cout<<Spuneti ce nod se elimina<<endl;
cout<<list.p-inf;
cininfo;
list=elimin d(list,info);
parcurg d(list.p);
parcurg i(list.u);
prim=sortin(list);
3.2. LISTE 45
cout<<Urmeaza lista sortata<<endl;
parcurg d(prim);}
/**********************************/
//functia de creare
lista creare(void)
{ nod *prim,*p,*q;int inf;char a; lista val;
prim=new nod;
cout<< Introduceti prim-inf<<endl;
cininf;
prim-inf=inf;prim-urm=NULL;prim-ant=NULL;
q=prim;cout<<Gata?[d/n]<<endl;
cina;
while (a!=d)
{ p=new nod;
cout<<p-inf<<endl;
cininf;
p-inf=inf ;p-urm=NULL;p-ant=q;
q-urm=p;
q=p;
cout<<Gata?[d/n]<<endl;
cina;}
val.p=prim;
if(!prim-urm) val.u=prim;
else val.u=p;
return val;}
/******************************/
//functia de parcurgere directa
void parcurg d(nod *prim)
{ nod *q;
if(!prim){ cout<<Lista vida;return;}
cout<<Urmeaza lista directa<<endl;
for(q=prim;q;q=q-urm)
cout<<q-inf<< ;}
/******************************/
//functia de parcurgere inversa
void parcurg i(nod *ultim)
{ nod *q;
if(!ultim){ cout<<Lista vida;return;}
46 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
cout<<Urmeaza lista inversa<<endl;
for(q=ultim;q;q=q-ant)
cout<<q-inf<< ;}
/**********************************/
/*functia de eliminare a nodurilor ce contin o informatie data*/
lista elimin d(lista list, int info){
nod *q,*r,*p;
while(list.p-inf==info)
if (list.p==list.u) { list.p=NULL;list.u=NULL;return list;}
else
{ q=list.p-urm;q-ant=NULL; delete list.p; list.p=q;}
q=list.p;
while(q-urm)
{ if(q-urm==list.u)
{ if (q-urm-inf==info)
{ r=q-urm;
list.u=q;q-urm=NULL;delete r;}
return list;}
if(q-urm-inf==info)
{ p=q;r=q-urm; q-urm=r-urm;
q-urm-ant=p; delete r;}
else q=q-urm;}
list.u=q;return list;}
/****************************/
//functia de sortare prin insertie
nod* sortin(lista list){
nod*s,*r,*p=list.p,*q;int m;
if(!list.p) cout<<Lista vida<<endl;
else
for(q=p-urm;q;q=q-urm)
{ for(s=q-ant;s;s=s-ant)
{ if (!s-ant) break;
if(q-inf=s-ant-inf) break;}
if(q-inf<s-inf)
{ m=q-inf;
for(r=q;!(r==s);r=r-ant)
r-inf=r-ant-inf;s-inf=m;} }
return list.p;}
3.3. STIVE 47
3.3 Stive
o.n este o lista cu restric@tii pentru care singurele operat ii permise sunt:
- adaugarea unui element n stiva;
- eliminarea, vizitarea sau modicarea ultimului element introdus n stiva.
Stiva funct ioneaza dupa principiul |.n| .n:n :.n| .. - 1n 1n
1.: C {111C).

In cazul alocarii dinamice (de care ne vom ocupa n cele ce urmeaza), ele-
mentele (nodurile) stivei sunt structuri n component a carora intra si adresa
nodului anterior.

In programul de mai jos dam funct iile push de adaugare n stiva a unei
nregistrari si pop de extragere. Cu ajutorul lor construim o stiva folosind
funct ia creare.
#include<iostream.h
struct nod{ int inf; nod*ant;} ;
nod*stiva;
/*********************************/
//functia de adaugare in stiva a unei noi inregistrari
nod*push(nod *stiva, int info)
{ nod *r;
r=new nod;
r-inf=info;
if(!stiva)r-ant=NULL;
else r-ant=stiva;
stiva=r;return stiva;}
/*********************************/
//functia de extragere din stiva a ultimului nod
nod *pop(nod*stiva)
{ nod *r;
if(!stiva)
cout<<Stiva vida <<endl;
else
{ r=stiva;stiva=stiva-ant;delete r;}
return stiva;}
/************************************/
/*functia de parcurgere a stivei in ordine inversa (de la ultimul nod intrat
la primul)*/
void parcurge(nod*stiva)
48 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
{ nod*r=stiva;
if(!stiva) cout<<Stiva vida<<endl;
else { cout<<Urmeaza stiva<<endl;
while(r){ cout<<r-inf<< ;r=r-ant;} }
return;}
/************************************/
/*functia de creare a stivei prin adaugari si eliminari*/
nod* creare()
{ char a;int info;nod*stiva;
cout<<Primul nod<<endl;
cout<<stiva-inf;
cin info;
stiva=new nod;
stiva-inf=info;
stiva-ant=NULL;
a=a;
while(!(a==t)){
cout<<Urmatoarea operatie[a/e/t]<<endl;
/* t=termina; a=adauga nod; e=elimina nod;*/
cina;
switch(a)
{ case t: break;
case a:cout<<Stiva-inf<<endl;
cininfo;
stiva=push(stiva,info);break;
default:stiva=pop(stiva);break;} }
return stiva;}
/******************************/
//functia principala
void main()
{ stiva=creare();
parcurge(stiva);}
3.3. STIVE 49
3.3.1 Tehnica elimin@arii recursivit@a@tii cu ajutorul
stivei
Variantele recursive ale programelor pot duce la timpi mari de execu@tie
@si pot necesita spa@tii de memorie prea mari. O metoda de eliminare a
recursivit@a@tii se bazeaz@a pe folosirea unei structuri de tip stiv@a.

In
scrierea unei variante nerecursive trebuie parcur@si to@ti pa@sii implica@ti
n varianta recursiv@a prin tehnici nerecursive. Fiec@arui apel recursiv i
corespunde o depunere de date n stiv@a, care sunt extrase la revenirea din
acel apel. Prezent@am eliminarea recursivit@a@tii pentru un program sim-
plu, care cite@ste caracterele tastate pan@a la un blanc, tip@arindu-le apoi
n ordine invers@a.
Prezent@am mai ntai varianta recursiv@a.
#include<iostream.h
#include<conio.h
/************************/
void invers car()
{ char car;
if((car=getche())!= )
invers car();
cout<<car;}
/*****************/
void main()
{ invers car;}
Urmeaz@a varianta nerecursiv@a (iterativ@a)
#include<iostream.h
#include<conio.h
struct nod{ char inf; nod*ant;} ;
nod*stiva;
/**************************/
nod*push(nod *stiva, char info)
{ nod *r;
r=new nod;
r-inf=info;
if(!stiva) r-ant=NULL;
else r-ant=stiva;
stiva=r;
return stiva; }
50 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
/***********************/
nod *pop(nod*stiva)
{ nod *r;
if(!stiva) cout<<Stiva vida<<endl;
else
{ r=stiva; stiva=stiva-ant; delete r;}
return stiva;}
/***************************/
void invers car()
{ char car; nod*stiva=NULL;
while ((car=getche())!= ) stiva=push(stiva,car);
while(stiva) { cout<<stiva-inf; stiva=pop(stiva);}
cout<<endl;}
/***************************/
void main()
{ invers car();}
3.4 Liste de tip coada
O nnn este o lista pentru care toate inserarile sunt facute la unul din capete
iar toate stergerile (consultarile, modicarile) sunt efectuate la celalalt capat.
Coada funct ioneaza pe principiul :.n| .n:n :.n| .. - 1.: 1n 1.:
C {111C).

In cazul alocarii dinamice, elementele (nodurile) cozii sunt structuri n


component a carora intra si adresa nodului urmator.

In programul de mai jos dam funct iile pune de adaugare n coada a unei
nregistrari si scoate de extragere. Cu ajutorul lor construim o coada folosind
funct ia creare. Vom reprezenta coada printr-o structura coada care cont ine
primul si ultimul nod al cozii.
#include<iostream.h
//urmeaza structura de tip nod
struct nod{ int inf;nod*urm;} ;
//urmeaza structura de tip coada
typedef struct{ nod *p;nod *u;} coada;
coada c;
/***********************************/
//functia de adaugare in coada
3.4. LISTE DE TIP COAD

A 51
coada pune(coada c,int n)
{ nod *r;
r=new nod;r-inf=n;r-urm=NULL;
if(!c.p)
{ c.p=r;c.u=r;}
else{ c.u-urm=r;c.u=r;} return c;}
/*********************************/
//functia de extragere din coada
coada scoate(coada c)
{ nod *r;
if(!c.p) cout<<Coada vida<<endl;
else
{ cout <<Am scos <<c.p-inf<<endl;
r=c.p;c.p=c.p-urm;delete r;return c;}
/***********************************/
//functia de listare (parcurgere) a cozii
void parcurge(coada c)
{ nod *r=c.p;
cout<<Urmeaza coada<<endl;
while(r)
{ cout<<r-inf<< ;
r=r-urm;}
cout <<endl;}
/***********************************/
//functia de creare a cozii
coada creare()
{ char a;int info;coada c;
cout<<Primul nod<<endl;
cout<<c.p-inf<<endl;
cin info;
c.p=new nod;
c.p-inf=info;
c.p-urm=NULL;
c.u=c.p;
a=a;
while((a==s)+(a==a)){
cout<<Urmatoarea operatie;
cout<<[a=pune nod/s=scoate/t=termina]<<endl;
52 CAPITOLUL 3. TIPURI DE STRUCTURI DE DATE
cina;
switch(a)
{ case s: c=scoate(c);break;
case a:cout<<c.p-inf<<endl;cininfo;
c=pune(c,info);break;
default:break;} }
return c;}
/**********************************/
//functia principala
void main()
{ c=creare();
parcurge(c);}
Capitolul 4
Tehnici (metode) de
programare a algoritmilor
4.1 Metoda backtracking (c@autare cu revenire)
Tehnica (metoda) backtracking se aplic@a algoritmilor pentru rezolvarea
urm@atoarelor tipuri de probleme: Fiind date n mul@timi :nnn S
1
, S
2
, ...S
n
,
ecare avand un num@ar s
i
de elemente, se cere g@asirea elementelor vec-
torului X = (x
1
, x
2
, ...x
n
) S = S
1
S
2
S
n
, astfel ncat s@a
e ndeplinit@a o anumit@a rela@tie (x
1
, x
2
, ..., x
n
) ntre elementele sale.
Rela@tia (x
1
, x
2
, ..., x
n
) se nume@ste :|n. .n:nn, mul@timea S =
S
1
S
2
... S
n
se nume@ste sn.| |..|: .h.|, iar vectorul
X se nume@ste |.n :.|n. Metoda backtracking determin@a toate
solu@tiile rezultat ale problemei. Dintre acestea se poate alege una care
ndepline@ste n plus o alt@a condi@tie.
La generarea vectorului X, se respect@a urm@atoarele condi@tii:
a) x
k
prime@ste valori numai dac@a x
1
, x
2
, ..., x
k1
au primit deja valori;
b) dup@a ce se atribuie o valoare lui x
k
, se veric@a :|n.n nn.n n
n.nn: (x
1
, x
2
, ..., x
k
), care stabile@ste situa@tia n care are sens s@a se
treac@a la calculul lui x
k+1
.
Nendeplinirea condi@tiei exprim@a faptul c@a oricum am alege x
k+1
,
x
k+2
, ..., x
n
nu se ajunge la solu@tia rezultat.

In caz de nendeplinire a
condi@tiei (x
1
, x
2
, ..., x
k
), se alege o nou@a valoare pentru x
k
S
k
@si
se reia vericarea condi@tiei . Dac@a mul@timea de valori x
k
S
k
s-a
epuizat, se revine la alegerea altei valori pentru x
k1
@s.a.m.d. Aceast@a
53
54CAPITOLUL 4. TEHNICI (METODE) DE PROGRAMARE AALGORITMILOR
mic@sorare a lui k d@a numele metodei, ilustrand faptul c@a atunci cand
nu se poate avansa se urm@are@ste napoi secven@ta curent@a din solu@tia
posibil@a.

Intre condi@tia intern@a @si cea de continuare exist@a o strans@a
leg@atur@a.
4.1.1 Backtracking iterativ
Pentru u@surarea n@telegerii metodei vom prezenta o rutin@a (schem@a
de program) general@a, aplicabil@a oric@arei probleme [?, sor].
void back()
{ int AS;
k=1; Init();
while(k0)
{
do{ } while ((AS=Am Succesor())&&!E Valid());
if(AS)
if(Solutie()) Tipar();
else{ k++; Init();}
else k;
} }
Aceast@a rutin@a genereaz@a solu@tiile cu ajutorul unei stive scrise sub
forma unui vector care are drept prim element pe x
1
S
1
, drept al doilea
element pe x
2
S
2
@s.a.m.d. Functia Void Init() ini@tializeaz@a x
k
astfel
ncat s@a avem x
k
< min
xS
x. Func@tia int Am Succesor calculeaz@a,
@tinand cont de structura de ordine denit@a pe S
k
, succesorul x

k
S
k
al
lui x
k
.Dac@a exist@a succesorul x

k
acesta este pus n stiv@a @si func@tia
returneaz@a 1, altfel func@tia returneaz@a 0. Func@tia int E Valid()
veric@a dac@a este satisf@acut@a rela@tia de continuare (x
1
, x
2
, ..., x
k1
, x

k
).
Dup@a efectuarea instruc@tiunilor
do{ } while ((AS=Am Succesor())&&!E Valid()) n cazul n care AS=1,
n stiv@a se vor aa cele mai mici l elemente (x

1
, ..., x

l
) pentru care rela@tia
de continuare este vericat@a. Testul dac@a s-a ajuns sau nu la o solu@tie
se face cu ajutorul func@tiei int Solutie(). Dac@a testul este trecut, se
tip@are@ste solu@tia cu ajutorul func@tiei Tipar(); dac@a nu este trecut se
mai adaug@a un element n stiv@a ini@tializat cu un minorant al lui S
l+1
@si
se reiau instruc@tiunile do{ } while ((AS=Am Succesor())&&!E Valid()).
Aceasta reprezint@a n:n n nn:. Dac@a dup@a efectuarea instruc@tiunilor
do{ } while ((AS=Am Succesor())&&!E Valid()) se ob@tine AS=0,
4.1. METODA BACKTRACKING (C@AUTARE CU REVENIRE) 55
se scoate un element din stiv@a @si se relanseaz@a instruc@tiunile do{ }
while ((AS=Am Succesor())&&!E Valid()) (n:n n :n.:).pan@a
cand se epuizeaz@a toate combina@tiile care satisfac rela@tia de continuare
@si n nal, se tip@aresc toate solu@tiile.
Ca exemplu vom genera permut@arile mul@timii 1, 2, ..., n. Vom folosi
rutina descris@a mai nainte, scriind n mod adecvat func@tiile Void Init(),
int Am Succesor, int E Valid(), int Solutie() @si Tipar():
#include <iostream.h
int st[10],n,k;
/***************/
void Init()
{ st[k]=0;}
/***************/
int Am Succesor ()
{ if (st[k]<n
{ st[k]++;
return 1;}
else return 0;
}
/****************/
int E Valid()
{ for (int i=1;i<k;i++)
if (st[i]==st[k]) return 0;
return 1;
}
/********************/
int Solutie
{ return k==n;}
/*******************/
void Tipar()
{ for (int i=1;i<=n;i++) cout <<st[i];
cout<<endl;
}
/*****************/
void back()
{ int AS;
k=1; Init();
while(k0)
56CAPITOLUL 4. TEHNICI (METODE) DE PROGRAMARE AALGORITMILOR
{
do{ } while ((AS=Am Succesor())&&!E Valid());
if(AS)
if(Solutie()) Tipar();
else{ k++; Init();}
else k;
} }
/*******************/
void main()
{ cout<<n=; cinn;
back();
}
4.1.2 Backtracking recursiv
Ne vom ocupa n cele ce urmeaz@a de varianta recursiv@a a tehnicii back-
tracking. Ca primul exemplu prezent@a problema gener@arii produsului
cartezian 1, 2, ..., l
h
.
#include <iostream.h
int h,l,k,st[10];
/******************/
void back r(int k)
{ for (int i=1;i<=l;i++)
{ st[k]=i;
if (k<h) back r(k+1);}
if (h==k) for (int j=1;j<=h;j++) cout<<st[j]<< ;
cout<<endl;}
/************************/
void main()
{ cout<<backtracking recursiv<<endl; l=3;h=3; back r(1); }

In gura 4.1 se prezint@a modul n care ac@tioneaz@a acest algoritm.


De obicei nu vrem sa calculam produsul cartezian, ci submultimi ale aces-
tuia astfel ncat rela@tiile intern@a @si de continuare s@a e satisf@acute.

In acest caz modic@am programul anterior dup@a cum urmeaz@a


#include <iostream.h
int h,m,k,st[10];
/******************/
int Relatie(int l)
4.1. METODA BACKTRACKING (C@AUTARE CU REVENIRE) 57
Figura 4.1: Backtracking recursiv
58CAPITOLUL 4. TEHNICI (METODE) DE PROGRAMARE AALGORITMILOR
{ .............................}
/******************/
void back r(int k)
{ for (int i=1;i<=m;i++)
{ st[k]=i;
if (h==k&&Relatie(k)){ for (int j=1;j<=h;j++) cout<<st[j]<<
;
cout<<endl;}
else if (Relatie(k))
back r(k+1);} }
/************************/
void main()
{ cout<<backtracking recursiv<<endl;
cout<<m=<<endl; cinm;
cout<<h=<<endl; cinh;
back r(1); }
Introducand n program func@tia int Relatie(int l) ne asigur@am c@a
sunt ndeplinite rela@tiile de continuare @si rela@tia intern@a.
O submultime a produsului cartezian 1, 2, ..., l
h
o reprezint@a com-
bin@arile a l elemente luate cate h. Elementele acestei submul@timi trebuie
s@a e liste de h numere aaalese din 1, 2, ..., l @si ordonate cresc@ator.
Punem de aceea
int Relatie(int l)
{ int r=1;
for (int j=1;j<l;j++)
if(st[l]<=st[j]) r=0;
return r;}
4.2 Metoda optimului local (greedy)
Algoritmii j:nj (n limba englez@a j:nj=|nn) se caracterizeaz@a prin
faptul c@a, la ecare nivel, se alege mereu cel mai bun candidat posibil,
dup@a luarean considera@tie a tuturor acestora. Algoritmii j:nj furnizeaz@a
solu@tii relativ bune dar nu mereu cea mai bun@a solu@tie (n .n|
:h|nn n.:n n :n|.).

In cazul n care se dore@ste ob@tinerea
solu@tiei optime, n locul tehnicii j:nj se aplic@a tehnica hn|:n|.nj, dar
4.2. METODA OPTIMULUI LOCAL (GREEDY) 59
n acest caz cre@ste timpul de execu@tie. Forma general@a a algoritmului
j:nj este:
ALGORITM Greedy(S)
S1S
SOLUTIE
WHILE (NOT Conditie terminare)
{
xoptim local din S1
S1S1-{ x}
if (SOLUTIE{ x} satisface conditiile)
{
SOLUTIESOLUTIE{ x}
}
}
4.2.1 Exemplu. Problema rucsacului
Se consider@a N obiecte care se pun la dispozi@tia unei persoane pentru
a le transporta cu ajutoril unui rucsac. Greutatea maxim@a ce poate
transportat@a cu acel rucsac este G. Pentru rcare obiect i, i = 1, ..., N,
se noteaz@a cu c(i) > 0 ca@stigul ob@tinut n urma transportului s@au
n ntregime la destina@tie, @si cu g (i) greutatea obiectului i. Deosebim
dou@a situa@tii posibile.

In prima situa@tie (problema continu@a a ruc-
sacului) pentru ecare obiect i este posibil s@a lu@am din el orice parte
x
i
[0, 1] .

In a doua situa@tie (problema discret@a a rucsacului) un obiect
poate nc@arcat numai n ntregime n rucsac adic@a x
i
0, 1. Prob-
lema const@a n determinarea ca@stigului maxim ce poate ob@tinut prin
umplerea rucsacului. Prin urmare se caut@a max
N

i=1
c (i) x(i) .
Problema continu@a a rucsacului
O modalitate de a ob@tine o solu@tie a problemei este aceea de a sorta
obiectele n ordinea descresc@atoare a ca@stigului specic c
i
/g
i
, i = 1, ..., N
@si de a le nc@arca n ntregime n rucsac cu excep@tia ultimului element
(cel care are cel mai mic ca@stig specic dintre elementele care ncap n
rucsac) din care se ncarc@a eventual numai o parte, astfel ncat s@a nu se
60CAPITOLUL 4. TEHNICI (METODE) DE PROGRAMARE AALGORITMILOR
dep@a@seasc@a greutatea maxim@a aadmisibil@a G. Se vor ordona cele N
obiecte descresc@ator n raport cu ca@stigul specic astfel ca
c
1
g
1

c
2
g
2
...
c
N
g
N
.
Vectorul X = (x
1
, x
2
, ..., x
N
) este o |. .h.|n a problemei continue
a rucsacului dac@a:
x
i
[0, 1] , i = 1, ..., N

N
i=1
g
i
x
i
G.
Mai amintim faptul c@a o solu@tie posibil@a reprezint@a o solu@tie op-
tim@a dac@a pentru ea se atinge maximul func@tiei obiectiv
N

i=1
c (i) x(i)
care m@asoar@a ca@stigul total.
Prezent@am n continuare programul de rezolvare a problemei continue
a rucsacului.
#include<iostream.h
int n;
oat g[10], c[10], x[10];
oat G;
/******************************/
void CITESTE()
{ int i;
cout<<Introduceti numarul de obiecte<<endl; cinn;
cout<<Introduceti greutatile obiectelor<<endl;
for (i=0;i<n;i++)
{ cout<<g[<<i<<]=<< ; cing[i];}
cout<<Introduceti castigurile<<endl;
for (i=0;i<n;i++)
{ cout<<c[<<i<<]=<< ; cinc[i];}
cout<<Introduceti capacitatea rucsacului<<endl;
cinG;cout<<endl;}
/************************************/
void SCRIE()
{ int i; oat ;
cout<<Solutia problemei este:<<endl;
C=0;
for (i=0;i<n;i++)
4.2. METODA OPTIMULUI LOCAL (GREEDY) 61
{ cout<<Greutate[<<i<<]=<<g[i]<< ;
cout<<Castig[<<i<<]=<<c[i]<< ;
cout<<Fractiune[<<i<<]=<<x[i]<< ;
C+=x[i]*c[i];}
cout<<endl;
cout<<Castigul total este=<<C<<endl;}
/**********************************/
void SORTARE INSERTIE()
{ oat cc,gg;
int i,j;
for(j=1;j<n;j++)
{ cc=c[j]; gg=g[j];
i=j-1;
while(i=0&&c[i]/g[i]<cc/gg)
{ c[i+1]=c[i]; g[i+1]=g[i];i;}
c[i+1]=cc; g[i+1]=gg;
}
/*******************************/
void INCARCA()
{ int i;
SORTEARE INSERTIE();
for (i=0;i<n;i++)
{
if(g[i]G)
{ x[i]=G/g[i]; G=0;}
else{ x[i]=1; G-=g[i];}
}
}
/*******************************/
void main()
{ CITESTE();
INCARCA();
SCRIE():}
62CAPITOLUL 4. TEHNICI (METODE) DE PROGRAMARE AALGORITMILOR
Capitolul 5
Grafuri
5.1 No@tiuni introductive
Un j:n] :.nn G este o pereche (V, E) unde V este o mult ime nita iar E
este o relat ie binara pe V . Mult imea V se numeste mult imea n:]:.|: iar
mult imea E se numeste mult imea n:|: lui G ((u, v) E, u V, v V ).

Intr-un j:n] n:.nn G = (V, E) mult imea muchiilor este constituita


din perechi de varfuri neordonate (u, v E, u V, v V ) . Daca (u, v) este
un arc dintr-un graf orientat G = (V, E) spunem ca (u, v) este .n.nn n.n
sau |nn n.n varful u si este .n.nn n sau .n:n n varful v. Despre
(u, v) sau u, v spunem ca sunt incidente varfurilor u si v. Daca (u, v) sau
u, v reprezinta un arc (muchie) ntr-un graf spunem ca varful v este nn.n-
n varfului u. (Pentru simplitate folosim notat ia (u, v) si pentru muchiile
grafurilor neorientate.)

In gura (5.1) prezentam un graf orientat si un graf neorientat. Adiacent a


grafurilor se pune n evident a cu ajutorul unei nn:. n nn.nn n. De
exemplu matricea de adiacent a a grafului orientat din gura este
0 1 2 3
0 0 1 0 1
1 0 1 1 0
2 0 0 0 0
3 0 1 0 0
.
0, 1, 2, 3 sunt etichetele varfurilor grafului considerat. Daca (u, v) 0, 1, 2, 3
0, 1, 2, 3 este un arc al grafului punem valoarea 1 pentru elementul aat pe
linia u si coloana v a matricei.

In caz contrar punem 0.
63
64 CAPITOLUL 5. GRAFURI
Figura 5.1: Exemple de grafuri
Matricea de adiacent a a grafului neorientat este
0 1 2 3
0 0 1 0 1
1 1 0 1 1
2 0 1 0 0
3 1 1 0 0
.
Daca u, v este o muchie a grafului punem valoarea 1 pentru elementul aat
pe linia u si coloana v a matricei.

In caz contrar punem 0. Observam ca
matricea de adiacent a a unui graf neorientat este simetrica.
G:nn| unui varf al unui graf neorientat este numarul muchiilor incidente
acestuia.

Intr-un graf orientat, j:nn| .:.: al unui varf este numarul
arcelor care pleaca din el iar j:nn| .n:.: al unui varf este numarul arcelor
ce intra n el. Suma gradului exterior si a gradului interior este j:nn|
varfului.
Un n:n n |nj.n k de la un varf u la un varf u

ntr-un graf G = (V, E)


este un sir de varfuri (v
0
, v
1
, v
2
, ..., v
k1
, v
k
) astfel ncat u = v
0
, u

= v
k
si
(v
i1
, v
i
) E pentru i = 1, 2, ..., k. Drumul n .n varfurile v
0
, v
1
, ..., v
k1
, v
k
si muchiile(arcele) (v
0
, v
1
) , (v
1
, v
2
) , ..., (v
k1
, v
k
) .
5.1. NO@TIUNI INTRODUCTIVE 65
1nj.nn unui drum este numarul de muchii (arce) din acel drum. Un
drum este |nnn: daca toate varfurile din el sunt distincte.
Prezentam n continuare un program scris n C care stabileste, pe baza
matricei de adiacent a daca ntre doua varfuri ale grafului exista un drum.
Ideea pe care se bazeaza programul este urmatoarea: daca ntre doua varfuri
i si j exista un drum, atunci oricare ar varful k, ntre i si k exista un drum
daca si numai daca (i, k) este arc al grafului sau ntre j si k exista un drum.

In nal programul aseaza o matrice n care elementul de pe linia i si coloana


j ia valoarea 1 daca exista un drum de la i la j si valoarea 0 daca nu exista.
Urmeaza programul:
# include <stdio.h
# dene maxcol 20
# dene maxlin 20
unsigned char i,j,k,n,x[maxcol][maxlin],y[maxcol][maxlin];
void main(void)
{ printf(Dimensiunea matricii n=);scanf(%d,&n);
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{ printf( x[%d][%d]=,i,j);
scanf(%d,&x[i][j]);y[i][j]=x[i][j];}
j=0;while(j<n)
{
i=0;
while(i<n)
{ if(y[i][j]!=0)
{ k=0;
while(k<n)
y[i][k]=y[i][k][[y[j][k]; k + +; ; ;
i++;} ;
j++;} ;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
printf(y[%d][%d]=%d ,i,j,y[i][j]);}
66 CAPITOLUL 5. GRAFURI
Pentru graful orientat din gura (5.1) se obt ine matricea
0 1 2 3
0 0 1 1 1
1 0 1 1 0
2 0 0 0 0
3 0 1 1 0
.

In cazul grafurilor neorientate, daca x


1
= x
r
si nici-o muchie nu este
parcursa de doua ori (adica nu apar perechi de muchii alaturate de forma
(x
i
x
i+1
), (x
i+1
, x
i
), drumul (x
1
, x
2
, ...., x
r
= x
1
) se numeste .|. Daca muchi-
ile ciclului sunt arce orientate avem un ciclu ntr-un graf orientat.
Un graf neorientat este n. daca pentru ecare doua varfuri u, v exista
un drum (u, ..., v) de la u la v. Daca un graf nu este conex, atunci el are r 2
componente conexe care sunt subgrafuri conexe maximale ale lui Gn raport
cu relat ia de incluziune a mult imilor. Numarul de varfuri [V (G)[ al unui graf
se numeste :n.n| grafului iar numarul muchiilor [E (G)[ reprezinta nn:.nn
grafului.
Un graf (neorientat) conex care nu cont ine cicluri se numeste n:h:. Dam
n continuare cateva teoreme de caracterizare a arborilor:
Teorema 2. 1. G = (V, E) n j:n] n:.nn n :n.n n 3. 1:nnn:|
nn. .. n |.n|n.
n) G n j:n] n. ]n:n .|:..
h) G n j:n] ]n:n .|:. nn..nn| {nnn . nnnjn |. G n|..
nn. nn: n .|).
) G n j:n] n. n.n.nn| {nnn :j n|. n |. G. j:n]|
:.|n n nn. n.).
1nn:n ..a) b). Fie G conex si fara cicluri. Fie u, v din V astfel
ca (u, v) E. Cum graful este conex, exista un drum (v, ...., u) de la v la
u. Adaugand si muchia (u, v) , graful G

= (V, E (u, v)) va cont ine ciclul


(v, ..., u, v) .
b) c) Daca G nu ar conex, n virtutea proprietat ii de maximalitate,
adaugand o noua muchie ce uneste doua varfuri din doua componente conexe
diferite nu obt inem un ciclu, ceea ce contrazice b). Asadar G este conex. Sa
presupunem mai departe prin absurd ca e E si G

= (V, E e) este
conex. Atunci exista un drum ce uneste extremitat ile lui e n G, deci G
cont ine un ciclu, ceea ce contrazice din nou b).
c) a). Daca G ar cont ine un ciclu si e ar o muchie a acestui ciclu,
atunci G

= (V, E e) ramane conex, ceea ce contrazice c).


5.1. NO@TIUNI INTRODUCTIVE 67
Teorema 3. C:. n:h: G = (V, E) n :n.n| n, n: n 1 n|...
1nn:n .. Mai ntai vom demonstra ca G cont ine cel put in un varf de
gradul 1 (varfuri terminale, frunze). Sa presupunem prin absurd ca gradul
d (v) 2 pentru orice v V.

In acest caz sa consideram n G un drum
D de lungime maxima si sa notam cu x o extremitate a acestui drum. Cu
presupunerea facuta, varful x ar avea gradul cel put in 2, deci n virtutea max-
imalitat ii lui D trebuie sa e adiacent cel put in altui varf din D, formandu-se
astfel un ciclu n G. Apare o contradict ie.
Acum proprietatea ca G are n 1 muchii rezulta usor prin induct ie. Ea
este adevarata pentru n = 1. Presupunem ca este adevarata pentru tot i
arborii de ordin cel mult n 1 si e G un arbore de ordin n. Daca x este un
nod terminal al lui G, atunci G

cu V (G

) = V x este si el un arbore de
ordinul n 1 si conform ipotezei de induct ie va avea n 2 muchii. Rezulta
ca G are n 1 muchii.
Teorema 4. 1. G n j:n] n :n.n| n 3. 1:nnn:| nn. .. n
|.n|n.
n) G n j:n] n. ]n:n .|:..
n) G n j:n] ]n: n .|:. n 1 n|...
) G n. . n: n 1 n|...
] ) 1..n n n. n:n n: :. nn n:]:. n..n n| |. G.
1nn:n ..a) d). Avem a) (b si teorema 2) d).
d) a). Presupunem prin absurd ca G nu este conex. Adaugand un
numar de muchii care sa uneasca elemente din diverse componente conexe
ale grafului putem obt ine un graf conex fara cicluri cu ordinul mai mare ca
n1. Se ajunge la o contradict ie (n virtutea teoremei 2), deci G este conex.
a) e). Avem a) (c si teorema 2) e).
e) a). Presupunem prin absurd ca G are cicluri. Extragand n mod
convenabil unele muchii, se ajunge astfel la un graf conex fara cicluri, de
ordin n cu mai put in de n1 muchii. Se obt ie astfel o contradict ie n virtutea
teoremei 3.
a) f). Rezulta imediat n virtutea denit iilor.
Din teoremele 2 si 4 obt inem sase caracterizari diferite ale arborilor :
(a) (f) .
68 CAPITOLUL 5. GRAFURI
Capitolul 6
Arbori binari
Ne vom ocupa n continuare de studiul n:h:.|: h.nn:. deoarece acestia con-
stituie una din cele mai importante structuri neliniare ntalnite n teoria
algoritmilor.

In general, structura de arbore se refera la o relat ie de ramni-
care la nivelul nodurilor, asemanatoare aceleia din cazul arborilor din natura.

In cele ce urmeaza vom introduce ntr-o alta maniera not iunea de arbore.
Arborii sunt constituit i din nn:. .n:n (puncte de ramnicare) si
nn:. :n.nn| {]:n.). Fie V = v
1
, v
2
, ... o mult ime de noduri interne
si B = b
1
, b
2
, ... o mult ime de frunze. Denim inductiv mult imea arborilor
peste V si B :
1n. ..
n) C:. |nn b
i
B n n:h:. b
i
n nnnn :nnn.nn
n. n:h:.
h) 1nn T
1
, ..., T
m
, m 1 n n:h:. n| .n.| n nn:. .n:n .
]:n. n.,n nn n nn .n: v V n n nn. nn. (m + 1)
- || T = v, T
1
, ..., T
m
) n n:h:. An| v :nnn.nn n:h:|..
(v) = m j:nn| n:h:|. .n: T
i
, i = 1, ..., m n hn:h:. n. |. T.
Cand reprezentam grac un arbore radacina este deasupra iar frunzele
sunt dedesupt (gura (6.1)).
Vom preciza termenii folosit i atunci cand ne referim n general la arbori.
Fie T un arbore cu radacina v si avand subarborii T
i
, 1 i m. Fie w
i
=
root (T
i
) radacina subarborelui T
i
. Atunci w
i
este | numarul i al lui v iar v
este nn| lui w
i
. De asemenea w
i
este fratele lui w
j
, i, j = 1, ..., m. Not iunea
de nnnn {nnnn) indica nchiderea tranzitiva si reexiva a relat iei
de u (tata).
A.|| (sau nn nn.nn) unui nod v al unui arbore T este denit astfel:
69
70 CAPITOLUL 6. ARBORI BINARI
Figura 6.1: Exemplu de arbore binar
daca v este radacina lui T atunci nivel (v, T) = 0. Daca v nu este radacina
lui T atunci pentru un anumit i, v apart ine subarborelui T
i
. Vom pune
nivel (v, T) = 1+nivel (v, T
i
). Vom omite al doilea argument din suma cand
contextul o va cere (T
i
= ) .

Inalt imea h(T) a unui arbore T este denita dupa cum urmeaza: h(T) =
max nivel (b, T) ; b este frunza lui T .

In exemplul din gura nivel (v
3
) =
1, nivel (v
4
) = 2, nivel (b
5
) = 3 si h(T) = 3.
Un arbore T este un n:h: h.nn: daca toate nodurile interne ale lui T sunt
radacini ale unor subarbori de gradul 1 sau 2. Un arbore T este n| daca
toate nodurile interne ale sale sunt radacini ale unor subarbori de gradul 2.
Arborele binar din gura este un arbore complet. Un arbore complet binar
cu n noduri interne are n + 1 frunze. Primul respectiv al doilea subarbore
este numit hn:h:| nnj respectiv n:
6.0.1 Parcurgerea arborilor binari

In cazul arborilor binari, informat iile pot stocate n frunze sau n nodurile
interne. Fiecare nod al arborelui binar este o structura care cont ine alaturi
de informat ia specica si adresele nodurilor u stang respectiv drept (gura
(6.2)).
71
Figura 6.2: Exemplu de arbore binar cu precizarea legaturilor
Pentru a accesa informat iile pastrate de un arbore, trebuie sa-l exploram
(parcurgem). Cum orice arbore binar are trei componente (o radacina, un
subarbore stang si un subarbore drept), n mod natural apar trei metode de
parcurgere a arborelui:
- Parcurgere n preordine (rsd): se viziteaza radacina, se parcurge sub-
arborele stang, se parcurge sub arborele drept.
- Parcurgere n postordine (sdr): se parcurge subarborele stang, se
parcurge subarborele drept, se viziteaza radacina.
- Parcurgere simetrica sau n inordine(srd): se parcurge subarborele
stang, se viziteaza radacina, se parcurge subarborele drept. Aplicand aceste
denit ii arborelui din gura obt inem v
1
v
2
b
1
v
4
b
4
b
5
v
3
b
2
b
3
, pentru parcurgerea
n preordine, b
1
b
4
b
5
v
4
v
2
b
2
b
3
v
3
v
1
pentru parcurgerea n postordine iar pentru
parcurgerea n inordine b
1
v
2
b
4
v
4
b
5
v
1
b
2
v
3
b
3
.
Vom prezenta n cele ce urmeaza un program C++ cu funct iile de creare,
parcurgere si stergere a unui arbore. Pentru utilizarea nodurilor unui ar-
bore binar a fost denita o structura cu urmatoarele campuri: info - campul
informat ie, n acest program de tip ntreg, st - adresa nodului descendent
stang, dr - adresa nodului descendent drept. Programul realizeaza prelu-
crarea arborelui prin urmatoarele funct ii:
a) Funct ia creare pentru crearea arborelui efectueaza operat iile:
72 CAPITOLUL 6. ARBORI BINARI
- aloca memoria necesara unui nod;
- citeste informat ia nodului si aseaza mesaje de invitat ie pentru crearea
nodurilor descendente (stang si drept);
- daca informat ia introdusa este un numar ntreg diferit de 0, se ape-
leaza recursiv funct ia pentru crearea subarborelui stang stocandu-sa adresa
de legatura pentru accesul la descendent i. Urmeaza apoi apelul recursiv
pentru crearea subarborelui drept;
- daca informat ia introdusa este 0, atunci nodului i se atribuie valoarea
NULL.
Funct ia ntoarce adresa nodului creat.
b) Funct ia rsd pentru parcurgerea n preordine efectueaza operat iile:
- prelucreaza (aseaza) radacina;
- trece la descendentul stang apelandu-se recursiv pentru parcurgerea
subarborelui stang;
- trece la descendentul drept apelandu-se recursiv pentru parcurgerea
subarborelui drept.
c) Funct ia srd pentru parcurgerea n inordine efectueaza operat iile:
- trece la descendentul stang apelandu-se recursiv pentru parcurgerea
subarborelui stang;
- prelucreaza (aseaza) radacina;
- trece la descendentul drept apelandu-se recursiv pentru parcurgerea
subarborelui drept.
d) Funct ia sdr pentru parcurgerea n postordine efectueaza operat iile:
- trece la descendentul stang apelandu-se recursiv pentru parcurgerea
subarborelui stang;
- trece la descendentul drept apelandu-se recursiv pentru parcurgerea
subarborelui drept;
- prelucreaza (aseaza) radacina.
e) Funct ia sterge pentru stergerea arborelui efectueaza operat iile:
- se sterge subarborele stang: se apeleaza recursiv stergerea pentru de-
scendentul stang;
- se sterge subarborele drept: se apeleaza recursiv stergerea pentru de-
scendentul drept;
- se sterge nodul curent;
Urmeaza programul.
#include <iostream.h
typedef struct nod { int info; struct nod *st; struct nod *dr;} arbore;
arbore *rad; int n;
73
// Se declara functiile
/***********************************/
arbore *creare();
void srd(arbore *rad);
void rsd(arbore *rad);
void sdr(arbore *rad);
void sterge(arbore *rad);
/*******************************/
// Functia principala
void main() {
cout<<Radacina=;
rad=creare();
cout<<Urmeaza arborele parcurs in inordine<<endl;
srd(rad);
cout<<Urmeaza arborele parcurs in preordine<<endl;
rsd(rad);
cout<<Urmeaza arborele parcurs in postordine<<endl;
sdr(rad);
sterge(rad);}
/*******************************/
// Functia de creare a arborelui
arbore *creare( )
{ arbore *p; cinn;
if (n!=0)
{ p=new arbore;
p-info=n;cout<<p-info<<-st ;p-st=creare( );
cout<<p-info<<-dr ;p-dr=creare( );return p;}
else
p=NULL;return p;}
/**********************************/
// Functia de parcurgere in inordine
void srd(arbore *rad)
{ if (rad!=NULL)
{ srd(rad-st);
cout<<rad-info<< ;
srd(rad-dr);} }
/*********************************/
// Functia de parcurgere in postordine
74 CAPITOLUL 6. ARBORI BINARI
void sdr(arbore *rad)
{ if (rad!=NULL)
{ sdr(rad-st);
sdr(rad-dr);cout<<rad-info<< ;} }
/************************************/
// Functia de parcurgere in preordine
void rsd(arbore *rad)
{ if(rad!=NULL)
{ cout<<rad-info<< ;
rsd(rad-st);
rsd(rad-dr);} }
/**********************************/
// Functia de stergere
void sterge(arbore *rad)
{ if (rad!=NULL){ sterge(rad-st);sterge(rad-dr);
cout<<S-a sters<<rad-info<<endl;
delete rad;} } ;
Vom prezenta mai departe o varianta nerecursiva de traversare a unui
arbore n inordine folosind o stiva auxiliara a. Schematic, algoritmul se
prezinta astfel:
p=radacina; a=NULL;
do {
while(p) { ap /*pune nodul p al arborelui in stiva*/;
p=p-st;}
if(!a) break;
else pa/*scoate p din stiva*/;
viziteaza p; p=p-adr;
while(p[[a);
Ideea algoritmului este sa salvam nodul curent p n stiva si apoi sa
traversam subarborele stang; dupa aceea scoatem nodul p din stiva, l vizitam
si apoi traversam subarborele drept.
Sa demonstram ca acest algoritm parcurge un arbore binar de n noduri
n ordine simetrica folosind induct ia dupa n. Pentru aceasta vom demonstra
urmatorul rezultat: dupa o intrare n ciclul do, cu p
0
radacina unui subar-
bore cu n noduri si stiva a cont inand nodurile a[1],...,a[m], procedura va
traversa subarborele n chestiune n ordine simetrica iar dupa parcurgerea
lui stiva va avea n nal aceleasi noduri a[1],...,a[m]. Armat ia este evi-
denta pentru n = 0. Daca n > 0, e p=p
0
nodul arborelui binar la intrarea
75
n setul de instruct iuni al ciclului do. Punand p
0
n stiva, aceasta devine
a[1],...,a[m],p
0
iar noua valoare a lui p este p=p
0
st. Acum subarborele
stang are mai put in de n noduri si conform ipotezei de induct ie subarborele
stang va traversat n inordine, dupa parcurgere stiva ind a[1],...,a[m],p
0
.
Se scoate p
0
din stiva (aceasta devenind a[1],...,a[m]), se viziteaza p=p
0
si
se atribuie o noua valoare lui p adica p=p
0
dr. Acum subarborele drept
are mai put in de n noduri si conform ipotezei de induct ie se va traversa
arborele drept avand n nal n stiva nodurile a[1],...,a[m].
Aplicand propozit ia de mai sus, se intra n ciclul do cu radacina arborelui
si stiva vida, si se iese din ciclu cu arborele traversat si stiva vida.
Urmeaza programul.
#include <iostream.h
typedef struct nod { int inf; nod *st; nod *dr;} arbore;
arbore *rad;int n;
typedef struct nods{ arbore *inf; nods*ant;} stiva;
typedef struct{ arbore *arb; stiva *stiv;} arbstiv;
/******************************/
//Functia de punere in stiva
stiva *push(stiva*a, arbore *info)
{ stiva *r;
r=new stiva;
r-inf=info;
if(!a)r-ant=NULL;
else r-ant=a;
a=r;return a;}
/*******************************/
//Functia de scoatere din stiva si de vizitare a nodului
arbstiv pop(stiva *a)
{ arbstiv q;stiva *r;
if(!a)
cout<<Stiva vida <<endl;
else
{ q.arb=a-inf;r=a;
cout<<(a-inf)-inf<< ;
a=a-ant;delete r;q.stiv=a;}
return q;}
/************************************/
//Functia de creare a arborelui
76 CAPITOLUL 6. ARBORI BINARI
arbore *creare()
{ arbore *p; ;cinn;
if (n!=0)
{ p=new arbore;
p-inf=n;cout<<p-inf<<-st ;p-st=creare( );
cout<<p-inf<<-dr ;p-dr=creare( );return p;}
else
p=NULL;return p;}
/*************************************/
//Functia principala care realizeaza traversarea in inordine
void main()
{ stiva *a; arbore *p;arbstiv q;
cout<<Radacina<<endl;
p=creare();
a=NULL;
do{
while (p) { a=push(a,p);p=p-st;}
if (!a) break;
else { q=pop(a);p=q.arb;a=q.stiv;}
p=p-dr;}
while(p[[a);
6.1 Algoritmul lui Human
Algoritmul de codicare (compresie, compactare) 1nnn poarta numele
inventatorului sau, David Human, profesor la MIT. Acest algoritm este
ideal pentru compresarea (compactarea, arhivarea) textelor si este utilizat n
multe programe de compresie.
6.1.1 Prezentare preliminara
Algoritmul lui Human apart ine familiei de algoritmi ce realizeaza codicari
cu lungime variabila. Aceasta nseamna ca simbolurile individuale (ca
de exemplu caracterele ntr-un text) sunt nlocuite de secvent e de bit i (cu-
vinte de cod) care pot avea lungimi diferite. Astfel, simbolurilor care
se ntalnesc de mai multe ori n text (sier) li se atribuie o secvent a mai
scurta de bit i n timp ce altor simboluri care se ntalnesc mai rar li se
6.1. ALGORITMUL LUI HUFFMAN 77
atribuie o secvent a mai mare. Pentru a ilustra principiul, sa presupunem
ca vrem sa compactam urmatoarea secvent a :AEEEEBEEDECDD. Cum
avem 13 caractere (simboluri), acestea vor ocupa n memorie 13 8 = 104
bit i. Cu algoritmul lui Human, sierul este examinat pentru a vedea care
simboluri apar cel mai frecvent (n cazul nostru E apare de sapte ori, D
apare de trei ori iar A, B si C apar cate o data). Apoi se construieste (vom
vedea n cele ce urmeaza cum) un arbore care nlocuieste simbolurile cu
secvent e (mai scurte) de bit i. Pentru secvent a propusa, algoritmul va utiliza
substitut iile (cuvintele de cod) A = 111, B = 1101, C = 1100, D = 10,
E = 0 iar secvent a compactata (codicata) obt inuta prin concatenare va
111000011010010011001010. Aceasta nseamna ca s-au utilizat 24 bit i n loc
de 104, raportul de compresie ind 24/104 1/4.. Algoritmul de compresie
al lui Human este utilizat n programe de compresie ca pkZIP, lha, gz,
zoo, arj.
6.1.2 Coduri prex. Arbore de codicare
Vom considera n continuare doar codicarile n care nici-un cuvant nu este
prexul altui cuvant. Aceste codicari se numesc codicari prex. Codi-
carea prex este utila deoarece simplica atat codicarea (deci compactarea)
cat si decodicarea. Pentru orice codicare binara a caracterelor; se con-
cateneaza cuvintele de cod reprezentand ecare caracter al sierului ca n
exemplul din subsect iunea precedenta.
Decodicarea este de asemenea simpla pentru codurile prex. Cum nici
un cuvant de cod nu este prexul altuia, nceputul oricarui sier codicat
nu este ambiguu. Putem sa identicam cuvantul de cod de la nceput, sa
l convertim n caracterul original, sa-l ndepartam din sierul codicat si
sa repetam procesul pentru sierul codicat ramas.

In cazul exemplului
prezentat mai nainte sirul se desparte n
111 0 0 0 0 1101 0 0 10 0 1100 10 10,
secvent a care se decodican AEEEBEEDECDD. Procesul de decodicare
necesita o reprezentare convenabila a codicarii prex astfel ncat cuvantul
init ial de cod sa poata usor identicat. O astfel de reprezentare poate
data de un arbore binar de codicare, care este un arbore complet (adica
un arbore n care ecare nod are 0 sau 2 i), ale carui frunze sunt caracterele
date. Interpretam un cuvant de cod binar ca ind o secvent a de bit i obt inuta
78 CAPITOLUL 6. ARBORI BINARI
etichetand cu 0 sau 1 muchiile drumului de la radacina pana la frunza ce
cont ine caracterul respectiv: cu 0 se eticheteaza muchia ce uneste un nod cu
ul stang iar cu 1 se eticheteaza muchia ce uneste un nod cu ul drept.

In
gura (6.3) este prezentat arborele de codicare al lui Human corespunzator
exemplului nostru. Notand cu ( alfabetul din care fac parte simbolurile
(caracterele), un arbore de codicare are exact [([ frunze, una pentru ecare
litera (simbol) din alfabet si asa cum se stie din teoria grafurilor, exact [([ 1
noduri interne (notam cu [([ , cardinalul mult imii ().
Figura 6.3: Exemplu de arbore Human
Dandu-se un arbore T, corespunzator unei codicari prex, este foarte
simplu sa calculam numarul de bit i necesari pentru a codica un sier.
Pentru ecare simbol c (, e f (c) frecvent a (numarul de aparit ii) lui
c n sier si sa notam cu d
T
(c) adancimea frunzei n arbore. Numarul de
bit i necesar pentru a codica sierul este numit costul arborelui T si se
calculeaza cu formula
COST (T) =

cC
f (c) d
T
(c) .
6.1. ALGORITMUL LUI HUFFMAN 79
6.1.3 Construct ia codicarii prex a lui Human
Human a inventat un algoritm greedy care construieste o codicare pre-
x optima numita codul Human. Algoritmul construieste arborele core-
spunzator codicarii optime (numit arborele lui Human) pornind n ,
n . Se ncepe cu o mult ime de [([ frunze si se realizeaza o secvent a de
[([ 1 operat ii de ]..nn:. pentru a crea arborele nal.

In algoritmul scris n
pseudocod care urmeaza, vom presupune ca ( este o mult ime de n caractere si
ecare caracter c ( are frecvent a f (c). Asimilam ( cu o nn: constituita
din arbori format i dintr-o singura frunza.Vom folosi o stiva o formata din
noduri cu mai multe campuri; un camp pastreaza ca informat ie pe f (c), alt
camp pastreaza radacina c iar un camp suplimentar pastreaza adresa nodului
anterior (care indica un nod ce are ca informat ie pe f (c

) cu proprietatea ca
f (c) f (c

)). Extragem din stiva varful si nodul anterior (adica obiectele cu


frecvent a cea mai redusa) pentru a le face sa fuzioneze. Rezultatul fuzionarii
celor doua noduri este un nou nod care n campul informat iei are f (c)+f (c

)
adica suma frecvent elor celor doua obiecte care au fuzionat. De asemenea n
al doilea camp apare radacina unui arbore nou format ce are ca subarbore
stang, respectiv drept, arborii de radacini c si c

. Acest nou nod este introdus


n stiva dar nu pe pozit ia varfului ci pe pozit ia corespunzatoare frecvent ei
sale. Se repeta operat ia pana candn stiva ramane un singur element. Acesta
va avea n campul radacinilor chiar radacina arborelui Human.
Urmeaza programul n pseudocod. T inand cont de descrierea de mai sus
a algoritmului numele instruct iunilor programului sunt sucient de sugestive.
n [([
S C
cat timp (S are mai mult decat un nod)
{ z ALOCA-NOD( )
x stanga[z] EXTRAGE-MIN(S)
y dreapta[z] EXTRAGE-MIN(S)
f (z) f (x) +f (y)
INSEREAZA(S, z) }
returneaza EXTRAGE-MIN(S) .

In cazul exemplului deja considerat, avem f (E) = 7, f (D) = 3, f (A) =


f (B) = f (C) = 1. Putem, pentru comoditate sa etichetam frunzele nu cu
simbolurile corespunzatoare, ci cu frecvent ele lor.

In gura (6.4) prezentam
arborii aat i n nodurile stivei, dupa ecare repetare a instruct iunilor din
ciclul while. Se pleaca cu o padure de frunze, ordonata descrescator dupa
80 CAPITOLUL 6. ARBORI BINARI
Figura 6.4: Construirea arborelui Human
frecvent ele simbolurilor si se ajunge la arborele de codicare.
Prezentam mai jos si programul n C + + pentru algoritmul Human de
codicare prexata.
#include<iostream.h
#include<string.h
typedef struct nod{ char *symb; nod*st;nod*dr;} arbore;
typedef struct nods{ arbore*rad; nods*ant;int frecv;} stiva;
/***********************************************/
/*Functia de punere a unui nou nod in stiva ordonata dupa frecventa*/
stiva *push(stiva*varf, arbore *a, int f)
{ stiva *v,*p,*q;
v=new stiva; v-rad=a;v-frecv=f;v-ant=NULL;
if(!varf)
{ varf=v; return varf;}
for(p=varf,q=NULL;(p!=NULL)* (p-frecv< f);q=p,p=p-ant)
v-ant=p;
if(q!=NULL) q-ant=v;
else { varf=v;}
return varf;}
/************************************************/
6.1. ALGORITMUL LUI HUFFMAN 81
//Functia de stergere a varfului stivei
void pop(stiva *
{ stiva*r;
r=varf;
varf=varf-ant;
delete r;
return;}
/***********************************************/
//Functia de fuzionare a doi arbori
arbore*fuziune(arbore *p, arbore*q)
{ arbore*r;
r=new arbore;
r-symb=0

;
r-dr=p;r-st=q;
return r;}
/*********************************************/
//Functia de parcurgere in preordine a arborelui lui Human
//si de codicare a frunzelor
void rsd(arbore*rad, char*shot)
{ char frunza[60];
for (int i=0;i<60;i++)
frunza[i]=0

;
if (rad==NULL)return;
if(rad-symb!=NULL)
cout<<rad-symb<<:<<shot<<endl;
if(shot!=NULL)
strcpy(frunza,shot);
strcat(frunza,0);
rsd(rad-st,frunza);
if(shot!=NULL)
strcpy(frunza,shot);
strcat(frunza,1);
rsd(rad-dr,frunza);}
/********************************************/
//Functia de creare a unui arbore constand dintr-o singura
//frunza (radacina) care contine simbolul ce trebuie codicat
arbore *frunza(char *simb)
{ arbore*r;r=new arbore;
82 CAPITOLUL 6. ARBORI BINARI
r-symb=simb;r-st=NULL;r-dr=NULL; return r;}
/********************************************/
//Functia de parcurgere a stivei
void parcurge(stiva*s)
{ stiva*rr;rr=s;
if(!rr) cout<<Stiva vida<<endl;
else { cout<<Urmeaza stiva<<endl;
while(rr){
cout<<(rr-rad)-symb<< ;
rr=rr-ant;} }
cout<<endl;return;}
/********************************************/
//Functia principala
void main()
{ arbore *r1,*r2;int f1,f2; stiva *vf;
vf=new stiva; vf=NULL;
vf=push(vf,frunza(D),3);
vf=push(vf,frunza(A),1);
vf=push(vf,frunza(B),1);
vf=push(vf,frunza(C),1);
vf=push(vf,frunza(E),7);
parcurge(vf);
cout<<endl;
do
{ r1=vf-rad;f1=vf-frecv;pop(vf);
r2=vf-rad;f2=vf-frecv;pop(vf);
vf=push(vf,fuziune(r1,r2),f1+f2);
} while(vf-ant);
cout<<Urmeaza codicarea<<endl;
rsd(vf-rad-st,0);
rsd(vf-rad-dr,1);}
6.1.4 Optimalitatea algoritmului Human
1n. .. Un arbore de codicare T pentru alfabetul ( este .n daca
pentru orice alt arbore de codicare T

al aceluiasi alfabet avem


6.1. ALGORITMUL LUI HUFFMAN 83
COST (T) =

cC
f (c) d
T
(c) COST (T

) =

cC
f (c) d
T
(c) .
Vom demonstra n cele ce urmeaza ca algoritmul Human construieste
un arbore de codicare optim.
Lema 1. 1. T n n:h: n n.n: .n n: n|]nh| (. 1nn
f (c) < f (c

) nn. d
T
(c) d
T
(c

) .
1nn:n .. Sa presupunem prin absurd ca avem f (c) < f (c

) si
d
T
(c) < d
T
(c

) . Schimband ntre ele frunzele care cont in pe c si c

obt inem
un nou arbore de codicare cu costul
COST (T) f (c) d
T
(c) f (c

) d
T
(c

) +f (c) d
T
(c

) +f (c

) d
T
(c) =
= COST (T) (f (c) f (c

)) (d
T
(c) d
T
(c

)) < COST (T) ,


ceea ce contrazice ipoteza de optimalitate.
Lema 2. 1. ]:n | n.n.n f
1
. f
2
:n.nn: .nh|:.|:
c
1
. c
2
n.n n|]nh| (. .n. ..n n n:h: n n.n: .n n n:
]:n.| c
1
. c
2
n ]:n ..
1nn: .. Fie h nalt imea unui arbore de codicare optim T. Fie
un nod de adancime h 1 si c
i
si c
j
i sai, care evident sunt frunze. Sa
presupunem ca f (c
i
) f (c
j
) . Conform lemei precedente sau f (c
i
) = f
1
sau d
T
(c
i
) d
T
(c
1
) de unde d
T
(c
i
) = d
T
(c
1
) din alegerea lui .

In ambele
cazuri putem schimba ntre ele frunzele c
i
si c
1
fara a afecta costul arborelui.
La fel procedam cu c
2
si c
j
si obt inem un arbore de codicare optim n care
c
1
si c
2
sunt frat i.
Teorema 5. .|j:.n| 1nnn n:. n n:h: n n.n:
.n.
1nn:n . (prin induct ie dupa n = [([). Teorema este evidenta pen-
tru n 2. Sa presupunem ca n 3 si e T
Huff
arborele construit cu
algoritmul Human pentru frecvent ele f
1
f
2
... f
n
. Algoritmul aduna
frecvent ele f
1
si f
2
si construieste nodul corespunzator frecvent ei f
1
+f
2
. Fie
T

Huff
arborele construit cu algoritmul Human pentru mult imea de frecvent e
f
1
+f
2
, f
3
, ..., f
n
. Avem
COST (T
Huff
) = COST
_
T

Huff
_
+f
1
+f
2
,
84 CAPITOLUL 6. ARBORI BINARI
deoarece T
Huff
poate obt inut din T

Huff
nlocuind frunza corespunzatoare
frecvent ei f
1
+f
2
cu un nod intern avand ca i frunzele de frecvent e f
1
si f
2
.
De asemenea, conform ipotezei de induct ie T

Huff
este un arbore de codicare
optim pentru un alfabet de n 1 simboluri cu frecvent ele f
1
+ f
2
, f
3
, ..., f
n
.
Fie T
opt
un arbore de codicare optim satisfacand lema anterioara, adica
frunzele de frecvent e f
1
si f
2
sunt frat i n T
opt
. Fie T

arborele obt inut din


T
opt
prin nlocuirea frunzelor de frecvent e f
1
si f
2
si a tatalui lor cu o singura
frunza de frecvent a f
1
+f
2
. Atunci
COST (T
opt
) = COST (T

) +f
1
+f
2

COST
_
T

Huff
_
+f
1
+f
2
= COST (T
Huff
) ,
deoarece COST (T

) COST
_
T

Huff
_
conform ipotezei de induct ie. Rezulta
de aici
COST (T
opt
) = COST (T
Huff
) .
Capitolul 7
Tehnici de sortare
7.1 Heapsort
1n. .. Un vector A[1..n] este un heap (ansamblu) daca satisface pro-
prietatea de heap :
A[k/2|] A[k] , 2 k n.
Folosim notat ia | pentru a desemna partea ntreaga a unui numar real.
Un vector A care reprezinta un heap are doua atribute: |nj.n].] este
numarul elementelor din vector si n.nn.n-|n].] reprezinta numarul el-
ementelor heap-ului memoratn vectorul A. Astfel, chiar daca A[1..lungime[A]]
cont inen ecare element al sau date valide, este posibil ca elementele urmatoare
elementului .]n.nn.n-|n].]], unde n.nn.n-|n].] |nj.n].],
sa nu apart ina heap-ului.
Structurii de heap i se ataseaza n mod natural un arbore binar aproape
complet (gura (7.1)).
Fiecare nod al arborelui corespunde unui element al vectorului care cont ine
valorile atasate nodurilor. Radacina arborelui este A[1]. Dat ind un in-
dice i, corespunzator unui nod, se poate determina usor indicii nodului tata,
TATA(i) si ai ilor STANG(i) si DREPT(i):
indicele TATA(i)
returneaza i/2| (partea ntreaga a lui i/2),
indicele STANG(i)
returneaza 2i,
indicele DREPT(i)
85
86 CAPITOLUL 7. TEHNICI DE SORTARE
Figura 7.1: Exemplu de heap reprezentat sub forma unui arbore binar si sub
forma unui vector
returneaza 2i + 1.
Pentru orice nod i diferit de radacina este, n virtutea denit iei heap-ului,
adevarata proprietatea de heap:
A[TATA(i)] A[i].
Denim nn| .nn unui nod al arborelui ca ind numarul muchiilor celui
mai lung drum ce nu viziteaza tatal nodului si leaga nodul respectiv cu o
frunza. Evident, nalt imea arborelui este nalt imea radacinii.
7.1.1 Reconstituirea proprietat ii de heap
Funct ia ReconstituieHeap este un subprogram important n prelucrarea
heap-urilor. Datele de intrare sunt un vector A si un indice i. Atunci cand se
apeleaza ReconstituieHeap se presupune ca subarborii avand ca radacini
nodurile STANG(i) si DREPT(i) sunt heap-uri. Dar cum elementul A[i]
poate mai mic decat descendent ii sai, este posibil ca acesta sa nu respecte
proprietatea de heap. Sarcina funct iei ReconstituieHeap este sa -
]nn n heap valoarea A[i], astfel ncat subarborele care are n radacina
valoarea elementului de indice i, sa devina un heap.
7.1. HEAPSORT 87
Figura 7.2: Efectul funct iei ReconstituieHeap
Urmeaza funct ia scrisa n pseudocod.
ReconstituieHeap(A,i)
stSTANG(i)
drDREPT(i)
daca st dimensiune-heap[A] si A[st]A[i] atunci
maximst
altfel
maximi
daca dr dimensiune-heap[A] si A[dr]A[i] atunci
maximdr
daca maxim,=i atunci
schimba A[i]A[maxim]
ReconstituieHeap(A,maxim)

In gura (7.2) este ilustrat efectul funct iei ReconstituieHeap.

In gura (7.2, a) este desenata congurat ia init iala a heap-ului unde


A[2] nu respecta proprietatea de heap deoarece nu este mai mare decat
descendent ii sai. Proprietatea de heap este restabilita pentru nodul 2 n
gura (7.2, b) prin interschimbarea lui A[2] cu A[4], ceea ce anuleaza propri-
etatea de heap pentru nodul 4. Apeland recursiv ReconstituieHeap(A, 4)
se pozit ioneaza valoarea lui i pe 4. Dupa interschimbarea lui A[4] cu A[9]
88 CAPITOLUL 7. TEHNICI DE SORTARE
asa cum se vede n gura (7.2, c), nodul 4 ajunge la locul sau si apelul recur-
siv ReconstituieHeap(A, 9) nu mai gaseste elemente care nu ndeplinesc
proprietatea de heap.
7.1.2 Construct ia unui heap
Funct ia ReconstituieHeap poate utilizata de , n pentru transfor-
marea vectorului A[1..n] n heap.
Cum toate elementele subsirului A[n/2| + 1..n] sunt frunze, ele pot
considerate heap-uri formate din cate un element. Funct ia Construieste-
Heap pe care o prezentam n continuare traverseaza restul elementelor si
executa funct ia ReconstituieHeap pentru ecare nod ntalnit. Ordinea
de prelucrare a nodurilor satisface cerint a ca subarborii, avand ca radacina
descendent i ai nodului i sa formeze heap-uri nainte ca ReconstituieHeap
sa e executat pentru aceste noduri.
Urmeaza, n pseudocod funct ia ConstruiesteHeap:
dimensiune-heap[A]lungime[A]
pentru i lungime[A]/2|,1 executa
ReconstituieHeap(A,i).
Figura (7.2) ilustreaza modul de aplicare a funct iei ConstruiesteHeap.

In gura se vizualizeaza structurile de date (heap-urile) n starea lor an-


terioara apelarii funct iei ReconstituieHeap. (a) Se considera vectorul A
cu 10 elemente si arborele binar corespunzator. Se observa ca variabila de
control i a ciclului n momentul apelului funct iei ReconstituieHeap(A,i)
indica nodul 5. (b) reprezinta rezultatul; variabila de control i a ciclului
indica acum nodul 4. (c)-(e) vizualizeaza iterat iile succesive ale ciclului pen-
tru din ConstruiesteHeap. Se observa ca atunci cand se apeleaza funct ia
ReconstituieHeap pentru un nod dat, subarborii acestui nod sunt deja
heap-uri. (f) prezinta heap-ul nal.
7.1.3 Algoritmul heapsort
Algoritmul de sortare heapsort ncepe cu apelul funct iei Construieste-
Heap n scopul transformarii vectorului de intrare A[1..n] n heap. Deoarece
cel mai mare element al vectorului este atasat nodului radacina A[1], acesta
va ocupa locul denitiv n vectorul ordonat prin interschimbarea sa cu A[n].
Mai departe, excluzand din heap elementul de pe locul n, si micsorand cu 1
n.nn.n-|n].], restul de A[1..(n 1)] elemente se pot transforma usor
7.1. HEAPSORT 89
Figura 7.3: Model de execut ie a funct iei ConstruiesteHeap
90 CAPITOLUL 7. TEHNICI DE SORTARE
n heap, deoarece subarborii nodului radacina au proprietatea de heap, cu
eventuala except ie a elementului ajuns n nodul radacina. Pentru aceasta se
apeleaza funct ia ReconstituieHeap(A,1). Procedeul se repeta micsorand
dimensiunea heap-ului de la n 1 la 2.
Urmeaza, scris n pseudocod, algoritmul descris de funct ia Heapsort(A):
ConstruiesteHeap(A),
pentru ilungime[A],2 executa
schimba A[1]A[i]
dimensiune-heap[A]dimensiune-heap[A]-1
ReconstituieHeap(A,i)
Vom scrie programul heapsort si n C + +. Fat a de descrierea de mai
nainte a algoritmului, vor interveni cateva mici modicari datorate faptului
ca n C + + indexarea elementelor unui vector ncepe de la 0 si nu de la 1.
/**************************************/
#include<iostream.h
void ReconstituieHeap(int* A, int i, int dimensiune )
{ int a,maxim, stang=2*i+1,drept=stang+1;
if (stang<dimensiune&&A[stang]A[i]) maxim=stang;
else maxim=i;
if (drept<dimensiune&&A[drept]A[maxim]) maxim=drept;
if(maxim!=i){ a=A[i];A[i]=A[maxim];A[maxim]=a;
ReconstituieHeap(A,maxim,dimensiune); } }
/************************************/
void ConstruiesteHeap( int* A, int dimensiune )
{ int i;
for (i = (dimensiune - 2)/2; i = 0; i)
ReconstituieHeap(A, i, dimensiune);}
/***********************************/
void heapsort( int* A, int dimensiune )
{ int i, temp;
ConstruiesteHeap( A, dimensiune );
for (i = dimensiune-1; i =1; i) {
temp = A[i]; A[i] = A[0]; A[0]=temp;
dimensiune=dimensiune-1;
ReconstituieHeap( A,0,dimensiune );} }
/***********************************/
void main()
{ int N=10;
7.2. COZI DE PRIORIT

ATI 91
int A[10];
A[0]=10;A[1]=8;A[2]=6;A[3]=5;
A[4]=11;A[5]=5;A[6]=17;A[7]=9;A[8]=3;A[9]=21;
heapsort(A,N);
cout<< Sirul sortat (metoda heapsort)<<endl;
for(int i=0;i<N;i++) cout<<A[i]<< ; }
Timpul de execut ie
Funct ia ReconstituieHeap consta din comparat ii si interschimbari ale de
elemente aate pe nivele consecutive. Timpul de execut ie al funct iei va
deci de ordinul nalt imii arborelui binar asociat care este de ordinul O(ln n)
unde n este numarul de elemente din heap.
Funct ia ConstruiesteHeap apeleaza funct ia ReconstituieHeap de n
ori. Deducem de aici ca timpul de execut ie al funct iei ConstruiesteHeap
este de ordinul O(nln n). Funct ia heapsort apeleaza o data funct ia Con-
struiesteHeap si de n 1 ori funct ia ReconstituieHeap. Rezulta de aici
ca timpul de execut ie al funct iei heapsort este O(nln n) +(n1)O(ln n) =
O(nln n) .
7.2 Cozi de prioritat i
Vom prezentan cele ce urmeazanca o aplicat ie a not iunii de heap: utilizarea
lui sub forma de coada cu prioritat i.
nnn :.:.n . este o structura de date care cont ine o mult ime S de
elemente, ecare avand asociata o valoare numita |.. Asupra unei cozi cu
prioritat i se pot efectua urmatoarele operat ii:
Insereaza(S, x) insereaza elementul x n mult imea S.Aceasta operat ie
este scrisa n felul urmator S S x .
ExtrageMax(S) elimina si ntoarce elementul din S avand cheia cea mai
mare.
O aplicat ie a cozilor de prioritat i o constituie planicarea lucrarilor pe
calculatoare partajate. Lucrarile care trebuie efectuate si prioritat ile rela-
tive se memoreaza ntr-o coada de prioritat i. Cand o lucrare este terminata
sau ntrerupta, funct ia ExtrageMax va selecta lucrarea avand prioritatea
cea mai mare dintre lucrarile n asteptare. Cu ajutorul funct iei Insereaza
oricand se introduce n coada o sarcina noua.
92 CAPITOLUL 7. TEHNICI DE SORTARE

In mod natural o coada cu prioritat i poate implementata utilizand un


heap. Funct ia Insereaza(S, x) insereaza un element n heap-ul S. La prima
expandare a heap-ului se adauga o frunza arborelui. Apoi se traverseaza un
drum pornind de la aceasta frunza catre radacina n scopul gasirii locului
denitiv al noului element.
Urmeaza instruct iunile funct iei Insereaza(S,x) n pseudocod.
dimensiune-heap[S]dimensiune-heap[S]+1
i dimensiune-heap[S]
cat timp i1 si S[TATA(i)]<cheie executa
S[i] S[TATA(i)]
iTATA[i]
S[i]cheie.
ExtrageMax(S) este asemanatoare funct iei Heapsort, instruct iunile
sale n pseudocod ind:
daca dimensiune-heap[S]<1 atunci
eroare depasire inferioara heap;
max S[1]
S[1] S[dimensiune-heap[S]]
dimensiune-heap[S]dimensiune-heap[S]-1
ReconstituieHeap(S,1)
returneaza max.
Urmeaza scris in C + + un program care implementeaza o coada cu pri-
oritat i.
#include<iostream.h
/*******************************************/
void ReconstituieHeap(int* A, int i, int dimensiune )
{ int a,maxim, stang=2*i+1,drept=stang+1;
if (stang<dimensiune&&A[stang]A[i]) maxim=stang;
else maxim=i;
if (drept<dimensiune&&A[drept]A[maxim])
maxim=drept;
if(maxim!=i){ a=A[i];A[i]=A[maxim];A[maxim]=a;
ReconstituieHeap(A,maxim,dimensiune); } }
/******************************************/
int ExtrageMax( int* A, int dim)
{ int max;
if(dim<0) cout<<Depasire inferioara heap<<endl;
max=A[0];
7.2. COZI DE PRIORIT

ATI 93
A[0]=A[dim];
ReconstituieHeap( A,0,dim- - );
return max;}
/******************************************/
void Insereaza(int* A, int cheie, int dimensiune )
{ int i,tata;
i=dimensiune+1;
A[i]=cheie;tata=(i-1)/2;
while(i0&&A[tata]<cheie)
{ A[i]=A[tata];i=tata;A[i]=cheie;tata=(i-1)/2;} }
/***********************************************/
void main() {
char x,y;int cheie,dimensiune,max, S[100];
cout<<Indicati primul element<<endl;
cinmax;
S[0]=max;
dimensiune=0;
cout<<Intrerupem?[d/n]<<endl;
cinx;
while(x!=d){
cout<<Extragem sau inseram[e/i]<<endl;
ciny;
switch (y)
{ case e:
max=ExtrageMax( S,dimensiune );
dimensiune=dimensiune-1;
if (dimensiune=0) { cout<<heap-ul ramas este:<<endl;
for (int i=0;i<=dimensiune;i++) cout<<S[i]<< ;
cout<<endl;
cout<<Elementul maxim este = <<max<<endl;
cout<<dimensiunea<<dimensiune<<endl;}
break;
default:cout<<Introduceti cheia<<endl;
cincheie; Insereaza(S,cheie,dimensiune);
dimensiune=dimensiune+1;
cout<<dimensiunea<<dimensiune<<endl;
cout<<heap-ul este:<<endl;
for (int i=0;i<=dimensiune;i++) cout<<S[i]<< ;
94 CAPITOLUL 7. TEHNICI DE SORTARE
cout<<endl;}
cout<<Intrerupem?[d/n]<<endl;
cinx;} }
7.3 Sortarea rapida
Sortarea rapida este un algoritm care sorteaza pe loc (n spat iul alocat sirului
de intrare). Cu acest algoritm, un sir de n elemente este sortat ntr-un
timp de ordinul O(n
2
), n cazul cel mai defavorabil. Algoritmul de sortare
rapida este deseori cea mai buna solut ie practica deoarece are o comportare
medie remarcabila: timpul sau mediu de execut ie este O(nln n) si constanta
ascunsa n formula O(nln n) este destul de mica.
7.3.1 Descrierea algoritmului
Ca si algoritmul de sortare prin interclasare, algoritmul de sortare rapida
ordoneaza un sir A[p..r] folosind tehnica n..n . n nn.
Divide : Sirul A[p..r] este rearanjat n doua subsiruri nevide A[p..q] si
A[q + 1..r] astfel ncat ecare element al subsirului A[p..q] sa e mai mic sau
egal cu ecare element al subsirului A[q + 1..r] . Indicele q este calculat de
procedura de partit ionare.
Stapaneste: Cele doua subsiruri A[p..q] si A[q + 1..r] sunt sortate prin
apeluri recursive ale aalgoritmului de sortare rapida.
Combina: Deoarece cele doua subsiruri sunt sortate pe loc, sirul A[p..r] =
A[p..q] A[q + 1..r] este ordonat.
Algoritmul (intitulat QUICKSORT(A, p, r) )n pseudocod este urmatorul:
QUICKSORT(A, p, r):
//urmeaza algoritmul
daca p < r atunci
q PARTITIE(A, p, r)
QUICKSORT(A, p, q)
QUICKSORT(A, q + 1, r).
Pentru ordonarea sirului Ase apeleaza QUICKSORT(A, 1,lungime[A]).
O alta funct ie a algoritmului este funct ia PARTITIE(A, p, r) care rear-
anjeaza pe loc sirul A[p..r] , returnand si indicele q ment ionat mai sus:
PARTITIE(A, p, r)
//urmeaza funct ia
7.3. SORTAREA RAPID

A 95
x A[p]
i p
j r
cat timp i<j executa
{ repeta
jj-1
pana cand A[j]x
repeta
ii+1
pana cand A[i]x
daca i<j atunci
interschimba A[i] A[j]; jj-1}
returneaza j.
Urmeaza programul scris n C + + :
#include <iostream.h
/*************************/
int Partitie(int*A, int p, int r) { int x,y,i,j;
x=A[p];
i=p;
j=r;
while(i<j)
{ while(A[j]x)j- - ;
while (A[i]<x) i+ +;
if(i<j){ y=A[i];A[i]=A[j];A[j]=y;j- -;} }
return j;}
/*****************************/
void Quicksort(int *A, int p, int r)
{ int q;
if (p<r)
{ q= Partitie(A,p,r);
Quicksort(A,p,q);
Quicksort(A,q+1,r); } }
/******************************/
void main()
{ int *A,n;
cout<<Introduceti numarul de elemente<<endl;
cinn;
A=new int[n];
96 CAPITOLUL 7. TEHNICI DE SORTARE
for(int i=0;i<n;i++)
{ cout<<A[<<i<<]=;cin*(A+i);}
Quicksort(A,0,n-1);
for(i=0;i<n;i++)
cout<< A[<<i<<]=<<A[i];}
7.3.2 Performant a algoritmului de sortare rapida
Timpul de execut ie al algoritmului depinde de faptul ca partit ionarea este
echilibrata sau nu.
Cazul cel mai defavorabil
Vom demonstra ca cea mai defavorabila comportare a algoritmului de sortare
rapida apare n situat ia n care procedura de partit ionare produce un vector
de n 1 elemente si altul de 1 element. Mai ntai observam ca timpul
de execut ie al funct iei Partitie este O(n) . Fie T (n) timpul de execut ie
al algoritmului de sortare rapida. Avem pentru partit ionarea amintita mai
nainte, formula de recurent a
T (n) = T (n 1) +O(n) ,
de unde rezulta
T (n) =
n

k=1
O(k) = O
_
n

k=1
k
_
= O
_
n
2
_
,
adica, pentru un c > 0 sucient de mare,
T (n) cn
2
. (7.1)
Vom arata prin induct ie ca estimarea (7.1) este valabila pentru orice
partit ionare.
Sa presupunem ca partit ionare produce subvectori de dimensiuni q si nq.
Cu ipoteza de induct ie avem
T (n) = T (q) +T (n q) +O(n)
c max
1qn1
_
q
2
+ (n q)
2
_
+O(n) = c
_
n
2
2n + 2
_
+O(n) cn
2
.
7.3. SORTAREA RAPID

A 97
Cazul cel mai favorabil
Daca funct ia de partit ionare produce doi vectori de n/2 elemente, algoritmul
de sortare rapida lucreaza mult mai repede. Formula de recurent a n acest
caz
T (n) = 2T (n/2) +O(n) ,
conduce, dupa cum s-a aratat n cazul algoritmului de insert ie prin inter-
clasare la un timp de execut ie de ordinul O(nln n) .
Estimarea timpului de execut ie mediu
Timpul de execut ie mediu se denet e prin induct ie cu formula
T (n) =
1
n
n1

q=1
(T (q) +T (n q)) +O(n) .
Presupunem ca
T (n) anln n +b,
pentru un a > 0 si b > T (1) .
Pentru n > 1 avem
T (n) =
2
n
n1

k=1
T (k) +O(n)
2
n
n1

k=1
(ak ln k +b) +O(n) =
=
2a
n
n1

k=1
k ln k +
2b
n
(n 1) +O(n) .
Tinand cont de inegalitatea
n1

k=1
k ln k
1
2
n
2
ln n
1
8
n
2
,
obt inem
T (n)
2a
n
_
1
2
n
2
ln n
1
8
n
2
_
+
2b
n
(n 1) +O(n)
anln n
a
4
n + 2b +O(n) = anln n +b +
_
O(n) +b
a
4
n
_
anln n +b,
deoarece valoarea lui a poate aleasa astfel ncat
an
4
sa domine expresia
O(n) +b. Tragem deci concluzia ca timpul mediu de execut ie a algoritmului
de sortare rapida este O(nln n) .
98 CAPITOLUL 7. TEHNICI DE SORTARE
7.4 Metoda bulelor (bubble method)
Principiul acestei metode de sortare este urmatorul: pornind de la ultimul
element al sirului catre primul, se schimba ntre ele ecare element cu cel
anterior, daca elementul anterior (de indice mai mic) este mai mare.

In
felul acesta primul element al sirului este cel mai mic element. Se repeta
procedura pentru sirul format din ultimele n1 elemente si asa mai departe,
obt inandu-se n nal sirul de n elemente ordonat. Numarul de comparat ii si
deci timpul de execut ie este de ordinul O(n
2
) .
Urmeaza programul scris n C + +.
#include <iostream.h
//returneaza p,q in ordine crescatoare
/*****************************/
void Order(int *p,int *q) {
int temp;
if(*p*q) { temp=*p; *p=*q; *q=temp; } }
//Bubble sorting
void Bubble(int *a,int n)
{ int i,j;
for (i=0; i<n; i++)
for (j=n-1; i<j; j)
Order(&a[j-1],&a[j]);}
//functia principala
void main()
int i,n=10; static int a[] = { 7,3,66,3,-5,22,-77,2,36,-12} ;
cout<<Sirul initial<<endl;
for(i=0; i<n; i++)
cout<<a[i]<< ;cout<<endl;
Bubble(a,n);
cout<<Sirul sortat<<endl;
for(i=0; i<n; i++)
cout<<a[i]<< ;cout<<endl;
//Rezultatele obtinute
* Sirul initial * 7 3 66 3 -5 22 -77 2 36 -12 *
* Sirul sortat * -77 -12 -5 2 3 3 7 22 36 66 *
Capitolul 8
Tehnici de cautare
8.1 Algoritmi de cautare
Vom presupune ca n memoria calculatorului au fost stocate un numar de n
nregistrari si ca dorim sa localizam o anumita n:j.:n:. Ca si n cazul
sortarii , presupunem ca ecare nregistrare cont ine un camp special, numit
|.. Colect ia tuturor nregistrarilor se numeste nh| sau .:. Un grup
mai mare de siere poarta numele de hn.n n nn.

In cazul unui n|j:.n n nn: se considera un anumit argument K, iar


problema este de a cauta acea nregistrare a carei cheie este tocmai K. Sunt
posibile doua cazuri: nn:n {:.n), candnregistrarea cu cheia
avuta n vedere este depistata, sau nn:n ]n:n {n:.n), cand
cheia niciunei nregistrari nu coincide cu cheia dupa care se face cautarea.
Dupa o cautare fara succes, uneori este de dorit sa inseram o nouanregistrare,
cont inand cheia K n tabel; metoda care realizeaza acest lucru se numeste
algoritmul de nn: . .n: ..
Desi scopul cautarii este de a aa informat ia din nregistrarea asociata
cheii K, algoritmii pe care i vom prezenta n continuare, t in n general cont
numai de cheie, ignorand celelalte campuri.

In multe programe cautarea este partea care consuma cel mai mult timp,
de aceea folosirea unui bun algoritm de cautare se reecta n sporirea vitezei
de rulare a programului.
Exista de asemenea o importanta interact iune ntre sortare si cautare,
dupa cum vom vedea n cele ce urmeaza.
99
100 CAPITOLUL 8. TEHNICI DE C

AUTARE
8.1.1 Algoritmi de cautare secvent iala (pas cu pas)
Algoritmul S (cautare secvent iala). Fiind dat un tabel de nregistrari
R
1
, R
2
, ..., R
n
, n 1, avand cheile corespunzatoare K
1
, K
2
, ..., K
n
, este cautata
nregistrarea corespunzatoare cheii K. Vom mai introduce o nregistrare c-
tiva R
n+1
cu proprietatea ca valoarea cheii K
n+1
este atat de mare ncat K nu
va capata nici-o data aceasta valoare (punem formal K
n+1
= ). Urmeaza
algoritmul scris n pseudocod
i0
Executa
ii+1
Cat timp in si K ,= K
i
Daca K = K
i
cautarea a avut succes
Altfel, cautarea nu a avut succes.

In cazul n care cheile sunt ordonate crescator, o varianta a algoritmului


de cautare secvent iala este
Algoritmul T (cautare secvent iala ntr-un tabel ordonat):
i0
Executa
ii+1
Cat timp in si K K
i
Daca K = K
i
cautarea a avut succes
Altfel, cautarea nu a avut succes.
Sa notam cu p
i
probabilitatea ca sa avem K = K
i
, unde p
1
+p
2
+...+p
n
=
1. Daca probabilitat ile sunt egale, n medie, algoritmul S consuma acelasi
timp ca si algoritmul T pentru o cautare cu succes. Algoritmul T efectueaza
nsa n medie de doua ori mai repede cautarile fara succes.
Sa presupunem mai departe ca probabilitat ile p
i
nu sunt egale. Timpul
necesar unei cautari cu succes este proport ional cu numarul de comparat ii,
care are valoarea medie
C
n
= p
1
+ 2p
2
+... + 3p
n
.
Evident, C
n
ia cea mai mica valoare atunci cand p
1
p
2
... p
n
, adica
atunci cand cele mai utilizate nregistrari apar la nceput. Daca p
1
= p
2
=
... = p
n
= 1/n, atunci
C
n
=
n + 1
2
.
8.1. ALGORITMI DE C

AUTARE 101
O repartit ie interesanta a probabilitat ilor este |jn |. Z.] care a observat ca
n limbajele naturale, cuvantul aat pe locul n n ierarhia celor mai utilizate
cuvinte apare cu o frecvent a invers proport ionala cu n:
p
1
=
c
1
, p
2
=
c
2
, ..., p
n
=
c
n
, c =
1
H
n
, H
n
= 1 +
1
2
+... +
1
n
.
Daca legea lui Zipf guverneaza frecvent a cheilor ntr-un tabel, atunci
C
n
=
n
H
n
iar cautarea ntr-o astfel de circumstant a, este de circa
1
2
ln n ori mai rapida
decat cautarea n cazul general.
8.1.2 Cautarea n tabele sortate (ordonate)

In cele ce urmeaza vom discuta algoritmi de cautare pentru tabele ale caror
chei sunt ordonate. Dupa compararea cheii date K cu o cheie K
i
a tabelului,
cautarea continua n trei moduri diferite dupa cum K < K
i
, K = K
i
sau
K > K
i
. Sortarea tabelelor (listelor) este recomandabila n cazul cautarilor
repetate. De aceea n aceasta subsect iune vom studia metode de cautare n
tabele ale caror chei sunt ordonate K
1
< K
2
< ... < K
n
. Dupa compararea
cheilor K si K
i
ntr-o tabela ordonata putem avea K < K
i
(caz n care
R
i
, R
i+1
, ..., R
n
nu vor mai luate n considerat ie), K = K
i
(n acest caz
cautarea se termina cu succes) sau K > K
i
(caz n care R
1
, R
2
, ..., R
i
nu vor
mai luate n considerat ie).
Faptul ca o cautare, chiar fara succes duce la eliminarea unora din cheile
cu care trebuie comparata K, duce la o ecientizare a cautarii.
Vom prezenta mai departe un algoritm general de cautare ntr-un tabel
sortat. Fie S = K
1
< K
2
< ... < K
n
stocata ntr-un vector K [1..n], adica
K [i] = K
i
si e o cheie a. Pentru a decide daca a S, comparam a cu un
element al tabelului si apoi continuam cu partea superioara sau cea inferioara
a tabelului. Algoritmul (numit n cele ce urmeaza algoritmul B) scris n
pseudocod este:
prim1
ultimn
urmatorun ntreg n intervalul [prim,ultim]
executa
102 CAPITOLUL 8. TEHNICI DE C

AUTARE
{ daca a<K[urmator] atunci ultimurmator-1
altfel primprim+1
urmatorun ntreg n intervalul [prim,ultim]}
cat timp a,=K[urmator] si ultim prim
daca a=K[urmator] atunci avem nn:
altfel avem nn: ]n:n .

In cazul n care un ntreg n intervalul [prim,ultim]=prim spunem


ca avem o nn: |.n.n:n.
Daca unntregn intervalul [prim,ultim]=(prim+ultim)/2| spunem
ca avem o nn: h.nn:n.
Amintim ca folosim notat ia | pentru partea ntreaga a unui numar, n
timp ce | are urmatoarea semnicat ie
a| =
_
a dac a a este ntreg,
a| + 1 dac a a nu este ntreg.
Prezentamn continuare un proiect pentru cautarea ntr-o lista ordonata
(cu relat ia de ordine lexicograca) de nume, pentru a aa pe ce pozit ie se aa
un nume dat. Proiectul cont ine programele cautare.cpp, fcautare.cpp (n
care sunt descrise funct iile folosite de cautare.cpp) si sierul de nume scrise
n ordine lexicograca search.dat.
/******************cautare.cpp**********/
#include <stdio.h
#include <string.h
#include <io.h
typedef char Str20[21]; //Nume de lungime 20
extern Str20 a[], cheie; //vezi fcautare.cpp
char DaNu[2], alegere[2];
int marime, unde, cate;
void GetList() {
FILE *fp;
fp=fopen(search.dat,r);
marime=0;
while (!feof(fp)) {
fscanf(fp, s, a[marime]);
marime++;
}
fclose(fp);
8.1. ALGORITMI DE C

AUTARE 103
printf(numarul de nume in lista = %dn

, marime
//functii implementate in fcautare.cpp
void GasestePrimul(int, int *);
void GasesteToate(int, int *);
void Binar(int, int, int *);
void main() {
GetList(); // citeste numele din sier
strcpy(DaNu,d);
while (DaNu[0]==d)
printf( Ce nume cautati? ); scanf(%s, cheie);
//Se cere tipul cautarii
printf( Secventiala pentru (p)rimul, (t)oate, ori (b)inara? );
scanf(%s,alegere);
switch(alegere[0]) {
case t:
GasesteToate(marime,
if (cate0)
printf(%d aparitii gasite.n

, cate);
else
printf( %s nu a fost gasit.n

, cheie);
break;
case b:
Binar(0,marime-1,
if (unde0)
printf( %s gasit la pozitia %d.n

, cheie, unde);
else
printf( %s nu a fost gasit.n

, cheie);
break;
case p:
GasestePrimul(marime,
if (unde0)
printf( %s gasit la pozitia %d.n

, cheie, unde);
else
printf( %s nu a fost gasit.n

, cheie);
printf( Inca o incercare (d/n)? ); scanf(%s, DaNu);
}
}
/******fcautare.cpp****************************/
104 CAPITOLUL 8. TEHNICI DE C

AUTARE
/******************************************
!* Functii de cautare intr-o lista de nume *
!* ce urmeaza a folosite de programul Cautare.cpp. *
/*****************************************/
#include <string.h
#include <stdio.h
typedef char Str20[21]; //Nume de lungimea 20
Str20 a[100], cheie;
void GasestePrimul(int marime, int *unde) {
// Cautare secventiala intr-o lista de nume pentru
// a aa prima aparitie a cheii.
int iun;
iun=0;
while (strcmp(a[iun],cheie)!=0
if (strcmp(a[iun],cheie)!=0) iun=-1;
*unde = iun+1;
}
void GasesteToate(int marime, int *cate) {
// Cautare secventiala intr-o lista de nume pentru
// a aa toate aparitiile cheii.
int cat, i;
cat=0;
for (i=0; i<marime; i++)
if (strcmp(a[i],cheie)==0) {
printf( %s pe pozitia %d.n

, cheie, i + 1);
cat++;
}
*cate = cat;
}
void Binar(int prim, int ultim, int *unde) {
// Cautare binara intr-o lista ordonata pentru o aparitie
// a cheii specicate. Se presupune ca prim<ultim.
int urmator, pr, ul, iun;
pr=prim; ul=ultim; iun=-1;
while (pr <= ul
urmator=(pr+ul) / 2;
if (strcmp(a[urmator],cheie)==0)
iun=urmator;
8.1. ALGORITMI DE C

AUTARE 105
else if (strcmp(a[urmator],cheie) 0)
ul=urmator-1;
else
pr=urmator+1; }
*unde = iun+1;
}
8.1.3 Arbori de decizie asociat i cautarii binare
Pentru a nt elege mai bine ce se ntampla n cazul algoritmului de cautare
binara, vom construi n:h:| n n... n.n nn:.. h.nn:.
Arborele binar de decizie corespunzator unei cautari binare cu nnregistrari
poate construit dupa cum urmeaza:
Daca n = 0, arborele este format din frunza [0]. Altfel nodul radacina este
n/2| , subarborele stang este arborele binar construit asemanator cu n/2|
1 noduri iar subarborele drept este arborele binar construit asemanator cu
n/2| noduri si cu indicii nodurilor incrementat i cu n/2| . Am avut n vedere
numai nodurile interne corespunzatoare unei cautari cu succes.
Prezentam mai jos un arbore de cautare binara pentru n = 16 (gura
8.1).
Figura 8.1: Arbore de cautare binara
106 CAPITOLUL 8. TEHNICI DE C

AUTARE
Prima comparat ie efectuata este K : K
8
care este reprezentata de nodul
(8) din gura. Daca K < K
8
, algoritmul urmeaza subarborele stang iar daca
K > K
8
, este folosit subarborele drept. O cautare fara succes va conduce la
una din frunzele numerotate de la 0 la n; de exemplu ajungem la frunza [6]
daca si numai daca K
6
< K < K
7
.
8.1.4 Optimalitatea cautarii binare
Vom face observat ia ca orice arbore binar cu n noduri interne (etichetate
cu (1) , (2) , (3) , ... (n))si deci n + 1 frunze (etichetate cu [0] , [1] , [2] ,
... [n 1] , [n]), corespunde unei metode valide de cautare ntr-un tabel
ordonat daca parcurs n ordine simetrica obt inem [0] (1) [1] (2) [2] (3)
... [n 1] (n) [n] . Nodul intern care corespunde n Algoritmul B lui
urmator[prim,ultim] va radacina subarborelui care are pe [ultim] drept
cea mai din dreapta frunza iar pe [prim] drept cea mai din stanga frunza.
De exemplu n gura 8.2, a) urmator[0,4]=2, urmator[3,4]=4, pe cand
n gura 8.2, b) urmator[0,4]=1, urmator[3,4]=4.
Figura 8.2: Arbori de cautare
Vom demonstra n cele ce urmeaza ca ntre arborii de decizie asociat i
algoritmului B de cautare, cei optimi sunt arborii corespunzatori cautarii
binara. Vom introduce mai ntai doua numere ce caracterizeaza arborele T:
8.1. ALGORITMI DE C

AUTARE 107
E (T) , |nj.nn n:n:.|: .:n ale arborelui reprezinta suma lungimilor
drumurilor (numarul de muchii) care unesc frunzele arborelui cu radacina iar
I (T) , |nj.nn n:n:.|: .n:n ale arborelui reprezinta suma lungimilor
drumurilor care unesc nodurile interne cu radacina. De exemplu n gura
8.2, a) E (T) = 2 + 2 + 2 + 3 + 3 = 12, I (T) = 1 + 1 + 2 = 4 iar n gura
8.2, b) E (T) = 1 + 2 + 3 + 4 + 4 = 14, I (T) = 1 + 2 + 3 = 6.

In continuare
ne va necesara
Lema 3. 1nn T n n:h: h.nn: n| n nnn N ]:n.. nn.
E (T) n.n.n nnn . nnn. nnn n ]:n.| |. T nn | n|
nn n.| n. { 2
q
N ]:n. n.|| q 1 . 2N 2
q
]:n.
n.|| q. nn q = log
2
N| , n.|| :nnn.n.. .nn 0).
1nn:n .. Sa presupunem ca arborele binar T are frunzele u si v (e
x tatal lor), pe nivelul L iar frunzele y si z pe nivelul l astfel ca L l 2.
Vom construi un alt arbore binar T
1
(vezi gura 8.3) transferand pe u si v
Figura 8.3: Optimizarea lungimii drumurilor externe
pe nivelul l + 1 ca i ai lui y; x devine frunza iar y nod intern. Rezulta ca
E (T) E (T
1
) = 2L (L 1) +l 2 (l + 1) = L l 1 1,
si deci T nu poate avea o lungime a drumurilor externe minima. Deci T are
toate frunzele pe un singur nivel daca N este o putere a lui 2 sau, altfel, pe
doua nivele consecutive q 1 si q. unde q = log
2
N| .
108 CAPITOLUL 8. TEHNICI DE C

AUTARE
Medoda de construire a arborilor binari de decizie asociat algoritmului B
conduce la
Lema 4. 1nn 2
k1
n < 2
k
, nn: ]|.nn n|j:.n|
B n.n | n| k nn:n ... 1nn n = 2
k
1, nn: ]n:n
n.n k nn:n .. .n: nnn 2
k1
n < 2
k
1. nn: ]n:n
n.n n k 1 n k nn:n ... .nn nnnnn n n: n = 2
k
1
n ]:n.| n:h:|. h.nn: n.n n|j:.n|. B n n.|| k .n:
n: 2
k1
n < 2
k
1 ]:n.| jn n.|| k 1 . k.
1nn:n .. Lema este adevarata pentru k = 1. Fie k 2 si sa
presupunem (ipoteza de induct ie) ca teorema este adevarata pentru orice
2
k1
n < 2
k
. Daca 2
k
n < 2
k+1
vom avea doua cazuri: (I) n este impar,
adica n = 2p + 1; (II) n este par adica n = 2p, unde p N, p 1.
Cazul (I): Radacina arborelui binar T, corespunzator algoritmului B are
eticheta p + 1 iar subarborele stang T
l
si subarborele drept T
r
cont in cate p
noduri interne. Din relat ia
2
k
2p + 1 < 2
k+1
,
rezulta ca
2
k1
p < 2
k
.
Aplicand ipoteza de induct ie ambilor subarbori T
l
si T
r
avemh(T
l
) = h(T
r
) =
k deci h(T) = k + 1, nalt imea lui T ind numarul maxim de comparat ii
ale cheilor efectuate de algoritmul B n cazul unei cautari cu succes. Daca
p = 2
k
1 toate frunzele lui T
l
si T
r
se aa pe nivelul k, deci daca n =
2p + 1 = 2
k+1
1 toate frunzele lui T se vor aa pe nivelul k + 1. Daca
2
k1
p < 2
k
1, ceea ce implica 2
k
n < 2
k+1
1, cum (cu ipoteza de
induct ie) atat T
l
cat si T
r
au frunzele pe nivelele k 1 sau k, deducem ca T
va avea frunzele pe nivelele k sau k + 1.
Cazul (II):

In acest caz radacina arborelui binar are eticheta p, T
l
are
p 1 noduri interne iar T
r
are p noduri interne. Cum 2
k
2p < 2
k+1
rezulta
ca 2
k1
p < 2
k
. Avem de asemenea 2
k1
p 1 < 2
k
n afara cazului
cand p = 2
k1
si deci n = 2
k
.

In acest ultim caz arborele T este asemanator
cu arborele din gura 8.1: el are h(T) = k + 1, N 1 frunze pe nivelul k si
doua frunze pe nivelul k + 1; teorema a fost demonstrata direct n aceasta
circumstant a (n = 2
k
). Ne mai ramane sa consideram cazul 2
k
< n < 2
k+1
.
Cu ipoteza de induct ie deducem ca h(T
l
) = h(T
r
) = k deci h(T) = k +1 iar
algoritmul B necesita cel mult k + 1 comparat ii pentru o cautare cu succes.
8.1. ALGORITMI DE C

AUTARE 109
Cum n este par, n ,= 2
s
1, s 1, avem de aratat ca frunzele lui T se aa
pe nivelele k sau k + 1. Aceasta rezulta din aplicarea ipotezei de induct ie la
subarborii T
l
si T
r
care au p 1 respectiv p noduri interne.

Intr-adevar, cum
p 1 ,= 2
k
1 rezulta ca frunzele lui T
l
se aa pe nivelele k 1 sau k. Pentru
T
r
toate frunzele se aa e pe nivelul k (daca p = 2
k
1) e pe nivelele k 1
sau k. Rezulta ca frunzele lui T se aa pe nivelele k sau k + 1.
Deducem ca numarul de comparat ii n cazul unei cautari (cu sau fara
succes) este de cel mult log
2
N| + 1.
Vom demonstra n continuare
Lema 5. 1n: :. n:h: h.nn: n| T n.]nn :|n .n
E (T) = I (T) + 2N,
nn N ::..nn nnn:| n nn:. .n:n n| |. T.
1nn:n .. Sa presupunem ca arborele binar T are
j
noduri interne
si
j
noduri externe (frunze) la nivelul j; j = 0, 1, ... (radacina este la nivelul
0). De exemplu n gura 8.2, a) avem (
0
,
1
,
2
, ...) = (1, 2, 1, 0, 0, ...) ,
(
0
,
1
,
2
, ...) = (0, 0, 3, 2, 0, 0, ...) iar n gura 8.2, b) avem (
0
,
1
,
2
, ...) =
(1, 1, 1, 1, 0, 0, ...) , (
0
,
1
,
2
, ...) = (0, 1, 1, 1, 2, 0, 0, ...) .
Consideram funct iile generatoare asociate acestor siruri
A(x) =

j=0

j
x
j
, B(x) =

j=0

j
x
j
,
unde numai un numar nit de termeni sunt diferit i de 0. Este valabila relat ia
2
j1
=
j
+
j
, j = 0, 1, ...,
deoarece toate cele
j1
noduri interne de la nivelul j 1 au ecare n parte
cate 2 i pe nivelul k si numarul total al acestor i este
j
+
j
. Rezulta de
aici ca
A(x) +B(x) =

j=0
(
j
+
j
) x
j
=
0
+
0
+

j=1
(
j
+
j
) x
j
=
= 1 + 2

j=1

j1
x
j
= 1 + 2x

j=1

j1
x
j1
= 1 + 2x

j=0

j
x
j
,
adica
A(x) +B(x) = 1 + 2xA(x) . (8.1)
110 CAPITOLUL 8. TEHNICI DE C

AUTARE
Pentru x = 1 se obt ine B(1) = 1 + A(1), dar B(1) =

j=0

j
este
numarul de frunze ale lui T iar A(1) =

j=0

j
este numarul de noduri
interne, deci numarul de noduri interne este cu 1 mai mic decat numarul de
nodduri externe. Derivand relat ia (8.1) obt inem
A

(x) +B

(x) = 2A(x) + 2xA

(x) ,
B

(1) = 2A(1) +A

(1) .
Cum A(1) = N, A

(1) =

j=0
j
j
= I (T) , B

(1) =

j=0
j
j
= E (T) ,
deducem relat ia
E (T) = I (T) + 2N. (8.2)
Reprezentarea sub forma de arbore binar a algoritmului binar de cautare
B, ne sugereaza cum sa calculam ntr-un mod simplu numarul mediu de
comparat ii. Fie C
N
numarul mediu de comparat ii n cazul unei cautari reusite
si e C

N
numarul mediu de cautari n cazul unei ncercari nereusite. Avem
C
N
= 1 +
I (T)
N
, C

N
=
E (T)
N + 1
. (8.3)
Din (8.2) si (8.3) rezulta
C
N
=
_
1 +
1
N
_
C

N
1. (8.4)
Rezulta ca C
N
este minim daca si numai daca C

N
este minim, ori dupa
cum am aratat mai nainte acest lucru se ntampla atunci si numai atunci
cand frunzele lui T se aa pe cel mult doua nivele consecutive. Cum lemei 2
arborele asociat cautarii binare satisface aceasta ipoteza, am demonstrat:
Teorema 6. nn:n h.nn:n .nn n n| n n.n.n..n.n
nnn:| nn. n nn:n .. .nn.]:n n :.n nn:...
8.2 Arbori binari de cautare
Am demonstrat n sect iunea precedenta ca pentru o valoare data n, arborele
de decizie asociat cautarii binare realizeaza numarul minim de comparat ii
necesare cautarii ntr-un tabel prin compararea cheilor. Metodele prezentate
n sect iunea precedenta sunt potrivite numai pentru tabele de marime xa
deoarece alocarea secvent iala a nregistrarilor face operat iile de insert ie si
8.2. ARBORI BINARI DE C

AUTARE 111
stergere foarte costisitoare.

In schimb, folosirea unei structuri de arbore
binar faciliteaza insert ia si stergerea nregistrarilor, facand cautarea n tabel
ecienta.
1n. .. 1n n:h: h.nn: n nn: n: n| .nn
S = x
1
< x
2
< ... < x
n

n n:h: h.nn: n nn:. v


1
, v
2
, ..., v
n
. . nn:. n .|n
|nn n| |. S. nn.n ..n ]n . .n,.n
CONTINUT : v
1
, v
2
, ..., v
n
S.
1.|n:n n:n.n :n.nn. nn.n n n.| n n: v
i
n nn n|
hn:h:|. nnj nn: .n nnn n:h:|. :nnn.nn v
k
,nn.
CONTINUT (v
i
) < CONTINUT (v
k
)
.n: n n.| n n: v
j
n nn n| hn:h:|. n: nn: .n nnn n:h:|.
:nnn.nn v
k
,nn.
CONTINUT (v
k
) < CONTINUT (v
j
) .
O denit ie echivalenta este urmatoarea : :n:n: n :n.n .n:.n
n n. n:h: h.nn: n nn: n: n| .nn S ::n :n.nn S.
Prezentam mai jos un program de insert ie si stergere a nodurilor ntr-un
arbore binar de cautare.
# include<iostream.h
# include<stdlib.h
int cheie;
struct nod{ int inf; struct nod *st, *dr;} *rad;
/*********************************************/
void inserare(struct nod**rad)
{ if(*rad==NULL)
{ *rad=new nod;(*rad)-inf=cheie;
(*rad)-st=(*rad)-dr=NULL;return;}
if(cheie<(*rad)-inf) inserare(&(*rad)-st);
else if(cheie(*rad)-inf) inserare(&(*rad)-dr);
else cout<<cheie<< exista deja in arbore.;}
/************************************************/
void listare(struct nod *rad,int indent)
112 CAPITOLUL 8. TEHNICI DE C

AUTARE
{ if(rad){ listare(rad-st, indent+1);
cheie=indent;
while(cheie) cout<< ;
cout<<rad-inf;
listare(rad-dr,indent+1);} }
/***********************************************/
void stergere(struct nod**rad)
{ struct nod*p,*q;
if(*rad==NULL)
{ cout<<Arborele nu contine <<cheie<<endl;return;}
if(cheie<(*rad)-inf) stergere(&(*rad)-st);
if(cheie(*rad)-inf) stergere(&(*rad)-dr);
if(cheie==(*rad)-inf)
{ if((*rad)-dr==NULL)
{ q=*rad;*rad=q-st; delete q;}
else
if((*rad)-st==NULL)
{ q=*rad;*rad=q-dr; delete q;}
else
{ for(q=(*rad),p=(*rad)-st;p-dr;q=p,p=p-dr);
(*rad)-inf=p-inf;
if((*rad)-st==p)(*rad)-st=p-st;
else q-dr=p-st; delete p;} } }
/*************************************************/
void main()
{ rad=new nod;
cout<<Valoarea radacinii este:;cinrad-inf;
rad-st=rad-dr=NULL;
do{
cout<<Operatia:Listare(1)/Inserare(2)/Stergere(3)/Iesire(0);
cout<<endl;
cincheie;if(!cheie) return;
switch(cheie)
{ case 1:listare(rad,1);cout<<endl; break;
case 2: cout<<inf=;cincheie;inserare(&rad);break;
case 3: cout<<inf=;cincheie;stergere(&rad);break;}
} while(rad);
cout<<Ati sters radacina<<endl;}
8.2. ARBORI BINARI DE C

AUTARE 113
Dupa cum se observa din program, ideea insert iei n arborele binar de
cautare este urmatoarea: daca arborele este vid, se creaza un arbore avand
drept unic nod si radacina nodul inserat; altfel se compara cheia nodului
inserat cu cheia radacinii. Daca avem cheia nodului inserat mai mica decat
cheia radacinii, se trece la subarborele stang si se apeleaza recursiv proce-
dura de inserare, altfel se trece la subarborele drept si se apeleaza recursiv
procedura.
Procedura prezentata n program de stergere a unui nod din arborele bi-
nar de cautare este de asemenea recursiva.

In cazul n care cheia nodului
ce urmeaza a sters este mai mica decat cheia radacinii, se trece la sub-
arborele stang si se apeleaza recursiv procedura de stergere; altfel se trece
la subarborele drept si se apeleaza recursiv procedura de stergere.

In cazul
n care nodul ce urmeaza a sters este chiar radacina vom avea mai multe
posibilitati: a) subarborele drept este vid: se sterge radacina iar ul stang
al radacinii devine noua radacina; b) subarborele stang este vid: se sterge
radacina iar ul drept al radacinii devine noua radacina; c) radacina are ambii
i; n acest se sterge cel mai din dreapta descendent al ului stang al radacinii
iar informat ia (cheia) acestuia nlocuieste informat ia (cheia) radacinii, ul
stang al nodului eliminat devine totodata ul drept al tatalui nodului elimi-
nat. Daca ul stang al radacinii nu are u drept, atunci el este cel eliminat,
informat ia lui nlocuieste informat ia radacinii iar ul lui stang devine ul
stang al radacinii (gura 8.4).
Algoritmul de cautare, dupa o anumita cheie, ntr-un arbore de cautare
este n esent a urmatorul: comparam cheia nregistrarii cautate cu cheia
radacinii. Daca cele doua chei coincid cautarea este reusita. Daca cheia
nregistrarii este mai mica decat cheia radacinii continuam cautarea n sub-
arborele stang iar daca este mai mare n subarborele drept.
De foarte multe ori este util sa prezentam arborii binari de cautare ca n
gura 8.5.
Aici arborele binar de cautare este prezentat ca un arbore complet n
care informat iile (cheile) sunt stocate n cele N noduri interne iar informat ia
ecarui nod extern (frunza) este intervalul deschis dintre doua chei consecu-
tive, astfel ncat, daca x
i
, x
i+1
sunt doua chei consecutive si vrem sa cautam
nodul ce cont ine cheia a cu a (x
i
, x
i+1
) sa m condusi aplicand algoritmul
de cautare (ntr-un arbore binar de cautare) la frunza etichetata cu (x
i
, x
i+1
) .
Vom arata ca nalt imea medie a unui arbore binar de cautare este de
ordinul O(ln N) (N este numarul nodurilor interne) si cautarea necesita n
medie circa 2 ln N 1, 386 log
2
N comparat ii n cazul n care cheile sunt
114 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.4: Stergerea radacinii unui arbore binar de cautare
Figura 8.5: Arbore binar de cautare
8.3. ARBORI DE C

AUTARE PONDERATI (OPTIMALI) 115


inserate n arbore n mod aleator. Sa presupunem ca cele N! ordonari posi-
bile ale celor N chei corespund la N! modalitat i de insert ie. Numarul de
comparat ii necesare pentru a gasi o cheie este exact cu 1 mai mare decat
numarul de comparat ii efectuate atunci cand cheia a fost inserata n arbore.
Notand cu C
N
numarul mediu de comparat ii pentru o cautare reusita si cu
C

N
numarul mediu de comparat ii pentru o cautare nereusita avem
C
N
= 1 +
C

0
+C

1
+... +C

N1
N
,
pentru ca nainte de reusita cautarii vom avea cautari nereusite n mult imi
de 0, 1, ..., n 2 sau n 1 elemente. Tinand cont si de relat ia
C
N
=
_
1 +
1
N
_
C

N
1,
deducem
(N + 1) C

N
= 2N +C

0
+C

1
+... +C

N1
.
Scazand din aceasta ecuat ie urmatoarea ecuat ie
NC

N1
= 2 (N 1) +C

0
+C

1
+... +C

N2
obt inem
(N + 1) C

N
NC

N1
= 2 +C

N1
C

N
= C

N1
+
2
N + 1
.
Cum C

0
= 0 deducem ca
C

N
= 2H
N+1
2,
de unde
C
N
= 2
_
1 +
1
N
_
H
N+1
3
2
N
2 ln N.
8.3 Arbori de cautare ponderat i (optimali)

In cele ce urmeaza vom asocia ecarui element din mult imea ordonata S
cate o pondere (probabilitate). Ponderile mari indica faptul ca nregistrarile
corespunzatoare sunt importante si frecvent accesate; este preferabil de aceea
116 CAPITOLUL 8. TEHNICI DE C

AUTARE
ca aceste elemente sa e cat mai aproape de radacina arborelui de cautare
pentru ca accesul la ele sa e cat mai rapid.
Sa analizam n continuare problema gasirii unui arbore optimal. De ex-
emplu, Fie N = 3 si sa presupunem ca urmatoarele chei K
1
< K
2
< K
3
au probabilitat ie p, q respectiv r. Exista 5 posibili arbori binari de cautare
avand aceste chei drept noduri interne (gura 8.6).
Figura 8.6: Arbori posibili de cautare si numarul mediu de comparat ii pentru
o cautare reusita
Obt inem astfel 5 expresii algebrice pentru numarul mediu de comparat ii
ntr-o cautare. Cand N este mare, este foarte costisitor sa construim tot i
arborii de cautare pentru a vedea care din ei este cel optim. Vom pune de
aceea n evident a un algoritm de gasire al acestuia.
Fie S = K
1
< K
2
< ... < K
N
. Fie p
i
0, i = 1, ..., N probabilitatea de
cautare a cheii a = K
i
si q
j
0, j = 0, 1, ...N probabilitatea de cautare a
cheii a (K
j
, K
j+1
) (punem K
0
= si K
N+1
= ). Avem deci
N

i=1
p
i
+
N

j=0
q
j
= 1.
(2N + 1) - tuplul (q
0
, p
1
, q
1
, ..., p
N
, q
N
) se numeste n.:.h .n :hnh.|.n .|:
{nn:.|:) n nn: {n). 1. T un arbore de cautare pentru S, e
T
i
8.3. ARBORI DE C

AUTARE PONDERATI (OPTIMALI) 117


adancimea (nivelul) nodului intern i (al i - lea nod intern n ordine simet-
rica) si e
T
j
adancimea frunzei j (al (j + 1) lea nod extern sau frunza
(K
j
, K
j+1
)).
Sa consideram o cautare a cheii a. Daca a = K
i
, vom compara a cu

T
i
+ 1 elemente din arbore; daca K
j
< a < K
j+1
, atunci vom compara a cu

T
j
elemente din arbore. Asadar
POND(T) =
N

i=1
p
i
_

T
i
+ 1
_
+
N

j=0
q
j

T
j
,
este nnn:| nn. n nn:n .. n: nn:. POND(T) este |nj.nn
nn:nn n n:n:.|: n:h:|. T (sau | |. T :|n. |n n.:.h .
nnn n :hnh.|.n .|: n nn:).
Vom considera POND(T) drept indicator de baza pentru ecient a operat iei
de cautare (acces) deoarece numarul asteptat de comparat ii ntr-o cautare
va proport ional cu POND(T). De exemplu, n cazul arborelui din gura
8.6 b) (unde n loc de p, q, r punem p
1
, p
2
, p
3
) avem

1
= 1,
2
= 2
3
= 0,
0
= 2,
1
= 3,
2
= 3,
3
= 1
si deci
POND(T) = 2q
0
+ 2p
1
+ 3q
1
+ 3p
2
+ 3q
2
+p
3
+q
3
.
(Vom omite indicele T cand este clar din context la ce arbore ne referim.)
1n. .. .:h:| n nn: T n| .nn :nnnn S n.:.h .n
nn:.|: n nn: (q
0
, p
1
, q
1
, ..., p
N
, q
N
). optimal nnn POND(T)
{| n:h:|. n |nj.nn nn:nn n n:n:.|: n:h:|.) n.n.n
n :n: :.| |:|n| . n:h:. n nn: S.
Vom prezenta mai departe un algoritm de construire a unui arbore de
cautare optimal. Fie un arbore de cautare peste S avand nodurile interne
etichetate cu 1, 2, ..., N (corespunzator cheilor K
1
, ..., K
N
) si frunzele etichetate
cu 0, 1, ..., N (corespunzand lui (, K
1
) , (K
1
, K
2
) , ..., (K
N1
, K
N
) , (K
N
, )). Un
subarbore al acestuia ar putea avea nodurile interne i + 1, ..., j si frunzele
i, ..., j pentru 0 i, j n, i < j. Acest subarbore este la randul sau ar-
bore de cautare pentru mult imea cheilor K
i+1
< ... < K
j
. Fie k eticheta
radacinii subarborelui.
Fie costul acestui subarborePOND(i, j)si j:nn n:
GREUT (i, j) = p
i+1
+... +p
j
+q
i
+... +q
j
,
118 CAPITOLUL 8. TEHNICI DE C

AUTARE
de unde rezulta imediat ca
GREUT (i, j) = GREUT (i, j 1) +p
j
+q
j
, GREUT (i, i) = q
i
.
Avem relat ia
POND(i, j) = GREUT (i, j) +POND(i, k 1) +POND(k, j) .

Intr-adevar, subarborele stang al radacini k are frunzele i, i + 1, ..., k 1,


iar subarborele drept are frunzele k, k + 1, ..., j, si nivelul ecarui nod din
subarborele drept sau stang este cu 1 mai mic decat nivelul aceluiasi nod n
arborele de radacina k. Fie C (i, j) = min POND(i, j) costul unui subarbore
optimal cu ponderile p
i+1
, ..., p
j
; q
i
, ..., q
j
. Rezulta atunci pentru i < j :
C (i, j) = GREUT (i, j) + min
i<kj
(C (i, k 1) +C (k, j)) ,
C (i, i) = 0.
Pentru i = j + 1 rezulta imediat ca
C (i, i + 1) = GREUT (i, i + 1) , k = i + 1.
Plecand de la aceste relat ii prezentam mai jos un program, scris n limba-
jul C de construire a unui arbore optimal. Campul informat iei ecarui nod
cont ine un caracter (litera) iar acestea se considera ordonate dupa ordinea
citirii.
/***********************************************/
#include<stdio.h
#include<stdlib.h
#include <io.h
# dene N 25
struct nod { char ch; struct nod *st,*dr;} *rd;
char chei[N];
//cheile de cautare se considera ordonate dupa ordinea citirii
int i,nr;
int p[N-1];/*ponderile cheilor*/
int q[N];
/*ponderile informat iilor aate intre 2 chei consecutive*/
int c[N][N], greut[N][N], rad[N][N];
FILE*f;
8.3. ARBORI DE C

AUTARE PONDERATI (OPTIMALI) 119


/****Functia de calcul a greutatii si costului**********/
void calcul()
{ int x,min,i,j,k,h,m;
for(i=0;i<=nr;i++)
{ greut[i][i]=q[i];
for(j=i+1;j<=nr;j++)
greut[i][j]=greut[i][j-1]+p[j]+q[j];}
for(i=0;i<=nr;i++) c[i][i]=q[i];
for(i=0;i<nr;i++)
{ j=i+1;
c[i][j]=greut[i][j];
rad[i][j]=j;}
for(h=2;h<=nr;h++)
for(i=0;i<=nr-h;i++)
{ j=i+h;m=rad[i][j-1];
min=c[i][m-1]+c[m][j];
for(k=m+1;k<=rad[i+1][j];k++)
{ x=c[i][k-1]+c[k][j];
if(x<min)m=k;min=x;} }
c[i][j]=min+greut[i][j];
rad[i][j]=m;} }
/****Functia de generare a arborelui optimal****/
struct nod *arbore(int i, int j)
{ struct nod *s;
if(i==j) s=NULL;
else{ s=new nod;
s-st=arbore(i,rad[i][j]-1);
s-ch=chei[rad[i][j]];
s-dr=arbore(rad[i][j],j);}
return s;}
/****** Functia de listare indentata a nodurilor arborelui*****/
void listare(struct nod*r, int nivel)
{ int i;
if(r){ listare(r-dr,nivel+1);i=nivel;
while(i) printf( );
printf(%cn

, r >ch);
listare(r-st, nivel+1);} }
Functiaprincipala
120 CAPITOLUL 8. TEHNICI DE C

AUTARE
void main() { f=fopen(arboptim.dat,r);
fscanf(f,%dn

, &nr);
if(nr0)
fscanf(f,%dn

, &q[0]);
for(i=1;i<=nr;i++)
fscanf(f,%c %dn%dn

, &chei[i], &p[i], &q[i]);


calcul();
printf(Lungimea medie a unei cautari: %fn

,
(oat)c[0][nr]/greut[0][nr]);
struct nod*radacina=arbore(0,nr);
listare(radacina,0);} }
/****************************************************/
Fisierul arboptim.dat cont ine pe prima linie numarul de noduri interne
ale arborelui, pe a doua linie valoarea ponderii q
0
iar pe celelalte linii cheile
K
i
cu ponderile p
i
si q
i
. Un exemplu de astfel de sier este urmatorul:
/******************arboptim.dat***********************/
5
1
a 0 2
b 1 1
c 1 0
f 2 2
e 3 0
d 1 2
/**************************************************/
8.4 Arbori echilibrat i
Insert ia de noi noduri ntr-un arbore binar de cautare poate conduce la ar-
bori dezechilibrat i n care ntre nalt imea subarborelui drept al unui nod si
nalt imea subarborelui stang sa e o mare diferent a.

Intr-un astfel de arbore
act iunea de cautare va consuma mai mult timp.
O solut ie la problema ment inerii unui bun arbore de cautare a fost de-
scoperita de G. M. Adelson-Velskii si E. M. Landis n 1962 care au pus n
evident a asa numit ii n:h:. |.|.h:n . (sau n:h:. .11).
8.4. ARBORI ECHILIBRATI 121
1n. .. 1n n:h: h.nn: echilibrat (AVL) nnn nn| .nn h-
n:h:|. nnj n| :.n:. nn n n.]:n nn. n| n n 1 n nn| .nn
hn:h:|. n n:.
1n. .. 1.]:n n n.n: nn| .nn hn:h:|. n: . nn| .nn h-
n:h:|. nnj n:n nn| n factor de echilibru n| n. nn.
Asadar, daca un arbore binar este echilibrat, atunci factorul de echilibru
al ecarui nod este 1, 0 sau 1.
8.4.1 Arbori Fibonacci
O clasa importanta de arbori echilibrat i este clasa de n:h:. 1.hnn. de care
ne vom ocupa n continuare.
Sa consideram mai ntai sirul (F
n
)
n1
de numere Fibonacci, denite prin
urmatoarea formula de recurent a:
F
1
= F
2
= 1,
F
n+2
= F
n+1
+F
n
, n 1.
(8.5)
Primii termeni din sirul lui Fibonacci sunt 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... .
Pentru a gasi o formula explicita pentru numerele Fibonacci, vom cauta
o solut ie a recurent ei (8.5) de forma F
n
= r
n
. Rezulta ca r satisface ecuat ia
algebrica de gradul 2 :
r
2
r 1 = 0,
cu solut ia
r
1,2
=
1

5
2
.
Solut ia generala va
F
n
= Ar
n
1
+Br
n
2
.
Constantele A si B vor determinate din condit iile F
1
= F
2
= 1 care conduc
la sistemul algebric
A
1 +

5
2
+B
1

5
2
= 1,
A
3 +

5
2
+B
3

5
2
= 1,
cu solut ia
A =

5
5
, B =

5
5
.
122 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.7: Arbori Fibonacci
8.4. ARBORI ECHILIBRATI 123
Obt inem astfel ]:n|n |. 1.n pentru numerele Fibonacci:
F
n
=

5
5
_
1 +

5
2
_
n

5
5
_
1

5
2
_
n
.
.
Arborii binari Fibonacci, notat i cu FT
k
, k = 0, 1, 2, ... sunt denit i prin
recurent a dupa cum urmeaza:
- FT
0
si FT
1
sunt format i ecare dintr-un singur nod extern etichetat cu
[0] ;
- pentru k 2, arborele Fibonacci de ordin k, FT
k
are radacina etichetata
cu F
k
; subarborele stang al radacinii este FT
k1
iar subarborele drept al
radacinii este arborele FT
k2
cu etichetele tuturor nodurilor incrementate cu
F
k
(eticheta radacinii lui FT
k
) (vezi gura 8.7).
Vom pune mai departe n evident a cateva proprietat i ale arborilor Fi-
bonacci.
Lema. 1n: :. k 1. n:h:| 1.hnn. FT
k
n n:h: |.|.-
h:n n nnn nn| .nn h(FT
k
) = k1. F
k+1
]:n. . F
k+1
1 nn:. .n:n.
1nn:n ..Pentru k = 1 si k = 2 proprietatea este vericata. Sa
presupunem ca proprietatea este adevarata pentru tot i arborii Fibonacci FT
k

cu k

< k. Fie FT
k
un arbore Fibonacci de ordin k > 3. Din denit ia
recursiva obt inem ca h(FT
k
) = h(FT
k1
) + 1 = k 1, numarul de frunze
al lui FT
k
este egal cu F
k
+ F
k1
= F
k+1
iar numarul de noduri interne este
1 + (F
k
1) + (F
k1
1) = F
k+1
1. Din ipoteza de induct ie, proprietatea
de echilibrare este satisfacuta pentru toate nodurile cu except ia radacinii.

In
ceea ce priveste radacina, subarborele stang arenalt imea k2 iar subarborele
drept are nalt imea k 3, deci FT
k
este echilibrat.
8.4.2 Proprietati ale arborilor echilibrat i
Arborii echilibrat i reprezinta o treapta intermediara ntre clasa arborilor op-
timali (cu frunzele asezate pe doua nivele adiacente) si clasa arborilor binari
arbitrari. De aceea este resc sa ne ntrebam cat de mare este diferent a
dintre un arbore optimal si un arbore echilibrat; prezentam n acest context
urmatoarea teorema:
Teorema.

1nn| .nn n. n:h: |.|.h:n T N nn:. .n:n nn
nnnnn n: log
2
(N + 1) . 1.4404 log
2
(N + 2) 0.328.
124 CAPITOLUL 8. TEHNICI DE C

AUTARE
1nn:n .. Un arbore binar de nalt ime h are cel mult 2
h
1 noduri
interne; deci
N 2
h(T)
1 h(T) log
2
(N + 1) .
Pentru a gasi limitarea superioara a lui h(T), ne vom pune problema aarii
numarului minim de noduri interne cont inute ntr-un arbore echilibrat de
nalt ime h. Fie deci T
h
arborele echilibrat de nalt ima h cu cel mai mic numar
de noduri posibil; unul din subarborii radacinii, de exemplu cel stang va avea
nalt imea h1 iar celalalt subarbore va avea nalt imea h1 sau h2. Cum
T
h
are numarul minim de noduri, va trebui sa consideram ca subarborele
stang al radacinii are nalt imea h 1 iar subarborele drept are nalt imea
h 2.Putem asadar considera ca subarborele stang al radacinii este T
h1
iar
subarborele drept este T
h2
.

In virtutea acestei considerat ii, se demonstreaza
prin induct ie ca arborele Fibonacci FT
h+1
este arborele T
h
cautat, n sensul
ca ntre tot i arborii echilibrat i de nalt ime impusa h, acesta are cel mai mic
numar de noduri. Conform lemei precedente avem
N F
h+2
1 =

5
5
_
1 +

5
2
_
h+2

5
5
_
1

5
2
_
h+2
1.
Cum

5
5
_
1

5
2
_
h+2
> 1,
rezulta ca
log
2
(N + 2) > (h + 2) log
2
_
1 +

5
2
_

1
2
log
2
5
h < 1.4404 log
2
(N + 2) 0.328.
Din considerat iile facute pe parcursul demonstrat iei teoremei, rezulta
urmatorul
Corolar.

1n: n:h:.. |.|.h:n . n nnn: nn n nn:.. n:h:..
1.hnn. n nn|.nn nn..nn, deci sunt cei mai put in performant i.
8.5. INSERTIA UNUI NOD

INTR-UN ARBORE ECHILIBRAT 125
8.5 Insert ia unui nod ntr-un arbore echili-
brat
Inserarea unui nou nod se efectueaza cu algoritmul cunoscut de inserare a
nodurilor ntr-un arbore binar de cautare. Dupa inserare nsa, va trebui sa
:-|.|.h:nn arborele daca vom ajunge ntr-una din urmatoarele situat ii:
- subarborelui stang al unui nod cu factorul de echilibru 1 i creste
nalt imea;
- subarborelui drept al unui nod cu factorul de echilibru 1i crestenalt imea.
Ambele cazuri sunt tratate apeland la :n .. (pe care le vom descrie n
cele ce urmeaza). Rotat iile implica doar modicari ale legaturilor n cadrul
arborelui, nu si operat ii de cautare, de aceea timpul lor de execut ie este de
ordinul O(1) .
8.5.1 Rotat ii n arbori echilibrat i
Rotat iile sunt operat ii de schimbare ntre ele a unor noduri aate n relat ia de
tata-u (si de refacere a unor legaturi) astfel ncat sa e pastrata structura de
arbore de cautare. Astfel, printr-o :n . .n|n n n. n:h: |n nnjn, ul
drept al radacinii init iale devine noua radacina iar radacina init iala devine
ul stang al noii radacini. Printr-o :n . .n|n n n. n:h: |n n:nn, ul
stang al radacinii init iale devine noua radacina iar radacina init iala devine
ul drept al noii radacini. O :n . nh|n |n n:nn afecteaza doua nivele:
ul drept al ului stang al radacinii init iale devine noua radacina, radacina
init iala devine ul drept al noii radacini iar ul stang al radacinii init iale
devine ul stang al noii radacini. O :n . nh|n |n nnjn afecteaza de
asemenea doua nivele: ul stang al ului drept al radacinii init iale devine
noua radacina, radacina init iala devine ul stang al noii radacini iar ul drept
al radacinii init iale devine ul drept al noii radacini.
Vom studia cazurile de dezechilibru ce pot aparea si vom efectua re-
echilibrarea prin rotat ii.
n.| 1. : nn| .nn hn:h:|. nnj n| nn|. a n: n: ]n:|
n |.|.h: .n. .n| 1.
a) Factorul de echilibru al ului stang b al lui a este 1 (gura 8.8).
Aceasta nseamna ca noul element a fost inserat n subarborele A. Re-
echilibrarea necesita o rotat ie simpla la dreapta a perechii tata-u (a > b).
b) Factorul de echilibru al ului stang b al lui a este 1.
126 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.8: Rotat ie simpla la dreapta pentru re-echilibrare
b.1) Factorul de echilibru al ului drept c al lui b este 1 (gura 8.9).
Aceasta nseamna ca noul element a fost inserat n subarborele C. Pentru
re-echilibrare vom avea nevoie de o rotat ie dubla la dreapta a nodurilor b <
c < a.
b.2) Factorul de echilibru al ului drept c al lui b este -1. Se trateaza ca
si cazul b.1) (gura 8.10). .
b.3) Factorul de echilibru al ului drept c al lui b este 0. Dezechilibrarea
este imposibila.
c) Factorul de echilibru al ului stang b al lui a este 0. Dezechilibrarea
este imposibila.
n.| 11. : nn| .nn hn:h:|. n: n| nn|. a n: n: ]n:|
n |.|.h: .n. .n| 1. Se trateaza asemanator cu cazul I).
a) Factorul de echilibru al ului stang b al lui a este 1 (gura 8.11).
Aceasta nseamna ca noul element a fost inserat n subarborele C. Re-
echilibrarea necesita o rotat ie simpla la stanga a perechii tata-u (a < b).
b) Factorul de echilibru al ului drept b al lui a este -1.
b.1) Factorul de echilibru al ului stang c al lui b este -1(gura 8.12).
Aceasta nseamna ca noul element a fost inserat n subarborele B. Pentru re-
echilibrare vom avea nevoie de o rotat ie dubla la stanga a nodurilor b > c > a.
8.5. INSERTIA UNUI NOD

INTR-UN ARBORE ECHILIBRAT 127
Figura 8.9: Rotat ie dubla la dreapta pentru re-echilibrare
Figura 8.10: Rotat ie dubla la dreapta pentru re-echilibrare
128 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.11: Rotat ie simpla la stanga pentru re-echilibrare
Figura 8.12: Rotat ie dubla la stanga pentru re-echilibrare
8.5. INSERTIA UNUI NOD

INTR-UN ARBORE ECHILIBRAT 129
b.2) Factorul de echilibru al ului drept c al lui b este 1. Se trateaza ca si
cazul b.1) (gura 8.13). .
Figura 8.13: Rotat ie dubla la stanga pentru re-echilibrare
b.3) Factorul de echilibru al ului drept c al lui b este 0. Dezechilibrarea
este imposibila.
c) Factorul de echilibru al ului stang b al lui a este 0. Dezechilibrarea
este imposibila.
8.5.2 Exemple

In arborele din gura 8.14 ne propunem sa inseram elementul 58. Suntem


n cazul I.a). Subarborii cu radacinile 70 si 80 devin dezechilibrat i. Pentru
echilibrare se roteste perechea (60, 70) la dreapta, obt inandu-se arborele din
gura 8.15.

In arborele din gura 8.16 ne propunem sa inseram elementul 68. Suntem


n cazul I.b.1). Pentru echilibrare se roteste la stanga perechea (60, 65) si
apoi se roteste la dreapta perechea (70, 65). Se obt ine n nal arborele din
gura 8.17.
130 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.14: Exemplu de insert ie ntr-un arbore echilibrat
Figura 8.15: Exemplu de insert ie ntr-un arbore echilibrat
8.5. INSERTIA UNUI NOD

INTR-UN ARBORE ECHILIBRAT 131
Figura 8.16: Exemplu de insert ie ntr-un arbore echilibrat
Figura 8.17: Exemplu de insert ie ntr-un arbore echilibrat
132 CAPITOLUL 8. TEHNICI DE C

AUTARE
8.5.3 Algoritmul de insert ie n arbori echilibrat i
Pentru a insera un element x ntr-un arbore binar de cautare echilibrat se
parcurg urmatoarele etape:
- se cauta pozit ia n care noul element trebuie inserat (ca n orice arbore
binar de cautare);
- se insereaza elementul x (ca n orice arbore binar de cautare);
- se reactualizeaza factorii de echilibru ai ascendent ilor lui x pana la
radacina sau pana se gaseste cel mai apropiat ascendent p al lui x, daca
exista, astfel ncat subarborele T cu radacina p sa e dezechilibrat;
- daca p exista, se re-echilibreaza subarborele T cu ajutorul unei rotat ii
(simple sau duble).
8.6 Stergerea unui nod al unui arbore echili-
brat
Stergerea este similara insert iei: stergem nodul asa cum se procedeaza n
cazul unui arbore binar de cautare si apoi re-echilibram arborele rezultat.
Deosebirea este ca numarul de rotat ii necesar poate tot atat de mare cat
nivelul (adancimea) nodului ce urmeaza a sters. De exemplu sa stergem
elementul x din gura 8.18.a).

In urma stergerii se ajunge la arborele ne-echilibrat din gura 8.18.b).


Subarborele cu radacina n y este ne-echilibrat. Pentru a-l echilibra este
necesara o rotat ie simpla la dreapta a perechii (y, i)obt inandu-se arborele din
gura 8.19.d); si acest arbore este ne-echilibrat, radacina a avand factorul
de echilibru 2. O rotat ie dubla la dreapta a lui e, b si a, re-echilibreaza
arborele ajungandu-se la arborele din gura 8.20.e).
8.6.1 Algoritmul de stergere a unui nod dintr-un ar-
bore echilibrat
Fie data nregistrarea avand cheia x. Pentru a sterge dintr-un arbore de
cautare echilibrat nodul cu cheia x parcurgem urmatoarele etape
- se localizeaza nodul r avand cheia x;
- daca r nu exista, algoritmul se termina;
- altfel, se sterge nodul r, utilizand algoritmul de stergere ntr-un arbore
binar de cautare;.
8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 133
Figura 8.18: Exemplu de stergere a unui nod dintr-un arbore echilibrat
Figura 8.19: Exemplu de stergere a unui nod dintr-un arbore echilibrat
134 CAPITOLUL 8. TEHNICI DE C

AUTARE
Figura 8.20: Exemplu de stergere a unui nod dintr-un arbore echilibrat
- e q nodul extern eliminat n urma aplicarii algoritmului de stergere si p
tatal sau. Se re-echilibreaza (daca este cazul) arborele prin rotat ii implicand
nodul p si eventual ascendent ii acestuia.
Vom arata ca numarul de rotat ii necesar stergerii unui nod poate ajunge
pana la numarul ce indica nivelul nodului n arbore. Observam mai ntai ca
o rotat ie reduce cu 1 nalt imea arborelui caruia i este aplicata. Fie a cel
mai apropiat predecesor al elementului sters x, astfel ca subarborele T
a
cu
radacina n a sa e neechilibrat. Daca nainte de rotat ie h(T
a
) = k, atunci
dupa rotat ie h(T
a
) = k 1.
Fie b tatal lui a (daca a nu este radacina arborelui). Avem urmatoarele
situat ii favorabile:
- factorul de echilibru al lui b este 0: nu este necesara nici-o rotat ie;
- factorul de echilibru al lui b este 1 si T
a
este subarborele drept al lui b:
nu este necesara nici-o rotat ie;
- factorul de echilibru al lui b este -1 si T
a
este subarborele stang al lui b:
nu este necesara nici-o rotat ie.
Dicultat ile se ivesc atunci cand:
- factorul de echilibru al lui b este -1 si T
a
este subarborele drept al lui b;
- factorul de echilibru al lui b este 1 si T
a
este subarborele stang al lui b

In ambele cazuri va trebui sa re-echilibram arborele T cu radacina n b.


8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 135
Sa observam ca nainte de echilibrare h(T) = k+1 iar dupa re-echilibrare
h(T) = k. Astfel, subarborele avand drept radacina pe tatal lui b poate
deveni ne-echilibrat si tot asa pana cand ajungem la radacina arborelui init ial
(n cel mai rau caz).

In continuare prezentam un program, scris n C de inserare si stergere de


noduri ntr-un arbore binar de cautare echilibrat.
Figura 8.21: Exemplu de stergere a unui nod dintr-un arbore echilibrat
# include<stdio.h
# include<malloc.h
# dene F 0
# dene T 1
struct Nod{ char Info;int FactEch;struct Nod *st;struct Nod *dr;} ;
struct Nod *Inserare (char , struct Nod *, int *);
void Listare(struct Nod *, int );
struct Nod *EchilibrareDr(struct Nod *, int *);
struct Nod *EchilibrareSt(struct Nod *, int *);
struct Nod *Sterge(struct Nod *, struct Nod *, int *);
struct Nod *StergeElement(struct Nod *, char , int *);
/*********************************************/
/* Functia de inserare in arborele de cautare */
struct Nod * Inserare (char Info, struct Nod *tata, int *H)
136 CAPITOLUL 8. TEHNICI DE C

AUTARE
{ struct Nod *Nod1;
struct Nod *Nod2;
if(!tata)
{ tata = (struct Nod *) malloc(sizeof(struct Nod));
tata-Info = Info;
tata-st = NULL;
tata-dr = NULL;
tata-FactEch = 0;
*H = T;
return (tata);}
if(Info < tata-Info)
{ tata-st = Inserare(Info, tata-st, H);
if(*H)
/* Creste inaltimea subarborelui stang */
{
switch(tata-FactEch)
{
case 1: /* Subarborele drept mai inalt*/
tata-FactEch = 0;
*H = F;
break;
case 0: /* Arbore echilibrat */
tata-FactEch = -1;
break;
case -1: /* Subarborele stang mai inalt */
Nod1 = tata-st;
if(Nod1-FactEch == -1)
{ //Cazul din gura 8.8
printf(Rotatie simpla la dreapta n

);
tata-st= Nod1-dr;
Nod1-dr = tata;
tata-FactEch = 0;
tata = Nod1;
tata-FactEch = 0;}
else
/*cazul Nod1-FactEch == 0 nu este posibil pentru ca
am avut *H=F; ramane Nod1-FactEch == 1 ca in
gurile 8.9 si 8.10 */
8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 137
{
printf(Rotatie dubla la dreapta n

);
Nod2 = Nod1-dr;
Nod1-dr = Nod2-st;
Nod2-st = Nod1;
tata-st = Nod2-dr;
Nod2-dr = tata;
if(Nod2-FactEch == -1)
tata-FactEch = 1;
else
tata-FactEch = 0;
if(Nod2-FactEch == 1)
Nod1-FactEch = -1;
else
Nod1-FactEch = 0;
tata = Nod2;}
tata-FactEch = 0;
*H = F;} } }
if(Info tata-Info)
{
tata-dr = Inserare(Info, tata-dr, H);
if(*H)
/* Subarborele drept devine mai inalt */
{
switch(tata-FactEch)
{
case -1: /* Subarborele stang este mai inalt */
tata-FactEch = 0;
*H = F;
break;
case 0: /* Arbore echilibrat */
tata-FactEch = 1;
break;
case 1: /* Subarborele drept este mai inalt */
Nod1 = tata-dr;
if(Nod1-FactEch == 1)
{ /*Cazul din gura 8.11 */
printf(Rotatie simpla la stanga n

);
138 CAPITOLUL 8. TEHNICI DE C

AUTARE
tata-dr= Nod1-st;
Nod1-st = tata;
tata-FactEch = 0;
tata = Nod1;
tata-FactEch = 0;}
else
/*cazul Nod1-FactEch == 0 nu este posibil pentru ca
am avut *H=F; ramane Nod1-FactEch == -1 ca in
gurile 8.12 si 8.136 */
printf(Rotatie dubla la stanga n

);
Nod2 = Nod1-st;
Nod1-st = Nod2-dr;
Nod2-dr = Nod1;
tata-dr = Nod2-st;
Nod2-st = tata;
if(Nod2-FactEch == 1)
tata-FactEch = -1;
else
tata-FactEch = 0;
if(Nod2-FactEch == -1)
Nod1-FactEch = 1;
else
Nod1-FactEch = 0;
tata = Nod2;
}
tata-FactEch = 0;
*H = F;} } }
return(tata);}
/*************************************************/
/* Functia de listare */
void Listare(struct Nod *Arbore,int Nivel)
{ int i;
if (Arbore)
{
Listare(Arbore-dr, Nivel+1);
printf(n

);
for (i = 0; i < Nivel; i++)
printf( );
8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 139
printf(%c, Arbore-Info);
Listare(Arbore-st, Nivel+1);
}
}
/* Echilibrare in cazul cand subarborele drept
devine mai inalt in comparatie cu cel stang*/
/**************************************/
struct Nod * EchilibrareDr(struct Nod *tata, int *H)
{
struct Nod *Nod1, *Nod2;
switch(tata-FactEch)
{
case -1:
tata-FactEch = 0;
break;
case 0:
tata-FactEch = 1;
*H= F;
break;
case 1: /* Re-echilibrare */
Nod1 = tata-dr;
if(Nod1-FactEch = 0)
/* Cazul din gura 8.18 a) cu tata==y */

printf(Rotatie simpla la stanga n

);
tata-dr= Nod1-st;
Nod1-st = tata;
if(Nod1-FactEch == 0)
{
tata-FactEch = 1;
Nod1-FactEch = -1;
*H = F;
}
else
{
tata-FactEch = Nod1-FactEch = 0;
}
tata = Nod1;
140 CAPITOLUL 8. TEHNICI DE C

AUTARE
}
else
{
printf(Rotatie dubla la stanga n

);
Nod2 = Nod1-st;
Nod1-st = Nod2-dr;
Nod2-dr = Nod1;
tata-dr = Nod2-st;
Nod2-st = tata;
if(Nod2-FactEch == 1)
tata-FactEch = -1;
else
tata-FactEch = 0;
if(Nod2-FactEch == -1)
Nod1-FactEch = 1;
else
Nod1-FactEch = 0;
tata = Nod2;
Nod2-FactEch = 0;
}
}
return(tata);
}
/* Echilibrare in cazul cand subarborele stang
devine mai inalt in comparatie cu cel drept*/
/*******************************************/
struct Nod * EchilibrareSt(struct Nod *tata, int *H)
{
struct Nod *Nod1, *Nod2;
switch(tata-FactEch)
{
case 1:
tata-FactEch = 0;
break;
case 0:
tata-FactEch = -1;
*H= F;
break;
8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 141
case -1: /* Re-echilibrare */
Nod1 = tata-st;
if(Nod1-FactEch <= 0)
/*Cazul gurii 8.18 a) cu tata==e */

printf( Rotatie simpla la dreapta n

);
tata-st= Nod1-dr;
Nod1-dr = tata;
if(Nod1-FactEch == 0)
{
tata-FactEch = -1;
Nod1-FactEch = 1;
*H = F; }
else
{ tata-FactEch = Nod1-FactEch = 0; }
tata = Nod1; }
else
/*cazul din gura 8.21 cu tata==e */
printf(Rotatie dubla la dreapta n

);
Nod2 = Nod1-dr;
Nod1-dr = Nod2-st;
Nod2-st = Nod1;
tata-st = Nod2-dr;
Nod2-dr = tata;
if(Nod2-FactEch == -1)
tata-FactEch = 1;
else
tata-FactEch = 0;
if(Nod2-FactEch == 1)
Nod1-FactEch = -1;
else
Nod1-FactEch = 0;
tata = Nod2;
Nod2-FactEch = 0; } }
return(tata); }
/* Inlocuieste informatia nodulului Temp in care a fost gasita cheia
cu informatia celui mai din dreapta descendent al lui R (pe care
apoi il sterge)*/
142 CAPITOLUL 8. TEHNICI DE C

AUTARE
/**********************************************/
struct Nod * Sterge(struct Nod *R, struct Nod *Temp, int *H)
{ struct Nod *DNod = R;
if( R-dr != NULL)
{ R-dr = Sterge(R-dr, Temp, H);
if(*H)
R = EchilibrareSt(R, H); }
else
{ DNod = R;
Temp-Info = R-Info;
R = R-st;
free(DNod);
*H = T; }
return(R); }
/* Sterge element cu cheia respectiva din arbore */
/**********************************************/
struct Nod * StergeElement(struct Nod *tata, char Info, int
*H)
{ struct Nod *Temp;
if(!tata) {
printf( Informatia nu exista n

);
return(tata); }
else { if (Info < tata-Info ) {
tata-st = StergeElement(tata-st, Info, H);
if(*H)
tata = EchilibrareDr(tata, H); }
else
if(Info tata-Info) { tata-dr = StergeElement(tata-dr,
Info, H);
if(*H)
tata = EchilibrareSt(tata, H); }
else { Temp= tata;
if(Temp-dr == NULL) {
tata = Temp-st;
*H = T;
free(Temp); }
else
if(Temp-st == NULL) { tata = Temp-dr;
8.6. STERGEREA UNUI NOD AL UNUI ARBORE ECHILIBRAT 143
*H = T;
free(Temp); }
else
{ Temp-st = Sterge(Temp-st, Temp, H);
if(*H)
tata = EchilibrareDr(tata, H); } } }
return(tata); }
/* Functia principala*/
/*****************************************/
void main()
{ int H;
char Info ;
char choice;
struct Nod *Arbore = (struct Nod *)malloc(sizeof(struct Nod));
Arbore = NULL;
printf( Tastati b pentru terminare: n

);
choice = getchar();
while(choice != b)
{ ush(stdin);
printf(Informatia nodului (tip caracter: a,b,1,2,etc.): n

);
scanf(%c,&Info);
Arbore = Inserare(Info, Arbore, &H);
printf(Arborele este: n

);
Listare(Arbore, 1); ush(stdin);
printf(Tastati b pentru terminare: n

);
choice = getchar(); } ush(stdin);
while(1) {
printf( Tastati b pentru terminare: n

);
printf( Introduceti cheia pe care vreti s-o stergeti: n

);
scanf(%c,&Info);
if (Info == b) break;
Arbore = StergeElement(Arbore, Info, &H);
printf( Arborele este: n

);
Listare(Arbore, 1); } }
144 CAPITOLUL 8. TEHNICI DE C

AUTARE
Capitolul 9
Subiecte pentru laborator
9.1 Sortarea prin insert ie si prin interclasare
.) on nn. :j:nn| C ++ n :n: n n. .: n n nn: n:j.
:.n .n: . .nn nnn n :n:n n: .n|n n ]. :n.nn n |n
|.n| :nn n| .:|. : :.n|.
# include <iostream.h
void main(void)
//datele de intrare
int n; int i=0;
cout<<n=; cinn;
int* a; a = new int [n];
while(i<n) { cout<<a[<<i<<]=; cin*(a+i); i=i+1} ;
//procedura de calcul
for(int j=1;j<n;j++) { int key=*(a+n-j-1);
//insereaza a[n-j-1] in sirul sortat a[n-j,...,n-1]
i=1;
while((i<j+1)*(*(a+n-j-1+i)<key))
{ *(a+n-j-2+i)=*(a+n-j-1+i); i=i+1;}
*(a+n-j-2+i)=key;
}
//datele de iesire
for(j=0;j<n;j++) cout<<a[<<j<<]=<<*(a+j)<<;;
}
..) on :. n :j:nn n: :n:n n. .: ]|.nn :nnn:n
145
146 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
nnn. :n.n .nnnnn :.n .n: . :.nn :. n nn ,nnn
n .:|. . n. .n:|nn.n | nn h.::..
# include <iostream.h
/************************/
void intecl(int p, int q, int m, int n, int *a)
{
int *b,i,j,k;
i=p,j=m+1,k=1;
b=new int[n];
while(i<=m && j<=q)
if(*(a+i)<=*(a+j))
{ *(b+k)= *(a+i);
i=i+1;k=k+1; ); }
else {
*(b+k)=*(a+j);
j=j+1;
k=k+1; }
if(i<=m)
for (j=i;j<=m;j++)
{ *(b+k)= *(a+j);
k=k+1; }
else
for(i=j;i<=q;i++)
{
*(b+k)=*(a+i);
k=k+1; }
k=1;
for(i=p;i<=q;i++)
{ *(a+i)=*(b+k);
k=k+1; } }
/**********************/
void main(void) ){
//datele de intrare
.nt n,j,m;
int i=0;
cout<<n=;
cinn;
int* a;
9.1. SORTAREA PRIN INSERTIE SI PRIN INTERCLASARE 147
a = new int [n];
while(i<n) [n];
{ cout<<a[<<i<<]=;
cin*(a+i);
i=i+1;
//se sorteaza prin insertie prima jumatate a sirului
for(j=1;j<n/2;j++)
{ int key=*(a+j);
//insereaza a[j] in sirul sortat a[0,...,j-1]
i=j-1;
while((i=0)*(*(a+i)key))
{ *(a+i+1)=*(a+i);
i=i-1; }
*(a+i+1)=key; );
}
//se sorteaza prin insertie a doua jumatate a sirului
for(j=1;j<(n-n/2);j++)
{ int key=*(a+n-j-1);
//insereaza a[n-j-1] in sirul sortat a[n-j,...,n-1]
i=1;
while((i<j+1)*(*(a+n-j-1+i)<key))
{ *(a+n-j-2+i)=*(a+n-j-1+i);
i=i+1; }
*(a+n-j-2+i)=key;
}
m=n/2-1;
//interclasarea
intecl(0, n-1, m, n, a);
//datele de iesire
for(j=0;j<n;j++)
cout<<a[<<j<<]=<<*(a+j)<<;; }
...) on :. n :j:nn n: . n .n . | :nnn.n |..-
j:n ]|.nn n|j:.n| n :n: :.n .n: ..
Pentru a ret ine cuvintele declaram un vector n care ecare componenta
ret ine un cuvant char cuvant[100][25]. Pentru a compara doua cuvinte
folosim funct ia strcmp (apelam pentru aceasta sierul <string.h) care
are forma generala int strcmp(const char *s1, const char *s2) si care
dupa ce compara doua siruri de caractere returneaza una din valorile
148 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
- < 0 daca s1 < s2;
- = 0 daca s1 = s2;
- > 0 daca s1 > s2.
Putem folosi si funct ia stricmp care are forma generala int stricmp(const
char *s1, const char *s2) si acelasi rol ca strcmp. Diferent a este ca nu
face distinct ie ntre literele mari si literele mici.
De asemenea mai trebuie folosita funct ia strcpy care are forma generala
char *strcpy(char *dest, const char *sursa) si are rolul de a copia sirul
de adresa sursa la adresa dest.
Urmeaza programul scris nC + +.
# include <iostream.h
#include <string.h
void main(void) {
int i,n,j;
char cuvant[100] [25],key[25];
//datele de intrare
cout<<Numarul de cuvinte (n) =; cinn;
i=0;
while(i<n)
{ cout<<cuvant[<<i<<]=;
cincuvant[i];
i=i+1; }
//procedura de calcul
for(j=1;j<n;j++)
{ strcpy(key,cuvant[j]);
//insereaza cuvant[j] in sirul sortat cuvant[0,...,j-1] ;
i=j-1;
while((i=0)*(strcmp(cuvant[i],key)0))
{ strcpy(cuvant[i+1],cuvant[i]);
i=i-1; }
strcpy(cuvant[i+1],key); [i]);
}
//datele de iesire
for(j=0;j<n;j++)
cout<<cuvant[<<j<<]=<<cuvant[j]<<;;
}
9.1. SORTAREA PRIN INSERTIE SI PRIN INTERCLASARE 149
.) on :. n :j:nn n: . n nn n.n:-n .:. | :-
nnn.n |..j:n ]|.nn n|j:.n| n :n: :.n .n: . . .n:n.n
n. nn| :nnn n .:.

Intr-un sier numele.dat vom insera n prima linie numarul de nume


ce urmeaza a citite iar n a doua linie numele (elemente ale vectorului
nume[100] )ce urmeaza a ordonate alfabetic. Prin instruct iunea FILE*fp
declaram un pointer la sier. Pentru a deschide/crea sierul folosim instruct iunea
fp=fopen(numele.dat, a+) unde a+ reprezinta modul de acces la
sier (citire si adaugare de date). Funct ia fscanf(fp,%d,&n) va citi
numarul de cuvinte ce urmeaza a ordonate iar funct ia fscanf(fp,%s,&nume[indice])
citeste numele corespunzator din sier. Pentru scrierea pe o coloana a listei
de nume ordonate folosim instruct iunea fprintf(fp,%sn

, nume[indice]).
Urmeaza programul:
#include <iostream.h
#include <string.h
#include<stdio.h
void main()
{ int indice,j,n,i;
typedef char vect20[21];
vect20 nume[100],key;
FILE*fp;
fp=fopen(numele.dat, a+);
fscanf(fp,%d,&n,n

);
indice=0;
do{
fscanf(fp,%s,&nume[indice]);
indice++;}
while(indice<n);
fclose(fp);
for(j=0;j<indice;j++)
cout<<nume[<<j<<]=<<nume[j]<<;;
cout<<n;
for(j=1;j<n;j++)
{ strcpy(key,nume[j]);
i=j-1;
while((i=0)*(strcmp(nume[i],key)0))
{ strcpy(nume[i+1],nume[i]);
i=i-1; }
150 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
strcpy(nume[i+1],key);
}
for(j=0;j<n;j++) key);
cout<<nume[<<j<<]=<<nume[j]<<;
fp=fopen(numele.dat,a+);
indice=0;
do{
fprintf(fp,%sn

, , nume[indice]);
indice++; }
while(indice<n);
}
9.2 Fractali
.) on nn. :j:nn| n nnn: n :|. |. o.:.n|.. n]| n n
n nn. n| n .. n|h .n: nn| n |:n n n.: nnn n j:.
{0.1).
Figura 9.1: Varianta a covorului lui Sierpinski
9.2. FRACTALI 151
function covor(i);
% functia covor(n) deseneaza a n-a iterat ie a covorului lui Sier-
pinski
%daca se apeleaza funct ia fara argument se considera implicit
5 iterat ii
switch nargin
case 0
i=5;
end
tic
M=0
%se creeaza recursiv o matrice cu elementele0 si 1
%indicand punctele covorului
for k=1:i
M=[M, M/2, ones(3(k-1));
M/3, ones(3(k-1)),M;
M/6,9*M/10,M/4];
end
% comenzile pentru reprezentarea graca
imagesc(M);
colormap(gray);
axis(equal);
axis o;
toc
..) o n :. n :j:nn n nnn: n :h. |. 1| nn.n n]|.
| nnjn :.nj|.| |.|n:n| hn.n :.n n.n jnn n n:.
. .n:.| n.n n :n: jnn| {j. 0..).
Prezentam mai jos o variant@a de program Matlab:
function kochmod()
lungime=0.01;
r=sqrt(3)/6;
clf;
set(gca,FontSize,24);
set(gcf,Color,[1,1,1]);
hold on;
skochm(0,1,0,0,lungime,r);
hold o;
axis equal;
152 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
Figura 9.2: Curba lui Koch modicat@a
axis tight;
axis o;
%
function skochm(xl,xr,yl,yr,lungime,r)
if (abs(xl-xr)<lungime)
plot([xl xr],[yl yr],b-,LineWidth,2);
end
return
zl=xl+sqrt(-1)*yl;
zr=xr+sqrt(-1)*yr;
z3=2*zl/3+zr/3;
z4=(zl+zr)/2+(zr-zl)*sqrt(-1)*r;
z5=zl/3+2*zr/3;
skochm(xl,real(z3), yl,imag(z3),lungime,r);
skochm(real(z3),real(z4), imag(z3),imag(z4),lungime,r);
skochm(real(z44), real(z3), imag(z3), imag(z44), lungime,r);
skochm(real(z4),real(z5), imag(z4),imag(z5),lungime,r);
skochm(real(z5), real(z44), imag(z5), imag(z44), lungime,r);
skochm(real(z5),real(zr), imag(z5),imag(zr),lungime,r);
return
9.2. FRACTALI 153
...) on :. n :j:nn n nnn: n ]|j|. |. 1| {j. 0.. 0.).
Figura 9.3: Fulgul lui Koch
Fulgul lui Koch se obt ine construind curbele lui Koch pe laturile
unui triunghi echilateral. Cel mai simplu este sa se creeze o curba
Koch iar celelalte doua sa se obt ina din prima prin transformari
geometrice (rotat ii, simetrii, translat ii).
Dam mai jos un program Matlab de creare a fulgului lui Koch.
Impunem ca procesul iterativ sa se termine atunci cand marimea
ecarui segment ce alcatuieste curba lui Koch scade sub o anumita
valoare.
function fulgkoch()
lungime=0.01;
r=sqrt(3)/6;
clf;
set(gca,FontSize,24);
set(gcf,Color,[1,1,1]);
hold on;
skoch(0,1,0,0,lungime,r);
hold o;
axis equal;
154 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
Figura 9.4: Varianta a fulgului lui Koch
axis tight;
axis o;
%
function skoch(xl,xr,yl,yr,lungime,r)
if (abs(xl-xr)<lungime)
plot([xl xr],[yl yr],b-,LineWidth,2);
hold on;
xxl=xl*cos(pi/3)-yl*sin(pi/3);
xxr=xr*cos(pi/3)-yr*sin(pi/3);
yyl=xl*sin(pi/3)+yl*cos(pi/3);
yyr=xr*sin(pi/3)+yr*cos(pi/3);
plot([xxl xxr],[-yyl -yyr],b-,LineWidth,2)
xxxl=xl*cos(pi/3)-yl*sin(pi/3);
xxxr=xr*cos(pi/3)-yr*sin(pi/3);
yyyl=-xl*sin(pi/3)-yl*cos(pi/3);
yyyr=-xr*sin(pi/3)-yr*cos(pi/3);
plot([-xxxl+1 -xxxr+1],[yyyl yyyr],b-,LineWidth,2)
end
return
zl=xl+sqrt(-1)*yl;
9.3. LISTE 155
zr=xr+sqrt(-1)*yr;
z3=2*zl/3+zr/3;
z4=(zl+zr)/2+(zr-zl)*sqrt(-1)*r;
z5=zl/3+2*zr/3;
skoch(xl,real(z3), yl,imag(z3),lungime,r);
skoch(real(z3),real(z4), imag(z3),imag(z4),lungime,r);
skoch(real(z4),real(z5), imag(z4),imag(z5),lungime,r);
skoch(real(z5),real(zr), imag(z5),imag(zr),lungime,r);
return
Pentru varianta fulgului lui Koch, corpul instuct iunii if va
nlocuit cu
plot([xl xr],[yl yr],b-,LineWidth,2)
hold on;
xxl=xl*cos(pi/3)+yl*sin(pi/3);
xxr=xr*cos(pi/3)+yr*sin(pi/3);
yyl=xl*sin(pi/3)-yl*cos(pi/3);
yyr=xr*sin(pi/3)-yr*cos(pi/3);
plot([xxl xxr],[yyl yyr],b-,LineWidth,2)
xxxl=xl*cos(pi/3)+yl*sin(pi/3);
xxxr=xr*cos(pi/3)+yr*sin(pi/3);
yyyl=-xl*sin(pi/3)+yl*cos(pi/3);
yyyr=-xr*sin(pi/3)+yr*cos(pi/3);
plot([-xxxl+1 -xxxr+1],[-yyyl -yyyr],b-,LineWidth,2)
return
9.3 Liste
.) on n:.nn. n n::jn . n. n nnn.
nn |..
D@am mai jos programul n C + +.
/******************list conc.cpp***********/
# include<iostream.h
struct nod { int inf; nod *adr ;} ;
/*****************************/
nod *creare(void);
void parcurge(nod *prim);
nod *ultim(nod *prim);
156 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
// functia ultim returneaza ultimul nod al listei
/************************/
void main(void)
{ nod *cap, *cap1,*p;
cap=creare();
parcurge(cap);
cap1=creare();
parcurge(cap1);
p=ultim(cap);
/*ultimul nod al listei este legat de primul
nod al celeilalte */
p-adr=cap1;
cout<<endl;
parcurge(cap); }
/***********************/
nod *creare(void)
{ nod *prim,*p,*q;int inf;char a;
prim=new nod;
cout<< Introduceti prim-inf<<endl;
cininf;
prim-inf=inf;
prim-adr=NULL;
q=prim;
cout<<Gata?[d/n]<<endl;
cina;
while (a!=d) {
p=new nod;
cout<<p-inf<<endl;
cininf;
p-inf=inf ;
p-adr=NULL;
q-adr=p;
q=p;
cout<<Gata?[d/n]<<endl;
cina;}
return prim;}
/***************************/
void parcurge(nod *cap)
9.3. LISTE 157
{ nod *q;
if(!cap) nod *cap){ cout<<Lista vida;return;}
cout<<Urmeaza lista<<endl;
for(q=cap;q;q=q-adr)
cout<<q-inf<< ; }
/***********************/
nod *ultim(nod *cap)
{ nod *q, *r;
for(q=cap,r=cap-adr;r;q=q-adr,r=r-adr){ }
return q;}
..) on :. n :j:nn n :n: n n. |. n nn :n |..-
j:n.
Se creaza primul element al listei; se creaza apoi urm@atoarele
noduri. Dac@a nodul este mai mic decat primul element al listei,
devine prim element; altfel se insereaz@a n locul cuvenit con-
form ordinii lexicograce, ref@acandu-se n mod corespunz@ator
leg@aturile.
#include<iostream.h
#include<string.h
/*********************/
typedef char cuvant[25];
struct nod { cuvant inf; nod *adr;} ;
nod *creare sort();
void parcurge (nod *cap);
/****************/
void main()
{ nod *cap;
cap=creare sort);
parcurge(cap);}
/*****************/
nod *creare sort()
{ nod *prim,*p,*q,*r;cuvant inf;char a;
prim=new nod;
cout<< Introduceti prim-inf<<endl;
cininf;
strcpy(prim-inf,inf);
prim-adr=NULL;
cout<<Gata?[d/n]<<endl;
158 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
cina;
while (a!=d)
{ p=new nod;
cout<<p-inf<<endl;
cininf;
strcpy(p-inf,inf) ;
if (strcmp(p-inf,prim-inf)<=0)
{ p-adr=prim;prim=p; }
else
{ q=prim;
while((q!=NULL)&&(strcmp(p-inf,q-inf)0))
{ r=q;q=q-adr; }
p-adr=r-adr; r-adr=p; }
cout<<Gata?[d/n]<<endl;
cin<<a;}
return prim;}
/*****************************/
void parcurge(nod *cap) ;
{ nod *q;
if(!cap) { cout<<Lista vida;return;}
cout<<Urmeaza lista<<endl;
for(q=cap;q;q=q-adr)
cout<<q-inf<< ;}
...) on :. n :j:nn n .n:|nn: n nn |. n nn :n
|..j:n.
Programul face apel la func@tiile descrise la punctul precedent.
Se modic@a func@tia principal@a dup@a cum urmeaz@a:
void main()
{ nod *cap,*cap1,*cap2,*p, *s, *r;
cap1=creare sort();
parcurge(cap1);
cap2=creare sort();
parcurge(cap2);
cap=new nod;
cap-adr=NULL;
if (strcmp(cap1-inf,cap2-inf)<=0)
{ strcpy(cap-inf,cap1-inf);
p=cap1;
9.4. METODA BACKTRACKING 159
cap1=cap1-adr;
delete p;}
else{
strcpy(cap-inf,cap2-inf);
p=cap2;
cap2=cap2-adr;
delete p;}
r=cap;
while(cap1&&cap2)
{ s=new nod; s-adr=NULL;
if (strcmp(cap1-inf,cap2-inf)<=0)
{ strcpy(s-inf,cap1-inf);
p=cap1; cap1=cap1-adr;delete p;}
else
{ strcpy(s-inf,cap2-inf);p=cap2;
cap2=cap2-adr;delete p; }
r-adr=s; r=s; }
cout<<cap1<<endl;
parcurge(cap1);
cout<<cap2<<endl;
parcurge(cap2);
if(!cap1) r-adr=cap2;
else r-adr=cap1;
cout<<Lista interclasata<<endl; parcurge(cap);}
9.4 Metoda backtracking
9.4.1 Backtracking iterativ
.) 1:h|nn |: n nnn. 1..nn nnn nh|n n n| nn. : n
|..| n n:nn,n: n n nnn n]| n n n n n nn nnn
nn. |.n.. |nnn n n.njnn|n {nnn| n n nn :.:
]?].)
Fie (i, st[i]) pozi@tia damei i. Cum damele nu se a@a pe aceea@si
linie sau coloan@a, trebui ca st[1], st[2], ..., st[n] s@a e o permutare
a mul@timii 1, 2, ..., n .

In plus pentru ca dou@a dame avand loca@tiile


(i, st[i]) @si (j, st[j]) s@a nu se atace pe diagonal@a trebuie ca [st[i] st[j][ , =
160 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
[i j[ . Programul de generare a pozi@tiilor celor n dame este cel de
generare a permut@arilor mul@timii 1, 2, ..., n la care, n func@tia
E Valid se mai adaug@a condi@tia [st[i] st[j][ , = [i j[ . Urmeaz@a
programul scris n C + + :
#include <iostream.h
#include<math.h
int st[10],n,k;
/***************/
void Init()
{ st[k]=0;}
/***************/
int Am Succesor ()
{ if (st[k]<n
{ st[k]++;
return 1;}
else return 0;
}
/****************/
int E Valid()
{ for (int i=1;i<k;i++)
if (st[i]==st[k][[abs(st[k] st[i]) == abs(k i))return0;
return 1;
}
/********************/
int Solutie
{ return k==n;}
/*******************/
void Tipar()
{ for (int i=1;i<=n;i++) cout <<st[i];
cout<<endl;
}
/*****************/
void back()
{ int AS;
k=1; Init();
while(k0)
{
do{ } while ((AS=Am Succesor())&&!E Valid());
9.4. METODA BACKTRACKING 161
if(AS)
if(Solutie()) Tipar();
else{ k++; Init();}
else k;
} }
/*******************/
void main()
{ cout<<n=; cinn;
back();
}
..) on jn:. n:nn,nnn| n n nn: 1, 2, ..., n |n n p
]o:. o:].
Algoritmul este asem@an@ator cu cel de la permut@ari, cu de-
osebirea c@a n cazul de fa@t@a stiva are n@al@timea maxim@a
p.
#include <iostream.h
int st[10],n,k;
/***************/
void Init()
{ st[k]=0;}
/***************/
int Am Succesor ()
{ if (st[k]<n
{ st[k]++;
return 1;}
else return 0;
}
/****************/
int E Valid()
{ for (int i=1;i<k;i++)
if (st[i]==st[k]) return 0;
return 1;
}
/********************/
int Solutie
{ return k==n;}
/*******************/
void Tipar()
162 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
{ for (int i=1;i<=n;i++) cout <<st[i];
cout<<endl;
}
/*****************/
void back()
{ int AS;
k=1; Init();
while(k0)
{
do{ } while ((AS=Am Succesor())&&!E Valid());
if(AS)
if(Solutie()) Tipar();
else{ k++; Init();}
else k;
} }
/*******************/
void main()
{ cout<<n=; cinn;
back();
}
...) on jn:. nh.nn:.| n n nn: 1, 2, ..., n |n n p
]o:. o:].
Algoritmul este asem@an@ator cu cel al aranjamentelor. Vec-
torul solu@tie se va c@auta sub forma X = (x
1
, x
2
, ..., x
n
) cu x
1

1, 2, 3, ..., n, x
2
x
1
+1, ..., n, ..., x
n
x
n1
+1, ..., n. Din aceast@a
cauz@a x
1
se ini@tializeaz@a cu 0 iar x
i+1
cu x
i
. Deoarece rezul-
tatele sunt sub forma unor @siruri cresc@atoare de numere avem
x
k
< n p + k + 1 @si func@tia int Am Succesor () se modic@a
n mod corespunz@ator. Din modul n care sunt construi@ti su-
ucessorii rezult@a c@a ace@stia sunt valizi. Func@tia int E Valid()
n cazul de fa@t@a ar putea omis@a din program. Urmeaz@a
programul scris n C + + :
#include <iostream.h
int st[10],n,k;
/***************/
void Init()
{ if (k1) st[k]=st[k-1]
else st[k]=0;}
9.4. METODA BACKTRACKING 163
/***************/
int Am Succesor ()
{ if (st[k]<n-p+k
{ st[k]++;
return 1;}
else return 0;
}
/****************/
int E Valid()
{ return 1;}
/********************/
int Solutie
{ return k==p;}
/*******************/
void Tipar()
{ for (int i=1;i<=n;i++) cout <<st[i];
cout<<endl;
}
/*****************/
void back()
{ int AS;
k=1; Init();
while(k0)
{
do{ } while ((AS=Am Succesor())&&!E Valid());
if(AS)
if(Solutie()) Tipar();
else{ k++; Init();}
else k;
} }
/*******************/
void main()
{ cout<<n=; cinn;
back();
}
.) on jn:. :n| n:..nn 1, 2, ..., n
p
= 1, 2, ..., n1, 2, ..., n
1, 2, ..., n.
#include<iostream.h i
164 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
nt st[10],n,k,p;
/*****************/
void Init();
{ st[k]=0;}
/****************/
int Am succesor()
{ if(st[k]<n)
{ st[k]++;
return 1; }
/*********************/
int E valid()
{ return 1; }
/********************/
int Solutie()
{ return k==p;}
/*******************/
void Tipar()
{ cout<<(;
for(int i=1;i<=p;i++) cout<<st[i]<< ;
cout<<)<<endl;}
/***********************/
void back()
{ int AS;
k=1; Init();
while(k0) ;
{ do{ } while ((AS=Am succesor())&& !E Valid());
if(AS)
if (Solutie()) Tipar();
else { k++; Init(); }
else k; } }
/*****************/
void main() )
{ cout<<Numarul de multimi inmultite cartezian p=;cinp
;
cout<<Numarul de elemente din multime n=;cinn;
back(); }
9.4. METODA BACKTRACKING 165
9.4.2 Backtracking recursiv
.) on jn:. n:nn,nnn n n |n n h.
#include <iostream.h
int h,m,k,st[10];
/******************/
int Relatie(int l)
{ int r=1;
for (int j=1;j<l;j++)
if(st[l]==st[j] r=0;
return r;}
/******************/
void back r(int k)
{ for (int i=1;i<=m;i++)
{ st[k]=i;
if (h==k&&Relatie(k)){ for (int j=1;j<=h;j++) cout<<st[j]<<
;
cout<<endl;}
else if (Relatie(k))
back r(k+1);} }
/************************/
void main()
{ cout<<Aranjamente<<endl;
cout<<m=<<endl; cinm;
cout<<h=<<endl; cinh;
back r(1); }
..) on jn:. :nn:.| n|.n.. 1, 2, ..., n.
#include <iostream.h
int n,k,st[10];
/******************/
int Relatie(int l)
{ int r=1;
for (int j=1;j<l;j++)
if(st[l]==st[j] r=0;
return r;}
/******************/
void back r(int k)
{ for (int i=1;i<=n;i++)
166 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
{ st[k]=i;
if (n==k&&Relatie(k)){ for (int j=1;j<=n;j++) cout<<st[j]<<
;
cout<<endl;}
else if (Relatie(k))
back r(k+1);} }
/************************/
void main()
{ cout<<Aranjamente<<endl;
cout<<n=<<endl; cinn;
back r(1); }
...) 1:h|nn |: n nnn.
#include <iostream.h
int n,k,st[10];
/******************/
int Relatie(int l)
{ int r=1;
for (int j=1;j<l;j++)
if(st[l]==st[j][[abs(st[l] st[j]) == abs(l j))r = 0;
return r;}
/******************/
void back r(int k)
{ for (int i=1;i<=n;i++)
{ st[k]=i;
if (n==k&&Relatie(k)){ for (int j=1;j<=n;j++) cout<<st[j]<<
;
cout<<endl;}
else if (Relatie(k))
back r(k+1);} }
/************************/
void main()
{ cout<<Aranjamente<<endl;
cout<<n=<<endl; cinn;
back r(1); }
9.5. METODA GREEDY 167
9.5 Metoda greedy
.) on nn. ]n.n void INCARCA() n]| n n n :.|
:h|nn n.:n n :n|..
#include<iostream.h
int n;
oat g[10], c[10], x[10];
oat G;
/******************************/
void CITESTE()
{ int i;
cout<<Introduceti numarul de obiecte<<endl; cinn;
cout<<Introduceti greutatile obiectelor<<endl;
for (i=0;i<n;i++)
{ cout<<g[<<i<<]=<< ; cing[i];}
cout<<Introduceti castigurile<<endl;
for (i=0;i<n;i++)
{ cout<<c[<<i<<]=<< ; cinc[i];}
cout<<Introduceti capacitatea rucsacului<<endl;
cinG;cout<<endl;}
/************************************/
void SCRIE()
{ int i; oat ;
cout<<Solutia problemei este:<<endl;
C=0;
for (i=0;i<n;i++)
{ cout<<Greutate[<<i<<]=<<g[i]<< ;
cout<<Castig[<<i<<]=<<c[i]<< ;
cout<<Fractiune[<<i<<]=<<x[i]<< ;
C+=x[i]*c[i];}
cout<<endl;
cout<<Castigul total este=<<C<<endl;}
/**********************************/
void SORTARE INSERTIE()
{ oat cc,gg;
int i,j;
for(j=1;j<n;j++)
{ cc=c[j]; gg=g[j];
168 CAPITOLUL 9. SUBIECTE PENTRU LABORATOR
i=j-1;
while(i=0&&c[i]/g[i]<cc/gg)
{ c[i+1]=c[i]; g[i+1]=g[i];i;}
c[i+1]=cc; g[i+1]=gg;
} }
/*******************************/
void INCARCA()
{ int i;
SORTEARE INSERTIE();
for (i=0;i<n;i++)
{
if(g[i]G)
{ x[i]=0; G=0;}
else{ x[i]=1; G-=g[i];}
}
}
/*******************************/
void main()
{ CITESTE();
INCARCA();
SCRIE():
Bibliograe
[1] T. H. CORMEN, C. E. LEISERSON, R. R. RIVEST, 1n:n-
: n n|j:.n., Edit. Computer Libris AGORA, Cluj-Napoca,
2000
[2] H. GEORGESCU, J|n.. n :j:nnn:, Edit. Universitat ii
Bucuresti, 2005.
[3] D. E. KNUTH, J| .: ] n: 1:j:nnn.nj, vol.1, Read-
ing, Massachusets, 1969; vol. 3, Addison-Wesley, 1973.
[2] M. MILOSESCU, 1n]:nn.n , Ed. Didactica si Peda-
gogica, 2006
[3] S. TUDOR, 1n.| :j:nnn:.. n , Edit L&S INFOMAT,
Bucuresti,1998
[Sor] S. TUDOR, 1n]:nn.n {J|n.. n :j:nnn:) 1n:.nnn ,
Edit L&S INFOMAT, Bucuresti,2000
[4] D. STOILESCU, |j: n , Edit. Radial, Galat i, 1998
[5] I. TOMESCU, 1nn o::, Edit. Universitat ii Bucuresti,
1998.
169

Vous aimerez peut-être aussi