Sunteți pe pagina 1din 188

RECENZIE,

Cartea este destinata elevilor de clasa XI-XII la disciplina


MATEMATICA profil vocational si profilurilor de matematica -
informatica cand se studiaza limbajul C++.
Cartea respecta perfect programa scolara.Toate pogramele au fost
verificate in sistemul Visual Studio.Net in asa fel incat cel care o
citeste sa inteleaga perfect rezolvarea.Felicitam din inima autorii.
.
Prof.univ.dr Octavian Dogaru Universitatea de Vest
Facultatea de Matematica si Informatica,Timisoara

Referenti ştiintifici:
Prof.univ.dr Dumitru Busneag,Universitatea din Craiova
Prof.univ.dr Ion Vladimirescu Rector ,Universitatea din Craiova
Prof.univ.dr Octavian Dogaru,Universitatea din Timisoara
Prof.univ.dr Udrişte Constantin ,Universitatea Politehnica,Bucuresti
Conf.univ.dr Constantin Lupşoiu ,Universitatea din Craiova

1
1. MATRICI

1.1. Despre matrici

Acest concept l-am întalnit înca din primul an de liceu,


atunci când s-a pus problema rexolvarii unui sistem de două
ecuaţii cu două necunoscute x, y, de forma .
Acestui sistem i-am asociat un tablou pătratic, care
conţine coeficienţii necunoscutelor (în prima linie sunt
coeficienţii lui x, y din prima ecuaţie, iar in a doua linie
figurează coeficienţii lui x, y din ecuaţia a doua): .
Am numit acest tablou matrice pătratică (sau matricea
sistemului). Pe cele două coloane ale matricei figurează
coeficienţii lui x (pe prima coloană a, ) şi respectiv
coeficienţii lui y (pe a doua coloană b, ).

Definiţie. Se numeşte matrice cu m linii şi n coloane


(sau de tip ) un tablou cu m linii şi n coloane

ale cărui elemente sunt numere complexe.

Uneori această matrice se notează şi unde şi


. Pentru elementul , indicele i arată linia pe care se
află elementul, iar al doilea indice j indică pe ce coloană este
situat.
Mulţimea matricilor de tip cu elemente numere reale
se notează prin . Aceleaşi semnificaţii au şi mulţimile
, , .

1) O matrice de tipul (deci cu o linie şi n coloane) se


numeşte matrice linie şi are forma
.

2
2) O matrice de tipul (cu m linii şi o coloană) se numeşte
matrice coloană şi are forma
.
3) O matrice de tip se numeşte nulă (zero) dacă toate
elementele ei sunt zero. Se notează cu O

.
4) Dacă numărul de linii este egal cu numărul de coloane, atunci
matricea se numeşte pătratică.

.
Sistemul de elemente reprezintă
diagonala principală a matricii A, iar suma acestor elemente
se numeşte urma matricii A notată Tr(A)
. Sistemul de elemente reprezintă
diagonala secundară a matricii A.
Mulţimea acestor matrici se notează . Printre aceste
matrici una este foarte importantă aceasta fiind

şi se numeşte matricea unitate (pe diagonala principală are


toate elementele egale cu 1, iar în rest sunt egale cu 0).

1.2. Operaţii cu matrici

1.2.1. Egalitatea a două matrici

Definiţie. Fie , . Spunem că


matricile A, B sunt egale şi scriem A = B dacă = ,
, .

Exemplu: Să se determine numerele reale x, y astfel încăt


să avem egalitatea de matrici

3
.
R. Matricile sunt egale dacă elementele corespunzătoare sunt
egale, adică:
Rezolvând acest sistem găsim
soluţia x = 1, y = -3.

1.2.2. Adunarea matricilor

Definiţie. Fie , , . Matricea C


se numeşte suma matricilor A, B dacă: = +
, , .

Observaţii
1) Două matrici se pot aduna dacă sunt de acelaşi tip, adică
dacă au acelaşi număr de linii şi acelaşi număr de coloane, deci
A, B .
2) Explicit adunarea matricilor A, B înseamnă:
+
=

Exemplu: Să se calculeze A + B pentru:


1. ;
2.
R. 1. Avem

2. Avem
.

Proprietăţi ale adunării matricilor

4
(Asociativitatea adunării). Adunarea matricilor este
asociativă, adică:
, A, B, C .
(Comutativitatea adunării). Adunarea matricilor este
comutativă, adică:
, A, B .
(Element neutru). Adunarea matricilor admite
matricea nulă ca element neutru, adică astfel
încât A+ = A, A .
(Elemente opuse). Orice matrice A are un
opus, notat , astfel încât
.

1.2.3. Înmulţirea cu scalari a matricilor

Definiţie.Fie C şi A = . Se numeşte
produsul dintre scalarul C şi matricea A, matricea notată
definită prin = .
Obs.: A înmulţi o matrice cu un scalar revine la a înmulţi toate
elementele matricii cu acest scalar.

Deci =
.

Exemplu Fie . Atunci 6A =

Proprietăţi ale înmulţirii matricilor cu scalari


, C, A ;
, C, A, B ;
, C, A ;
,1 C, A ;

5
1.2.4. Înmulţirea matricilor

Definiţie. Fie A = ,B= .


Produsul dintre matricile A şi B (în aceasta ordine), notat AB
este matricea C = definită prin
, , .

Observaţii
1) Produsul AB a două matrici nu se poate efectua întotdeauna
decât dacă A ,B , adică numărul de coloane
ale lui A este egal cu numărul de linii ale lui B, când se
obţine o matrice C = AB .
2) Dacă matricile sunt pătratice A, B atunci are sens
întotdeauna atât AB cât şi BA, iar, în general, AB BA adică
înmulţirea matricilor nu este comutativă.

Proprietăţi ale înmulţirii matricilor


(Asociativitatea înmulţirii). Înmulţirea matricilor
este asociativă, adică
, A , B ,
C .
(Distributivitatea înmulţirii în raport cu adunarea).
Înmulţirea matricilor este distributivă în raport cu adunarea
matricilor, adică
A, B, C
matrici pentru care au sens operaţiile de adunare şi înmulţire.
Dacă este matricea unitate, atunci
A .
Se spune că este element neutru în raport cu operaţia de
înmulţire a matricilor.

1.2.5. Puterile unei matrici

6
Definiţie. Fie A . Atunci , , ,
…, , n . (Convenim ).

TEOREMA Cayley – Hamilton. Orice matrice A


îşi verifică polinomul caracteristic .
Pentru n = 2.

polinom caracteristic

Generalizat.

2. DETERMINANŢI

2.1. Definiţia determinantului de ordin n 4

Fie A= o matrice pătratică. Vom asocia


acestei matrici un număr notat det(A) numit determinantul
matricii A.

Definiţie. Dacă A= este o matrice pătratică


de ordinul întâi, atunci
det(A) = .
Definiţie. Determinantul matricii este
numărul

7
şi se numeşte determinant de ordin 2. Termenii ,
se numesc termenii dezvoltării determinantului de ordin 2.
Definiţie. Determinantul matricii
este
numărul

şi se numeşte determinant de ordin 3. Termenii care apar în


formulă se numesc termenii dezvoltării determinantului.

Pentru calculul determinantului de ordin trei se utilizează


trei tehnici simple:

Regula lui Sarrus


Fie determinantul de ordin 3, Pentru a calcula
un astfel de determinant se utilizează tabelul de mai jos.

(am scris sub determinant


primele două linii)

Se face produsul elementelor de pe diagonale. Produsul


elementelor de pe o diagonală descendentă este cu semnul plus.
Avem trei astfel de produse: .
Produsul elementelor de pe o diagonală ascendentă este cu
semnul minus. Avem trei astfel de produse:
.
Suma celor şase produse dă valoarea determinantului d de
ordin 3. Acest procedeu de calcul se numeşte „regula lui
Sarrus”.

Regula triunghiului

8
Am văzut că determinantul de ordin trei are în
dezvoltarea sa şase termeni, trei cu semnul plus şi alţi trei cu
semnul minus.
Primul termen cu plus se găseşte înmulţind elementele de
pe diagonala principală, iar ceilalţi doi, înmulţind elementele
situate în vârfurile celor două triunghiuri care au o latură
paralelă cu cu diagonala principală. După aceeaşi regulă,
referitoare la diagonala secundară, se obţin termenii cu minus.
Obs.: Atât „regula lui Sarrus” cât şi „regula triunghiului” se
aplică numai determinanţilor de ordin 3.

Exemplu. Să se calculeze prin cele două metode de mai


sus determinantul

R. Regula lui Sarrus.

Regula triunghiului

Recurent (sau dezvoltare după o linie sau o


coloană)
Determinantul de ordin 3 are 6 ( = 3!) termeni dintre care
trei sunt cu semnul plus, iar ceilalţi cu semnul minus.
Are loc următoarea proprietate:
, (1)
= . (2)
Observaţii
1) Egalitatea (1) se mai numeşte dezvoltarea determinantului
după elementele liniei întâi, iar egalitatea (2) se numeşte
dezvoltarea determinantului după elementele coloanei întâi.
2) Formulele (1) şi (2) sunt relaţii de recurenţă, deoarece
determinantul de ordin 3 se exprimă cu ajutorul unor
deteminanţi de ordin inferior (2).

9
2.2. Definiţia determinantului de ordin n

Voi defini în continuare determinantul de ordin n prin


recurenţă cu ajutorul determinanţilor de ordin n – 1. Pentru
aceasta sunt necesare unele precizări.
Fie A= .
Definiţie1. Se numeşte minor asociat elementului
determinantul matricii pătratice de ordin n – 1 obţinut prin
suprimarea liniei i şi coloanei j din matricea A. Se notează acest
minor prin sau .
Definiţie2. Se numeşte complement algebric al
elementului numărul . Exponentul al lui (–1)
este suma dintre numărul liniei i şi coloanei j pe care se află
.

Definiţie. Determinantul matricii A= de ordin n este


suma produselor elementelor din prima linie cu complemenţii lor
algebrici adică
.
Observaţii
1) Elementelor, liniilor şi coloanelor matricii A le vom spune de
asemenea elementele, liniile şi coloanele determinantului

.
2) Formula din definiţie spunem că reprezintă dezvoltarea
determinantului de ordin n după elementele primei linii.
3) Definiţia determinantului de mai sus este încă puţin
eficientă (o voi ilustra mai jos pentru n = 4). De aceea se
impune stabilirea unor proprietăţi ale determinanţilor care să
fie comode atât din punct de vedere al teoriei şi din punct de
vedere calculatoriu. Aceste proprietăţi le prezint în paragraful
următor.

10
4) Continuând cu explicitarea determinanţilor de ordin n – 1 din
definiţie se obţine pentru o sumă de
produse de elemente din determinant, fiecare produs conţinând
elemente situate pe linii şi coloane diferite.
5) Determinantul este o funcţie .

Exemplu Să se calculeze determinantul de ordin 4:

.
R. Aplicăm definiţia dată mai sus pentru n = 4 şi dezvoltăm
determinantul după elementele liniei întâi. Avem:
=
= ,
unde determinanţii de ordin 3 i-am calculat prin una din
metodele prezentate la determinanţii de ordin 3.

2.3. Proprietăţile determinanţilor

Determinantul unei matrici coincide cu determinantul


matricii transpuse, adică dacă A , atunci
.
Demonstraţie. Fie şi .
Atunci , iar . Prin urmare
.

Dacă toate elementele unei linii (sau coloane) dintr-o


matrice sunt nule, atunci determinantul matricii este nul.
Demonstraţie. Avem şi .

Dacă într-o matrice schimbăm două linii (sau două


coloane) între ele obţinem o matrice care are determinantul
egal cu opusul determinantului matricii iniţiale.
Demonstraţie. Prin schimbarea liniilor să arăt că
avem egalitatea . Avem evident .

11
Dacă o matrice are două linii (sau coloane) identice,
atunci determinantul său este nul.
Demonstraţie. Verific pentru linii (şi tot odată
pentru coloane). Avem:
.

Dacă toate elementele unei linii (sau coloane) ale unei


matrici sunt înmulţite cu un număr , obţinem o matrice al
cărei determinant este egal cu înmulţit cu determinantul
matricii iniţiale.
Demonstraţie. Verificăm pentru linii proprietatea.
.

Dacă elementele a două linii (sau coloane) ale unei


matrici sunt proporţionale, atunci determinantul este nul.
Demonstraţie. Verificăm pentru linii.
.

Dacă linia i a unei matrici A este suma a doi vectori,


atunci determinantul ei este egal cu suma a doi determinanţi
corespunzători matricelor care au aceleaşi linii ca A, cu
excepţia liniei i unde au câte unul din cei doi vectori.

.
Demonstraţie. Am de arătat că:
.
Într-adevăr membrul stâng este egal cu
. Membrul drept este
şi egalitatea se verifică.
Obs.: O proprietate analogă are loc şi pentru coloane.

12
Dacă o linie (o coloană) a unei matrici pătratice este o
combinaţie liniară de celelalte linii (coloane), atunci
determinantul matricii este zero.

Dacă la o linie (o coloană) a matricii A adunăm


elementele altei linii (coloane) înmulţite cu acelaşi număr, atunci
această matrice are acelaşi determinant ca şi matricea A.
Demonstraţie. Voi aduna la linia întâi linia a doua
înmulţită cu . Vom nota acest fapt prin . Avem:
.

A .

Dacă A= este o matrice triunghiulară (sau


diagonală), atunci . (Valoarea determinantului
este egală cu produsul elementelor de pe diagonala principală).

Dacă A, B , atunci
(Determinantul produsului a două matrici pătratice este egal cu
produsul determinanţilor acelor matrici).
În particular n .

Teoremă. Determinantul unei matrici A este egal


cu suma produselor dintre elementele unei linii şi
complemenţii lor algebrici, adică

.
(Formula lui dă dezvoltarea determinantului după
elementele liniei i).

Această teoremă permite să calculăm determinantul unei


matrici după oricare linie. Se va alege acea linie care are mai

13
multe zerouri sau pe care se pot realiza (cât mai uşor) mai
multe zerouri.
Observaţie: Ţinând seama de proprietatea teorema
precedentă are loc şi pentru coloane sub forma:

2.4. Calculul inversei unei matrici

Definiţie. Fie A . Matricea A se numeşte


inversabilă dacă există matricea B cu proprietatea că
, fiind matricea unitate.
Matricea B din definiţie se numeşte inversa matricii A şi
se notează . Deci
.

Teoremă. Matricea A este inversabilă dacă şi


numai dacă O astfel de matrice se numeşte
nesingulară.

Construcţia lui presupune următorii paşi:

Pasul 1. (Construcţia transpusei)


Dacă
,
atunci construim transpusa lui A
.

Pasul 2. (Construcţia adjunctei)


Matricea

obţinută din , inlocuin fiecare element cu complementul său


algebric se numeşte adjuncta matricii A.

14
Pasul 3. (Construcţia inversei) Se ţine cont de teorema
precedentă şi se găseşte că:

iar de aici

Ultimele egalităţi arată că

2.5. Ecuaţii matriciale

Voi prezenta în continuare o tehnică de rezolvare a unor


ecuaţii de forma , , , unde A, B, C sunt
matrici cunoscute, iar X este matricea de aflat. Astfel de
ecuaţii se numesc ecuaţii matriciale.
Astfel de ecuaţii se pot rezolva numai atunci când A, B
sunt matrici pătratice inversabile.

Pentru rezolvarea ecuaţiei înmulţim la stânga


egalitatea cu şi avem:
.
Deci soluţia ecuaţiei date este .

Pentru determinarea soluţiei ecuaţiei vom înmulţi


la dreapta cu şi analog vom găsi , soluţia ecuaţiei
matriciale.

Pentru găsirea soluţiei ecuaţiei înmulţim


egalitatea la stanga cu şi la dreapta cu şi obţinem
.

15
APLICAŢII

1.Să se determine numerele reale x, y, z astfel încât să


aibă loc egalitatea de matrici, în cazurile

1.a)
b)
c)
2. Să se calculeze în cazurile:
a) , .
b) ,
3. Se consideră matricile
,
,
.
Să se determine m, n, p astfel încât .
4. Se consideră matricile .
, .
Să se calculeze: , .

5. Calculaţi produsele de matrici , unde

a) şi

b) şi

c) şi

6. Să se calculeze , dacă:
;
7. Fie . Să se calculeze , .

16
INDICATII SI RASPUNSURI

1.a)

b)

c)

I. dacă , atunci
II. dacă , atunci

2.a) , .

b) ,

3. .

.
Deci
4.

17
5.a)

b)

c)

6.

7.

Inducţie matematică

(A)

Deci .
8. 1)
2)
3)

Regula lui Cramer :   Se da un sistem de ecuatii cu   


necunoscute.

                                 

18
  Matricea lui este                     Se
noteaza cu det

Se noteaza : ,

        

Daca   sistemul are solutie unica data de formulele lui Cramer :

   ,        ,………,  

Sistemul este compatibil determinat

19
Gradul de nedeterminare al sistemului este

Pentru un sistem cu ,spunem ca este compatibil determinat

Un sistem de ecuatii care nu are solutii se numeste incompatibil.


Un sistem de ecuatii liniare este incompatibil daca determinantul sau
este nul şi unul din determinantii dj, 1≤j≤n, este nenul (atunci toti
determinantii dj, 1≤j≤n, sunt nenuli).

Un sistem de ecuatii care are mai mult de o solutie se numeste


compatibil nedeterminat. Un sistem de ecuatii liniare este compatibil
determinat daca determinantul sau este nul şi unul din determinantii dj,
1≤j≤n, este nul (deoarece atunci toti determinantii dj, 1≤j≤n, sunt nuli).

20
21
22
23
Calculul şi interpretarea anumitor indicatori statistici  ce se referă la rezultatele obţinute de elevi în
urma aplicării unor probe de evaluare pot conduce la informaţii preţioase legate de procesul de
învăţământ.

Media este indicatorul cel mai cunoscut şi se calculează după formula:

Modulul este valoarea cea mai des întâlnită. În exemplul dat, frecvenţa cea mai
mare o are punctajul de 8.

Indicatorii tendinţei centrale nu dau nici o indicaţie asupra împrăştierii, a


modului cum termenii seriei se abat între ei sau de la medie. Centrul de grupare poate
fi acelaşi pentru două sau mai multe serii de date, dar gradul de împrăştiere să fie
diferit în jurul centrului de grupare. Media ascunde structura colectivităţii şi nu
permite cunoaşterea abaterilor termenilor seriei faţă de media lor. De aceea, este
necesar să verificăm gradul de reprezentativitate al indicatorilor tendinţei generale.

Indicatorii de variaţie permit separarea influenţei cauzelor esenţiale de cea a


cauzelor întâmplătoare.

Amplitudinea variaţiei oferă posibilitatea determinării câmpului de variaţie a


fenomenului studiat. Ea se prezintă sub două forme:

         Amplitudinea absoută: 

         Amplitudinea relativă: 

Abaterea medie liniară se calculează ca o medie aritmetică simplă sau


ponderată a abaterilor absolute al termenilor seriei de la media lor, luate în
modul:

şi arată, în medie, cu cât se abat termenii seriei de la media lor. Abaterea medie liniară
se foloseşte la determinarea

 intervalului mediu de variaţie                         

            Dispersia se calculează ca o medie aritmetică simplă sau ponderată a


pătratelor abaterilor termenilor seriei de la tendinţa centrală:

24
sau:

                                                                 

            Dispersia arată modul în care valorile caracteristice gravitează în jurul mediei.
Ea măsoară variaţia totală a caracteristicii studiate datorită cauzelor esenţiale şi
întâmplătoare.
            Abaterea standard sau abaterea medie pătratică se calculează ca rădăcină
pătrată din dispersie:
                                                                                                     
O serie de date (în cazul nostru particular, o clasă de elevi, privită de ochiul
profesorului de o anumită specialitate) prezintă omogenitate mare cu cât  este mai
mic.
La fel ca şi abaterea medie liniară, abaterea standard poate fi folosită pentru

determinarea intervalului mediu de variaţie astfel: .


Coeficientul de variaţie urmăreşte verificarea reprezentativităţii mediei
variabilelor analizate, precum şi compararea omogenităţii seriilor de date:

El se mai poate calcula înlocuind în formula precedentă abaterea standard cu abaterea


medie liniară. Astfel, coeficientul de variaţie arată câte unităţi din abaterea standard
sau din abaterea medie liniară revin la 100 de unităţi de medie.
            Dacă :
      Cv = 0 înseamnă că avem de-a face cu o lipsă de variaţie, toate valorile fiind
egale între ele;
      Cv < 35% se admite că seria prezintă un grad de omogenitate ridicat,
împrăştierea este mică, iar indicatorii tendinţei centrale sunt reprezentativi
pentru acea serie;
      Cv > 35% înseamnă că variaţia este foarte mare, media nu este semnificativă şi
ascunde o structură eterogenă a colectivităţii.

APLICATIE
Prima etapă a prelucrării statistice a datelor constă în organizarea acestora. Astfel, în
cazul finalizării unei probe de evaluare la o disciplină oarecare se poate construi un
tabel în care în primele două coloane să fie „descărcate” numărul de puncte obţinute şi
numărul elevilor care au obţinut acelaşi punctaj (vezi tabelul de mai jos).

25
26
27
1. Mulţimea numerelor raţionale

• Mulţimea numerelor raţionale ; reprezentarea


numerelor raţionale pe axa numerelor, opusul
unui număr raţional; valoarea absolută (modulul);

28
29
30
Aplicatii:
1)

31
32
O funcţie pătratică, în matematică, este o funcţie polinomială de forma
, unde . Graficul unei funcţii pătratice este de forma
unei parabole ale cărei axe majore sunt paralele cu axa y.

Expresia ax2 + bx + c din definiţia unei funcţii pătratice, este un polinom de grad 2
sau funcţie polinomială de grad 2, pentru că cel mai mare exponent al variabilei x
este 2.

Dacă se pune condiţia ca funcţia pătratică să fie egală cu zero, atunci va rezulta o
ecuaţie pătratică. Soluţiile acestei ecuaţii sunt numite rădăcini pătrate ale ecuaţiei, sau
puncte de nul ale funcţiei.

Rădăcini

Cele două rădăcini ale ecuaţiei pătratice , în care sunt:

 Fie

33
 Dacă , atunci există două rădăcini distincte pentru că este un
număr real pozitiv.
 Dacă , atunci cele două rădăcini sunt egale, pentru că este zero.
 Dacă , atunci cele două rădăcini sunt conjugate complexe, pentru că
este un număr imaginar.

Considerând şi sau invers, se


poate da factor comun sub forma .

FUNCTII ELEMENTARE IN R

1 Functia constanta

2 Functia identica

-interpretarea geometrica a graficului functiei identice este prima bisectoare


-functie strict crescatoare, bijectiva, inversabila.
3 Functia putere

4 Functia radical
5 Functia polinomiala

34
Cazuri particulare
 Functia polinomiala de gradul I
f(x)=ax+b
functie bijectiva, inversabila
a>0 – strict crescatoare; a<0 – strict descrescatoare
Gf – o dreapta

 Functia polinomiala de gradul II

Semnul functiei de gradul II:Intre radacinile ecuatiei f(x)=0 semnul este opus lui a;
In afara radacinilor acelasi semn cu a;

6 Functia exponentiala

35
7.Functia logarithm

Vectori si operatii

1. Adunarea vectorilor
 Fie u si v doi vectori in plan de directii diferite . Fie O un punct in
plan . Construim OA=u si OB=v . Fie S un al patrulea varf opus lui O al
paralelogramului cu trei varfuri in O,A si B .
B S

v
u+v

O u A

36
OS = u + v ( regula paralelogramului )
1) Daca u si v sunt doi vectori de aceeasi directie si acelasi sens atunci
u+v este vectorul de aceeasi directie si sens si de lungime | u |+| v | .
2) Daca u si v au aceeasi directie si sensuri opuse atunci daca | u |>| v |
vectorul u+v are aceeasi directie cu vectorii u si v , are sensul vectorului u
si lungimea | u |-| v | .
3) Daca u si v au aceeasi directie , sensuri opuse si | u |<| v | atunci u+v
este vectorul de aceeasi directie cu sensul vectorului v si cu lungimea | v |
-|u| .

 Se stie ca intr-un Δ , AC < AB + BC si atunci | u+v | < | u | + | v | .


 Cand A,B,C sunt colineare si vectorii AB si BC au acelasi sens
atunci | u+v | = | u | + | v | . Deci in general | u+v | ≤ | u | + | v | pentru
orice 2 vectori u si v egalitatea avand loc numai daca u si v sunt coliniari
si au acelasi sens .

 Proprietetile adunarii :
