Documente Academic
Documente Profesional
Documente Cultură
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
(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
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(
);
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
)
);
)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
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)
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)
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)
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
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 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 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); }
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 ).
= 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.
= (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
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
)
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
;
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
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
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
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
) +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)
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
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 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.
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 .
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) ,
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
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
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
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) .
, 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
,
(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
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).
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
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 */
);
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 */
);
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 .:.
, 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 .