1. (u+v) +w = u+ (v+w) – asociativitate ;
2. u+v = v+u – comutativitate ;
3. exista 0 , a.i. oricare ar fi v , v+0 = 0+v = v – element neutru ;
4. oricare ar fi vectorul v exista (–v) a.i v+(-v)=(-v)+v=0 – element
sincretic ;
(- v) = opusul lui v , are aceeasi directie , lungime dar
sensul e opus .
 | u | + | v | = √(u²+v²+2uv*cos α) ;

2. Inmultirea unui vector cu un scalar

Fie α care apartine lui R , v- vector => αv se obtine din v


astfel :
a) pentru α>0 vectorul αv are aceeasi directie cu v ,
acelasi sens si lungimea = α|v| ;
b) pentru α<0 vectorul αv are aceeasi directie cu v , sens
opus acestuia si lungimea |α|*|v| ;
c) pentru α=0 => 0*v = 0 ;

 Proprietatile inmultirii unui vector cu un scalar :


Fie α , β apartin lui R , u,v = 2 vectori ;
1. α( βv ) = ( αβ )v ;

37
2. α( v+u ) = αv + αu ;
3. 1* (v) = v ;
4. 0* (v) = 0 ;
5. α0=0;
- Daca α=-1 vectorul (-v) se numeste opusul vectorului v si
se obtine din acesta pastrandu-i directia si modulul , dar schimbandu-i
sensul .

Teorema : 2 vectori nenuli sunt paraleli ( sau coliniari ) daca unul


se obtine din celalalt prin inmultire cu un scalar nenul .
u,v ≠ 0
u || v <=> exista α apartinand lui R a.i. u = αv ;

 Daca A',B',C', sunt mijloacele laturilor Δ ABC atunci


AA'+BB'+CC'=0 A

C'
B'

B
A' C

 Intr-un patrulater segmentul ce uneste mijloacele a doua laturi este


egal cu semisuma bazelor ( EF=1/2(AB+DC));
-Daca in rel. demonstrata trecem la norme ||EF||=1/2 (||AB|+|DC||)≤1/2(||
AB||+||DC||);
-Egalitatea are loc<=> vectorii AB si CD sunt coliniari si de acelasi sens
<=> AB || DC <=> ABCD – trapez ;
-In general FE ≤1/2(AB+DC) – intr-un patrulater ;
-Egalitatea are loc in trapez .
C

D
F
E

A B
 Intr-un patrulater segmentul ce uneste mijloacele celor doua
diagonale este egal cu semidiferenta D
bazelor ( MN=1/2(BC-AD));
A

M N

38
B C
 Intr-un Δ ABC , M apartine BC a.i. MB/MC=k => AM=1/(k+1)AB-
k/(k+1)AC ;
- Caz particular MB=MC => mediana AM=1/2(AB+AC) ;
 Fie G = c.g. Δ ABC , M – un punct in plan , atunci
MA+MB+MC=3MG ;
 Fie H= ortocentrul Δ inscris in C(O,r) , atunci HA+HB+HC=2HO ;
H,G,O-coliniare si
OH=3OG ;
- Dreapta care contine aceste trei puncte ( c.c.circumscris – O ,
centrul de greutate – G si ortocentrul – H ) se numeste dreapta lui Euler .
 Intr-un Δ , G=c.g. , M apartine lui AB , N apartine lui AC , si MN
trece prin G => MB/MA + NC/NA =1 .
A

G N
M

B A' C
Teorema lui Menelaus si a lui Ceva

1.Teorema lui Menelaus


A

B'
C'

A' B C

39
 O dreapta d care nu trece prin nici un varf al Δ ABC intersecteaza
dreptele suport ale laturilor Δ ABC in punctele A',B',C' . Atunci
A'B/A'C*B'C/B'A*C'A/C'B=1 .
 Reciproca : Daca A' apartine lui BC , B' apartine lui CA , C' apartine
lui AB si daca A',B',C' sunt situate doua pe laturi si unul pe prelungirea
laturii sau toate trei pe prelungirile laturilor si daca
A'B/A'C*B'C/B'A*C'A/C'B=1 atunci punctele A',B',C' sunt coliniare .

2. Teorema lui Ceva


A

C' B'
K

B A' C
 Se da Δ ABC si dreptele concurente AA',BB',CC' ≠ laturi atunci
A'B/A'C*B'C/B'A*C'A/C'B=1 .
 Reciproca : Se da Δ ABC , A' apartine lui BC , B' apartine lui CA ,
C' apartine lui AB ≠ varfuri , situate pe laturi sau un punct pe o latura si
doua pe prelungirile laturilor . Daca A'B/A'C*B'C/B'A*C'A/C'B=1 =>
dreptele AA' , BB' , CC' sunt concurente .

OBSERVATIE !
1. Dreptele concurente A'A , B'B , C'C se numesc ceviene .
2. Reciproca Teoremei lui Ceva este utila in rezolvarea problemelor de
concurenta .

Geometria analitica a dreptei

1. Geometria analitica a dreptei – distanta dintre doua puncte


y
A
yA

yB B
P

O xA xB x

40
AB=√[(xA-xB)²+(yA-yB)²]

2. Elemente de geometrie analitica

 Se numeste versor al dreptei d un vector de lungime 1 , care are


directia dreptei d . Daca A apartine lui d ii asociem un numar real , unic x
, numit coordonata sa . Atunci OA=x*i . Daca x>0 atunci A este in sensul
pozitiv al axei Ox . Daca x<0 atunci A este in sensul negativ al axei Ox .
 Fie xOy un sistem de axe ortogonale . Fie i si j versorii axelor . Fie u
un vector in plan . Orice vector u poate fi scris in mod unic u=xi+yj ;
y

B M u

O i A x

 AB = (xB-xA)i + (yB-yA)j ;

3. Modulul uni vector A

O x
 u = xi + yj => |u| = √(x²+y²)
 |AB|=||AB||=AB
|u|=||u||=u
y

M
B
u

A O x

41
4. Suma a doi vectori

 u=x1i+y1j
v=x2i+y2j
 u+v = (x1+x2)i+(y1+y2)j

5. Conditia de paralelism

 u||v <=> x1/x2=y1/y2 , pt. x2,y2 ≠0

6. Conditia de coliniaritate a 3 puncte

 A,B,C – coliniare <=> AB||AC => (x2-x1)/(x3-x1)=(y2-y1)/(y3-y1)

7. Conditia de perpendicularitate

 u┴v <=> x1*x2+y1*y2 = 0

8. Coordonatele mijlocului unui segment

 xM=(xA+xB)/2
yM=(yA+yB)/2

9. Coordonatele centrului de greutate al unui Δ

 xG=(xA+xB+xC)/3
yG=(yA+yB+yC)/3

10. Ecuatia dreptei in plan

 Graficul functiei de gradul I , f : R → R , f(x) = ax + b , cu a≠0 este


o dreapta formata din punctele de coordonatele (x,y) unde y=ax+b . Orice
dreapta este bine determinata de doua puncte distincte ale sale .
- Daca a=0 , dreapta de ecuatie y=b este orizontala dusa prin b ;
- Daca a≠0 dreapta de ecuatie y=ax+b este oblica ;
- Mai exista dreapta verticala de ecuatie x=c .

11. Ecuatia dreptei care trece printr-un punct dat si are o directie
data

42
 Ecuatia dreptei care trece printr-un punct A(x0,y0) si are directia
vectorului u=pi+qj este (x-x0)/p=(y-y0)/q , p,q ≠0
 Daca p=0 => u=qj => d||Oy si dreapta este verticala cu ecuatia x=x0
 Daca q=0 => u=pi => d||Ox si dreapta este orizontala cu ecuatia
y=y0

12. Coeficientul unghiular . Panta unei drepte .


y
d

A α
O x

 Fie d o dreapta in sistemul de axe xOy . Unghiul α format de dreapta


d cu sensul pozitiv al axei Ox se numeste coeficientul unghiular al dreptei
d.
 Dreapta d:y=mx+n are panta m=tg.α , unde α = unghiul format de
dreapta d cu sensul pozitiv al axei Ox .
 Ecuatia dreptei care trece printr-un punct dat A(x0,y0) si are panta
data m , este y--y0=m(x-x0).

13. Conditia de paralelism a doua drepte

 d1 : y=m1x+n1
d2 : y=m2x+n2
d1||d2
d1||d2 <=> m1=m2 ( au aceeasi panta )

14. Conditia de perpendicularitate a doua drepte

 d1 : y1=m1x+n1
d2 : y2=m2x+n2
d1┴d2 <=> m1*m2 = -1
15. Ecuatia dreptei care trece prin 2 puncte date

43
 Ecuatia dreptei care trece prin 2 puncte date A,B = AB :
(y-yA)/(yB-yA)=(x- -xA)/(xB-xA)

CONCLUZIE : Ecuatia generala a dreptei d : ax+by+c=0 unde


a²+b²≠0 .

SEMIGRUPURI ŞI GRUPURI

Algebra se ocupă cu studiul structurilor algebrice. La baza acestui


studiu stă noţiunea de operaţie algebrică. Este deci necesar să definim, mai
întâi, această noţiune şi apoi să trecem la studiul diferitelor structuri algebrice.

Operaţii algebrice binare

Vom numi operaţie algebrică binară în mulţimea A≠∅, orice funcţie


f:A×A→A.
În studiul operaţiilor binare se obişnuieşte a se folosi notaţia a * b în
loc de f ((ab, )), sau alte notaţii cum sunt a o b,a + b,a-b,a − b,avb etc.

44
Dacă în mulţimea A ≠∅, s-a definit o operaţie binară *, atunci cuplul
(A, *) se numeşte grupoid. Grupoidul este un prim exemplu de structură
algebrică abstractă. Pentru înţelegerea corectă a acestei noţiuni, enumerăm
următoarele exemple de grupoizi:
1. (N, +), (Z, + ), (Q, +) şi (R, +), unde + este simbolul operaţiei de
adunare,
definită în modul cunoscut în aceste mulţimi numerice.
2. (N, •), (Z, •), (Q, •) şi (R, •), unde · este simbolul operaţiei de
înmulţire.
3. (Z, −), (Q, -) şi (R, −), unde - este simbolul operaţiei de scădere.
4. (Q*, :) şi (R*, :), unde : este simbolul operaţiei de împărţire, iar Q
= Q* −{0}
R* =R-{0}.
5. (N, *), unde * este simbolul operaţiei de aflare a celui mai mare
divizor
comun a două numere naturale.
6. (N, °), unde ° este simbolul operaţiei de aflare a celui mai mic
multiplu
comun a două numere naturale.
7. (P(M), ∩), unde ∩ este simbolul operaţiei de intersecţie a două
submulţimi
ale mulţimii M.
8. (P(M), U) unde U este simbolul operaţiei de reuniune a două
submulţimi
ale mulţimii M.
9. (A , o), unde A este mulţimea tuturor funcţiilor de la mulţimea A
la ea
însăşi, iar o este simbolul operaţiei de compunere a funcţiilor.

45
46
47
48
49
50
51
52
53
54
55
RELATII DE ECHIVALENTA. PARTITII

I) Definirea unei relatii si proprietati


O relatie este definita prin :-o multime A, numita multime de plecare
a lui
- o multime B, numita multime de sosire a
lui
- o parte G a produsului cartezian A x B,
numita
graful relatiei .
Prin definitie, spunem ca elementul x A se afla in relatia cu
elementul y B, daca si numai daca(x ;y ) G .
Acest fapt se scrie : x y si se citeste ,,elementul x din multimea A se
afla in relatia cu elementul y din multimea B. Se spune ca este o
relatie de la A la B (sau intre A si B) si se noteaza cu = ( A,B, G)
Observatii :
- Din definitie rezulta : G = { (x,y) .AxB / x y }
- Produsul cartezian a doua multimi defineste o relatie de la A la B, al
carui graf este AxB.
- Graful unei relatii poate fi uneori vizualizat fie printr o reprezentare cu
sageti, fie printr o reprezentare carteziana.
- Daca A=B, atunci relatia se numeste binara in A.
- Definitia unei relatii de la multimea A la multimea B prezinta
urmatoarele particularitati : a) un element din A poate avea mai multe
imagini in B.
b) pot exista in A elemente care sa nu aiba imagini in B
c) un element din B poate fi imaginea mai multor elemente
din B.
d) poate exista in B unul sau mai multe elemente care sa
nu fie imaginea niciunui elemnt din A
II ) Proprietati generale ale relatiilor definite intr o multime:
1) Reflexivitatea:
Definitie: o relatie definita pe o multime A este reflexiva daca
pentru orice x din multimea A avem x x.
O relatie binara in AxA este reflexiva daca graful sau contine
diagonala lui AxA.
Exemple : - in multimea numerelor intregi relatia ,,este mai mic sau
egal cu ‘’ este reflexiva deoarece pentru orice x din Z, se poate
scrie x x.
- in multimea dreptelor din plan, relatia ,,este paralela cu ‘’
este o relatie reflexiva

56
2 ) Simetria :
Definitie : o relatie definita pe o multime A se numeste
simetrica daca si numai daca pentru orice cuplu (x,y) AxA
avem x y y x .

-2-
Exemple : - pe multimea Z definim relatia (x y) x+y=0, care
este o relatie simetrica deoarece x+y=0 y+x=0.

3 )Tranzitivitatea :
Definitie :O relatie definita pe o multime A se numeste
tranzitiva daca si numai daca pentru oricare elemente x,y,z, din A
avem  (x y si y z ) (x z).
Exemple : - pe multimea Z, relatia ,,este strict mai mic decat ‘’ este
tranzitiva, deoarece pentru orice x,y,z din Z, avem (x<y si y<z)
x<z .
- pe multimea numerelor naturale N, relatia ,,este divizibil
prin’’este o relatie tranzitiva, deoarece pentru orice x,y,z din N , (x / y
si y / z ) ( x / z)

III ) RELATIA DE ECHIVALENTA


O relatie definita pe o multime E se numeste relatie de
echivalenta daca :
1) esta reflexiva 1) x E, x y
2) esta simetrica adica 2) x,y E, (x y) (y x)
3) este tranzitiva 3) x,y,z E, (x y) si (y
z) (x z)
Doua elemente x si y din E, astfel incat x y se numesc echivalente in
raport cu
relatia .
Exemple :
1)Pe multimea dreptelor din plan relatia ,,a b (a b= sau a = b)
‘’,unde a si b sunt drepte din plan , este o relatie de echivalenta.
2) Pe multimea E a elevilor din liceu relatia ,,x este coleg de clasa cu y
‘’este o relatie de echivalenta.
3) Fie multimea intregilor Z si p Z , relatia binara al carui graf G este
alcatuit din cuplurile (a,b) cu proprietatea ca : (a,b) G ( a-b)se divide
cu p, este o relatie de echivalenta. Intr adevar,
a) a-a este intotdeauna divizibil cu p
b) daca a-b se divide cu p, atunci si b-a se divide cu p.
c) daca a-b si b-c se divid cu p, atunci si a-c se divide cu p.
Aceasta relatie de echivalenta in Z se numeste congruenta modulo p si se
noteaza

57
cu : a b ( mod p).
4) Relatia obtinuta intre doua figuri plane atunci cand ele se pot
suprapune este o relatie de echivalenta.(Adjectivul ,,egal’’il folosim doar
pentru aceeasi figura).

IV )CLASE DE ECHIVALENTA SI MULTIMEA CÂT


Definitie: Fie o relatie de echivalenta definita pe multimea E si a E.
Se numeste clasa de echivalenta a elementului a in raport cu relatia
, notata C(a),multimea elementelor din E care sunt echivalente cu
a , adica,
C(a) = { x E / x a}.

-3-
Proprietatile claselor de echivalenta
1) Doua elemente ce apartin aceleiasi clase de echivalenta sunt
echivalente
intre ele.
2) Clasele a doua elemente echivalente sunt egale.
3)Doua clase de echivalenta care au in comun cel putin un element
sunt egale.
4)Relatia determina o descompunere a multimii E in multimi
disjuncte, adica o partitie.
Definitie : Se numeste partitie a multimii E orice familie de submultimi
ale lui E cu proprietatile : a) nici o submultime nu este vida
b) oricare doua submultimi sunt disjuncte
c) reuniunea tuturor submultimilor este E.
Exemplu : Fie multimea Z si relatia intre doua elemente a si b din Z, a
b data prin,
,, a – b este divizibil cu 4 ‘’
Avem proprietatile : a ) a - a=0, 0= 4k, k Z –
reflexivitatea
b) a – b = 4k b – a = - 4k, k Z –simetria
c) ( a - b= 4k si b – c = 4k ) ( a – c = 4k ),
unde k ,k ,k Z
si k = k - k . –
tranzitivitatea
Relatia ,,a – b este divizibil cu 4’’ imparte multimea Z in clase de
echivalenta:
C (0) = {.....,-8,-4,0, 4, 8, ......}. C(1) = { .....,-7, -3, 1, 5,
9, .....}
C(2) = { ......,-6, -2, 2, 6, 10, .....} C(3) = { ......, -5, -1, 3, 7,
11, .....}
Multimea acestor clase se noteaza cu Z si se numeste multimea cât.

58
Teorema: Orice relatie de echivalenta intr-o multime E determina o
partitie a lui E si reciproc, orice partitie a lui E determina o relatie de
echivalenta in E.
In general, descompunerea multimii E in submultimi formate din
elemente echivalente
intre ele, constituie o partitie: un element oarecare din E face parte dintr
o clasa de echivalenta, relatia fiind reflexiva pentru orice x din E avem x
x.
Aceste submultimi sunt nenule, disjuncte si reuniunea lor formeaza
multimea E
.
Un exemplu remarcabil : Relatia de congruenta in Z.
Fie ( Z,+, ) multimea numerelor intregi Z, inzestrata cu operatia de
adunare si inmultire si fie m N.
Intre elementele lui Z introducem relatia ,,a congruent cu b modulo m ‘’
astfel :
( a b (mod m)) ( m / (a - b ))
Aceast este o relatie de echivalenta in Z deoarece :
a) m / (a – a) ( a a ( mod m ) ) -reflexivitate
b) ( a b ( mod m) ( m / ( a – b ) m / ( b – a ) (b a
(mod m ))
- simetria
c) [ ( a b ( mod m) si b c ( mod m )] m / ( a – b ) si m /
(b – c)
[m / ( a-b +b – c) ] [ m / ( a – c )] a c (mod m ) –
tranzitivitatea

-4-

Multimea Z este impartita de relatia de congruenta in clase de


echivalenta .
Multimea cât este Z= {0, 1, 2, 3, …..,m-1. }, iar elementele ei se mai
numesc si clase de resturi modulo m.
Relati de congruenta ,,( mod m )’’ este compatibila cu adunarea,
respectiv inmultirea numerelor intregi. Aceast inseamna ca doua
congruente in raport cu acelasi modul se pot aduna membru cu membru
si se pot inmulti membru cu membru.
Pe aceasta baza se definesc pe multimea claselor de resturi doua
operatii , numite,
,, adunarea ’’ respectiv ,, inmultirea ’’ claselor, astfel :
a b a + b si a b a b , unde a si b sunt reprezentantii
claselor a si b , iar si semnifica adunarea si inmultirea claselor.

59
Deci, oricare ar fi elementul clasei lui a si elementul clasei lui b, suma
( respectiv
produsul lor ) apartine clasei a + b ( respectiv a b).

SISTEME DE DOUA ECUATII CU


DOUA NECUNOSCUTE
Un ansamblu de doua ecuatii cu doua necunoscute notat :
unde este un sistem de doua
ecuatii cu doua necunoscute.
Definitie
O pereche de numere reale(x,y) care verifica simultan cele
doua ecuatii se numeste solutie a sistemului.
A rezolva sistemul de ecuatii inseamna a-i determina solutiile.
Doua sisteme se numesc echivalente daca au aceeasi multime de
solutii.
Avem doua metode de rezolvare a unui sistem de doua ecuatii
cu doua necunoscute: metoda reducerii si metoda substitutiei.
METODA REDUCERII
Se procedeaza astfel:
1. Se inmultesc termenii unei ecuatii cu un numar, iar
termenii celeilalte ecuatii cu un alt numar astfel incat
prin adunarea sau scaderea egalitatilor sa se anuleze
termenii ce contin una din necunoscute.(termenii se
reduc)
2. Se rezolva ecuatia cu o singura necunoscuta obtinuta.
3. Se introduce valoarea necunoscutei aflate intr-una dintre
ecuatiile sistemului si se rezolva ecuatia obtinuta.( sau
se poate rezolva tot prin reducere pentru a afla a doua
necunoscuta.)
4. Perechea de numere obtinuta este solutia sistemului.
5. Este posibil ca in urma amplificarii si adunarii celor
doua ecuatii sa se anuleze toti termenii ce contin
necunoscutele.In acest caz sistemul nu are solutie unica.

60
EXERCITII
1.

Sistemul are solutia:


2.

Sistemul are solutia (7;3)

3.

Sistemul are solutia (-3;2)

METODA SUBSTITUTIEI
Se procedeaza astfel:
1. Se scoate o necunoscuta din una din ecuatii.
2. Se introduce necunoscuta scoasa in a doua ecuatie.
3. Se afla necunoscuta.
4. Cu solutia aflata se revine la prima ecuatie si se afla a
doua necunoscuta.

Exemplu:
1.

2.

61
S=
METODE COMBINATE
1.

Notam si

Avem deci :

2.

3.

Dar

62
Executarea unui program in mod simplu se face cu combinatia de taste CTRL si F5
CONSTRUCTIA PROGRAMELOR IN Visual C++.Net
1)Intru in Visual studio.net
2)File/new project
3)Visual C++ si aleg Window Command
Apas PE
OBJECT
63
BROWSER
4)dau numele proiectului
5)

APAS PE
ACEASTA
OPTIUNE

SCRIU PROGRAMUL SI
O SA–L RULEZ cu CTRL
si F5

64
Il rulez
apasand
dublu clic

asa am vazut executia

65
POINTARI SI ADRESE

66
Memoria internă poate fi privită ca o succesiune de octeţi.
Pentru a-i distinge, aceştia sunt numerotaţi. Numărul de ordine al
unui octet se numeşte adresa lui.

Orice variabilă ocupă un număr succesiv de octeţi. De


exemplu, o variabilă de tip int ocupă doi octeţi (în varianta
VISUAL C++ .NET). Adresa primului octet al variabilei se numeşte
adresa variabilei.
Variabilele de tip pointer se caracterizează prin faptul că valorile
pe care le pot memora sunt adresa ale unor variabile.

Limbajul VISUAL C++.NET face distincţie între natura


adreselor care pot fi memorate. Astfel, există adrese ale
variabilelor de tip int, adrese ale variabilelor de tip float, adrese
ale variabilelor de tip char, etc
 O astfel de variabilă - capabilă să reţină adrese – se
declară astfel:
Tip *nume
 Adresa unei variabile se obţine cu ajutorul operatorului
de referinţe “&”, care trebuie să preceadă numele
variabilei:
&Nume_variabilă;
Fie o variabilă a, de tip pointer către tipul x şi n un număr
natural. Atunci au sens operaţiile a+n şi a-n.
 a+n reprezintă adresa care se obţine ca diferenţă
între adresa lui a şi n înmulţit cu numărul de octeţi
care sunt ocupaţi de o variabilă de tipul x.
 a-n reprezintă adresa care se obţine ca diferenţă
între adresa lui a şi n înmulţit cu numărul de octeţi
care sunt ocupaţi de variabila de tipul x.
 În VISUAL C++.NET numele unui masiv (tablou)
este pointer. Vom analiza în amănunt acest fapt mai
întâi pentru vectori şi apoi pentru tablouri p-
dimensionale.
 Numele vectorului este un pointer constant (nu poate fi

//SA SE DETERMINE un exemplu prin care se pun in evidenta


//faptul ca variabile de tipuri diferite se memoreaza la
adrese diferite
#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{

67
unsigned short shortVar=5;
unsigned long longVar=65535;
long sVar = -65535;

cout << "shortVar:\t" << shortVar;


cout << " Address of shortVar:\t";
cout << &shortVar << endl;

cout << "longVar:\t" << longVar;


cout << " Address of longVar:\t" ;
cout << &longVar << endl;

cout << "sVar:\t" << sVar;


cout << " Address of sVar:\t" ;
cout << &sVar << endl;

return 0;
}
//SA SE DETERMINE un exemplu prin care se pun in evidenta
//faptul ca o variabila data rin numele ei si valoarea sa
//dar si pointerul *la numele ei e tot una
#include "stdafx.h"
#include<iostream>
using namespace std;
typedef unsigned short int USHORT;
int main()
{
USHORT myAge; // a variable
USHORT * pAge = 0; // a pointer
myAge = 5;
cout << "myAge: " << myAge << "\n";

pAge = &myAge; // assign address of myAge to


pAge

cout << "*pAge: " << *pAge << "\n\n";

cout << "*pAge = 7\n";

*pAge = 7; // sets myAge to 7

cout << "*pAge: " << *pAge << "\n";


cout << "myAge: " << myAge << "\n\n";

cout << "myAge = 9\n";

myAge = 9;

cout << "myAge: " << myAge << "\n";


cout << "*pAge: " << *pAge << "\n";

68
return 0;
}
//transmiterea prin referinta

#include "stdafx.h"
#include<iostream>
using namespace std;

void intersc(int*x,int*y)
{ int man;
man=*x;*x=*y;*y=man;
}
main()
{ int a=2,b=3;
intersc(&a,&b);
cout<<a<<" "<<b;
}

//SA SE DETERMINE un exemplu prin care se pun in evidenta


//faptul ca o adresa e diferita de valoarea unei
variabile
//data pprin pointerul variabilei respective
#include "stdafx.h"
#include<iostream>
typedef unsigned short int USHORT;
int main()
{
unsigned short int myAge = 5, yourAge = 10;
unsigned short int * pAge = &myAge; // a pointer

cout << "myAge:\t" << myAge << "\tyourAge:\t" <<


yourAge << "\n";
cout << "&myAge:\t" << &myAge << "\t&yourAge:\t"
<< &yourAge <<"\n";

cout << "pAge:\t" << pAge << "\n";


cout << "*pAge:\t" << *pAge << "\n";

pAge = &yourAge; // reassign the pointer

cout << "myAge:\t" << myAge << "\tyourAge:\t" <<


yourAge << "\n";
cout << "&myAge:\t" << &myAge << "\t&yourAge:\t"
<< &yourAge <<"\n";

cout << "pAge:\t" << pAge << "\n";


cout << "*pAge:\t" << *pAge << "\n";

cout << "&pAge:\t" << &pAge << "\n";

69
return 0;
}

//SA SE DETERMINE un exemplu prin care se pun in evidenta


//faptul ca pot pune valoarea unei variabile la o anumita
adresa
//daca accesez variabila de la acea adresa am aceiasi
valoare ca la destinatie
#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{
int intOne;
int &rSomeRef = intOne;

intOne = 5;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;

rSomeRef = 7;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;
return 0;
}
//SA SE DETERMINE un exemplu prin care se pun in evidenta
//faptul ca pot pune valoarea unei variabile la o anumita
adresa
//daca accesez variabila de la acea adresa am aceiasi
valoare ca la destinatie
//adresele sursa si destinatie ale variabilelor o sa
coincida
#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{
int intOne;
int &rSomeRef = intOne;

intOne = 5;
cout << "intOne: " << intOne << endl;
cout << "rSomeRef: " << rSomeRef << endl;

cout << "&intOne: " << &intOne << endl;


cout << "&rSomeRef: " << &rSomeRef << endl;

return 0;
}

70
//SA SE DETERMINE un exemplu prin care se pun in evidenta
//faptul ca pot pune valoarea unei variabile la o anumita
adresa
//daca accesez variabila de la acea adresa am aceiasi
valoare ca la destinatie
//adresele sursa si destinatie ale variabilelor o sa
coincida
//cand fac initializarea variabilei sursa adresa acesteia
se schimba
//dar variabila destinatie ramane la aceiasi adresa
#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{
int intOne;
int &rSomeRef = intOne;

intOne = 5;
cout << "intOne:\t" << intOne << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
cout << "&intOne:\t" << &intOne << endl;
cout << "&rSomeRef:\t" << &rSomeRef << endl;

int intTwo = 8;
rSomeRef = intTwo; // not what you think!
cout << "\nintOne:\t" << intOne << endl;
cout << "intTwo:\t" << intTwo << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
cout << "&intOne:\t" << &intOne << endl;
cout << "&intTwo:\t" << &intTwo << endl;
cout << "&rSomeRef:\t" << &rSomeRef << endl;
return 0;
}

STIVA
 Stiva este o listă pentru care singurele operaţii permise sunt:
● Adăugarea unui element în stivă;
● Eliminarea, consultarea, sau modificarea ultimului element introdus în stivă.
Stiva funcţionează pe principiul LIFO (Last In First Out) – “ultimul
intrat, primul ieşit”.
Pentru a înţelege modul de lucru cu stiva, ne imaginăm un număr n de
farfurii identice, aşezate una peste alta (o “stivă” de farfurii). Adăugarea sau
scoaterea unei farfurii se face, cu uşurinţă, numai în vârful stivei.

Oricât ar părea de simplu principiul stivei, el are consecinţe uriaşe în


programare.

71
Stivele se pot aloca secvenţial (ca vectori). Fie ST[i] un vector. ST[1], ST[2], … ,
ST[n] pot reţine numai litere sau numai cifre. O variabilă k indică în
permanenţă vârful stivei, adică ultimul element introdus.

În stivă iniţial vidă se introduce litera A, vârful stivei va fi la nivelul 1,


(k=1);

B
A
A Introducem în stivă litera B, deci k va lua valoarea 2.

Scoatem din stivă pe B (A nu poate fi scos deocamdată); k=1

Scoatem din stivă pe A; stiva rămâne vidă k=0

Observaţii:

În mod practic, la scoaterea unei variabile din stivă, valoarea variabilei ce indică
vârful stivei scade cu 1, iar atunci când scriem ceva în stivă, o eventuală valoare
reziduală se pierde.

Pe un anumit nivel se reţine, de regulă, o singură informaţie (literă sau cifră), însă este
posibil, aşa cum va rezulta din exemplele prezentate în lucrare, să avem mai multe
informaţii, caz în care avem stive duble, triple, etc.

În cazul stivei, alocarea secvenţială nu prezintă mari dezavantaje, ca în cazul mai


general, al listelor, pentru că nu se fac operaţii de inserare sau ştergere în interiorul
stivei. Singurul dezavantaj, în comparaţie cu alocarea dinamică înlănţuită este dat de
faptul că numărul de noduri care pot fi memorate la un moment dat este mai mic –
depinde de gradul de ocupare al segmentului de date.

În literatura de specialitate veţi întâlni termenul PUSH pentru operaţia de adăugare în


stivă a unei înregistrări şi POP, pentru extragere.

Exemple:

□ Funcţia Manna-Pnueli. Se citeşte xЄZ. Se cere programul pentru calculul funcţiei:

F(x)= x-1, x≥12


F(F(x+2)), x<12

72
Vom începe prin a studia modul de calcul al funcţiei pentru x=15 şi x=8.

f(15)=14;
f(8)=f(f(10))=f(f(f(12)))=f(f(11))=f(f(f(13)))=f(f(12))=f(11)=f(f(13))= f(12)=11.
Algoritmul va folosi o stivă ST şi o variabilă k, ce indică în permanenţă vârful
stivei. Algoritmul se bazează pe următoarele considerente:
■ la o nouă autoapelare a funcţiei f, se urcă în stivă (k se incrementează cu 1) şi se
pune noua valoare.

■ în situaţia în care pentru valoarea aflată pe nivelul k se poate calcula funcţia, se


coboară în stivă, punându-se pe acest nivel noua valoare.

■ algoritmul se încheie când se ajunge în stivă la nivelul 0.

Pentru exemplul dat, prezentăm schematic funcţionarea sa:

12 13

10 10 11
11

8 8 8 8 8

12 13

8 11 11 12
f=11

//sa se determine FUNCTIA MANNA PNUELI CU STIVA


F(x)= x-1, x≥12
F(F(x+2)), x<12
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int st[100], n, k;
main( )
{ cout<<"n="; cin>>n;
k=1; st[1]=n;
while (k>0)

73
if (st[k]<12)
{ k++;
st[k]=st[k-1]+2;
}
else
{ k--;
if (k>0) st[k]=st[k+1]-1;
}
cout<<"n="<<st[1]-1;
}
Funcţia lui Ackermann. Se dă funcţia următoare, definită pe produsul cartezian NXN.. Se citesc
m şi n. Să se calculeze Ack(m, n).

n+1, m=0
Ack(m,n)= Ack(m-1, 1), n=0
Ack(m-1, Ack(m, n-1)), altfel

Pentru a elabora algoritmul, studiem un exemplu numeric:

ack(2,1)=ack(1,ack(2,0))=ack(1, ack(1,1))=ack(1, ack(0, ack(1,0))= ack(1, ack(0, ack(0,1))=ack(1,


ack(0,2))=ack(1,3)=ack(0, ack(1,2))= ack(0, ack(0, ack(1,1))=ack(0, ack(0, ack(0, ack(1,0))))=
ack(0, ack(0, ack(0, ack(0,1))))=ack(0, ack(0, ack(0,2)))=ack(0,,ack(0,3))= ack(0,4)=5.

Pentru calculul acestei funcţii, folosim o stivă dublă, ST. Iniţial, valorile m şi n se reţin la nivelul
1. Pe nivelul k al stivei se reţin valorile curente m şi n. În funcţie de valorile acestora se
procedează astfel:

■ pentru m şi n diferite de 0, este necesar un nou calcul de funcţie, caz în care se urcă în stivă şi
pe noul nivel se pun argumente m şi n-1.

■ pentru cazul n=0, se rămâne pe acelaşi nivel în stivă, punând în locul lui m valoarea m-1, iar în
locul lui n valoarea 1.

■ în situaţia în care m=0, funcţia se poate calcula; se coboară în stivă şi se înlocuieşte valoarea lui
m cu m-1, valoarea lui n cu valoarea calculată anterior.

În continuare, prezentăm grafic modul de funcţionare a algoritmului pentru exemplul ack(2,1):

10 01
20 11 11 11
21 21 21 21 21

10
11 11
02 13 12 12
21 13 12 13 13

74
01
11 02
12 12 03
13 13 13 04

//sa se determine FUNCTIA lui Ackerman


n+1, m=0
Ack(m,n)= Ack(m-1, 1), n=0
Ack(m-1, Ack(m, n-1)), altfel

#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int st[10000][2];
main( )
{ int m, n, k;
cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
k=1; st[k][0] =m; st[k][1] =n;
while (k>0)
if (st[k][0] && st[k][1])
{
k++;
st[k][0] =st[k-1][0];
--st[k-1][1];
st[k][1] =st[k-1][1];
}
else
if (!st[k][1])
{
--st[k][0];
st[k][1] = 1;
}

else
{
k--;
if (k>0)
{ --st[k][0] ;
st[k][1] =st[k+1][1] +1;
}
}
cout<<"ac("<<m<<", "<<n<<") = "<<st[1][1] +1;
}

75
//sa se determine dinamic notiunea de stiva cu operatiile
corespunzatoare
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct Nod
{
int info;
Nod* adr_inap;
};

Nod* v;
int n;

void Push( Nod*& v, int n)


{
Nod* c;
if (!v)
{
v= new Nod; v->info=n; v->adr_inap=0;
}
else
{c= new Nod; c->info=n; c->adr_inap=v;
v=c;
}
}

void Pop(Nod*& v)
{
Nod* c;
if (!v) cout<<"stiva este vida";
else
{
c=v;
cout<<"am scos"<< c->info<<endl;
v=v->adr_inap;
delete c;
}
}

main( )
{ Push(v,1); Push(v,2); Push(v,3);
Pop(v); Pop(v); Pop(v); Pop(v);
}
//sa se determine dinamic cu notiunea de stiva functia
Manna Pnueli
#include "stdafx.h"
#include<iostream>
#include<math.h>

76
using namespace std;

struct Nod
{ int info;
Nod* adr_inap;
};
Nod* v;
int n;
void Push(Nod*& v, int n)
{ Nod* c;
if (!v)
{ v= new Nod; v->info=n; v->adr_inap=0;}
else
{ c= new Nod; c->info=n; c->adr_inap=v;
v=c;
}
}
void Pop (Nod*& v)
{ Nod* c;
if (!v) cout<< "stiva este vida";
else
{ c=v;
cout<<" am scos"<<c->info<<endl;
v=v->adr_inap;
delete c;
}
}
main( )
{ int Man;
cout<<"n="; cin>>n;
Push(v,n);
while (v)
if (v->info<12)
{
Push(v,v->info+2);
}
else
{ Man=v->info;
Pop(v);
if (v) v->info=Man-1;
}
cout<<"f="<<Man-1;
}
COADA

 O coadă este o listă pentru care toate inserările sunt făcute la unul din capete,
toate ştergerile (consultările, modificările) la celălalt capăt.

Coada funcţionează pe principiul FIFO (First In First Out) – “primul


intrat, primul ieşit”.

77
Este cu totul nerecomandabilă alocarea secvenţială a cozii, deoarece în această situaţie, are loc
un fenomen de migraţie a datelor către ultimele componente ale vectorului (cele de indice mare).

Să presupunem că simulăm o coadă cu ajutorul unui vector cu zece componente, care reţin
numere întregi. Introducem în coadă, pe rând, numerele 1, 2, 3, 4.

1 2 3 4

Dacă scoatem din coadă pe 1 şi introducem în coadă pe 5, coada va arăta astfel:

2 3 4 5

Scoatem din coadă pe 2 şi introducem pe 6:

3 4 5 6

Se observă acest fenomen de “migraţie”.

Alocarea dinamică înlănţuită a cozii. O variabilă v va reţine adresa elementului care urmează a
fi scos (servit). O alta, numită sf, va reţine adresa elementului introdus în coadă. Figura următoare
prezintă o coadă în care primul element care urmează a fi scos are adresa în v, iar ultimul introdus are
adresa sf.

v sf

7 3 5 2

//sa se determine dinamic cu notiunea de coada


#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct Nod
{
int info;
Nod* adr_urm;
};

Nod* v, *sf;
int n;

78
void Pune( Nod*& v, Nod*& sf, int n)
{
Nod* c;
if (!v)
{ v= new Nod; v->info=n; v->adr_urm=0;
sf=v;
}
else
{ c= new Nod;
sf->adr_urm=c;
c->info=n;
c->adr_urm=0;
sf=c;
}
}

void Scoate(Nod*& v)
{ Nod* c;
if (!v) cout<<"coada este vida"<<endl;
else
{ cout<<"Am scos"<<v->info<<endl;
c=v; v=v->adr_urm;
delete c;
}
}

void Listare(Nod* v)
{ Nod* c=v;
while ( c )
{ cout<<c->info<<" ";
c=c->adr_urm;
}
cout<<endl;
}
main( )
{ Pune(v, sf, 1); Pune(v, sf, 2); Pune(v, sf, 3);
Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
}

79
DIVIDE ET IMPERA

Descrierea metodei

Dându-se o funcţie care lucrează asupra a n date de intrare, tehnica


“DIVIDE ET IMPERA” (Desparte şi stăpâneşte) presupune desfacerea
(divizarea) intrărilor în K submulţimi distincte, , care produc k
subprobleme. Aceste probleme trebuie rezolvate şi apoi trebuie gasită o metodă
de combinare a subsoluţiilor pentru a da soluţia întregii probleme.
Dacă subproblemele sunt încă mari tehnica poate fi reaplicată, dacă ele
sunt de acelaşi tip cu problema generală şi acesta este cazul cel mai frecvent.
Reaplicarea metodei conduce la o procedură recursivă. În acest fel se generează
subprobleme din ce în ce mai mici care în cazurile cele mai simple, se rezolvă
fără a fi redivizate. De obicei problema se împarte în două subprobleme de
acelaşi gen.
Această metodă e una din cele mai larg folosite.
Fie n intrări memorate în tabloul A (1:n) . Procedura generală de tip funcţie, cu
numele DETI este apelată iniţial prin DETI (1,n). Funcţia DETI (p,q), rezolvă o
problemă cu intrările A(p,q).
Descrierea funcţiei este următoarea:

procedure DETI(p,q)
global n,A(1:n);integer p,q,m // //
if MIC(p,q) then return(G(p,q))
else
m:=DIVIDE(p,q);
return(combină (DETI(p,m),DETI(m+1,q)))
endif
end DETI

//sa determine prin divide et impera maximul dintr-un


vector
#include "stdafx.h"
#include<iostream>
using namespace std;
int v[10],n;

int max(int i,int j)


{ int a,b;
if (i==j) return v[i];
else
{ a=max(i,(i+j)/2);
b=max((i+j)/2+1,j);
if (a>b) return a;
else return b;

80
}
}
main ( )
{ cout<<"n=";cin>>n;
for (int i=1;i<=n;i++)
{cout<<"v["<<i<<"]=";cin>>v[i];}
cout<<"max="<<max(1,n);
}
//sa determine prin divide et impera GASIREA UNUI ELEMENT
//dintr-un vector prin cautare binara
#include "stdafx.h"
#include<iostream>
using namespace std;

int v[100],n,nr;

void caut(int i,int j)


{ if (nr==v[(i+j)/2])
cout<<"gasit"<<" "<<"indice"<<(i+j)/2;
else
if(i<j)
if (nr<v[(i+j)/2])
caut(i,(i+j)/2-1);
else caut ((i+j)/2+1,j);
};
main( )
{ cout<<"n=";cin>>n;
for (int i=1;i<=n;i++)
{ cout<<"v["<<i<<"]=";cin>>v[i];}
cout<<"nr=";cin>>nr;
caut(1,n);
}
//sa determine prin divide et impera sortarea elementelor
//dintr-un vector prin interclasare
#include "stdafx.h"
#include<iostream>
using namespace std;

int a[10],n;

void sort(int p,int q,int a[10])


{
int m;
if (a[p]>a[q])
{
m=a[p];
a[p]=a[q];
a[q]=m;}
}
void interc(int p,int q,int m,int a[10])
{

81
int b[10],i,j,k;
i=p;j=m+1;k=1;
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;j<=q;j++)
{
b[k]=a[i];
k=k+1;
}
k=1;
for (i=p;i<=q;i++)
{
a[i]=b[k];
k=k+1;
}
}
void divimp (int p,int q,int a[10])
{
int m;
if ((q-p)<=1) sort(p,q,a);
else
{ m=(p+q)/2;
divimp(p,m,a);
divimp(m+1,q,a);
interc(p,q,m,a);
}
}
main( )
{ int i;
cout<<"n=";cin>>n;
for (i=1;i<=n;i++)
{
cout<<"a["<<i<<"]=";cin>>a[i];}

82
divimp(1,n,a);
for (i=1;i<=n;i++)
cout<<a[i]<<" ";
}
//sa determine prin divide et impera sortarea elementelor
//dintr-un vector prin sortare rapida
#include "stdafx.h"
#include<iostream>
using namespace std;

int a[100],n,k;

void poz(int li,int ls,int& k,int a[100])


{
int i=li,j=ls,c,i1=0,j1=-1;
while (i<j)
{ if (a[i]>a[j])
{
c=a[j];
a[j]=a[i];
a[i]=c;
c=i1;
i1=-j1;
j1=-c;
}
i=i+i1;
j=j+j1;
k=i;
}
}

void quick (int li,int ls)


{ if (li<ls)
{ poz (li,ls,k,a);
quick(li,k-1);
quick(k+1,ls);
}
}
main( )
{ int i;
cout<<"n=";cin>>n;
for (i=1;i<=n;i++)
{ cout<<"a["<<i<<"]=";cin>>a[i];}
quick(1,n);
for (i=1;i<=n;i++) cout <<a[i]<<endl;
}
//sa determine prin divide et impera mutarea turnurilor
din Hanoi
#include "stdafx.h"
#include<iostream>
using namespace std;

83
char a,b,c;
int n;

void han(int n,char a,char b,char c)


{
if (n==1) cout<<a<<b<<endl;
else
{
han(n-1,a,c,b);
cout<<a<<b<<endl;
han(n-1,c,b,a);
}
}
main ( )
{
cout<<"N=";cin>>n;
a='a';
b='b';
c='c';
han(n,a,b,c);
}

84
Tehnica backtracking

Aspecte teoretice
Aceasta tehnica se foloseste in reezolvarea problemelor care indeplinesc simultan
urmatoarele conditii:
 solutia lor poate fi pusa sub forma unui vector S=x1,x2,..,xn,cu x1 apartine de
de A1,x2 apartine de A2,…,xn apartine de An;
 multimile A1,A2,…,An sunt multimi finite,iar elementele lor se considera ca
se afla intr-o relatie de ordine bine stabilita;
 nu se dispune de o alta metoda de rezolvare,mai rapida.
Observatii:
 nu pentru toate problemele n este cunoscut de la inceput;
 x1,x2,…,xn pot fi la randul lor vectori;
 in multe probleme,multimile A1,A2,…,An coincide.
Observatie: tehnica Backtracking are ca rezultat obtinerea tuturor solutiilor
problemei.In cazul in care se cere o singura solutie,se poate forta oprirea,atunci cand a
fost gasita.
 Pentru usurarea intelegerii metodei,vom prezenta o rutina unica(aplicabila
oricarei probleme),rutina care este elaborate folosind structura de stiva.Rutina
va apela functii care au intotdeauna acelasi nume si care,din punct de vedere al
metodei,realizeaza acelasi lucru.Sarcina rezolvitorului este sa scrie
explicit,pentru fiecare problema in parte,functiile apelate de rutina
backtracking.
 Evident o astfel de abordare conduce la programe lungi.Nimeni nun e opreste
ca,dupa intelegerea metodei,sa scriem programe scurte,specifice fiecarei
probleme in parte(de exemplu,scurtam substantial textul doar daca renuntam
utilizarea unor functii,scriind instructiunile lor chiar in corpul rutinei).
De exemplu,pentru generarea permutarilor multimii {1,2,3…,n},orice nivel al stivei
va lua valori de la 1 la n.Initializarea unui nivel(oarecare) se face cu valoarea
0.Functia de initializare se va numi init( ).
→ Gasirea urmatorului element al multimii Ak+1,element netestat,se face cu ajutorul
functiei int Am_Succesor( ).Daca exista successor,acesta este pus in stiva si functia
returneaza 1,altfel functia returneaza 0.
→ Testul daca s-a ajuns sau nu la solutia finala se face cu ajutorul functiei int Solutie(
)
→ Solutia se tipareste cu ajutorul functiei Tipar( ).
→Testarea conditiilor de continuare(adica daca avem sansa sau nu ca prin valoarea
aflata pe nivelul k+1 sa ajungem la solutie) se face cu functia int E_Vakid( )
careintoarce 1 daca conditiile sunt indeplinite,sau 0 in caz contrar.

//sa determine prin backtracking permutarile numerelor de


la 1 la n
#include "stdafx.h"
#include<iostream>
using namespace std;

85
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 (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
back( );
}
//Prin Bactracking sa se rezolve problema damelor

86
//ex:n=4
D

D
D
D
//Adica:2413

#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdio.h>
#include <math.h>
int st[100],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[k]==st[i]) return 0;
else
if (abs(st[k]-st[i])==abs(k-i) ) 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 (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{

87
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
back( );
}
//sa determine prin backtracking aranjamente de n
elemente luate cate // k ,conteaza ordinea adica (1 2) nu
e tot una cu (2 1)
#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdio.h>
#include <math.h>

int st[10],n,k,p;

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[k]==st[i]) return 0;
return 1;
}
int Solutie( )
{
return (k==p);}

void Tipar( )
{
for (int i=1; i<=p;i++) cout<<st[i];
cout<<endl;
}

88
void back( )
{int AS;
k=1; Init( );
while (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
cout<<"p="; cin>>p;
back( );
}
//sa determine prin backtracking combinari de n elemente
luate cate k
//NU conteaza ordinea adica {1 2} e tot una cu {2 1} ca
multimi de elemente
#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdio.h>
#include <math.h>

int st[10],n,k,p;
void Init ( )
{ if (k>1) st[k]=st[k-1];
else st[k]=0;
}

int Am_Succesor ( )
{ if (st[k]<n-p+k)
{ st[k]++;
return 1;
}
else return 0;
}
int E_Valid( )
{
for (int i=1; i<k; i++)

89
if (st[k]==st[i]) return 0;
return 1;
}

int Solutie ( )
{ return k==p;}

void Tipar ( )
{ for ( int i=1; i<=p;i++) cout <<st[i];
cout<<endl;
}

void back( )
{int AS;
k=1; Init( );
while (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
cout<<"p="; cin>>p;
back( );
}
//sa determine prin backtracking produsul cartezian a N
multimi
#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdio.h>
#include <math.h>

int st[10],a[10],n,k;

void Init( )
{ st[k]=0; }

int Am_Succesor ( )
{ if (st[k]<a[k])

90
{ st[k]++; return 1;}
else return 0;
}
int E_Valid ( )
{ 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 (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
for (int i=1; i<=n; i++)
{ cout<<"a["<<i<<"]="; cin>>a[i];}

back( );
}
//sa determine prin backtracking problema colorarii
hartilor
#include "stdafx.h"
#include<iostream>
using namespace std;
#include<stdio.h>
#include <math.h>

int st[10],a[20][20],n,k;

91
void Init( )
{ st[k]=0; }

int Am_Succesor ( )
{ if (st[k]<4)
{st[k]++;
return 11;
}
else return 0;
}

int E_Valid( )
{
for (int i=1; i<=k-1; i++)
if ( st[i]==st[k] && a[i][k]==1) return 0;
return 1;
}
int Solutie( )
{ return k==n;}

void Tipar( )
{ cout<<"varianta"<<endl;
for (int i=1;i<n; i++)
cout<<"tara"<<i<<"culoarea"<<st[i]<<endl;
cout<<endl;
}
void back( )
{int AS;
k=1; Init( );
while (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
{
if (Solutie( )) Tipar( );
else
{
k++; Init( );
}
}
else k--;
}
}

main ( )
{ cout<<"n=";cin>>n;
for (int i=1; i<=n; i++)
for (int j=1; j<=i-1;j++)
{ cout<<"a["<<i<<","<<j<<"]="; cin>>a[i][j];
a[j][i]=a[i][j];
}

92
back( );
}

93
GREEDY

Descrierea metodei
Metoda Greedy este o metodă generală de elaborare a
algoritmilor; provenienţa numelui o vom explica mai jos. În esenţă, ea
se aplică problemelor în care se dă o mulţime A conţinând n date de
intrare cerându-se să se determine o submulţime B a sa care să
îndeplinească anumite condiţii pentru a fi acceptată; cum în general
există mai multe astfel de mulţimi se mai dă şi un criteriu conform
căruia dintre submulţimile acceptabile (numite soluţii posibile) să
alegem una singură (numită soluţie optimă) ca rezultat final. Soluţiile
posibile au următoarea proprietate: dacă B este o soluţie posibilă şi
CB, atunci şi C este o soluţie posibilă; vom presupune că Ø este
totdeauna soluţie posibilă.
Metoda Greedy tratează acest tip de probleme în următoarele
două moduri, care urmează aceeşi idee, dar diferă doar prin ordinea
de efectuare a unor operaţii.
Se pleacă de la soluţia vidă. Se alege pe rând într-un anumit
fel un element din A neales la paşii precedenţi. Dacă adăugarea lui la
soluţia parţială anterior construită conduce la o soluţie posibilă,
construim noua soluţie posibilă prin adăugarea elementului ales (vezi
procedura GREEDY1).

FORMA 1:

Procedure GREEDY1(A,n,B);
BØ
For i=1,n
Call ALEGE(A,i,x)
Call POSIBIL(B,x,v)
If v=1 then call ADAUG(B,x)
Endif
Repeat
Return
End

FORMA 2:

Procedure GREEDY2(A,n,B);
Call PREL(A); BØ
For i=1,n
Call POSIBIL(B,ai,v)
If v=1 then call ADAUG(B,ai)
Endif
Repeat
End

94
//sa se determine ordinea de planificare a spectacolelor
stiind cand
//incepe si cat dureaza
//ex:2
//n=2
//2
//20
//2
//40
//3
//10
//3
//20
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int s[2][10],o[10],n,i,h1,m1,h2,m2,ora;
void sortare( )
{
int gata,m,i;
do
{ gata=1;
for (i=1;i<=n-1;i++)
if (s[i][o[i]] >s[1][o[i+1]])
{
m=o[i];
o[i]=o[i+1];
o[i+1]=m;
gata=0;
}
}
while (!gata);
}
main( )
{ cout<<"n=";cin>>n;
for (i=1;i<=n;i++)
{o[i]=i;
cout<<"ora de inceput pentru spectacolul"<<i<<"( h h) m
m )=";
cin >>h1>>m1;
s[0][i]=h1*60+m1;
cout <<"oradesfarsit pentru spectacolul "<<i<<"(hh mm)=";
cin>>h2>>m2;
s[1][i]=h2*60+m2;
}
sortare( );
cout<<"ordinea spectacolelor este"<<endl<<o[1]<<endl;
ora=s[1][o[1]];
for (i=2;i<=n;i++)

95
{ if (s[0][o[i]]>=ora)
{cout<<o[i]<<endl;
ora=s[1][o[i]];}
}
}
//sa se determine alegerea de obiecte pentru umplerea
//unui rucsac cunoscand capacitatea lui,greutatea
obiectelor ,valoarea lor
//Ne intreseaza sa ducem maxim cantitativ si cu valoare
maxima
ex:3
// 3
// 2
// 2
// 4
// 1
// 6
// 3
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

double c[9],g[9],ef[9],gv,man,castig;
int n,i,man1,inv,ordine[9];
main( )
{cout<<"Greutatea ce poate fi transportata=";cin>>gv;
cout<<"Numar de obiecte=";cin>>n;
for(i=1;i<=n;i++)
{
cout<<"c["<<i<<"]=";cin>>c[i];
cout<<"g["<<i<<"]";cin>>g[i];
ordine[i]=i; ef[i]=c[i] /g[i];
}
do
{
inv=0;
for(i=1;i<=n-1;i++)
if(ef[i]<ef[i+1])
{
man=ef[i];ef[i]=ef[i+1];ef[i+1]=man;
man=c[i]; c[i]=c[i+1]; c[i+1]=man;
man=g[i];g[i]=g[i+1]=man;
inv=1;
man1=ordine[i];ordine[i]=ordine[i+1];ordine[i+1]=man1;
}
}
while (inv);
i=1;
while(gv>0 && i<=n)
{

96
if (gv>g[i])
{ cout <<"Obiectul "<<ordine[i]<< " "<<1<<endl;
gv-=g[i];castig+=c[i];
}
else
{cout<<"Obiectul "<<ordine[i]<<" " <<gv /g[i]<<endl;
castig+c[i]*gv /g[i];
gv=0;
}
i++;
}
cout<<"Castig total="<<castig;
}

97
PROGRAMARE DINAMICA

Alaturi de Greedy,programarea dinamica este o tehnica ce conduce, de


cele mai multe ori, la un timp de calcul polinomial.Mai mult, ea furnizeaza in
totdeauna solutia optima .Din nefericire, programarea dinamica nu se poate
aplica tuturor problemelor, ci numai care indeplinesc anumite conditii.
Se considera o problema in care rezultatul se obtine ca urmare a unui sir de
decizii D1, D2,......Dn. In urma decizei D1 sistemul evolueaza din starea S0 in
starea S1,in urma decizei D2 sistemul evolueaza din starea S1 in starea S2,....,in
urma decizei Dn sistemul evolueaza din starea Sn-1 in stareaSn.
Daca D1, D2,....Dn este un sir de decizii care comduce sistemul in mod optim din
S0 in Sn,atunci trebuie indeplinita una din conditiile urmatoare (principiul de
optimalitate):
1)Dk...Dn este un sir de decizii ce conduce optim sistemul din starea Sk-1 in
stareaSn,Ak,1<=k<=n;
2)D1....Dk este un sir de decizii ce conduce optim sistemul din starea S0 in
stareaDk,Ak,1<=K<=;
3)Dk+1...Dn,D1...Dk sunt siruri de decizii care conduc optim sistemul starea Sk
in starea Sn, respectiv din starea D0 in starea Sk,Ak,1<=k<=n.
=>Daca principiulde optimalitate se verifica in forma 1,spunem ca se aplica
programarea dinamica metoda inainte.
=>Daca principul de oplimalitate se verifica in forma 2, spunem ca se aplica
programarea dinamica inapoi.
=>Daca primcipiul de optimalitate se verifica in forma 3, spunem ca se aplica
programarea dinamica metoda mixta.
Programarea dinamica se poate aplica problemelor la care optimul general
implica optimul partial.
Faptulca optimul general determina optimul partial, nu inseamna ca optimul
partial determina optimul general.
cu toate acestea, faptul ca potimul general impune optimul partial ne este de
mare ajutor:cautam optimul general,intre optimele partiale, pe care le retinem la
fiecare pas.Oricum,cautarea se reduce considerabil.

//sa se determine prin programare dinamica suma maxima


//in parcurgerea si sumarea elemntelor
//din matricea triunghiulara ,stiind ca ma pot deplasa
numai o pozitie jos sau dreapta-jos
//ex:2
// 2
// 3
// 5

Exemplu:n=4;
2
3 5
6 3 4
5 6 1 4
Se pot forma mai multe sume:
S1=2+3+6+5=16;
S2=2+5+4+1=12;
Sk=2+3+6+6=17
98
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int t[50][50],c[50][50],drum[50][50],n,i,j;
main()
{

cout<<"n=";cin>>n;
for (i=1;i<=n;i++)
for (j=1;j<=i;j++)
{ cout <<"t["<<i<<","<<j<<"]=";
cin>>t[i][j];
}
for (j=1;j<=n;j++) c[n][j]=t[n][j];
for (i=n-1;i>=1;i--)
{
for (j=1;j<=i;j++)
if (c[i+1][j]<c[i+1][j+1])
{
c[i][j]=t[i][j]+c[i+1][j+1];drum[i][j]=j+1;
}
else
{
c[i][j]=t[i][j]+c[i+1][j];
drum[i][j]=j;
}
};

cout<<"suma maxima="<<c[1][1]<<endl;
i=1;
j=1;
while (i<=n)
{
cout<<t[i][j]<<endl;
j=drum[i][j];
i++;
};
}
//sa se determine prin programare dinamica
//un subsir crecator care are maxim de componente dar sa
fie crescator
// ex:5
// 4

99
// 1
// 7
// 6

L=(3,3,2,2,1).
Componentele vectorului L au fost calculate astfel:
-cel mai lung subsir care se poate forma cu elementul 7,aflat pe ultima pozitie,are lungimea 1;
-cel mai lung subsir care se poate forma cu elementul 6 aflat pe pozitia 4 are lungimea
(1+L(5)), pentru ca pe pozitia 5se gaseste elementul 7 care este mai mare decat 6;
-cel mai lung subsir care se poate forma cu elementul aflat pe pozitia 3 are lungimea2(1+L(5))
pentru ca 7 este egal cu 7;
-algoritmul continua in acest mod pana se completeazaL(1).
// 7

#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int v[20],l[20],n,i,k,max,t;
main()
{
cout<<"n="; cin>>n;
for(i=1;i<=n;i++)
{cout<<"v["<<i<<"]=";cin>>v[i];
}
l[n]=1;
for(k=n-1;k>=1;k--)
{
int max=0;
for(i=k+1;i<=n;i++)
if ( v[i]>=v[k] && l[i]>max)
max=l[i];
l[k]=1+max;
}
int max=l[1]; t=1;
for(k=1; k<=n;k++)
if (l[k]>max)
{max=l[k]; t=k;}
cout<<"lungimea maxima:"<<max<<endl<<v[t]<<endl;
for(i=t+1;i<=n;i++)
if (v[i]>v[t]&& l[i] ==max-1)
{cout<<v[i] << endl;
max--;
}
}
//sa se determine prin programare dinamica
//imultirea otima a unui sir de matrice

100
// ex:4
// 10
// 1
// 10
// 1
// 10

Sa consideram produsul de matrice A1xA2x....xAn


(A1(d1,d2),A2(d2,d3)...,An(dn,dn+1)).Se cunoaste ca legea de compozitie produs de matrice
nu este comutativa in schimb este asociativa.De exemplu ,daca avem de inmultit trei m atrice
A,B,C produsul se poate face in doua moduri:(AxB) xC; Ax(BxC).Este interesant de observat
ca nu este indiferent modul de inmultire a celor n matrice.Sa considera ca avem de inmultit
patru matrice A1(10,1),A2(1,10),A3(10,1),A4(1,10).
Pentru inmultirea lui A1 cu A2 se fac 100 de inmultiri si se obtine o matrice cu 10 lini si 10
coloane.Prin inmul
tirea acesteia cu A3 se fac 100 de inmultiri si se obtine o matrice cu 10 linii si o coloana.daca
aceasta matrice se inmulteste cu A4 se fac 100 inmultiri. In concluzie, daca acest produs se
efectueaza in ordine naturala,se efectueaza 300 inmultiri.
SAefectuam acelas produs in ordinea care rezulta din expresia A1x((A2XA3) x
A4).Efectuand produsul A2 cu A3 se efectueaza 10 inmultiri si se obtine o matrice cu o linie
si cu o coloana.aceasta matrice se inmulteste cu A4 se fac 10 inmultiri si se obtine o matrice
cu 1 linie si 10 coloane.Daca o inmultim pe aceasta cu prima, efectuam 100 inmtltiri ,
obtinand rezultatul final cu numai 120 de inmultiri.

#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

int i,n,dim[10];
int a[10][10];

void costopt(int n,int dim[10],int a[10][10])


{int k,i,j,l;
long m;
for (i=1;i<=n;i++) a[i][i]=0;
for (k=1;k<=n-1;k++)
for (i=1;i<=n-k;i++)
{ j=i+k;
a[i] [j]=100000;
for(l=i;l<=j;l++)
{m=a[i] [l] +a[l+1] [j]+dim[i]*dim[l+1]*dim[j+1];
if (a[i] [j]>m)
{
a[i] [j]=m;;
a[j] [i]=l;
}
}
}

101
cout<<"cost optim:"<<a[1][n]<<endl;

}
main()
{cout<<"n=";cin>>n;
for (i=1;i<=n+1;i++)
{ cout<<"d=";cin>>dim[i];}
costopt(n,dim,a);
}

Alocarea dinamica a memoriei

Variabile de tip pointer


Memoria interna poate fi privita ca o succesiune de octeti.Numarul de ordine al unui octet
se numeste adresa lui.Adresa unei variabile nu trebuie confundata cu valoarea pe care
aceasta o memoreaza.
Definitie: Adresa primului octet al variabilei se numeste adresa variabilei.
Adresele variabilelor se memoreaza cu ajutorul variabilelor de tip pointer.
Tipul unei variabile de tip pointer se declara astfel:
tip *nume
Adresa unei variabile se obtine cu ajutorul operatorului ‘ & ‘, care trebuie sa preceada
numele variabilei:
&Nume_variabila;

//sa se puna in evidenta notiunea de pointer si adresa

#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;
void main()
{
int a=7, *adr=&a;
cout<<*adr;
}
//sa se puna in evidenta notiunea de pointer si adresa
//la structuri de date
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct elev
{

102
char nume[20], prenume[20];
};

void main()
{
elev a, *adra=&a;
strcpy(a.nume, "Bojian");
strcpy(a.prenume, "Andronache");
cout<<(*adra).nume<<" "<<(*adra).prenume<<endl;

Alocarea dinamica a memoriei

Spatiul necesar memorarii este rezervat in HEAP, in timpul executiei


programului, iar atunci cand variabila nu mai este utila, spatiul din
memorie este eliberat.
In C++, pentru alocarea dinamica se utilizeaza urmatorii operatori:
 Operatorul new aloca spatiu in HEAP pentru o variabila
dinamica.Dupa alocare, adesa variabilei se atribuie lui p, care este o
variabila de tip pointer catre tip:
p=new tip
 Operatorul delete elibereaza spatiul rezervat pentru variabila a
carei adresa este retinuta in p.Dupa eliberare, continutul variabilei p
este nedefinit.
delete p
//sa se puna in evidenta notiunea de alocare dinamica si
eliberarea spatiului
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

void main()
{
int *adr1;
adr1=new int;
*adr1=7;
cout<<*adr1;
delete adr1;

103
}
//sa se puna in evidenta notiunea de alocare dinamica si
eliberarea spatiului
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

void main()
{
float* adresa;
adresa=new float;
*adresa=7.65;
cout<<adresa<<endl;
cout<<*adresa;
delete adresa;
}
//sa se puna in evidenta notiunea de alocare dinamica si
//eliberarea spatiului la structuri
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct inreg
{ char nume[20], prenume[20];
int varsta;
};

void main()
{ inreg* adr;
adr=new inreg;
cin>>adr->nume>>adr->prenume>>adr->varsta;
cout<<adr->nume<<adr->prenume<<adr->varsta;
delete adr;
}

104
Alocarea dinamica a masivelor
 Un tablou p-dimensional se declara astfel:
tip nume[n1] [n2] … [np]
 Numele tabloului de tip p-dimensional de mai sus este pointer constant catre un
tablou p-1 dimensional de forma [n2] … [np], care are componentele de baza de
acelasi tip cu cele ale tabloului.
 Un pointer catre un tablou k dimensional cu [l1] [l2] …[lk] componente de un
anumit tip
se declara astfel:
tip (*nume) [l1] [l2] …[lk];
 Intrucat numele unui masiv p dimensional este pointer catre un masiv p-1
dimensional, pentru a aloca dinamic un masiv se va utiliza un pointer catre masive
p-1 dimensionale (ultimele p-1 dimensiuni).

//sa se puna in evidenta notiunea de alocare dinamica si


//eliberarea spatiului la tablouri
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

void main()
{ int *a=new int[4];
a[3]=2;
cout<<a[3];
delete a;
}
//sa se puna in evidenta notiunea de alocare dinamica si
//eliberarea spatiului la tablouri
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

void main()
{ double (*a)[5]=new double [3] [5];
a[1][2]=7.8;

cout<<a[1][2];
delete a;
}
//sa se puna in evidenta notiunea de alocare dinamica si
//eliberarea spatiului la tablouri bidimensionale

#include "stdafx.h"

105
#include<iostream>
#include<math.h>
using namespace std;

main ( )
{
int m,n,i,j,(*adr)[10];
adr=new int[10] [10];
cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
cin>>adr[i][j];
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
cout<<adr[i][j]<<" ";
cout<<endl;
}
delete adr;
}
//sa se puna in evidenta notiunea de alocare dinamica si
//eliberarea spatiului la tablouri bidimensionale
//sa se sumeze doua maatrice si sa se returneze un
pointer catre un VOID
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

void* Cit_Mat(int m, int n)


{
int i,j,(*adr1)[10]=new int [10] [10];
for(i=0;i<m;i++)
for(j=0;j<n;j++) cin>>adr1[i][j];
return adr1;
}
void Tip_Mat(int m, int n, int(*adr1)[10])
{ int i,j;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
cout<<adr1[i][j]<<" ";
cout<<endl;
}
}
void* Suma_Mat(int m, int n, int(*adr1)[10],int (*adr2)
[10])
{ int i,j,(*adr)[10]=new int[10][10];
for(i=0;i<m;i++)
for(j=0;j<n;j++) adr[i][j]=adr1[i][j]+adr2[i][j];

106
return adr;
}
main ( )
{ int m,n,i,j,(*adr)[10],(*adr1)[10],(*adr2)[10];
cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
adr1=(int(*)[10]) Cit_Mat(m,n);
adr2=(int(*)[10]) Cit_Mat(m,n);
adr=(int(*)[10]) Suma_Mat(m,n,adr1,adr2);
Tip_Mat(m,n,adr);
}
//• Pentru fiecare dintre cele n materiale aflate intr-o
magazie se cunoaste: denumirea materialului, unitatea de
masura si cantitatea care se gaseste in magazie.Programul
de mai jos citeste informatiile si le organizeaza astfel:
//-segmentul de date contine un vector cu n
componente, unde fiecare componenta
//retine un pointer catre inregistrarea cu datele
citite pentru fiecare material;
//-datele citite pentru fiecare material se
organizeaza ca struct, alocat in HEAP.

#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct material
{ char denumire[20], unit_mas[5];
float cantitate;
};
void Cit_Material(int i,material* v[100])
{
v[i]=new material;
cout<<"Denumire "; cin>>v[i]->denumire;
cout<<"Unitate de masura "; cin>>v[i]->unit_mas;
cout<<"Cantitate "; cin>>v[i]->cantitate;
}
void Tip_Material(int i,material* v[100])
{
cout<<v[i]->denumire<<endl<<v[i]-
>unit_mas<<endl<<v[i]->cantitate;
}
main ( )
{ int n,i;
material* v[100];
cout<<"n="; cin>>n;
for(i=0;i<n;i++) Cit_Material(i,v);
for(i=0;i<n;i++) Tip_Material(i,v);
}

107
Liste liniare
Definitia listelor

Def.:O lista liniara este o colectie de n>=0 noduri, X1,X2,…,Xn, aflate intr-o relatie de
ordine.Astfel, X1 este primul nod al listei, X2 este al doilea nod al listei,…, Xn este ultimul
nod.Operatiile permise sunt:
 Accesul la oricare nod al listei in scopul citirii sau modificarii informatiei continute de
acesta.
 Adaugarea unui nod, indiferent de pozitia pe care o ocupa in lista.
 Stergerea unui nod,indiferent de pozitia pe care o ocupa in lista.
 Schimbarea pozitiei unui nod in cadrul listei.

Liste liniare alocate simplu inlantuit

Prezentare generala

O lista liniara simplu inlantuita este o structura de forma:


in1 adr1 in2 adr2 inn 0
adr1 adr2 adrn

Dupa cum se observa, fiecare nod, cu exceptia ultimului, retine adresa nodului
urmator.
1. Accesul la un nod al listei se face parcurgand nodurile care il preced.
2. Informatiile ocupa memorie, fiind prezente in cadrul fiecarui nod.
3. Avantajele alocarii inlantuite sunt date de rapiditatea operatiilor de adaugare si
eliminare.
//sa se puna in evidenta notiunea de lista simplu
inlantuita
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct Nod
{
int info;
Nod* adr_urm;
};

Nod*v;
int nr;

108
void Adaug(Nod*& v, int nr)
{
Nod* c=new Nod;
c->info=nr;
c->adr_urm=v;
v=c;
}

void Tip(Nod* v)
{
Nod*c=v;
while(c)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}

main( )
{
cout<<"numar="; cin>>nr;
while(nr)
{
Adaug(v,nr);
cout<<"numar="; cin>>nr;
};
Tip(v);
}
//sa se puna in evidenta notiunea de lista simplu
inlantuita
// si operatii cu liste simple
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

#include<iostream>
#include<math.h>
using namespace std;

struct Nod
{
int info;
Nod* adr_urm;
};

Nod* v,*sf;
int i;

void Adaugare(Nod*& v, Nod*& sf, int val)


{

109
Nod* c;
if(v==0)
{
v=new(Nod); v->info=val; v->adr_urm=0;
sf=v;
}
else
{
c=new(Nod); sf->adr_urm=c;
c->info=val; c->adr_urm=0;
sf=c;
}
}

void Inserare_dupa(Nod* v, Nod*& sf, int val, int val1)


{
Nod* c=v, *d;
while(c->info!=val) c=c->adr_urm;
d=new Nod; d->info=val1;
d->adr_urm=c->adr_urm; c->adr_urm=d;
if(d->adr_urm==0) sf=d;
}
void Inserare_inainte(Nod*& v, int val, int val1)
{
Nod* c,*d;
if(v->info==val)
{
d=new Nod; d->info=val1; d->adr_urm=v; v=d;
}
else
{
c=v;
while(c->adr_urm->info!=val) c=c->adr_urm;
d=new Nod; d->info=val1; d->adr_urm=c->adr_urm; c-
>adr_urm=d;
}
}

void Sterg(Nod*& v, Nod*& sf, int val)


{
Nod* c,*man;
if(v->info==val)
{
man=v; v=v->adr_urm;
}
else
{
c=v;
while(c->adr_urm->info!=val) c=c->adr_urm;
man=c->adr_urm; c->adr_urm=man->adr_urm;
if(man==sf) sf=c;

110
}
delete man;
}

void Tip(Nod* v)
{
Nod*c=v;
while(c)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}

main( )
{
for (i=1;i<=10;i++)
Adaugare(v,sf,i);
Tip(v);
Inserare_dupa(v,sf,7,11);
Inserare_dupa(v,sf,10,12);
Inserare_dupa(v,sf,1,13);
Tip(v);
Inserare_inainte(v,13,14);
Inserare_inainte(v,1,15);
Tip(v);
Sterg(v,sf,15);
Sterg(v,sf,13);
Sterg(v,sf,12);
Tip(v);
}
//sa se puna in evidenta notiunea de lista simplu
inlantuita
// si sortarea prin insertie a acesteia
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

const MaxInt=32000;
struct Nod
{
int info;
Nod* adr_urm;
};
int n,i;
Nod *v,*adr,*c,*c1;
main ( )
{
cout<<"n="; cin>>n;

111
v=new Nod; v->info=MaxInt; v->adr_urm=0;
for(i=1;i<=n;i++)
{
adr=new Nod;
cout<<"numar="; cin>>adr->info;
if(adr->info<=v->info)
{
adr->adr_urm=v;
v=adr;
}
else
{
c=v;
c1=v->adr_urm;
while(c1->info<adr->info)
{
c=c->adr_urm;
c1=c1->adr_urm;
}
c->adr_urm=adr;
adr->adr_urm=c1;
}
}
c=v;
while(c->info!=MaxInt)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}

Liste alocate dublu inlantuit

O lista alocata dublu inlantuit este o structura de date de forma:

0 in1 adr2 adr1 in2 adr3 adrn-1 inn 0


0 0000adr3
adr1 adr2
adrn

Operatiile posibile asupra unei liste dublu inlantuite sunt:

1) creare;
2) adaugare la dreapta;
3) adaugare la stanga;

112
4) adaugare in interiorul listei;
5) stergere din interiorul listei;
6) stergere la stanga listei;
7) stergere la dreapta listei;
8) listare de la stanga la dreapta;
9) listare de la dreapta la stanga.

1) Creare

O lista dublu inlantuita se creeaza cu o singura inregistrare.Pentru a ajunge la


numarul de inregistrari dorit, utilizam functii de adaugare la stanga si la
dreapta.Functia creare realizeaza citirea informatiei numerice, alocarea de
spatiu pentru inregistrare, completarea inregistrarii cu informatia si
completarea adreselor la dreapta si la stanga cu 0.

2) Adaugare la dreapta

Functia add citeste informatia numerica, aloca spatiu pentru inregistrare,


completeaza adresele, modifica adresa la dreapta a inregistrarii din s cu adresa
noii inregistrari si ii atribuie
lui s valoarea noii inregistrari.

3) Adaugare la stanga

4) Adaugare in interiorul listei

Functia includ parcurge lista pentru a gasi inregistrarea cu informatia m, in


dreapta careia urmeaza sa introducem noua inregistrare, citeste informatia,
aloca spatiu, completeaza informatia, completeaza adresa stanga a noii
inregistrari cu valoarea adresei inregistrarii de informatie m, si completeaza
adresa dreapta a noii inregistrari cu valoarea adresei dreapta a inregistrarii cu
informatia utila m.

5) Stergere in interiorul listei

Functia Sterg parcurge lista pentru a gasi informatia care va fi stearsa, atribuie
inregistrarii precedente campul de adresa dreapta al inregistrarii care va fi
stearsa, iar inregistrarii care urmeaza celei care va fi stearsa i se atribuie campul
de adresa stanga al inregistrarii pe care o stergem, dupa care se elibereaza
spatiul de memorie rezervat inregistrarii care se sterge.

6)-7) Stergere la stanga si la dreapta listei

8) Listare de la stanga la dreapta

Functia listare porneste din stanga listei si tipareste informatia numerica a


fiecarei inregistrari, atata timp cat nu s-a ajuns la capatul listei.

9) Listare de la dreapta la stanga

113
//sa se puna in evidenta notiunea de lista dubleu
inlantuita
// si operatii cu aceasta
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;

struct Nod
{ Nod *as,*ad;
int nr;
};

Nod *b,*s,*c;
int n,m,i;

void Creare(Nod*& b, Nod*& s)


{ cout<<"n="; cin>>n;
b=new Nod; b->nr=n;
b->as=b->ad=0;
s=b;
}
void Addr(Nod*& s)
{ cout<<"n="; cin>>n;
Nod* d=new Nod;
d->nr=n;
d->as=s; d->ad=0;
s->ad=d; s=d;
}

void Listare(Nod*& b)
{ Nod* d=b;
while(d)
{ cout<<d->nr<<endl;
d=d->ad;
}
}

void Includ(int m,Nod* b)


{
Nod *d=b, *e;
while(d->nr!=m) d=d->ad;
cout<<"n="; cin>>n;
e=new Nod;
e->nr=n;
e->as=d;
d->ad->as=e;
e->ad=d->ad;
d->ad=e;
}

114
void Sterg(int m, Nod *b)
{ Nod* d=b;
while(d->nr!=m) d=d->ad;
d->as->ad=d->ad;
d->ad->as=d->as;
delete d;
}

main ( )
{ cout<<"Creare lista cu o singura inregistrare "<<endl;
Creare(b,s);
cout<<"Cate inregistrari se adauga ?"; cin>>m;
for(i=1;i<=m;i++) Addr(s);
cout<<"Acum listez de la stanga la dreapta"<<endl;
Listare(b);
cout<<"Includem la dreapta o inregistrare"<<endl;
cout<<"Dupa care inregistrare se face includerea?";
cin>>m;
Includ(m,b);
cout<<"Acum listez de la stanga la dreapta"<<endl;
Listare(b);
cout<<"Acum stergem o inregistrare din
interior"<<endl;
cout<<"Ce inregistrare se sterge?";
cin>>m;
Sterg(m,b);
cout<<"Acum listez de la stanga la dreapta"<<endl;
Listare(b);
}

115
Grafuri orientate
Grafuri orientate –notiuni de baza

Grafuri orientate – noţiuni de bază

Noţiunea de graf orientat.


Un exemplu de graf orientat este: reţeaua de străzi a unui oraş. Străzile sunt muchii în
graf, iar intersecţiile reprezintă vârfurile grafului. Întrucât mergând pe jos ne putem
deplasa pe orice stradă în ambele sensuri, vom spune că din punctul de vedere al
pietonilor, „graful unui oraş” este neorientat.
Cu totul altfel stau lucrurile în ceea ce priveşte conducătorii auto, pentru că în orice
oraş există străzi cu sens unic. Pentru un şofer străzile trebuie să primească în graf o
anumită orientare. Desigur că acele străzi pe care se poate circula în ambele sensuri
vor primi orientare dublă. Am ajuns astfel la noţiunea de graf orientat.
Numim graf orientat, o pereche ordonată de mulţimi G=(X,U), unde:
X este o mulţime finită şi nevidă numită mulţimea nodurilor (vârfurilor);
U este o mulţime formată din perechi ordonate de elemente ale lui X, numită
mulţimea arcelor (muchiilor)
observaţii:
Prin noţiunea de perechi ordonate nu trebuie
să înţelegem că o muchie este mai mare decât U1
alta, ci pur şi simplu că facem deosebire între
o muchie de forma (x,z) şi o alta de forma 1
(y,x). Cu alte cuvinte muchiile sunt
diferenţiate prin ordinea de scriere a U2
simbolurilor. 2
Arcul (x,y) nu este tot una cu arcul (y,x). U3
Exemplu: U5
Pentru graful G=(X,U) din figura 1. avem: U4
X={1, 2, 3, 4} şi U={u1, u2, u3, u4, u5, u6,
u7,}= {(1,1), (2,1), (3,2), (2,3), (2,4), (3,4), 3
U6
(3,4)} 4
arc va fi de forma u= (x,y), unde x se numeşte
U7
extremitate iniţială, iar y se numeşte
extremitate finală a arcului. Cu alte cuvinte,
“arcul iese din nodul x şi intră în nodul y”. La Figura1
fel ca la grafurile neorientate, vom spune că
nodurile x şi y sunt adiacente, iar arcul u şi nodul x sunt incidente (la fel arcul x şi
nodul y). Nodul y se numeşte succesor al lui x, iar nodul x se numeşte predecesor al
lui y. Un arc de forma (x,x), care iese din nodul x şi intră tot x, se numeşte buclă.
Exemplu:
În graful din figura 1, avem bucla (1,1).
Într-un graf putem avea două sau mai multe arce identice.
Exemplu:
În graful din figura 1, există două arce identice, u6 = u7 = (3,4)
Definiţie
Se numeşte p-graf, un graf orientat în care numărul arcelor identice este mai mic sau
egal cu o valoare dată p.

116
În cele ce urmează vom analiza numai 1-grafuri fără bucle.
Graful unui vârf. Mulţimile Γ şi ω
Gradul exterior al unui vârf x, notat d*(x), reprezintă numărul arcelor care ies din
nodul x, adică numărul arcelor de forma (x,z) ε U.
Analog, se defineşte gradul interior al unui vârf x, notat d-(x), ca fiind numărul arcelor
care intră în nodul x (de forma (y,x) ε U).
Exemplu:
În graful reprezentat în figura 1, pentru nodul x=2, avem:
d*(2) =3 → există trei arce care ies din nodul 2, şi anume : u2=(2,1), u4=(2,3) şi u5 =
(2,4).
d-(2) =1 → în nodul 2 intră un singur arc, în speţă arcul u3=(3,2).
Se mai definesc următoarele mulţimi:
  x   y  X x, y  U 
→ mulţimea nodurilor ce constituie extremităţi finale ale
arcelor care pleacă din nodul x. Pe scurt, mulţimea succesorilor lui x;
  x   y  X  y , x  U 
→ mulţimea nodurilor ce constituie extremităţi iniţiale ale
arcelor care pleacă din nodul x. Pe scurt, mulţimea predecesorilor lui x;
Exemplu:
În graful din figura 1, pentru nodul x=2, avem:
- Γ+(2) = {1,3,4} → urmare a faptului că muchiile care pleacă din nodul 2 sunt (2,1),
(2,3) şi (2,4), putem spune că mulţimea succesorilor nodului 2 este {1,3,4}.
- Γ-(2) = {3} → în nodul 2 intră doar muchia (3,2), deci mulţimea predecesorilor lui 2
conţine doar nodul 3.
ω+(x) = {u = (x,y)| u ε U} → mulţimea arcelor care ies din nodul x;
ω-(x) = {u = (y,x)| u ε U} → mulţimea arcelor care intră în nodul x;
Exemplu:
În graful din figura 1, pentru nodul 2, avem:
ω+(x) = {(2,1), (2,3), (2,4)}, ω-(x) = {(3,2)}
Graf parţial şi subgraf
Fie graful G = (X,U). Un graf parţial al lui G, este un graf G1= (X,V), cu V  U .
Altfel spus, un graf parţial G1 al lui G, este chiar G, sau se obţine din G păstrând toate
vârfurile şi suprimând nişte muchii.
Fie graful G = (X,U). Un graf parţial al lui G, este un graf G1= (Y,T), unde V  X şi
T  U , iar T va conţine numai muchiile care au ambele extremităţi în Y. Altfel spus,
un graf parţial G1 al lui G, se obţine din G eliminând nişte vârfuri şi păstrând doar
acele muchii care au ambele extremităţi în mulţimea vârfurilor rămase.
Exemplu:
Se consideră graful G = (X,U) din figura 2, în care X = {1,2,3,4,5,6} şi U={u1, u2,
u3, u4, u5, u6, u7}.
- Construim graful parţial G1 = (X,V), unde X = {1,2,3,4,5,6} şi V = { u2, u3,
u4, u6, u7} (figura 3) din graful G au fost eliminate arcele u1 şi u5.

117
- Construim subgraful G2 = (Y,T), unde Y = {3,4,5,6} şi T = {u4, u5, u6, u7}
(figura 4) din graful G au fost eliminate nodurile 1 şi 2 precum şi arcele u1, u2
şi u3 care au o extremitate în afara mulţimii nodurilor rămase.
Reprezentarea grafurilor orientate
Considerăm un graf orientat G= (X,U) cu m arce şi n noduri.
Cele mai cunoscute forme de reprezentare sunt: matricea de adiacenţă, matricea
vârfuri – arce, matricea drumurilor şi listele vecinilor.
u2 u2
1 2 1 2
u1
u3 u3
u4 3 u5 u4 u4
3 3 u5
4 4 4
5 5 5
u7 u6 u7 u7
u6 u6

6 6 6
Figura 2 Figura 3 Figura 4

Matricea de adiacenţă
Are aceeaşi semnificaţie ca în cazul grafurilor neorientate: fiecare element a[i,j], cu i,j
ε {1,2,...,n}, este: 1 dacă există arcul (i,j), respectiv 0 în caz contrar.
Datorită orientării, aşa cum am mai spus, arcul (i,j) nu este totuna cu arcul (j,i). Prin
urmare, a[i,j] ≠ a[j,i]. Aşadar matricea de adiacenţă nu mai este simetrică faţă de
diagonala principală, aşa cum se întâmpla în cazul grafurilor neorientate.
Exemplu:
Pentru graful G=(X,U) din figura 5, matricea de adiacenţă este:
coloana 1 2 3 4
0 0 0 0 1
1 0 1 1 2
A=
0 0 0 1 3
0 1 0 0 4

2 3

1
Continuăm cu câteva 4 aplicaţii.
Aplicaţie:
Citirea de la Figura 5 tastatură şi
afişarea matricei de adiacenţă

118
Prin citirea matricei de adiacenţă de la tastatură, putem memora arcele existente
între nodurile unui graf. În cazul grafurilor neorientate, citeam doar
“porţiunea” de deasupra diagonalei principale în matrice, deoarece matricea este
simetrică. Acum trebuie să citim toate elementele matricei. Avem de-a face cu
algoritmii banali de citire şi afişare a unei matrice cu n linii şi n coloane (unde n
este numărul de noduri) în două cicluri for, deci nu considerăm că mai sunt
necesare alte explicaţii.
Matricea vârfuri – arce
Este o matrice b cu n linii şi m coloane, în care fiecare element b[i,j] este:
- 1, dacă nodul i este o extremitate iniţială a arcului uj;
- -1, dacă nodul i este o extremitate finală a arcului uj;
- 0, dacă nodul i nu este o extremitate a arcului uj.
Exemplu:
Pentru graful de mai jos cu n=4 noduri şi m=5 arce, matricea vârfuri – arce este:
1 0 0 0 0
-1 -1 -1 0 0
0 1 0 1 -1
0 0 1 -1 1

u1 u3
u2 1
u4
3 4
u5
Figura 6

Pentru a exemplifica construcţia matricei, vom deduce elementele liniei 3:


- a[3,1]= 0 pentru că nodul 3 nu este o extremitate a arcului u1. Analog, a[3,3]=
0 deoarece nodul 3 nu este extremitate a arcului u3.
- a[3,5]= -1 pentru că nodul 3 este o extremitate finală a arcului u5.
- a[3,2]= 1 şi a[3,4]= 1 întrucât nodul 3 este o extremitate iniţială a arcului u2
respectiv u4.
Observaţii:
Pe fiecare coloană j (aferentă arcului uj), avem exact două elemente nenule: un 1
(linia i pe care se află reprezintă extremitatea iniţială a arcului uj) şi un -1 (linia i pe
care se află reprezintă extremitatea finală a arcului uj)
Numărul valorilor de 1 de pe linia i, reprezintă gradul exterior al nodului i (numărul
arcelor ce au ca extremitate iniţială pe i), iar numărul valorilor de -1 de pe linia i
reprezintă gradul interior al nodului i (numărul arcelor care au ca extremitate finală pe
i).

119
Listele vecinilor
Pentru fiecare nod x se construiesc două liste ale vecinilor săi:
- L*(x) → lista vecinilor succesori; conţine nodurile ce sunt extremităţi finale
ale arcelor care ies din nodul x.
- L-(x) → lista vecinilor predecesori; conţine nodurile ce sunt extremităţi iniţiale
ale arcelor care intră în nodul x.
Exemplu:
În graful din figura 6 de mai sus, pentru nodul x=4 avem:
- arcele care ies din nodul 4 sunt (4,2) şi (4,3). În consecinţă, lista vecinilor
succesori L*(4) conţine nodurile 2 şi 3;
- în nodul 4 intră un singur arc, şi anume (3,4), motiv pentru care lista vecinilor
predecesori L-(4) conţine doar nodul 3.
Prezentăm în continuare aceste liste ale vecinilor pentru graful din figura 6.

Nodul x L*(x) L-(x)


1 2 vidă
2 vidă 1,3,4
3 2,4 4
4 2,3 3

Matricea drumurilor
Această matrice va fi reprezentată în cadrul capitolului “Drumuri şi circuite în grafuri
orientate”.
Reprezentarea grafului ca un vector de muchii
Fiecare arc al grafului poate fi privit ca o înregistrare cu două componente, în speţă
cele două noduri care constituie extremităţile arcului:
- nod_in -> nodul din care iese arcul (“nodul de început” al arcului);
- nod_sf -> nodul în care intră arcul (“nodul de sfârşit” al arcului);
Putem defini tipul de date ARC, astfel:
Drumuri si circuite in grafuri orientate
Se numeşte lanţ intr-un graf orientat, o mulţime de arce L={u1,u2,...,uk}, cu
proprietatea ca oricare doua arce vecine in mulţime au o extremitate comuna.
Un lanţ este de fapt un traseu care uneşte prin arce doua noduri numite extremităţile
lanţului, fără a tine cont de orientarea arcelor componente.
Se numeşte drum în graful G, un şir de noduri D={z1, z2, z3, …, zk}, unde z1, z2, z3, …,
zk aparţin lui x, cu proprietatea că oricare două noduri consecutive sunt adiacente,
adică există arcele [z1, z2], [z2, z3], …, [zk-1,zk] aparţin lui U.
Practic, un drum poate fi privit ca un traseu în care toate arcele au aceeaşi orientare,
dată de sensul de deplasare de la z1 la zk.
Dacă nodurile z1, z2, z3, …, zk sunt distincte două câte două, drumul se numeşte
elementar. În caz contrar, drumul este ne-elementar.
Asemenea uni lanţ într-un graf neorientat, un drum într-un graf orientat este de fapt un
traseu pe care l-am parcurge între două noduri deplasându-ne de-a lungul unor arce şi
trecând prin nişte noduri intermediare. Deosebirea unui drum faţă de un lanţ constă în
faptul că de-a lungul unui arc ne putem deplasa numai în sensul dat de orientarea
arcului.
Se numeşte circuit într-un graf, un lanţ L={z1, z2, z3, …, zk} cu proprietatea că z1=zk şi
arcele [z1, z2], [z2, z3], …, [zk-1,zk] sunt distincte două câte două.

120
Dacă într-un circuit, toate nodurile cu excepţia primului şi ultimului sunt distincte
două câte două, atunci circuitul se numeşte elementar. În caz contrar, el este ne-
elementar.

u2
1 2
u1
u3
u4 3 u5
4
5
u7 u6

6
Figura 8
Matricea drumurilor.
Este o matrice d cu n linii şi n coloane, în care fiecare element d[i,j] este :
- 1, dacă există drum de la nodul i la nodul j în graf;
- 0, în caz contrar.
Algoritmul Roy-Warshall de determinare a matricei drumurilor
Matricea drumurilor se obţine aplicând matricei de adiacenţă transformări succesive.
Vom spune că există drum de la nodul i la nodul j, dacă găsim un nod k (diferit de i, j)
cu proprietatea că există drum de la i la k şi drum de la j la k. Astfel:
 un element a[i, j] care este 0, devine 1, dacă există un nod k astfel încât a[i,
k]=1 şi a[k, j]=1. Pentru a găsi arcele nodului k, trebuie parcurse pe rând în
varianta k toate nodurile 1, 2, …, n.

Metode de reprezentare in memorie


a unui graf

Exista mai multe metode de reprezentare in memorie a unui graf orientat:


1-metoda matricei adiacente:
1, pentru[I,j] apartin de g
a[I,j]=

0, pentru[I,j]nu apartinde g
fisierul graf.txt este in calea: C:\Documents and Settings\ticatica\My Documents\
Visual Studio Projects\p2
//sa se puna in evidenta notiunea de matrice de adiacenta
si
//sa se listeze dintr-un fisier
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;
#include<fstream>

121
int A[50][50],n,I,j;
void Citire(char Nume_fis[20],int a[50][50],int& n)
{
fstream f(Nume_fis,ios::in);
int I,j;
f>>n;
while (f>>I>>j) A[I][j]=1;
f.close();
}

main()
{
Citire("Graf.txt",A,n);
for (I=1;I<=n;I++)
{ for (j=1;j<=n;j++) cout <<A[I][j]<<" ";
cout<<endl;
}
}
Parcurgerea grafurilor orientate

Parcurgerea in latime(BF-breadth first)


-parcurgerea in latime se face incepand de la un nod I, pe care il consideram parcurs;
-parcurgem apoi toti descendentii sai – multimea nodurilor j pentru care exista
[I,j]apartin de g;
-parcurgem apoi toti descendentii nodurilor parcurse la pasul anterior.
Parcurgerea BF se efectueaza prin utilizarea structurii numita coada, avand grija ca un
nod sa fie vizitat o singura data. Coada va fi alocata prin utilizarea unui vector.
Fie graf.txt ce contine:
7
1 2
1 6
1 3
3 6
2 5
2 4
3 7
=>1 3 2 6 4 5 7
//sa se puna in evidenta parcurgerea in latime la grafuri
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;
#include<fstream>

int coada[50],s[50],A[50][50],I_c,sf_c,I,j,n;
void bf_r1()
{ int k;
if (I_c<=sf_c)

122
{ for (k=1;k<=n;k++)
if ( (A[coada[I_c]][k]==1) && (s[k]==0) )
{ sf_c++;
coada[sf_c]=k;
s[k]=1;
}
I_c++;
bf_r1();
}
}
void Citire(char Nume_fis[20],int a[50][50],int& n)
{
fstream f(Nume_fis,ios::in);
int I,j;
f>>n;
while (f>>I>>j) A[I][j]=1;
f.close();
}
main()
{
Citire("Graf.txt",A,n);
for (I=1;I<=n;I++)
{ for (j=1;j<=n;j++) cout <<A[I][j]<<" ";
cout<<endl;
}
I_c=sf_c=1;
coada[I_c]=s[1]=1;
bf_r1();
for(I=1;I<=sf_c;I++)cout<<coada[I]<<" ";
}

Parcurgerea in adancime(DF-depth first)

-se face incapand de la un nod I


-dupa parcurgerea unui nod se parcurge primul dintre descendntii sai
neparcursii inca.

Parcurgerea in latime(BF-breadth first)


-parcurgerea in latime se face incepand de la un nod I, pe care il consideram parcurs;
-parcurgem apoi toti descendentii sai – multimea nodurilor j pentru care exista
[I,j]apartin de g;
-parcurgem apoi toti descendentii nodurilor parcurse la pasul anterior.
Parcurgerea BF se efectueaza prin utilizarea structurii numita coada, avand grija ca un
nod sa fie vizitat o singura data. Coada va fi alocata prin utilizarea unui vector.
Fie graf.txt ce contine:
7
1 2

123
1 6
1 3
3 6
2 5
2 4
3 7
=>1 2 4 5 3 6 7
//sa se puna in evidenta parcurgerea in adancime la
grafuri
#include "stdafx.h"
#include<iostream>
#include<math.h>
using namespace std;
#include<fstream>

int s[50],A[50][50],I,j,n;
void df_r(int nod)
{
int k;
cout<<nod<<" ";
s[nod]=1;
for (k=1;k<=n;k++)
if ((A[nod][k]==1) && (s[k]==0))
df_r(k);
}

void Citire(char Nume_fis[20],int a[50][50],int& n)


{
fstream f(Nume_fis,ios::in);
int I,j;
f>>n;
while (f>>I>>j) A[I][j]=1;
f.close();
}
main()
{
Citire("Graf.txt",A,n);
for (I=1;I<=n;I++)
{ for (j=1;j<=n;j++) cout <<A[I][j]<<" ";
cout<<endl;
}
df_r(1);
}

// cu liste de adiacenta sa se implemeteze


//parcurgerea in adancime
// 7
// 1 2
// 1 6

124
// 1 3
// 3 6
// 2 5
// 2 4
// 3 7
//=>1 3 7 6 2 5 4

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

struct Nod
{
int nd;
Nod* adr_urm;
};

int s[50],n;
Nod *L[50];
void df_r1(int nod)
{
Nod* p;
cout<<nod<<" ";p=L[nod];
s[nod]=1;
while (p)
{
if (s[p->nd]==0)
df_r1(p->nd);
p=p->adr_urm;
}
}

void Citire_1( Nod* L[50], int& n)


{
Nod* p;
int I,j;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++) L[I]=0;
while (f>>I>>j)
{ p=new Nod;
p->adr_urm=L[I]; p->nd=j;
L[I]=p;
}
f.close();
}
main()
{
Citire_1(L,n);

125
df_r1(1);
}
// cu liste de adiacenta sa se implemeteze
//parcurgerea in latime recursiv
// 7
// 1 2
// 1 6
// 1 3
// 3 6
// 2 5
// 2 4
// 3 7
//=>1 3 6 2 7 4 5

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

struct Nod
{
int nd;
Nod* adr_urm;
};

Nod* L[50],*p;
int coada[50],s[50],I_c,sf_c,I,n;
void bf_r1()
{
Nod*p;
if (I_c<=sf_c)
{
p=L[coada[I_c]];
while (p)
{
if (s[p->nd]==0)
{
sf_c++;
coada[sf_c]=p->nd;
s[p->nd]=1;
}
p=p->adr_urm;
}
I_c++;
bf_r1();
}
}

void Citire_1( Nod* L[50], int& n)

126
{
Nod* p;
int I,j;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++) L[I]=0;
while (f>>I>>j)
{ p=new Nod;
p->adr_urm=L[I]; p->nd=j;
L[I]=p;
}
f.close();
}

main()
{ Citire_1(L,n);
I_c=1; sf_c=1; coada[I_c]=1; s[1]=1;
bf_r1();
for (I=1;I<=sf_c;I++) cout<<coada[I]<<" ";
}
// cu liste de adiacenta sa se implemeteze
//parcurgerea in latime iterativ
// 7
// 1 2
// 1 6
// 1 3
// 3 6
// 2 5
// 2 4
// 3 7
//=>1 3 6 2 7 4 5

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

struct Nod
{
int nd;
Nod* adr_urm;
};

Nod* L[50],*p;
int coada[50],s[50],I_c,sf_c,I,n;

void bf_r1()

127
{
Nod*p;
while (I_c<=sf_c)
{
p=L[coada[I_c]];
while (p)
{
if (s[p->nd]==0)
{
sf_c++;
coada[sf_c]=p->nd;
s[p->nd]=1;
}
p=p->adr_urm;
}
I_c++;
//bf_r1();
}
}

void Citire_1( Nod* L[50], int& n)


{
Nod* p;
int I,j;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++) L[I]=0;
while (f>>I>>j)
{ p=new Nod;
p->adr_urm=L[I]; p->nd=j;
L[I]=p;
}
f.close();
}

main()
{ Citire_1(L,n);
I_c=1; sf_c=1; coada[I_c]=1; s[1]=1;
bf_r1();
for (I=1;I<=sf_c;I++) cout<<coada[I]<<" ";
}

MATRICEA DRUMURILOR

m[I,j]= 1, daca exista drum de la I la j,


0, daca nu exista drum de la I la j, sau I=j.
// cu liste de adiacenta sa se implemeteze
//obtinerea matricei drumurilor

128
// 7
// 1 2
// 1 6
// 1 3
// 3 6
// 2 5
// 2 4
// 3 7
//=>
// 0 1 1 1 1 1 1
// 0 0 0 1 1 0 0
// 0 0 0 0 1 1 1
// 0 0 0 0 1 0 0
// 0 0 0 0 0 0 0
// 0 0 0 0 1 0 0

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

struct Nod
{
int nd;
Nod* adr_urm;
};

int s[50],n,I,j,M_dr[50][50];
Nod* L[50];
void df(int baza,int nod)
{
Nod* p;
if (baza!=nod) M_dr[baza][nod]=1;
p=L[nod]; s[nod]=1;
while(p)
{
if (s[p->nd]==0) df(baza,p->nd);
p=p->adr_urm;
}
}

void Citire_1( Nod* L[50], int& n)


{
Nod* p;
int I,j;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++) L[I]=0;
while (f>>I>>j)

129
{ p=new Nod;
p->adr_urm=L[I]; p->nd=j;
L[I]=p;
}
f.close();
}

main()
{
Citire_1(L,n);
for (I=1;I<=n;I++)
{
for (j=1;j<=n;j++) s[j]=0;
df(I,I);
}
for (I=1;I<=n;I++)
{
for (j=1;j<=n;j++) cout<<M_dr[I][j]," ";
cout<<endl;
}
}
Componente tare conexe

Definitie.G1=(X1g1) este o componenta tare conexa daca:


1)Oricare ar fi x , y din multimea X1, exista drum de la x la y si drum de
la y la x.
2)Nu exista un alt subgraf al lui G care indeplineste conditia 1)
// sa se implemeteze componentele tare conexe
//ale unui graf ca intersectie intre predecesori si
succesori la care adaug nodul
5
1 2
2 3
3 1
1 4
4 5
5 4
=>copmomenta I
1 2 3
comonenta II
4 5

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>

130
using namespace std;

int suc[50],pred[50],A[50][50],n,nrc,I,j;

void Citire(char Nume_fis[20],int a[50][50],int& n)


{
fstream f(Nume_fis,ios::in);
int I,j;
f>>n;
while (f>>I>>j) A[I][j]=1;
f.close();
}

void df_r1(int nod)


{
int k;
suc[nod]=nrc;
for (k=1;k<=n;k++)
if ( (A[nod][k]==1)&&(suc[k]==0) )
df_r1(k);
}
void df_r2(int nod)
{
int k;
pred[nod]=nrc;
for (k=1;k<=n;k++)
if ( (A[k][nod]==1)&&(pred[k]==0))
df_r2(k);
}

main()
{
Citire("Graf.txt",A,n);
nrc=1;
for (I=1;I<=n;I++)
if (suc[I]==0)
{
suc[I]=nrc;
df_r1(I);df_r2(I);
for (j=1;j<=n;j++)
if (suc[j]!=pred[j]) suc[j]=pred[j]=0;
nrc++;
}
for (I=1;I<=n;I++)
{
cout<<"Componenta "<<I<<endl;
for (j=1;j<=n;j++)
if (suc[j]==I) cout<<j<<" ";
cout<<endl;
}

131
}
Drumuri in grafuri
Consideram un graf orientat G=(X,U) cu n noduri, in care fiecarui arc ii este asociat un numar intreg numit cost.
Semnificatia
acestui cost poate fi foarte variata , in functie de domeniul pe care il descrie graful .

Matricea costurilor

Pentru evidentierea costurilor tuturor arcelor unui graf cu n noduri se poate defini o matrice a, cu n linii * n
coloane.Exista doua forme ale acestei matrici:
Forma a): Fiecare element a[i,j] poate fi:
c , daca exista un arc de cost c>0 intre nodurile i si j;
0 , daca i=j;
+∞ ,daca nu exista arc intre nodurile i si j .

Forma b): Este absolut similara,cu singura deosebire ca in loc de +∞ avem -∞.
Forma a) se foloseste pentru determinarea drumurilor de cost minim intre doua noduri , iar forma b) este
utilizata in aflarea drumurilor de cost maxim.
Daca dorim sa citim matrice costurilor ,evident ca nu putem introduce de la tastatura “+”! In loc de +
vom da un numar intreg foarte mare.

Algoritmul lui Dijkstra

Se consideră un graf neorientat, conex cu N noduri. Graful este dat prin matricea costurilor A.

c, dacã existã un arc c între nodurile i si j



Aij  0, daca i  j
x, dac nu exista un arc intre nodurile i si j

Se consideră un nod iniţial R. Se cer lungimile drumurilor minime de la R la celelalte noduri ale grafului,
precum şi nodurile prin care trec aceste drumuri.
Problema are numeroase aplicaţii practice. Să presupunem că dispunem de o hartă cu oraşele unei ţări.
Acestea sunt unite prin şosele. Oraşele formează nodurile grafului, iar şoselele – arcele acestuia. Algoritmul
furnizează drumurile optime de la un oraş iniţial la celelalte oraşe. Este interesant de observat faptul că, spre
deosebire de alţi algoritmi, acesta furnizează şi nodurile prin care trec drumurile optime, nu numai
lungimile acestora.

Exemplu: Se consideră graful din figură:

132
Pentru acest graf matricea costurilor este:

0  13  16 8 
 
 0  6  10 
13  0 14  11 
 
 6 14 0 5 17 
16   5 0 7 

8 10 11 17 7 0 

Întrucât graful este neorientat, matricea este simetrică.


Algoritmul selectează nodurile grafului, unul câte unul, în ordinea crescătoare a costului drumului de la
nodul R la ele, într-o mulţime S, care iniţial conţine numai nodul R. În felul acesta ne încadrăm în strategia
generală GREEDY. În procesul de prelucrare se folosesc trei vectori: D,S şi T.
Vectorul D este vectorul costurilor de la nodul R la celelalte noduri ale grafului. Prin D(I), unde I{1..N},
se înţelege costul drumului găsit la un moment dat, între nodul R şi nodul I.
Vectorul T indică drumurile găsite între nodul R şi celelalte noduri ale grafului.
Vectorul S (vector caracteristic) indică mulţimea S a nodurilor selectate. S(I)=0 dacă nodul I este neselectat
şi S(I)=1 dacă nodul I este selectat.

Prezentarea algoritmului.
P1) Nodul R este adăugat mulţimii S iniţial vidă (S(R)=1);
- costurile drumurilor de la R la fiecare nod al grafului se preiau în vectorul D de pe linia R a matricii
A;
- pentru toate nodurile I având un cost al drumului de la R la ele finit, se pune T(I)=R.
P2) Se execută de n-1 ori secvenţa

133
- printre nodurile neselectate se caută cel aflat la distanţa minimă faţă de R şi se selectează adăugându-l
mulţimii S;
- pentru fiecare din nodurile neselectate se actualizează în D costul drumurilor de la R la el, utilizând
ca nod intermediar nodul selectat, procedând în felul următor: se compară distanţa existentă în vectorul D
cu suma dintre distanţa existentă în D pentru nodul selectat şi distanţa de la nodul selectat la nodul pentru
care se face actualizarea distanţei (preluată din A), iar în cazul în care suma este mai mică, elementul din D
corespunzător nodului pentru care se face actualizarea capătă ca valoare aceasă sumă şi elementul din T
corespuzător aceluiaşi nod ia valoarea nodului selectat (drumul trece prin acest nod). Presupunând că a
fost selectat nodul K, se actualizează distanţa pentru nodul L şi se compară D(K)+A(K,L) cu D(L).
P3) Pentru fiecare nod al grafului, cu excepţia lui R, se trasează drumul de la R la el.

Pentu acest exemplu se consideră R=1.


Linia 1 a matricei A aste încărcată în vectorul D, vectorii S şi T au iniţial valorile care se observă:
D 0   8
13 16

S 1 0 0 0 0 0

T 0 0 1 0 0
1

Se selectează nodul 6 (costul drumului minim).


Se actualizează conţinutul vectirilor D, S şi T.

D(6)+A(6,2)=8+10=18< D(2)=18 şi T(2)=6


D(6)+A(6,3)=8+11=19>13D(3)=13.
D(6)+A(6,4)=8+17=25< D(4)=25 şi T(4)=6.
D(6)+A(6,5)=8+7=15<16 D(5)=15 şi T(5)=6.

D 0 13 15 8
18 25

S 1 0 0 0 0 1

T 0 6 1 6 6 1

Se selectează nodul 3.
D(3)+A(3,2)=13+>18D(2)=18.
D(3)+A(3,4)=13+14=27>25D(3)=13.
D(3)+A(3,5)=13+>15D(5)=15.

D 0 8

134
18 13 25 15

S 1 0 1 0 0 1

T 0 6 1 6 6 1

Se selectează nodul 5.
D(5)+A(5,2)=15+>18D(2)=18.
D(5)+A(5,4)=15+5=20D(4)=20 şi T(4)=5.

D 0 8
18 13 20 15

S 1 0 1 0 1 1

T 0 6 1 5 6 1

Se selectează nodul 2.
D(2)+A(2,4)=18+6=24>20D(4)=20.

D 0 18 8
13 20 15

S 1 1 1 0 1 1

T 6 1 5 6 1
0

Se selectează nodul 4.
În continuare, pentru fiecare nod se trasează drumul de la nodul iniţial la el.
Vom considera, ca exemplu, nodul 4. Distanţa de la nodul 1 la el D(4) şi anume 20.
Avem T(4)=5; T(5)=6; T(6)=1; drumul trece prin nodurile 1,6,5.
Avem A(1,6)=8; A(6,5)=7; A(5,4)=5.Rezultă 8+5+7=20.
Întrucât drumul se trasează în ordine inversă faţă de cum este găsit, în program se foloseşte o procedură
recursivă.
În cele ce urmează vom nota nodurile cu Ni1,Ni2,…,Nin. Acestea sunt tot nodurile 1,2,…,R,…,N
cosiderate în altă ordine. Nodului R îi corespunde nodul Ni1.

Demonstrarea algoritmului.

Lema 1. Algoritmul selectează nodurile în ordinea costului drumului de la nodul iniţial la ele.

135
Demonstraţie:
Utilizăm metoda inducţiei matematice. Prima dată se selectează nodul Ni2 (are costul drumului minim faţă
de Ni1). Aceasta înseamnă că ipoteza de inducţie se verifică pentru prima selecţie. Presupunem că nu au
fost selectate nodurile Ni2,Ni3,…,Nip (p<n) în ordinea costului drumului de la Ni1 la ele. Se selectează
nodul Nip+1. Presupunem, prin absurd, că există un alt nod N1, neselectat, care are un cost al drumului de
la Ni1 la el mai scăzut decât al nodului Nip+1. Atunci, acesta poate fi plasat în şirul nodurilor deja selectate,
aşezate în ordinea distanţei faţă de Ni1, în următoarele două moduri:
1) Ni1,…,N1,…,Nip,Nip+1.
În acest caz, se contrazice ipoteza de inducţie (nodurile Ni1,…,Nip erau aşezate în ordine distanţei faţă de
Ni1).

2) Ni2,…, Nip,N1,Nip+1.
Aici este contrazisă alegerea lui Nip+1. Acesta a fost ales ca nod cu distanţă minimă faţă de Nil dintre toate
nodurile neselectate, iar N1 este un nod neselectat.

Lema 2. Orice drum optim de la nodul iniţial la oricare din nodurile selectate trece numai prin noduri
selectate.

Demonstraţie:
Presupunem prin absurd că există un drum de cost mai mic de la Ni1 la Nik (Nik este un nod deja selectat)
care nu trece numai prin noduri selectate. Fie Nz primul nod prin care trece acest drum şi care nu este
selectat. Aceasta înseamnă că nodul Nz are costul drumului de la Ni1 la el mai mic decât cel de la Ni1 la
Nik (nod care este selectat). Aceasta contrazice LEMA 1.

TEOREMĂ.
Odată selectat un nod, nu există un drum de cost mai mic între Ni1 şi el, decât drumul de cost indicat de
vectorul D.

Demonstraţie:
În demonstrarea acestui fapt utilizăm metoda inducţiei matematice.
Pentru selectarea lui Ni2 afirmaţia este evidentă, întrucât acesta este cel mai apropiat de Ni1.
Presupunem selectate nodurile Ni2,…,Nip. Selectăm Nip+1. Presupunem că de la Ni1 la el există un drum
de cost mai mic decât cel indicat de vectorul D.
Avem două posibilităţi:

1) Drumul de cost mai mic trece numai prin noduri selectate.


Analizăm modul de funcţionare a algoritmului până în acest moment.
La selecţia nodurilor Ni1,…,Nip s-au actualizat distanţele reţinute în vectorul D (pentru fiecare nod
neselectat s-a comparat vechea distanţă reţinută în D cu suma dintre distanţa de la Ni1 la nodul selectat şi
distanţa de la nodul selectat la nodul pentru care se face actualizarea, iar în cazul în care suma din urmă era
mai mică, aceasta era reţinută în vectorul D). În concluzie, nu există un drum mai scurt între Ni1 şi Nip+1
care trece numai prin noduri selectate.

2) Drumul de cost mai mic trece şi prin noduri neselectate.


În acest caz se contrazice LEMA 2.

136
// sa se implemeteze un program care aplica algoritmul
DIJKSTRA
//pentru determinarea drumurilor de lungime minima
//nodul de plecare e 1
5
1 2 1
1 3 9
1 5 3
2 4 3
2 3 7
4 3 2
4 1 1
5 2 4
5 4 2
=>drumul minim de la 1 la 3 e 6 si trece prin nodurile 1
2 4 3
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;
const float Pinfinit=1.e20;
float A[50][50],D[50],min;
int S[50],T[50],n,I,j,r,poz;
void Citire_cost (char Nume_fis[20],float A[50][50],int&
n)
{
int I,j;
float c;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Pinfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}

void drum(int I)
{
if (T[I]) drum(T[I]);
cout<<I<<" ";
}
main()
{ float min;
Citire_cost("Graf.txt",A,n);
cout<<"Introduceti nodul de pornire "<<endl;
cout<<"r=";cin>>r;S[r]=1;
for (I=1;I<=n;I++)

137
{
D[I]=A[r][I];
if (I!=r)
if (D[I]<Pinfinit) T[I]=r;}
for (I=1;I<=n;I++)
{
min=Pinfinit;
for(j=1;j<=n;j++)
if (S[j]==0)
if (D[j]<min)
{
min=D[j];
poz=j;
}
S[poz]=1;
for (j=1;j<=n;j++)
if (S[j]==0)
if (D[j]>D[poz]+A[poz][j])
{
D[j]=D[poz]+A[poz][j];
T[j]=poz;
}
}
for (I=1;I<=n;I++)
if (I!=r)
if(T[I])
{
cout<<"distanta de la "<<r<<" la "<<I<<" este
"<<D[I]<<endl;
drum(I);
cout<<endl;
}
else
cout<<"nu exista drum de la "<<r<<" la "<<I<<endl;
}

Algoritmul Roy Floyd


Fiind dat un graf prin matricea ponderilor sa se calculeze drumul (lungimea lui adica
suma costurilor) minim de la orice doua noduri din graf.

138
matricea ponderilor este( am notat ω cu INFINIT)
0 1 9 ω 3
ω 0 7 3 ω
ω ω 0 ω ω
1 ω 2 0 ω
ω 4 ω 2 0

Etapa 1)Caut drumurile optime intre oricare doua noduri dar drumurile trec prin nodul
intermediar 1
a[4,2]= ω >a[4,1]+a[1,2]=1+1=2 deci a[4,2]=2
a[4,5]=ω>a[4,1]+a[1,5]=1+3=4 deci a[4,5]=4
matricea ponderilor este

0 1 9 ω 3
ω 0 7 3 ω
ω ω 0 ω ω
1 2 2 0 4
ω 4 ω 2 0

139
Etapa 2)Caut drumurile optime intre oricare doua noduri dar drumurile trec prin nodul
intermediar 2
a[1,3]=9>a[1,2]+a[2,3]=1+7=8 deci a[1,3]=8
a[1,4]=ω>a[1,2]+a[2,4]=1+3=4 deci a[1,4]=4
a[5,3]=ω>a[1,2]+a[2,3]=4+7=11 deci a[5,3]=11
a[5,4]=ω>a[5,2]+a[2,4]=4+3=7 deci neschimbat

matricea ponderilor este

140
0 1 8 4 3
ω 0 7 3 ω
ω ω 0 ω ω
1 2 2 0 4
ω 4 11 2 0

Etapa 3)Caut drumurile optime intre oricare doua noduri dar drumurile trec prin nodul
intermediar 3 dar nu avem nimic nou.
Etapa 4)Caut drumurile optime intre oricare doua noduri dar drumurile trec prin nodul
intermediar 4
a[1,3]=ω>a[1,4]+a[4,3]=4+2=6 deci a[1,3]=6
a[2,1]=ω>a[2,4]+a[4,1]=3+1=4 deci a[2,1]=4
a[2,3]=ω>a[2,4]+a[4,3]=3+2=5 deci a[2,3]=5
a[2,5]=ω>a[2,4]+a[4,5]=3+4=7 deci a[2,5]=7
a[5,1]=ω>a[5,4]+a[4,1]=2+1=3 deci a[5,1]=3
a[5,3]=ω>a[5,4]+a[4,3]=2+2=4 deci a[5,3]=4

01 6 4 3
4 0 5 3 7
ω ω 0 ω ω
1 2 2 0 4
3 4 4 2 0

141
// sa se implemeteze un program care aplica algoritmul
Roy Floyd
5
1 2 1
1 3 9
1 5 3
2 4 3
2 3 7
4 3 2
4 1 1
5 2 4
5 4 2
=>5 4 1 2
are lungimea 4
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;
const float Pinfinit=1.e20;
float minfinit=-1.e20;
float A[50][50];
int n;
void Citire_cost (char Nume_fis[20],float A[50][50],int&
n)
{
int I,j;
float c;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Pinfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}

void Drum(int I, int j)


{
int k=1,gasit=0;
while ( (k<=n) && (!gasit ))
{
if ( (I!=k) && (j!=k) && (A[I][j]==A[I][k]+A[k][j]) )
{
Drum(I,k);Drum(k,j);
gasit=1;
}

142
k++;
}
if (!gasit) cout<<j<<" ";
}

void Scriu_drum(int Nod_Initial, int Nod_Final)


{
if (A[Nod_Initial][Nod_Final]<Pinfinit)
{
cout<<"Drumul de la "<<Nod_Initial<<" la
"<<Nod_Final<<" are lungimea "<<A[Nod_Initial]
[Nod_Final]<<endl;
cout<<Nod_Initial<<" ";
Drum(Nod_Initial,Nod_Final);
}
else
cout<<"Nu exista drum de la "<<Nod_Initial<<" la
"<<Nod_Final;
}
void Lungime_Drumuri()
{
int I,j,k;
for (k=1;k<=n;k++)
for(I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (A[I][j]>A[I][k]+A[k][j])
A[I][j]=A[I][k]+A[k][j];
}
main()
{
Citire_cost("graf.txt",A,n);
Lungime_Drumuri();
Scriu_drum(5,2);
}
// sa se implemeteze un program care aplica algoritmul
Roy Floyd
//pentru determinarea drumurilor de lungime maxima
//sa nu avem circuite
5
1 2 1
1 3 9
1 5 3
2 4 3
2 3 7
4 3 2
4 1 1
5 2 4
5 4 2

=>are lungimea 27
#include "stdafx.h"

143
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;
const float Minfinit=-1.e20;
float A[50][50];
int n;

void Citire_cost1(char Nume_fis[20], float A[50][50],


int& n)
{
int I,j;
float c;
fstream f(Nume_fis,ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Minfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}

void Drum(int I, int j)


{
int k=1,gasit=0;
while ( (k<=n) && (!gasit ))
{
if ( (I!=k) && (j!=k) && (A[I][j]==A[I][k]+A[k][j]) )
{
Drum(I,k);Drum(k,j);
gasit=1;
}
k++;
}
if (!gasit) cout<<j<<" ";
}

void Scriu_drum(int Nod_Initial, int Nod_Final)


{
if (A[Nod_Initial][Nod_Final]>Minfinit)
{
cout<<"Drumul de la "<<Nod_Initial<<" la
"<<Nod_Final<<" are lungimea "<<A[Nod_Initial]
[Nod_Final]<<endl;
cout<<Nod_Initial<<" ";
Drum(Nod_Initial,Nod_Final);
}
else
cout<<"Nu exista drum de la "<<Nod_Initial<<" la
"<<Nod_Final;

144
}
void Lungime_Drumuri()
{
int I,j,k;
for (k=1;k<=n;k++)
for(I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (A[I][j]<A[I][k]+A[k][j])
A[I][j]=A[I][k]+A[k][j];
}
main()
{
Citire_cost1("graf.txt",A,n);
Lungime_Drumuri();
Scriu_drum(1,2);
}

Grafuri neorientate
Notiuni introductive
Grafuri neorientate
Definiţie. Se numeşte graf neorientat o pereche ordonată de mulţimi (X, U), X
fiind o mulţime finită şi nevidă de elemente numite noduri sau vârfuri, iar U o
mulţime de perechi neordonate ( submulţimi cu două elemente) din X, numite muchii.
Ex.

2
6
1 +8
3
5

7
4
Fig.1

145
Pentru graful de mai sus avem:
X={1, 2, 3, 4, 5, 6, 7, 8}
U={[1,2], [1,4], [1,5], [2,3], [2,5], [3,4], [6,7]}
Dacă u1 şi u2 sunt două muchii care au o extremitate comună ele se vor numi
adiacente.
Definiţie. Un graf parţial al grafului G=(X,U) este un graf G 1=(X,V) astfel
încât VU, adică G1 are aceeaşi mulţime de vârfuri ca G iar mulţimea de muchii V
este chiar U sau o submulţime a acesteia.
Ex. Mai jos avem un graf parţial al grafului de mai sus (Fig.1)
6
2 +8
1
3
5

7
4

Fig.2

Cu alte cuvinte, un graf parţial al unui graf se obţine păstrând aceeaşi mulţime
de vârfuri şi eliminând o parte din muchii.
Definiţie. Un subgraf al unui graf G=(X,U) este un graf H=(Y,V) astfel încât
Y X iar V conţine toate muchiile din U care au ambele extremităţi în Y. Vom spune
că subgraful H este indus sau generat de mulţimea de vârfuri Y.

Ex. Mai jos avem un subgraf al grafului din Fig.1 obţinut prin eliminarea
nodului 3

2
6
+8
1
5

7
4
Definiţie. Gradul unui vârf x este numărul muchiilor incidente cu x.
Gradul vârfului x se notează cu d(x).
Ex. în Fig.1 d(1)=3, d(4)=2, d(8)=0, d(6)=1
Un vârf care are gradul 0 se numeşte vârf izolat.
Un vârf care are gradul 1 se numeşte vârf terminal.
Propoziţia 1. Dacă un graf G=(X,U) are m muchii şi n vârfuri iar
X={x1,x2,..,xn}, atunci d(x1)+d(x2)+...+d(xn)=2m.
Corolar. În orice graf G există un număr par de vârfuri de grad impar.
Definiţie. Se numeşte graf complet cu n vârfuri un graf care are proprietatea
că orice două noduri diferite sunt adiacente.
Propoziţia 2. Un graf complet Kn are n(n-1)/2 muchii.

146
Definiţie. Un graf G=(X,U) se numeşte bipartit dacă există două mulţimi
nevide A, B astfel încât X=A U B, A  B = şi orice muchie u a lui G are o
extremitate în A iar cealaltă în B.
Definiţie. Un graf bipartit se numeşte complet dacă pentru orice x din A şi
orice y din B, există în G muchia [x,y].
Definitie.Un graf partial al unui graf neorientat dat G=(X,G) este un graf G1=(X,g1
unde G1 inclus sau=cu G.
Definitie.Un subgraf al unui graf neorientat G=(X,g) este un graf H=(Y<G1), unde
Y inclus in X, iar muchiile din G1 sunt toate muchiile din G care au ambele
extremitati in multimea Y.
Definitie.Lant.Pentru graful neorientat , un lant este o succesiune de varfuri cu
proprietatea ca oricare doua varfuri sunt adiacente.

Graf complet si bipartit

Se numeste graf complet cu n varfuri , notat Kn , un graf G=(X,U) cu proprietatea ca


oricare doua varfuri sunt adiacente adica :
() x,y є x → ca exista muchia [x,y] apartine de U
Teorema:
Un graf complet cu n varfuri , are n(n-1)/2 muchii.
Definitia :
Se numeste graf bipatrat ,un graf G=(X,U) cu propietate ca exista doua multimi A si
B incluse in X, astfel incat :
-A ∩ B=Ø ,A U B=X,
-toate muchiile grafului au o extremitate in A si cealalta in B .
Definitie :
Se numeste graf bipatrat complet, un graf bipatrat cu propietatea ca pentru orice varf
x din A si orice varf y din B,exista muchia (x,y) (unde A si B sunt cele doua
submultimi care partitioneaza miltimea varfurilor X).

Notiunea de lant si ciclu

Definitie :
Se numeste lant in graful G ,o succesiune de varfuri (z1, z2,z3 ,….zk),unde x1,x2,
…,xk apartine de X , cu propietatea ca oricare doua varfuri consecutive sunt adiacente
,adica exista muchiile [z1, z2],[z2 ,z3],….,[zk-1,zk] apartine de U.
Varfurile z1 si zk se numesc extremitatile lantului ,iar numarul de muchii care intra
in componenta sa reprezinta lungimea lantului .
Definitie :
Se numeste ciclu intr-un graf ,un lant L=(z1,z2,….zk) cu propietatea ca z1=zk si
muchiile [z1,z2 ],[z2,z3], ….,[zk-1,zk] sunt disticte doua cate doua.
Daca intr-un ciclu , toate varfurile cu exceptia primului si a ultimului sunt disticte
doua cate doua ,atunci
ciclul se numeste elementar .In contrar, el este neelementar.
CONEXITATE

Fie G=(X, U) un graf neorientat, X={x1, x2, ..., x2}.

147
Definiţie. Se numeşte lanţ în G succesiunea de vârfuri L={xi1, xi2,..., xik} cu
proprietatea că orice două noduri consecutive din L sunt adiacente, adică [x i1, xi2],
[xi2, xi3],..., [xik-1, xik]  U.
Dacă vârfurile xi1, xi2,..., xik sunt diferite două câte două atunci lanţul se numeşte
elementar. În caz contrar, lanţul este neelementar.
1 2 5

8
7

3 4 6

Ex. L1=[1,2,4] – lanţ elementar


L2=[1,2,3,1,2,4] – lanţ neelementar
Definiţie. Se numeşte ciclu în G un lanţ L pentru care x i1=xik şi toate muchiile adică
[xi1, xi2], [xi2, xi3],..., [xik-1, xik] sunt diferite două câte două.
Ex. C=[1,2,3,1] este un ciclu.
Definiţie. Se numeşte ciclu elementar un ciclu care are proprietatea că oricare două
vârfuri ale sale, cu excepţia primului şi ultimului, sunt diferite două câte două.
Ex. C1=[1,2,3,1] este ciclu elementar.
C2=[3,1,2,4,8,2,3] este un ciclu neelementar.
Definiţie. Un graf G se numeşte conex dacă pentru orice două vârfuri x şi y diferite
ale sale există un lanţ care le leagă.
Definiţie. Se numeşte componentă conexă a grafului G=(X, U) un subgraf C=(X 1,
U1), conex, a lui G care are proprietatea că nu există nici un lanţ în G care să lege un
vârf din X1 cu un vârf din X-X1.

Un graf G este conex , daca oricare ar fi doua varfuri ale sale ,exista un lant care le leaga.
Definitie:

Se numeste componenta conexa a grafului G=(X,U),un subgraf G1=(X1,U1) a lui G ,conex ,


cu propietatea ca nu exista nici un lant care sa lege un nod din X1 cu un nod din X-X1(pentru orice nod ,
nu exista un lant intre acel nod si nodurile care nu fac parte din subgraf).

148
Definitie.Daca multimea G are proprietatea de simetrie, graful se numeste neorientat.
Definitie.Un graf partial al unui graf neorientat dat G=(X,g) este un graf G1=(X,g1
unde g1 submultime a lui g.
Definitie.Un subgraf al unui graf neorientat G=(X,g) este un graf H=(Y,g1), unde
Yinclus in X, iar muchiile din g1 sunt toate muchiile din g care au ambele extremitati
in multimea Y.
Definitie.Lant.Pentru graful neorientat , un lant este o succesiune de varfuri cu
proprieatea ca oricare doua varfuri sunt adiacente.

// sa se implemeteze un program care determina


componentele conexe
// la un graf neorientat
5
1 2
1 3
4 5
=>componenta 1
1 2 3
coponenta 2
4 5

#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

int S[50],A[50][50],n,I,k;

void CitireN (char Nume_fis[20],int A[50][50],int& n)


{
int I,j;
fstream f(Nume_fis,ios::in);
f>>n;
while (f>>I>>j)A[I][j]=A[j][I]=1;
f.close();
}

void df_r(int nod)


{
int k;
cout<<nod<<" "; S[nod]=1;
for (k=1;k<=n;k++)
if ( (A[nod][k]==1)&& (S[k]==0) ) df_r(k);
}
main()

149
{

CitireN("graf.txt",A,n);
k=1;
for (I=1;I<=n;I++)
if (S[I]==0)
{
cout<<"componenta "<<k<<endl;
df_r(I);
cout<<endl;
k++;
}
}
Grafuri hamiltoniene
Definitie:
Se numeste ciclu hamiltonian intr-un graf, un ciclu elementar care contine toate varfurile grafului .
Se numeste graf hamiltonian ,un graf care contine un ciclu hamiltonian.
Se numeste lant hamiltonian intr-un graf, un lant elementar care contine toate varfurile grafului.
Teorema:
Daca intr-un graf G=(X,U) cu n>=3 varfuri, gradul fiecarui varf x verifica conditia d(x)>=n/2, atuncigraful este
hamiltonian,

// sa se implemeteze un program care determina un ciclu


hamiltonian intr-un graf si care e
9
1 2
1 4
2 4
2 3
2 5
3 4
3 9
3 8
4 6
5 6
5 7
5 8
7 8
7 5
8 9
=>1 2 3 9 8 7 5 6 4 ca ciclu hamiltonian
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

int st[50],a[50][50],n,k;

150
void CitireN (char Nume_fis[20],int A[50][50],int& n)
{
int I,j;
fstream f(Nume_fis,ios::in);
f>>n;
while (f>>I>>j)A[I][j]=A[j][I]=1;
f.close();
}

void Init()
{ st[k]=1; }
int AmSuccesor ()
{ if (st[k]<n)
{ st[k]++;
return 1;
}
else return 0;
}
int E_Valid()
{ if (!a[st[k-1]][st[k]]) return 0;
else
for (int I=1;I<=k-1;I++)
if (st[I]==st[k]) return 0;
if (k==n && !a[1][st[k]]) return 0;
return 1;
}
int Solutie()
{ return k==n;}
void Tipar()
{ for (int I=1;I<=n;I++) cout<<"Nodul "<<st[I]<<endl;
k=0;
}
void back()
{ int AS;
k=2; Init();
while (k>1)
{ do {} while ((AS=AmSuccesor ()) && !E_Valid());
if (AS)
if (Solutie()) Tipar();
else {k++;Init();}
else k--;
}
}
main () { st[1]=1;k=2;st[k]=1;
CitireN("graf.txt",a,n);
back();
}

Grafuri euleriene

151
Definitie.Un graf care contine un ciclu eulerian se numeste eulerian.
Un lant L al unui graf G care contine fiecare muchie o data si numai o data se numeste lant
eulerian;
Daca x0=xp si lantul e eulerian atunci ciclul se numeste ciclu eulerian;

// sa se implemeteze un program care determina un ciclu


eulerian intr-un graf si care e
9
1 2
1 4
2 4
2 3
2 5
3 4
3 9
3 8
4 6
5 6
5 7
5 8
7 8
7 5
8 9
=>1 2 4 6 5 7 8 3 9 8 5 2 3 4 1 ca ciclu eulerian
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;
struct Nod
{
int nd;
Nod* adr_urm;
};

int A[50][50],S[50],n;
Nod* Alista, *Indice;

void CitireN (char Nume_fis[20],int A[50][50],int& n)


{
int I,j;

152
fstream f(Nume_fis,ios::in);
f>>n;
while (f>>I>>j)A[I][j]=A[j][I]=1;
f.close();
}
void ciclu(Nod* v)
{int Nodul;
Nod *ANod_baza,*ANod_gasit,*ANod_urm;
ANod_urm=v->adr_urm;
ANod_baza=v;
do
{
Nodul=1;
while (A[ANod_baza->nd][Nodul]==0) Nodul++;
A[ANod_baza->nd][Nodul]=0;A[Nodul][ANod_baza->nd]=0;
ANod_gasit=new Nod; ANod_gasit->nd=Nodul;
ANod_gasit->adr_urm=0;
ANod_baza->adr_urm=ANod_gasit;ANod_baza=ANod_gasit;
} while (ANod_gasit->nd!=v->nd);
ANod_baza->adr_urm=ANod_urm;
}
int adauga()
{
int I,gasit=0;
Indice=Alista;
while (Indice && !gasit)
{
for (I=1;I<=n;I++)
if (A[Indice->nd][I]==1) gasit=1;
if (!gasit) Indice=Indice->adr_urm;
}
if (Indice)
{
ciclu (Indice);
return 1;
}
else return 0;
}
int grade_pare()
{
int I=1,j,s,gasit=0;
while ( (I<=n) && ! gasit)
{
s=0;
for (j=1;j<=n;j++) s+=A[I][j];
if (s%2) gasit=1;
I++;
}
return !gasit;
}
void df(int nod)

153
{
int k;
S[nod]=1;
for (k=1;k<=n;k++)
if ( (A[nod][k]==1) && (S[k]==0)) df(k);
}
int conex()
{
int gasit=0,I;
df(1);
for (I=1;I<=n;I++)
if (S[I]==0) gasit=1;
return !gasit;
}
main()
{ CitireN("Graf.txt",A,n);
if (conex())
if (grade_pare())
{
Alista=new Nod;
Alista->nd=1;Alista->adr_urm=0;
while (adauga());
Indice=Alista;
while (Indice)
{ cout<<Indice->nd<<" ";
Indice=Indice->adr_urm;
}
}
else cout <<"Graful nu indeplineste conditiile de
paritate";
else cout<<"Graful nu este conex";
}

Flux intr-o retea de transport

154
O retea de transport este un graf orientat G=(V,E) care satisface urmatoarele
conditii:
a) exista un unic varf s apartine V in care nu intra nici un
arc(gradul interior 0) numit sursa;
b) exista un unic varf d apartine V in care nu iese nici un
arc(gradul exterior 0);
c) pentru fiecare nod x apartine V-(s,d)exista cel putin un drum de
la sursa la destinatie;cu alte cuvinte graful G este conex;
d) pe multimea arcelor se defineste o functie c:E->R+,numita functie
de capacitate;
Algoritmul se executa intr-un nr finit de pasi.in cadrul surselor prezentate
in continuare determinare drumului de crestere din graful rezidual asociat se
realizeaza printr-o parcurgere BF a acestuia.

// sa se implemeteze un program care determina algoritmul


lui Ford //Furkerson
7
1 7
1 4 6
1 2 1
2 3 8
2 5 4
3 4 3
5 6 5
6 7 8
3 7 1
=>1 2 3 7 dat prin matricea de adiacenta ca fiind de cost
minim
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;
struct Nod
{
int nd;
Nod* adr_urm;
};

void Citire_Cap(char Nume_fis[20],int A[50][50][2],int&


n,int& st,int& fin)

155
{
int I,j, cost;
fstream f(Nume_fis,ios::in);
f>>n>>st>>fin;
while (f>>I>>j>>cost)A[I][j][0]=cost;
f.close();
}
int A[50][50]
[2],n,st,fin,gasit,coada[50],s[50],f[50],I_c,sf_c,I,j,min
;
void refac(int Nod)
{int min;
if (Nod!=st)
if (s[Nod]>0)
{
if (min>(A[s[Nod]][Nod][0]-A[s[Nod]][Nod][1]))
min=A[s[Nod]][Nod][0]-A[s[Nod]][Nod][1];
refac(s[Nod]);
A[s[Nod]][Nod][1]+=min;
}
else
{
if (min>A[Nod][s[abs(Nod)]][1])
min=A[Nod][abs(s[Nod])][1];
refac(abs(s[Nod]));
A[Nod][abs(s[Nod])][1]-=min;
}
}
void drum_in_crestere()
{
gasit=0;
I_c=sf_c=1;coada[I_c]=st;
while( (I_c<=sf_c) && (coada[sf_c]!=fin) )
{
I=1;
while ( (I<=n) && !gasit)
{
if ((A[coada[I_c]][I][0]-A[coada[I_c]][I][1]>0) &&
(s[I]==0))
{
s[I]=coada[I_c];coada[++sf_c]=I;
}
else
if ((A[I][coada[I_c]][1]>0) && (s[I]==0) && (I!=st) )
{
s[I]=-coada[I_c];coada[++sf_c]=I;
}
I++;
}
I_c++;
}

156
if (coada[sf_c]==fin) gasit=1;
}
void caut()
{int min;
do
{
for (I=1;I<=n;I++)s[I]=0;
drum_in_crestere();
if (s[fin])
{
min=32000;
refac(fin);
}
} while (gasit);
}
main()
{
Citire_Cap("Graf.txt",A,n,st,fin);
caut();
for (I=1;I<=n;I++)
{
for (j=1;j<=n;j++) cout <<A[I][j][1]<<" ";
cout<<endl;
}
}
Arbori
Noţiunea de arbore

Definiţie. Se numeşte arbore un graf orientat care este conex si nu conţine cicluri.

Problema. Se citeşte un graf sa se scrie un program care sa verifice daca este arbore.

1. Mai întâi trebuie văzut daca graful este conex. In cazul grafurilor orientate aceasta
problema se rezolva printr-o simpla parcurgere in adâncime (DF). Dar in cazul
grafurilor neorientate? Aici apare o problema. De la nodul i la nodul j exista doua
arce, de la i la j si de la j la i. Aceasta conduce la semnalarea unui ciclu fals. Pentru
rezolvarea, după alegerea unei muchii ,de exemplu de la i la j se elimina muchia de la
j la i. In final daca au fost atinse toate nodurile(adică daca orice componenta a
vectorului s retine numai 1) înseamnă ca graful este conex.

2. Trebuie analizat daca graful nu are cicluri. Aceasta problema se rezolva tot cu
ajutorul parcurgerii in adâncime. Parcurgerea asigura selecţia unei muchi o singura
data. Daca graful are cel puţin un ciclu, atunci un nod este atins de doua ori. Cum ne
putem da seama ca aceasta are loc? simplu, daca a fost atins un nod i , după care
s[i]=1.

//exemplu de determinare averificarii daca un graf este


arbore

157
//4
//1 2
//1 4
//2 3
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >

int A[50][50],S[50],n,i,suma,gasit;

void CitireN (char Nume_fis[20],int A[50][50],int& n)


{
int I,j;
fstream f(Nume_fis,ios::in);
f>>n;
while (f>>I>>j)A[I][j]=A[j][I]=1;
f.close();
}
void df_r(int nod)
{
int k;
S[nod]=1;
for (k=1;k<=n;k++)
if (A[nod][k]==1)
{
A[k][nod]=0;
if (S[k]==0) df_r(k);
else gasit=1;
}
}
main()
{

CitireN("graf.txt",A,n);
df_r(1);
suma=0;
for (i=1;i<=n;i++)
suma+=S[i];
if (suma!=n) cout<<"e conex"<<endl;
else

158
if (gasit) cout<<"are un ciclu macar"<<endl;
else cout<<"este arbore";
}
Mai jos este reprezentat grafic acelasi arbore in mai multe feluri. Ultimele 3 reprezentări sunt făcute
considerând pe rând ca vârf al arborelui nodurile 1,3,2. de asemenea ultimele trei reprezentări justifica
si denumirea data grafurilor conexe si fara cicluri, cea de arbori.

Teorema. Fie G un graf neorientat cu n noduri. G este arbore daca si numai daca g are
n-1 muchii si nu contine cicluri.

Demonstraţie :

 Vom demonstra prin inducţie. Daca n=1,numarul muchiilor este 0(se verifica).
Vom presupune proprietatea adevărata pentru arbori cu n noduri(vom avea n-1
muchii). Fie un arbore cu n+1 noduri. Exista cel putin un nod terminal(nod care are o
singura muchie incident). Daca nu ar exista un astfel de nod, arborele va contine
cicluri(se contrazice definiţia). Eliminam nodul terminal si muchia care ii este
incident. Obţinem un arbore cu n noduri. Conform ipotezei făcute aceasta va avea n-1
muchii. Înseamnă ca arborele cu n+1 noduri va avea n muchii(n- 1 +1).

 Fie un graf cu n-1 muchii care nu contine cicluri. Rămâne de dovedit ca G este
conex. Vom demonstra prin reducere la absurd. Presupunem ca G nu este conex. Fie
G1, G2, .. ,Gp componentele conexe. Fiecare dintre ele indeplineste conditiile:

1) Este conexa(asa a fost aleasa);


2) Nu contine cicluri(pentru ca G nu contine cicluri).

159
Rezulta ca fiecare dintre ele este arbore. Fie mi numarul muchiilor si ni numarul
nodurilor fiecarui arbore Gi. Avem mi=ni-1. Dar m1+m2+….+mp=n-1. Rezulta: n1-
1+n2-1+…..np-1=n-1, deci n1+n2+…..+np=n+p-1. Dar G are n noduri. Rezulta :n+p-
1=n,deci p=1. In concluzie exista o singura componenta conexa care nu contine
cicluri. Deci G este arbore.

 Din demonsratie s-a vazut ca un graf neorientat fara cicluri


dar neconex este alcătuit, din mai mulţi arbori. Din acest
motiv, un astfel de graf se numeşte pădure !

 Tot in aceasta demonstatie, a fost introdus termenul de nod terminal al


unui arbore cu o singura muchie incident.

 Ati reţinut, ca arborele poate fi privit ca avand un anumit varf ! Varf al


arborelui poate fi oricare nod al sau, deci si unul termial !

Algoritmul lui Prim

Fie G=(X,) un graf neorientat şi conex. Graful este dat prin matricea
costurilor. Se cere să se determine arborele de cost parţial minim (între oricare două
noduri să existe un drum), iar în plus suma muchiilor să fie minimă.
O problemă concretă, în care intervine problema enunţată, este cea a conectării
oraşelor cu cost minim.
Avem N oraşe precum şi costul conectării anumitor perechi de oraşe. Se cere
să se genereze muchii care asigură existenţa unui drum între oricare două oraşe astfel
încât costul cuplării să fie minim.
Pentru rezolvare se folosesc trei vectori:
-vectorul S, vector caracteristic. Avem S(I)=0 dacă nodul I nu aparţine
arborelui construit şi S(I)=1 în caz contrar;
-vectorul T reţine pentru fiecare nod care se adaugă arborelui nodul părinte al
acestuia (cu ajutorul acestui vector se poate reconstitui arborele);
-vectorul P reţine pentru fiecare nod C costul arcului de la părinte la el.

Prezentarea algoritmului.

P1) Se citeşte N (numărul nodurilor), matricea costurilor A şi nodul de pornire


V (S(V)=1);

P2) Se alege muchia de cost minim (L,C) care are o extremitate într-unul din
nodurile arborelui deja construit (S(L)=1), iar cealălaltă într-un nod care nu aparţine
arborelui (S(C)=0). Se pune T(C)=L şi P(C)=A(L,C);

P3) Dacă nu au fost alese N-1 muchii, se reia pasul 2.

160
Exemplu. Fie graful din figura următoare: (s=(0 1 1 1 1) )

3 2

5 2
5
2 1 5

4 3
4
Se porneşte cu nodul 1(v=1).
Alegem muchia de cost minim (1,2).
S=(0 0 2 2 1)

Pentru nodurile 1 şi 2, ca puncte de plecare, se alege muchia de cost


minim (2,4) si s=(0 0 4 0 1)

1
4

161
Pentru nodurile 1,2 şi 4 se alege muchia de cost minim (1,5).
S=(0 0 4 0 0)

2 3
5
2

Pentru nodurile 1,2,4 şi 5 se alege muchia de cost minim (4,3) .

11
3
2 5
2

1
4

4
3

s=(0 0 0 0 0)
Am ales patru muchii şi s-a obţinut muchia de cost minim.
Configuraţiile finale ale vectorilor T şi P sunt:

1 2 3 4 5
T 0 1 4 2 1
P 0 2 4 1 3

162
Dacă pornim dintr-un alt nod, este posibil să obţinem un alt arbore dar şi el va
avea costul minim.

Demonstrarea algoritmului.

În urma aplicării algoritmului se obţine un arbore (vom avea N noduri şi N-1


muchii).
Rămâne de demonstrat că acest arbore este de cost minim. Notăm cu
Gk=(Xk,k) arborele obţinut după alegerea a k muchii, cu k{1,2,…,N-1}. Fie G1 un
arbore de cost minim. Fie M1,M2,…,Mk primele k muchii găsite în urma aplicării
algoritmului pentru arborele Gk+1. Considerăm că muchia Mk este prima muchie care
nu aparţine lui G1. Fie P şi Q cele două noduri unite de muchia Mk. Este evident că în
G1, cele două nu sunt unite direct (în caz contrar muchia Mk ar face parte din arborele
G1). Aceasta înseamnă că în G1, nodurile P şi Q sunt unite printr-un lanţ. Acest lanţ nu
aparţine lui Gk+1, pentru că el împreună cu Mk ar forma un ciclu. Înseamnă că acest
lanţ uneşte în G1 două noduri, P1 şi Q1 cu P1Xk şi Q1Xk. În caz contrar dacă ar uni
numai noduri din Xk înseamnă că Gkm ar conţine un lanţ (conţine muchia Mk, care
uneşte pe P şi Q, conţine şi restul lanţului din G1 pentru că primele k noduri în G1 şi
Gk sunt legate identic). Presupunem nodurile P1 şi Q1 unite prin muchia M1. Înlocuim
în G1 pe M1 cu Mk. În urma acestei înlocuiri,G1 se transformă într-un alt arbore G2.
Dar A(P,Q)A(P1,Q1) pentru că în caz contrar, prin logica algoritmului, s-ar fi ales
muchia M1. Aceasta înseamnă că arborele parţial G2 rămâne tot de cost minim. În plus
G1 şi Gk+1 au k+1 muchii care coincid. Repetăm procedeul până obţinem coincidenţa a
n-1 muchii. Rezultă de aici că l-am transformat pe G1 în arborele generat de algoritm
fără a-i modifica costul. În concluzie arborele generat de algoritm este de cost minim.

//algoritmul lui Prim


5
122
153
142
255
241
237
344
327
454
412
421
434
531
544
525
513
=>cost 10
0 1 4 2 1 e traseul
//
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>

163
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >
const float Pinfinit=1.e20;
float A[50][50],min,cost;
int s[50],t[50],n,i,j,k,v;

void Citire_cost (char Nume_fis[20],float A[50][50],int&


n)
{
int I,j;
float c;
fstream f("graf.txt",ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Pinfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}

void main( )
{
cost=0;
Citire_cost("graf.txt",A,n);
cout<<"nodul de pornire";
cin>>v;
for (i=1;i<=n;i++)
if (i==v) s[i]=0;
else s[i]=v;
for (k=1;k<=n-1;k++)
{
float min=Pinfinit;
for (i=1;i<=n;i++)
if (s[i])
if (A[s[i]][i]<min)
{
float min=A[s[i]][i];
j=i;
}
t[j]=s[j];
cost+=A[s[j]][j];

164
s[j]=0;
for (i=1;i<=n;i++)
if (s[i] && A[i][s[i]]>A[i][j]) s[i]=j;
}
cout<<"cost="<<cost<<endl;
for (i=1;i<=n;i++) cout<<t[i]<<" ";
}
Notiunea de arborescenta

Notiunea de arbore poate fii extinsa si pentru grafuri orientate.

Definitie. Fiind date doua noduri i si j ale unui graf orientat, se spune ca exista un lant
de la i la j daca exista o succesiune de arce, indiferent de sensul lor prin care cele doua
noduri sunt unite.
 Daca exista un lant de de la i la j, exista si unul de la j la i.

Definitie. Un graf orientat G=(x,) exte conex daca  ijx


exista un lant de la i la j.

 Daca un graf orientat este tare conex, atunci el este conex, dar reciproca
nu este adevarata. Graful de mai sus este conex.
Definitie. Un graf orientat admite un ciclu daca exista un mod i, pentru care
exista un lant de la i la i.

Exemplu. In graful anterior nodurile 4 2 3 sunt unite printr-un ciclu.

Definitie. Un graf orientat este arbore daca nu este conex si nu admite cicluri.

Exemple:

1. Graful anterior nu este arbore pentru ca


admite un ciclu.
2.

165
Definitie. Un graf orentat g=(x,) admite o radacina vx daca pentru orice xx-(v)
exista un drum (atnete, nu lant) de la v la x.

Exemple:

1. graful de mai sus nu admite o radacina. De exemplu nu exista drum de la 1 la 3


(1 nu este radacina), nu exista drum de la 3 la 4 (nici 3 nu este radacina) etc.

2. Graful alaturat are radacinile 1, 2, 3,4.

Definitie. Se numeste arborescenta un graf orientat care indeplineste simultant doua


conditii :
1.Este arbore;
2.Admite radacina.

 Intr-o arborestenta radacina este unica. Daca de exemplu exista doua radacini v1 si
v2 atunci exista drum de la v1 la v2 si de la v2 la v1. aceasta presupune existenta unui
ciclu, deci nu este arbore.
 De regula, atunci cand se da o arborescenta se preciaza radacina sa.

Alaturat este prezentata o arborescenta de radacina 2.

166
 Se observa ca din radacina pleaca arce, nu intra. Daca ar si intra s-ar obtine un
ciclu.

 de asemenea, orice nod are un singur predecesor. Daca, prin absurd, ar avea doi sau
mai multi, s-ar obtine un ciclu care include radacina.

Problema. Se citeste un graf orientat. Acesta este dat prin matricea de adiacenta.se
cere ca programul dv. sa decida daca graful este sau nu oarborescenta. In caz
afirmativ, se va tipari radacina acesteia.

167
Rezolvare.

1. Tebuie decis mai intai daca graful orientat este arbore. Acesta se face printr-o
singura parcurgere in adancime,asemanator cu altgoriymul aplicat in cazul
grafurilor orienta.
2. In cazul cand graful este arbore , trebuie vazut daca acesta are o radacina. Se
testeaza, pe rand, fiecare nod daca indeplineste conditia de radacina. Fiecare
test se face printr-o parcurgere in adancime pornind de la nodul respectiv.

//exemplu de determinare arborescenta

//4
//1 2
//1 4
//2 3
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >

int A[50][50],s[50],b[50]
[50],ok,radacina,n,i,j,r,suma,gasit;

void Citire(char Nume_fis[20],int a[50][50],int& n)


{
fstream f(Nume_fis,ios::in);
int I,j;
f>>n;
while (f>>I>>j) A[I][j]=1;
f.close();
}
void df_r(int nod)
{
int k;
cout<<nod<<" ";
s[nod]=1;
for (k=1;k<=n;k++)
if ((A[nod][k]==1) && (s[k]==0))
df_r(k);
}

main()

168
{
Citire("Graf.txt",A,n);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
b[i][j]=A[i][j];
df_r(1);
for (i=1;i<=n;i++) suma+=s[i];
if (suma!=n) cout<<"NU e conex"<<endl;
else cout<<" e conex"<<endl;
if (gasit) cout<<"are un ciclu"<<endl;
else cout<<" nu are cicluri"<<endl;
if (suma==n && ! gasit)
{
cout<<"este arbore"<<endl;
ok=1;
}
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
b[i][j]=A[i][j];
if (ok)
{
r=1;
do
{
suma=0;
for (i=1;i<=n;i++) s[i]=0;
df_r(r);
for (i=1;i<=n;i++) suma+=s[i];
if (suma==n)
{
cout<<"radacina este "<<r<<endl<<"este
arborescenta";
radacina=1;
}
else r++;
}
while (!radacina && r<=n);
if (!radacina) cout <<"nu are radacina";
}
}

Metode de memorare a arborilor

1. asa cum am aratat, un arbore este un graf neorientat, cu anumite proprietati.


Aceasta inseamna ca el poate fi reprezentat ca un graf. De aici rezulta ca pentru
reprezentarea unui arbore se pot utilza:

- matricea de adiacenta;
- liste de adiacente.

169
2. O a doua forma de reprezentare a arborilor este legatura de tip
TATA . arborele se reprezinta sub forma unui vector t cu n
componente: daca t[i]=k, atunci nodul i este descendent al
nodului k. Daca nodul i este varf, t[i]=0.

5 3 5 5 0 3 4 1 7--------- k
1 2 3 4 5 6 7 8 9 --------- t[i]

 Legatura de tip TATA se mai numeste si legatura cu referinte ascendente.

 In mod evident, legatura de tip TATA este determinata si de nodul ales ca varf !
Daca, de exemplu pentru acelasi arbor varful este 9, reprezentarea este alta. Este ca si
cum „apucam” arborele de un nod si celelalte cad !

 De fapt prin legatura de tip TATA se reprezinta o padure !

035003 417
123456789

 Aceasta reprezentare a mai fost intalnita. De exemplu, la altgoritmul lui Dijkstra !

3.Codul lui Puffer(facultativ). Pentru a reprezenta un arbore oarecare se pot utiliza doi
vectori, pe care-i vom numi t-de la nodul terminal – si pt – de la parinte nod terminal.
Modul in care acestia retin datele este explicat mai jos:

 Se cauta cel mai mic nod terminal, acesta este retinut in vectorul t iar parintele
sau este memorat in vectorul pt.
 Se obtine un nou arbore renuntand la nodul terminal si la muchia care-l uneste
de parinte.

170
 Procedeul se rea pana cand ramane un arbore cu un singur nod.

Exemplu .

Pentru arborele alaturat cel mai mic nod terminal este doi,iar parintele trei.

In noul arbore ,cel mai mic nod terminal este 6, iar parintele lui 6 este 3.

Cel mai mic nod terminal este 3, iar parintele 5.

Cel mai mic nod terminal este 8, iar parintele sau este 1.

Cel mai mic nod terminal este 1, iar parintele sau este 5.

171
 Pana in acest
moment am memorat arborele prin utilizarea a 2 vectori t si pt. Fiecare dintre ei, are
n-1 componente!

 Prin logica altgoritmului nodul care ramane este cel mai mare adica nodul n.
Acesta este ultimul memorat in vectorul pt.Prin urmare, daca se cunoaste n sunt
suficiente n-2 componente pentru vectorul pt.

 Foarte important! Vectorul t se poate reconstitui doar daca cunoastem vectorul pt.
aceasta inseamna ca un arbore cu n noduri a fost memorat doar printr-un vector cu n-2
componente!

172
Pentru reconstituirea vectorului t pornim de la pt,vom incepe prin a memora in pt,
numarul n, pentru componenta n-1. vectorul t se reconstituie prin formula:

i{1,2…n-1}
t[i]=min{kk{t[1]=t[2],…t[i-1],pt[i],pt[i+1]…pt[n-1]}}

Cum s-a dedus formula de mai sus?

- daca numarul se gaseste in pt[i],pt[i+1],…pt[n-1] inseamna ca nodul respectiv nu a


fost eliminat pana la pasul i, pentru ca este parinte.
- daca numarul se gaseste in t[1],t[2],…t[i-1] inseamna ca a fost extras la unul din
pasii anteriori.
- dintre nodurile care nu se gasesc ca mai sus , la pasul i a fost extras nodul
minim.

Exemplu. Plecam de la n=9,si pt={3 3 5 1 5 4 7 }


Pasul 1
T={}
Pt={3 3 5 1 5 4 7,9}
Cel mai mic numar care nu se gaseste in pt si este intre 1 si 9 este 2.

Pasul 2
T={2}
Pt={3 3 5 1 5 4 7,9}
Se alege 6.

Pasul 3.
T={2 6 }
Pt={3 3 5 1 5 4 7,9}
Se alege 8.


//exemplu la codul PUFFER
//n=9
//3 3 5 1 5 4 7 9
//=>2 6 3 8 1 5 4
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >

int t[50],pt[50],i,j,k,n,gasit;

173
main()
{
cout<<"n=";cin>>n;
for (i=1;i<=n-2;i++)
{
cout<<"PT["<<i<<"]";
cin>>pt[i];
}
pt[n-1]=n;k=1;
for (i=1;i<=n-1;i++)
{
k=1;
do
{
gasit=0;
for (j=0;j<=i-1;j++)
if (t[j]==k) gasit=1;
if (!gasit
)
for (j=i;j<=n-1;j++)
if (pt[j]==k) gasit=1;
if (gasit) k++;
} while (gasit);
t[i]=k;
}
for (i=1;i<=n-1;i++)
cout<<t[i]<<" ";
}

174
Arbori binari

Notiunea de arbore binar

Programatorii folosesc de multe ori prin abuz de limbaj, termenul de arbore, in loc de
arborescenta. In astfel de cazuri se specifica radacina care se mai numeste si varf si se considera
ca sensul arcelor este implicit,motiv pentru care nu se mai reprezinta grafic.

In cazul in care arborescenta are cel mult doi descendenti, unul stang si unul drept,
spunem ca avem un arbore binar.

Exemplu. Mai jos vedeti o arborescenta cu radacina 1 care este un arbore binar.
Alaturat obs. Modul in care se reprezinta grafic renuntand la sensurile arcelor, care
sunt implicite.

175
 Se presupune ca nodurile sunt asezate pe mai multe niveluri. Radacina
este pe nivelul 0, descenndentii direci directi ai radacini sunt pe nivelul
1, descendentii directi ai nodurilor de pe nivelul 1 sunt pe nivelul 2 etc.
 Numarul de niveluri mai putin cel al radacinii, ocupate de nodurile
grafului,se numeste adancimea arborelui binar.
 Vom face dinstinctie intre descendentul stang si cel drept. De
exemplu, arborii de mai jos sunt considerati dinstincti.

Modalitati de reprezentare a arborilor binari

Desigur, arborii binari pot fi reprezentati ca orice grf orientat, dar


aceasta forma este deosebit de greoaie in aplicatii. Din acest motiv sunt
cu mult mai convenabile reprezentarile pe care le urmarim in continuare.

1. reprezentarea cu ajutorul vectorilor ( clasica ).

176
Un arbore binar poate fi reprezentat cu ajutorul a doi vectori, pe care ii vom numi st
(de la stanga ) si dr (de la dreapta). Pentru fiecare nod i dintre cele n, st[i] retine nr. de
ordine al nodului stang subordonat de i, iar dr[i] retine nr de ordine al nodului drept
subordonat de i. Daca nu exista nod subordonat retine 0.

Exemplu. Arborele binar cu 7 noduri si v=1 de pe pagina anterioara se reprezinta


astfel:

st 2 4 5 0 0 0 0

1 2 3 4 5 6 7

dr 3 0 6 0 7 0 0

2. Reprezentarea in HEAP (actuala).

Programul va retine doar adresa radacinii (varfului). In aplicatii veti intalni numeroase
exemple in acest sens, motiv pentru care ne oprim aici cu aceasta reprezentare.

3. Reprezentarea cu ajutorul legaturii TATA.

Aceasta a mai fost prezentata in cazul arborilor neorientati. Mentionam doar faptul ca
aceasta reprezentare este neconvenabila in majoritatea aplicatiilor.

Modalitati de parcurgere a arborilor binari

177
In scopul prelucrarii,este necesar ca nodurile arborelui binar sa fie vizitate. Axista mai multe

modalitati de parcurgere a arborilor binari carer difera prin ordinea de vizitare a nodurilor.

 Putem considera ca fiecare nod al arborelui binar subordoneaza un subarbore


binar stang si unul drept. Pe baza acestei observatii putem scrie usor
subprograme recursive pentru diverse le modalitati de pacurgere.

Principalele modalitati de parcurgere ale unui arbore binar sunt:

A) Parcurgerea in inordine – SVD – se se patrcurge mai


intai subarborele stang, apoi varful apoi subarborele
drept.
B) Parcurgerea in preordine – VSD – se parcurge mai
intai subarborele stang, apoi subarborele drept.
C) Parcurgerea in postordine – SD – se parcurge mai
intai subarborele stang, apoi subarborele drept, apoi
varful.
D) Parcurgerea in adancime – a fost studiata pentru
grafuri, aici se aplica in particular. Se parcurge
radacina, apoi nodurile de pe nivelul 1,apoi cele de pe
nivelul 2 etc.

 De exemplu parcurgerea SDV se face pe urmatoarea idee: varful subordoneaza


doi subarbori: cel stang si cel drept. Se trece la subarborele stang, apoi la varf,
apoi la subarborele drept. Pentru fiecare subarbore se repeta rationamentul.

Ordinea de parcurgere pentru arborele alaturat este: inordine –SDV-:4 2 1 5 7 3 6

preordine –VSD-:1 2 4 3 5 7 6

postordine –SDV-:4 2 7 5 6 3 1

latime:1 2 3 4 5 6 7

178
Programul urmator citeste si parcurge in inordine,postordine si preordine
un arbore binar. Arborele este retinut in HEAP. Pentru a marca faptul ca
descendentul unui anumit nod lipseste se introduce 0.
//sa se testeze parcurgerile de arbori binari
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >

struct nod {
int nr ;
struct nod * st ;
struct nod * dr ;
} ;

nod * c;
nod* arb()
{
int n;
nod* c;
cout<<"n=";cin>>n;
if (n)
{
c=new nod;
c->nr=n;
c->st=arb();

179
c->dr=arb();
return c;
}
else
return 0;
}

void Preordine ( nod *a )


{
if ( a)
{
cout<< a -> nr ;
Preordine ( a -> st ) ;
Preordine ( a->dr ) ;
}
}
void Postordine ( nod *a )
{
if ( a )
{
Postordine (a -> st) ;
Postordine ( a -> dr )
;
cout<< a -> nr ;
}
}
void Inordine ( nod *a )
{
if ( a)
{
Inordine (a -> st ) ;
cout<< a -> nr ;
Inordine ( a -> dr ) ;
}
}
void main( )
{
c=arb();
Inordine (c);cout<<endl;
Postordine (c); cout<<endl;
Preordine (c);cout<<endl;
}
Arbore binar de cautare
Definitie. Se numeste arbore de cautare un arbore binar ale carui noduri
au o cheie de identificare,iar pentru fiecare nod avem proprietatiile
urmatoare:

- orice cheie asociata unui nod al subbarborelui stang este mai mica decat cheia
asociata nodului;

180
- orice cheie asociata unui nod al subarborelui drept este mai mare decat cheia
asociata nodului.

Cheile de identificare sunt distincte !

Alaturat observati un arbore de cautare.

1. INSERAREA

Crearea arborilor de cautare se face aplicand de un nr de ori operatia de inserare.


Regula de inserare este urmatoarea:

 Se compara cheia asociata unui nod cu cheia inregistrarii care se insereaza. Avem
trei posibilitati:

- cheia coincide cu nr – se renunta la inserarea acelui numar;


- cheia este mai mare decat numarul – se incearca inserarea in subarborele
stang;
- cheia este mai mica decat numarul – se incearca inserarea in subarborele drept.

 Inserarea propriu-zisa se realizeaza atunci cand subarborele


stang,respectiv drept,este vid,astfel se reia.

Vezi procedura INSERARE.

2. CAUTAREA

Se face in mod asemanatorcu inserarea motiv pentru care nu o mai comentam.

3. LISTAREA

Informatia se poate lista utilizand oricare din metodele cunoscute pentru parcurgerea
arborilor. Daca dorim listarea informatiilor in ordinea strict crescatoare a cheilor,se
utilizeaza metoda stang-varf-dreapta (inordine), intrucat pentru orice nod avem
urmatoarele:

 cheile nodurilor din subarborele stang sunt mai mici decat cheia
asociata nodului;
 cheile nodurilor din subarborele drept sunt mai mari decat cheia
asociata nodului.

181
Vezi procedura SVD.

4. STERGEREA

Dupa stergerea unui nod care are o anumita cheie, arborele ramas trebuie sa fie de
cautare.

Se disting 4 situatii posibile:

a) nodul care urmeaza sa fie sters este nod terminal – in acest caz se face
stergerea avand grija ca la parintele lui sa inlocuim adresa catre el cu
nil;
b) nodul care urmeaza a fi sters subordoneaza un singur subarbor – cel
drept – caz in care parintelui se va inlocui adresa catre el cu adresa
subarborelui drept,iar nodul respectiv se sterge;
c) nodul care urmeaza a fi sters subordoneaza un singur subarbore – cel
stang – caz in care parintelui i se va inlocui adresa catre el cu adresa
subarborelui stang, iar nodul respectiv se sterge;
d) nodul care urmeaza a fi sters( dupa cum vom vedea, acesta se va sterge
numai logic) subordoneaza doi subarbori, caz in care se fac operatiile:

- se indentifica cel mai din dreapta nod al subarborelui stang coorespunzator


nodului care urmeaza a fi sters (acesta va fi sters in mod efectiv, nu inainte de
a muta informatiile sale la nodul care se sterge logic);
- cheia acestuia si alte informatii utile continute de el se muta la nodul care
urmeaza a fi sters
- subarborele stang al nodului care se va sterge fizic se leaga:
- in stanga nodului care se sterge logic (daca nodul indentificat ca cel mai din
dreapta din subarborele stang este descendent direct al nodului care se sterge
logic)
- in dreapta tatalui nodului care se sterge fizic( in caz contrar);

Exemple de stergere:

1. Arborelui din stanga i se sterge nodul 8. acesta nu subordoneaza nici un alt


arbore.

2. Arborelui din stanga i se sterge nodul 9. acesta subordoneaza un singur


subarbore, cel drept.

182
3. Arborelui din stanga i se sterge nodul 3. Cel mai din dreapta nod al
subarborelui stang este 1. se obtine:

4. Arborelui din stanga i se sterge nodul 7. cel mai din dreapta nod al
subarborelui stang este 6. tatal nodului care se sterge fizic este 3. in dreapta sa
se leaga subarborele 5 4 . se obtine:

Vezi procedura STERG. In situatia in care nodul care urmeaza a fi sters subordoneaza
doi arbori,se apeleaza procedura CMMD.

183
 Intrebarea la care trebuie sa raspundem in continuare este
urmatoarea:de ece daca se sterge in acest mod un nod,arborele ramane
de cautare? Stergerea unui nod se face in mod dinstinct pentru fiecare
din cele 4 cazuriaratate. Datorita simplitati prelucrarii primele tri cazuri
nu necesita comentarii. In cazul 4 se indentifica nodul cel mai din
dreaptadin arborele stang. Cheia acestuia trece in locul cheii nodului
care se sterge. Aceasta este mai mica decat cheia initiala,este in acelasi
timp cea mai mare din subarborele stang,deci este cea mai mare cheie
mai mica decat cheia care se sterge. Iata motivul pentru care aceasta
trece in locul cheii sterse logic.
 Transmiterea parametrului c se face prin referinta. Adresa de alocare
va fi trimisa automat parintelui,caadresa de subarbore stang sau drept
(duopa caz).
 Altgoritmul are complexitatea in cazul cel mai defavorabil 0(n2). In
cazul in care, de exemplu cheile inserate sunt in ordine crescatoare,
arborele va degenera intr-o lista liniara –orice nod are numai un
descendent drept,iar
 inserarea unui nod inseamna parcurgerea tuturor nodurilor deja inserate
in pasul anterior.

Teoria regasirii informatiei este deosebit de importanta pentru viata practica. Aceasta
conduce la o cautare rapida (putine comparatii).
//
#include <stdlib.h>
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <math.h>
#include <string.h>
#include <time.h>
#include <iomanip>
using namespace std;
#include <stdio.h >
# include <conio.h >

struct nod
{
int nr;
nod *as,*ad;
};
nod *v,*man;
char opt;
int k;

void inserare(nod*& c,int k)


{
if (c)
if (c->nr==k)
cout<<"este deja";
else

184
if (c->nr<k) inserare(c->ad,k);
else
inserare(c->as,k);
else
{
c=new nod;c->as=c->ad=0;
c->nr=k;
}
}

void cautare(nod*& c,nod*& adr,int k)

{
if (c)

if (c->nr<k) cautare(c->ad,adr,k);
else
if (c->nr>k)
cautare(c->as,adr,k);
else
adr=c;
else
adr=0;
}

void parcurg (nod* c)


{
if (c)
{
parcurg(c->as);
cout<<c->nr<<endl;
parcurg(c->ad);
}
}

void cmmd(nod*&c,nod*& f)
{
if (f->ad) cmmd(c,f->ad);
else
{
c->nr=f->nr;
man=f;
f-f->as;
delete man;
}
}
void sterg (nod*& c,int k)
{
nod* f;
if (c)
if (c->nr==k)

185
if (c->as==c && c->ad==0)
{
delete c;
c=0;
}
else

if (c->as==0)
{
f=c->ad;
delete c;
c=f;
}
else
if (c->ad==0)
{
f=c->as;
delete c;
c=f;
}
else
cmmd(c,c->as);
else
if (c->nr<k)
sterg (c->ad,k);
else
sterg(c->as,k);
else
cout<<"nu e numarul ";
}
main()
{char opt;
v=0;
do
{
cout<<"tastati t-terminare,i-inserare,l-listare,c-
cautare,s-stergere";cin>>opt;
switch (opt)
{
case 'i':
cout<<"k=";cin>>k;
inserare(v,k);
break;
case 'l':parcurg(v);
break;
case 'c':
cout<<"k=";cin>>k;
cautare(v,man,k);
if (man) cout<<k<<endl;
else
cout <<"nu exista acest nod"<<endl;

186
break;
case 's':
cout<<"pe cine stergi ";
cin>>k;
sterg(v,k);
break;
}
}while (opt!='t');
}

CUPRINS

MATRICI …………………………………………………………………………pag. 1
DETERMINANŢI ……………………………………………………………….pag. 8
Regula lui Cramer :  ……………….pag. 22
Ecuatii,inecuatii sisteme……………….pag. 23
Statistica……………….pag. 27
Mulţimea numerelor raţionale……………….pag. 31
Functii ……………….pag. 35
Vectori si operatii ……………….pag. 40
Geometria analitica a dreptei……………….pag. 44
SEMIGRUPURI ŞI GRUPURI……………….pag. 48
RELATII DE ECHIVALENTA……………….pag. 59
SISTEME DE DOUA ECUATII CU DOUA
NECUNOSCUTE……….pag. 63
GRAFURI in C++---------------------------- pag.68
POINTARI SI ADRESE……….pag. 70
STIVA……….pag. 74
COADA……….pag. 80
DIVIDE ET IMPERA……….pag. 83
Tehnica backtracking……….pag. 88
GREEDY……….pag. 97
Programare dinamica……….pag. 101
Alocarea dinamica a memoriei……….pag. 106
Liste liniare……….pag. 111
Grafuri orientate……….pag. 119
Grafuri neorientate……….pag. 149
Arbori……….pag. 161

187
Cuprins……….pag. 191
BIBLIOGRAFIA…………………….……………………………….pag.192

BIBLIOGRAFIE:
1.Manual de Matematică, clasa a XI-a, Editura
Fair Partners, 2006
2. C. Năstăsescu, C. Niţă, Culegere de probleme
pentru liceu, Algebra, Editura Rotech Pro, 2002
3.Caiet de notiţe
4. Internet
5. Agenda tehnica 1980
6. Visual Studio C++ .NET Developer's Guide (2002)
7. Visual.C++.For.Dummies
8. Database developer with visual c++ 4
9. Special Edition Using Visual C++ 6
10.Jurnal de matematica de Gorj

188

S-ar putea să vă placă și