Documente Academic
Documente Profesional
Documente Cultură
PCLP3 - Indrumar de Laborator
PCLP3 - Indrumar de Laborator
Programarea Calculatoarelor
i Limbaje de Programare III
ndrumar de laborator
BRAOV, 2012
Cuprins
2. LISTE
11
11
11
2.1.1.1. Enumerri
11
2.1.1.2. Structuri
12
2.2. POINTERI
14
16
16
17
17
18
2.3. LISTE
18
18
19
3. ARBORI BINARI
23
23
4. BACKTRACKING
27
28
ndrumar de laborator
5. ALGORITMI DE CUTARE
33
33
6. ALGORITMI DE SORTARE
35
35
36
7. GRAFURI
39
39
40
7.2.1. DIJKSTRA
40
7.2.2. FLOYD
41
42
45
Date de intrare
PROGRAM
Date de ieire
Pentru a uura lucrul cu datele necesare unui program, toate limbajele de programare ofer
mecanismul de variabil. O variabil este, ntr-o definiie foarte simpl, o etichet asociat unei
adrese de memorie, deci, indirect, unei locaii de memorie.
Cea mai important caracteristic a unei variabile l constituie tipul de date asociat.
Tipul de date specific natura valorilor, modul de reprezentare a acestora i plaja de valori ce se
poate memora n zona de memorie asociat variabilei. Majoritatea limbajelor de programare ofer
3
ndrumar de laborator
un set redus de tipuri de date, numite de baz. Pe lng acest set, limbajele de programare
moderne ofer i mecanisme pentru crearea de noi tipuri de date (tipuri de date utilizator),
utiliznd tipurile deja existente. Printre aceste mecanisme se remarc enumerrile, structurile i
clasele.
Exist i limbaje de programare (cum ar fi Microsoft Visual Basic) care nu au dect un
singur tip de date, numit generic tipul variant. Practic, tipul variabilelor sunt extrapolate
din contextul n care apar sau sunt convertite explicit n momentul utilizrii. Astfel, o
variabil poate fi tratat att ca numr, ct i ca ir de caractere.
Majoritatea algoritmilor necesit structuri de date specifice pentru a-i putea ndeplini scopul.
Aceste structuri se obin fcnd apel la mecanismele de creare a tipurilor de date utilizator. Acest
ndrumar va trata structurile de date n mod progresiv, pe msur ce algoritmii prezentai o vor
necesita.
Unii algoritmi fac apel la metode speciale de calcul/prelucrare, denumite tehnici de programare,
majoritatea fiind mprumutate din matematic. Printre tehnicile ce vor fi utilizate n acest
ndrumar se numr: greedy, recursivitate, backtracking i divide et impera.
Muhammad ibn
Musa Khwarizmi
#include <stdio.h>
void main()
{
FILE *fp;
if (!(fp=fopen("d:\\fisier.txt","r+b")))
{
puts("Nu pot deschide fisierul");
exit(1); //terminare fortata a executiei cu returnarea codului 1
}
}
Fiierul "d:\fisier.txt" este deschis pentru citire i scriere ("r+"), n mod binar ("b"). Pentru modul
"r", dac fiierul nu exist pe disc sau operaia de deschidere nu reuete (de exemplu, dac
unitatea de disc nu este pregtit), fopen() ntoarce valoarea NULL. n caz contrar deschide
fiierul, creeaz o structur FILE i ntoarce adresa sa.
ndrumar de laborator
#include <stdio.h>
void main()
{
FILE *fps, *fpd;
char c;
if (!(fps=fopen("d:\\fin.dat","rb"))==NULL)
{
puts("Nu pot deschide fisierul sursa");
return;
}
if((fpd=fopen("d:\\fout.dat","wb"))==NULL)
{
puts("Nu pot crea fisierul destinatie");
return;
}
while (!feof(fps))
{
c=getc(fps);
if (!feof(fps))
{
putc(c, fpd);
}
}
fclose(fps);
fcolse(fpd);
}
Deoarece fiierele conin date oarecare, ciclul de copiere folosete funcia feof( ) pentru a
detecta sfritul de fiier. Aceast precauie nu era necesar n cazul unui fiier text (alctuit din
coduri ASCII), unde ar fi suficient testarea valorii ntoarse de getc( ) pentru a detecta caracterul
EOF. Trebuie remarcat c funcia feof() semnaleaz sfritul fiierului abia dup ce acesta a fost
ntlnit la operaia de intrare precedent. Din acest motiv, dac instruciunea if ar lipsi, n fiierul
d:\fout.dat s-ar copia n plus valoarea EOF.
De asemenea, datorit faptului c limbajul Java utilizeaz sistemul Unicode pentru tipul caracter,
ceea ce nseamn c un caracter ocup 16 bii, API-ul Java introduce dou tipuri de fluxuri:
Aceste fluxuri sunt fluxuri primitive, deoarece sunt conectate direct de sursa sau destinaia datelor.
Exist i fluxuri de procesare care se construiesc pe baza fluxurilor primitive i ofer faciliti
suplimentare. Un tip de flux de procesare este fluxul cu zona de memorie tampon ("buffered");
astfel se pot utiliza urmtoarele clase:
de caractere;
7
ndrumar de laborator
de octei.
Paii de urmat n lucrul cu fluxuri sunt:
1. Deschidere flux (prin instanierea unei clase de lucru cu fluxuri)
2. Procesare date (folosind metodele specifice clasei alese pentru lucrul cu fluxuri)
3. nchidere flux (prin apelarea metodei close() a instanei clasei de lucru cu fluxuri)
Orice eroare n lucrul cu fiierele va rezulta n aruncarea unei excepii, care provine din clasa
IOException.
Din acest motiv este obligatoriu ca programele care utilizeaz fluxuri s trateze
importjava.util.*;
import java.io.*;
public class CitireaDinFisier
{
public static void main(String[] args)
{
String linie;
try {
BufferedReader in = new BufferedReader(new FileReader(
"\\fis.txt"));
while ((linie = in.readLine()) != null)
{
System.out.println(linie);
}
}
catch (IOException e)
{
System.out.println(e);
}
}
}
component este memorat pe un octet, astfel c intensitatea luminoas poate varia ntre 0 (lipsa
componentei de culoare) i 255 (intensitate maxim a culorii).
Pentru a obine versiunea n tonaliti de gri a unei imagini este suficient a se calcula o medie ntre
intensitile culorilor componente ale fiecrui pixel. Deoarece ochiul uman este mai sensibil la
culoarea verde, se prefer utilizarea unei medii ponderate, n care culoarea verde este
predominant. Aceast medie este folosit n calcularea luminanei (componenta Y a spaiului de
culoare YUV). Formula de calcul poate fi regsit pe linia 27 a programului.
BmpClass.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package bmp;
import
import
import
import
java.io.BufferedInputStream;
java.io.BufferedOutputStream;
java.io.FileInputStream;
java.io.FileOutputStream;
ndrumar de laborator
Headerul fiierului de intrare se copiaz fr modificri, deoarece doar culorile imaginii se schimb,
nu proprietile acesteia.
Fiierul este parcurs apoi octet cu octet i grupuri de cte trei octei sunt utilizate pentru a calcula
luminana. n fiierul de ieire este nscris de trei ori luminana, ceea ce nseamn c se
nlocuiete un pixel colorat cu unul de culoare gri la intensitatea dat de luminan.
S se scrie un program care s genereze, pe baza unui fiier bitmap de intrare, trei fiiere
bitmap, care prezint separat cele trei componente de culoare.
10
2. Liste
2.1. Tipuri de date
2.1.1. Tipuri de date definite de utilizator
Acestea se obin din tipurile de baz sau din combinaii ale acestora sau chiar ale altor tipuri
utilizator.
Limbajele C/C++ pun la dispoziie o serie de mecanisme i structuri ce permit definirea unor tipuri
de date noi. Printre acestea putem meniona structurile, uniunile, enumerrile i clasele.
Noilor tipuri de date li se pot asocia nume (identificatori) fie direct, fie prin utilizarea declaraiei
typedef.
Declararea unor variabile de tip utilizator se face la fel cu cea a tipurilor predefinite. Sintaxa
general este:
Sintaxa utilizat:
Spre deosebire de declararea variabilelor de tip predefinit, declararea variabilelor de tip utilizator
se poate face i n locul definirii tipului respectiv (n cazul n care nu se utilizeaz declaraia
typedef).
2.1.1.1. Enumerri
Se folosesc la definirea unor mulimi de constante. Cuvtul cheie care semnaleaz declaraia unei
enumerri este enum. Sintaxa este:
Format general:
enum [id_tip_nou] {nume_const_1 [=<valoare>], ...} [lista_variabile];
unde:
este valoarea atribuit constantei respective; poate lipsi, caz n care valoarea ei
va fi egal cu valoarea constantei definite anterior incrementat cu 1;
lista_variabile lista variabilelor ce se doresc a fi utilizate; aceast list poate lipsi,
fiind posibil o declarare ulterioar (n cazul n care a fost denumit noul tip).
valoare
11
ndrumar de laborator
Exemplu:
enum mVid {LASTMODE=-1, BW40=0, C40, BW80, C80, MONO=7};
2.1.1.2. Structuri
Structurile reprezint unul dintre cele mai puternice mijloace de definire a noi tipuri de date,
permind nglobarea mai multor variabile ntr-un ansamblu unitar. Definirea unei structuri se
realizeaz cu ajutorul cuvntului cheie struct.
Format general:
struct [id_structura]
{
[id_tip id_var[, id_var, ...]] ;
...
} [lista_variabile];
unde:
Trebuie menionat faptul c dac lipsesc att numele ct i lista de variabile, structura va fi
inutilizabil n program (nu va putea fi referit).
Declararea variabilelor de tip structur se poate realiza n dou moduri:
12
n locul definirii tipului structur (dac nu a fost definit cu ajutorul declaraiei typedef);
oriunde n program, utiliznd numele dat structurii ca tip al variabilei.
n cazul utilizrii declaraiei typedef, definirea unei structuri trebuie s respecte sintaxa:
Format general:
typedef struct
{
[id_tip id_var[, id_var, ...]] ;
...
} [id_structura];
Dup cum a fost menionat anterior, o structur grupeaz mai multe variabile de diferite tipuri
ntr-o aa-numit nregistrare (record); aceste variabile poart denumirea de membri ai structurii
i ei pot fi accesai att direct, ct i referii prin adresele lor. Practic, o variabil de tip structur va
ncorpora mai multe variabile de diferite tipuri sub un singur identificator.
STRUCTURA
Membru 1
Membru 2
Membru 3
Fig. 2 Structura grupeaz mai multe variabile sub un singur nume (identificator)
Accesul la oricare dintre membri unei variabile de tip structur se face cu ajutorul operatorului de
selecie direct ..
Sintaxa utilizat pentru accesarea unui membru al unei structuri este:
Sintaxa utilizat:
nume_variabil_structur.identificator_membru
Atribuirea valorilor membrilor structurii se face exact n ordinea n care au fost aezai n definirea
tipului structura (prima valoare va fi atribuit primului membru din definiia structurii, a doua
celui de-al doilea membru, .a.m.d.).
Pentru a ilustra modul de lucru cu structurile, vom defini o structur ce va conine numele unui
student i media notelor acestuia; o vom iniializa, apoi, cu valorile Popescu Andrei pentru nume
i 9,78 pentru medie; vom afia numele i media pe ecran.
13
ndrumar de laborator
Student.cpp
#include <stdio.h>
// definim structura
struct Student
{
char strNume[30];
float fMedia;
};
// programul principal
void main()
{
// declaram o variabila v1 de tip Student si o initializam
Student v1={Popescu Andrei, 9.79};
// afisam continutul structurii v1
printf(Studentul %s are media %g.\n,v1.strNume,v2.fMedia);
}
S se scrie un program care s determine vectorul sum obinut prin adunarea mai multor
vectori bidimensionali a cror componente se vor citi de la tastatur, i s se reprezinte
grafic obinerea acesteia (fiecare vector va fi reprezentat prin alt culoare).
Indicaii:
Un vector bidimensional este definit de dou componente (lund un sistem de referin
ortogonal) aa cum este prezentat n figura de mai jos:
2.2. Pointeri
Pointerul este un tip de date ce permite declararea unor variabile capabile s memoreze adresa
unei alte variabile sau a unei funcii.
14
Variabilele de tip pointer nu includ nici o informaie despre tipul datelor stocate la acea adres, de
aceea, n momentul declarrii unei variabile de tip pointer, trebuie s se specifice ctre ce tip de
date va face referire.
Pointerul indic adresa primului byte din variabila la care face referire. Prin urmare, un pointer va
ocupa acelai spaiu de memorie, indiferent de tipul de date ctre care face referire. Spaiul de
memorie ocupat de o variabil pointer poate varia ntre 16 bii i 64 de bii.
n limbajele C/C++, declararea unui pointer ctre o variabil de tip int se face astfel:
int *pVal;
Caracterul * ne indic faptul c variabila pVal este un pointer i nu o variabil de tip int.
Preluarea adresei de memorie a unei variabile se face cu ajutorul operatorului & (adres).
Accesul la coninutul variabilei referite de un pointer (coninutul zonei de memorie referite) se
realizeaz cu ajutorul operatorului * plasat naintea numelui variabilei de tip pointer. Tipul
variabilei pointer indic numrul de octei ce vor fi citii din memorie i modul cum vor fi
interpretai.
Pentru a exemplifica modul de lucru cu pointerii, vom comenta urmtorul exemplu:
#include <stdio.h>
1
2
3
4
5
void main()
{
int val=2,*pVal;
pVal=&val;
*pVal+=5;
printf(Noua valoare a variabilei val este: %d.\n,val);
printf(Variabila val este memorata la adresa %p, ocupa %d octeti si
are valoarea %d.\n,pVal,sizeof(val),*pVal);
}
n linia 1 a funciei main() se declar dou variabile: un int (val) i un pointer ctre int (pVal).
Variabila val este iniializat cu valoarea 2.
Linia 2 determin iniializarea variabilei de tip pointer pVal cu adresa varabilei val.
O variabil de tip pointer trebuie ntotdeauna iniializat nainte de a o ntrebuina.
Pentru a specifica o non-valoare (adic variabila pointer nu conine nici o adres) se
utilizeaz constanta NULL.
15
ndrumar de laborator
Utiliznd pointerul pVal se modific valoarea variabilei val (coninutul de tip int aflat la adresa
specificat de pointerul pVal) n linia 3.
n linia 4 se afieaz valoarea variabilei val, care acum va fi 7.
Linia 5 afieaz adresa variabilei val (n format hexazecimal), dimensiunea n octei i valoarea de
tip int aflat la adresa dat de valoarea pointerului.
Se pot declara i pointeri generici (la care nu se specific un tip bine definit), dar n momentul
operrii asupra lor trebuie convertii n mod explicit, n concordan cu tipul datelor referite. De
exemplu , se poate scrie un program astfel:
#include <stdio.h>
1
2
3
4
void main()
{
float PI=3.14;
void *pOrice;
pOrice=&PI;
printf(Variabila PI se afla la adresa %p si are
valoarea %g.\n,pOrice,*(float*)pOrice);
}
n momentul afirii a trebuit convertit pointerul generic pOrice ntr-un pointer ctre tipul float
(tipul variabilei referite) i apoi afiat coninutul acestei locaii de memorie.
* (float*) pOrice
pointer generic
conversia explicit n pointer ctre float
coninutul aflat la adresa de memorie
indicat de pointerul obinut prin
conversia pointerului generic
16
n urma unei atribuiri de acest fel se aloc o zon de memorie (n memoria heap memoria de
acumulare, adic nu aparine stivei programului) corespunztoare tipului declarat, care va putea fi
referit prin intermediul variabilei pointer var_pointer. n cazul imposibilitii alocrii de
memorie, operatorul new va returna valoarea NULL.
Operatorul new permite alocarea de memorie pentru orice tip de date.
17
ndrumar de laborator
Dei limbajele C/C++ pun la dispoziie mai multe funcii destinate lucrului dinamic cu memoria,
este indicat utilizarea n pereche a acestora; astfel, dac alocarea s-a fcut utiliznd operatorul
new,
delete var_pointer;
Acest apel va produce eliberarea zonei de memorie, alocat n prealabil cu operatorul new i
referit de pointerul var_pointer.
ntotdeauna trebuie pstrat o referin ctre o zon de memorie alocat dinamic. n caz
contrar, ea nu va mai putea fi eliberat, lucru ce poate conduce la consumarea memoriei
din sistem i, implicit, funcionri defectuoase ale programului ce pot bloca sistemul pe
care ruleaz.
2.3. Liste
Definiie: Listele ordonate liniar sunt structuri de date alctuite dintr-o mulime A=,A1, A2, , An}de
elemente (de obicei identice), ntre care exist o relaie determinat de poziia lor relativ. Astfel,
fiecare element Ak are un predecesor A k-1 i un succesor A k+1 (mai puin elementele prim i ultim).
Reprezentarea n memorie se poate realiza:
secvenial;
18
tablou n care sunt memorate elementele ei). (Un tablou este un caz particular de list, acela n
care elementul i al listei se afl memorat n tab *i+).
2.3.2. Liste nlnuite
Definiie: O list nlnuit este alctuit din noduri cu structura date i legturi n care:
Observaie: Informaia de secven se adaug explicit pentru fiecare element, sub forma adreselor
elementelor adiacente, astfel nct ordinea listei devine independent de ordinea plasrii
elementelor n memorie.
Clasificare:
List simplu nlnuit - conine noduri n care este specificat legtura (adresa) spre elementul
urmtor.
Exemplu de structur nod pentru lista simplu nlnuit:
struct nlsi
{
double data; /* informaia din nodul listei */
struct nlsi *urm;
/* adresa nodului urmtor */
};
Definiie: O list nlnuit n care legtura ultimului element are valoarea NULL, care marcheaz
sfritul listei, se numete lan.
Definiie: Dac legtura elementului final specific primul element se obine o list circular.
Observaie: n anumite aplicaii algoritmii sunt simplificai de utilizarea unui nod distinct, numit
nod capt, la care este ataat lista propriu-zis.
Pentru o list simplu nlnuit, ataarea unui element nou i extragerea unui element din list
sunt operaii banale i rapide, cu condiia s fie cunoscut adresa elementului precedent.
Exemplu de utilizare a listelor simplu nlnuite:
1
2
3
4
5
6
/*
cap = [inf|urm] -> [inf|urm] -> ... -> [inf|NULL]
*/
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
19
ndrumar de laborator
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
20
// structura de date
struct Nod
{
int inf; // informatia utila, pe care dorim sa o memoram
Nod* urm; // adresa urmatorului Nod (un pointer catre o variabila Nod)
};
// Functia returneaza numarul de elemente din lista
int nrElemente(Nod* lista)
{
int nr=0;
Nod *aux = lista;
while (aux!=NULL)
{
nr++;
aux = aux->urm;
}
return nr;
}
/*
Adaugare in pozitia N a unui element:
- daca N este 1, atunci adaugarea se face la inceputul listei;
- daca N este mai mare decat numarul de elemente, atunci se face adaugare
la sfarsit de lista.
Lista vida va fi simbolizata de valoarea NULL pentru capul acesteia.
*/
void adauga(Nod *&lista, int val, int N)
{
if (N==1 || lista == NULL) // adaugare la inceputul listei => capul
listei
// isi va schimba valoarea
{
Nod *aux = new Nod; // se aloca memoria necesara memorarii
// unei variabile de tip Nod
aux->inf = val; // se completeaza partea de informatie utila
aux->urm = lista; // urmatorul element va fi chiar vechiul cap al
listei
lista = aux; // noul cap al listei este noul element creat
}
else
{
int nr = nrElemente(lista); // preiau numarul de elemente din lista
if (N>nr+1) // daca pozitia primita ca parametru este mai mare decat
// numarul de elemente + 1, atunci ii modific valoarea in
// numar de elemente in lista + 1
N=nr+1;
Nod *tmp = new Nod; // creare noului nod - alocare memorie
tmp->inf = val; // completarea informatiei utile
// se parcurge lista pana la al N-1 -lea element
Nod *aux = lista;
for (int i=1;i<N-1;i++)
aux = aux->urm;
// realizarea inlantuirii
tmp->urm = aux->urm;
aux->urm = tmp;
}
}
if (poz == 1)
{
lista=lista->urm; // se muta capul listei pe elementul urmator
delete aux; //se sterge capul listei ce e tinut acum de variabila aux
}
else
{
// pozitionare pe elementul dinaintea pozitiei poz
for (int i=1;i<poz-1;i++)
aux = aux->urm;
Nod *tmp = aux->urm;
aux->urm = tmp->urm;
delete tmp;
}
21
ndrumar de laborator
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
}
void main()
{
clrscr(); // stergere ecran
Nod *L = NULL; // initial lista L este vida
int opt=0;
do
{
// se afiseaza "meniul" programului
cout<<"Operatii"<<endl<<"------"<<endl<<"1 Adaugare element"<<endl;
cout<<"2 - Eliminare element"<<endl<<"3 - Afisare lista"<<endl;
cout<<"0 - Iesire"<<endl<<"------"<<endl;
// se citeste codul operatiei pe care utilizatorul vrea sa o execute
cout<<"?> ";
cin>>opt;
switch (opt)
{
case 1: // adaugare
{
int v,p;
cout<<endl<<"Introduceti valoarea: ";
cin>>v;
cout<<"Introduceti pozitia (lista are "<<nrElemente(L)<<" el): ";
cin>>p;
adauga(L,v,p);
afisare(L);
break;
}
case 2: // eliminare
{
int p;
cout<<endl<<"Pozitia (lista are "<<nrElemente(L)<<" el): ";
cin>>p;
elimin(L,p);
afisare(L);
break;
}
case 3: // afisare
{
cout<<endl;
afisare(L);
break;
}
}
fflush(stdin); // pentru eliberarea bufferului tastaturii
getch();
}
while (opt!=0);
eliberaremem(L); // se elibereaza memoria alocate la crearea listei
}
22
3. Arbori binari
Organizarea liniar de tip list nu este ntotdeauna cea mai adecvat pentru unele aplicaii. Astfel,
dac trebuie s descriem structura unui produs, de cele mai multe ori nu prezentm o list a
tuturor componentelor, ci utilizm o descriere ierarhic. De exemplu, din punct de vedere
constructiv, un calculator este compus din unitate central, terminal, claviatur, alte periferice.
Unitatea central are o carcas n care se afl conectori i plci, pe fiecare plac fiind montate
diverse componente - circuite integrate, condensatori, etc.
De altfel, n vederea studierii unor proprieti, aproape orice obiect poate fi descompus n alte
obiecte, mai simple. Procesul de descompunere poate fi continuat pe mai multe niveluri,
terminndu-se ns dup un numr finit de etape, dependent de natura aplicaiei. Fiecare obiect n
parte este definit printr-un set de atribute i prin mulimea obiectelor componente, care la rndul
lor, sunt descrise n acelai mod . Se observ c aceast definiie este recursiv (un obiect este
compus din mai multe obiecte) i pune n eviden o ierarhie a obiectelor.
Organizarea ierarhic este ntlnit n cele mai diverse domenii, de la organizarea administrativ a
unei ri, la planificarea meciurilor n cadrul unui turneu sportiv, de la structurarea unei cri, pn
la stabilirea ordinii de execuie a operaiilor efectuate pentru determinarea valorii unei expresii
aritmetice.
Tot o structur ierarhic este i cea a cataloagelor n care sunt grupate fiierele de pe discurile fixe
sau flexibile. Aceast organizare este impus, in principal, de raiuni de gestionare ct mai comod
a fiierelor de diverse tipuri, aparinnd diverilor utilizatori ai aceluiai sistem de calcul.
Dac toate nodurile dintr-un arbore au cel mult doi descendeni direci (fii), atunci arborele este
denumit arbore binar, iar cei doi poteniali subarbori ai unui arbore nevid sunt denumii subarbore
stng i subarbore drept.
ndrumar de laborator
prelucrarea dorit. n cazul n care este solicitat lista personalului cu funcii de conducere, aceasta
poate fi tiprit in dou variante:
1. grupnd persoanele pe nivelurile ierarhice;
2. astfel nct s reflecte relaiile de subordonare.
n prima variant, parcurgerea arborelui se efectueaz n lime, iar n cea de-a doua n adncime.
Tot o parcurgere implic i calculul numrului de persoane angajate n fiecare compartiment
(reprezentat printr-un subarbore). Cele dou parcurgeri n adncime menionate se deosebesc
prin ordinea relativ de prelucrare a nodului rdcin i, respectiv, a subarborilor. n primul caz,
informaia specific nodului rdcin este prelucrat (tiprit) naintea informaiilor din celelalte
noduri ale (sub)arborelui. Acest tip de parcurgere este denumit parcurgere n preordine. Deoarece
numrul de persoane angajate ntr-un compartiment poate fi calculat (i eventual tiprit) numai
atunci cnd se cunoate numrul de angajai din toate compartimentele subordonate, rezult c
prelucrarea de la nivelul nodului rdcina se face dup prelucrarea restului arborelui respectiv,
deci parcurgerea se realizeaz n postordine.
Implementarea de mai jos utilizeaz recursivitatea ca tehnic de programare.
Parcurgerea arborilor binari:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
24
#include <iostream.h>
#include <conio.h>
struct ArboreBinar
{
int inf;
ArboreBinar *st;
ArboreBinar *dr;
};
ArboreBinar* createTree()
{
int are;
ArboreBinar *nod = new ArboreBinar;
cout<<"\nInformatia pentru nodul nou: ";
cin>>nod->inf;
cout<<"Nodul "<<nod->inf<<" are subarbore stang [0=NU;1=DA]? ";
cin>>are;
if (are!=0)
nod->st = createTree();
else
nod->st = NULL;
cout<<"Nodul "<<nod->inf<<" are subarbore drept [0=NU;1=DA]? ";
cin>>are;
if (are!=0)
nod->dr = createTree();
else
nod->dr = NULL;
cout<<endl;
return nod;
}
25
ndrumar de laborator
95
96
97
98
99
// Eliberarea memoriei
freeTree(root);
root = NULL;
getch();
}
26
4. Backtracking
Tehnica Backtracking (a cutrii cu revenire) se poate aplica doar pentru probleme ce admit
conceptul de candidat parial de soluie i ofer un test relativ rapid asupra posibilitii ca un
astfel de candidat s fie completat ctre o soluie valid. Cnd se poate aplica, ns, backtrackingul
este adesea mult mai rapid dect cutarea prin metoda forei brute prin toi candidaii, ntruct
este capabil s elimine dintr-un singur test un mare numr de candidai.
Metoda Backtracking, n general ineficient, avnd complexitate exponenial, poate fi utilizat la
optimizarea procesului de cutare, evitnd cile care nu duc la o soluie.
n multe aplicaii, gsirea soluiilor este rezultatului unui proces de cutare sistematic, cu
ncercri repetate i reveniri n caz de nereuit. De exemplu, un ntreprinztor care dispune de un
capital C, pe care dorete s-l investeasc n vederea obinerii unui profit, va proceda n felul
urmtor: va alege dintre n oferte la care trebuie avansate fondurile f(i) i care aduc beneficiile b(i),
pe acelea pe care le poate onora cu capitalul de care dispune i care-i aduc beneficiul maxim.
Dintre problemele ale cror soluii se gsesc prin cutare menionm cteva exemple:
ndrumar de laborator
O soluie final este obinut n momentul n care a fost fcut o selecie dintre cele n oferte.
Aceasta este comparat cu soluia optim determinat pn acum, fiind reinut sau ignorat.
28
#include <stdio.h>
#include <stdlib.h>
/* Dimensiunea tablei de sah este definita ca si constanta. */
#define N 8
#define INVALID -1
int main(void)
{
/* Pentru o tabla de dimensiune N solutiile sunt memorate intr-un
vector de dimensiune N*N. Fiecare element din vector va fi,
la randul lui, un vector cu doua elemente; primul element va
memora linia de pe tabla, iar al doilea element va memora
coloana de pe tabla. */
int c[N*N][2];
int k, i;
int pe_tabla, continuare;
int delta_l, delta_c;
/* Sunt numarate solutiile gasite. */
int count = 0;
/* Pentru inceput se marcheaza toate elementele vectorului "c" cu
INVALID, semn ca nu a fost ales nici un element din multimile
produsului cartezian. */
29
ndrumar de laborator
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
30
/* Daca este
epuizarea
atribuita
else
{
pe_tabla
}
= 0;
31
ndrumar de laborator
32
5. Algoritmi de cutare
O mare gam a cercetrilor n acest domeniu urmresc obinerea minimului n strategii minimax.
n problemele privind parcurgerea combinaional a arborilor de cutare, au fost dezvoltai un
numr mare de algoritmi pentru o cutare ct mai eficient.
Algoritmii se pot grupa dup dou aspecte:
Cei mai muli algoritmi de acest tip au caracter euristic. O regul euristic ofer o metod de
rezolvare a problemelor, sau o metod de cutare. Ea nu d rezultate corecte la orice moment de
timp i nu este garantat c gsete cea mai bun soluie, dar n acelai timp poate reduce timpul
de cutare. Metodele de acest tip pot fi folosite pentru reducerea mrimii arborilor de cutare n
cazurile arborilor foarte mari.
Metodele relaionale sunt metodele prin care cunoaterea este reprezentat pornind de la relaiile
ntre obiecte sub form de grafuri i reele.
Pornind de la o structur de concepte care poate fi reprezentat arborescent, naintarea spre
frunz reprezint o specializare, pe cnd apropierea de rdcin este o generalizare a conceptului.
package mainprg;
import java.util.Arrays;
import java.util.Scanner;
public class MainPrg
{
33
ndrumar de laborator
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Realizai varianta iterativ a cutrii binare ntr-un program C/C++ sau Java.
34
6. Algoritmi de sortare
n practica de zi cu zi este des necesar ca un volum de date s fie aranjat ntr-o anumit ordine (s
fie sortat).
n acest scop au fost creai algoritmi de sortare precum:
n continuare sunt prezentai doi algoritmi: bubblesort (algoritm de tip greedy) i quicksort (ce
utilizeaz tehnica divide et impera).
se parcurge irul i se verific dac fiecare dou elemente vecine sunt n ordinea
corespunztoare; dac se gsete o neconcordan cele dou elemente n cauz sunt
interschimbate i se memoreaz faptul c irul nu era sortat;
odat ajuni la sfritul irului, dac irul s-a dovedit a nu fi sortat, se reia procedeul; dac
nu a fost semnalat nicio neconcordan, atunci orice element ak este n relaie corect cu
vecinul su ak+1,prin urmare irul este sortat.
Acest algoritm, dei simplu, este foarte lent, necesitnd multe iteraii pentru obinerea soluiei.
Implementarea algoritmului de sortare Bubblesort ntr-un program C/C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <conio.h>
#include <iostream.h>
void main()
{
int TAB[100], // elementele ce
n, // numarul de elemente:
i=0, // variabila folosita
sortat; // varibila logica
vor fi sortate
este citit de la tastatura
pentru ciclari, pe post de contor
ce va semnaliza daca tabloul este sortat
35
ndrumar de laborator
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// se citesc elementele
for (;i<n;i++)
{
cout<<"Elementul "<<i+1<<" este: ";
cin>>TAB[i];
}
// SORTARE
sortat = 0; // initial vectorul nu este sortat
while (!sortat) // cat timp vectorul nu este sortat
{
sortat = 1; // presupunem ca vectorul este sortat
for (i=0;i<n-1;i++)
if (TAB[i]>TAB[i+1]) // ordinea elementelor nu este corecta
{
// se inverseaza elementele care nu au fost in ordine
int aux = TAB[i];
TAB[i] = TAB[i+1];
TAB[i+1] = aux;
// se semnaleaza faptul ca vectorul nu a fost ordonat, dupa cum
// s-a presupus, deci e posibil ca vectorul sa nu fie inca
// sortat, deci mai trebuie verificat inca o data
sortat = 0;
}
}
// se afiseaza vectorul sortat
cout<<"Vectorul sortat este: ";
for (i=0;i<n;i++)
cout<<TAB[i]<<" ";
getch();
}
36
#include <conio.h>
#include <iostream.h>
37
ndrumar de laborator
38
7. Grafuri
n faa unui mare numr de situaii, o veche obinuin ne ndeamn s trasm pe hrtie puncte
reprezentnd indivizi, localiti, corpuri chimice i altele, legate ntre ele prin linii sau prin sgei
care simbolizeaz o anumit relaie. Aceste scheme se ntlnesc peste tot i fr ndoial D. Knig
a fost primul care a propus ca astfel de scheme s se numeasc grafuri i care le-a studiat
sistematic proprietile.
Printr-un graf se nelege o mulime de noduri (numite i vrfuri) i o aplicaie definit pe aceast
mulime cu valori n aceeai mulime, care face legtura ntre aceste noduri, legturi numite arce,
care pot fi sau nu orientate.
O cale este o succesiune de noduri aleas astfel nct s existe arce care s reuneasc nodurile
respective. O cale este simpl dac toate nodurile, cu excepia primului i ultimului, sunt distincte
ntre ele.
n multe din problemele la a cror rezolvare se folosete calculatorul este necesar reprezentarea
unor relaii generale ntre obiecte. Un model potrivit n astfel de cazuri este graful orientat (dac
relaia este nesimetric) sau neorientat (dac relaia este simetric)
Un graf orientat sau digraf (prescurtare de la directed graf) G=(V,E) const deci ntr-o mulime V
de vrfuri (sau noduri) i o mulime E de arce. Un arc poate fi privit ca o pereche ordonat de
vrfuri (v,w) unde v este baza arcului iar w este vrful arcului. Se spune c w este adiacent lui v.
Un graf neorientat sau graf G=(N,R) este alctuit dintr-o mulime de noduri N i o mulime R de
muchii. O muchie este atunci o pereche ordonat de noduri (v,w)=(w,v).
Un graf este echivalent cu un digraf n care pentru fiecare arc (v,w) exist i perechea lui (w,v).
Se spune c o cale (succesiune de vrfuri) v*1+,v*2+,....,v*k+ conecteaz v*1+ i v*k+.
matricea de adiacene i
listele de adiacene.
Alegerea uneia dintre ele trebuie fi fcut n funcie de frecvena operaiilor de acces la nodurile i
muchiile grafurilor.
39
ndrumar de laborator
Dat fiind G=(V,E) s considerm mulimea vrfurilor V=,1,2,3,...n- avnd elementele n ordinea
natural a numerelor prin care sunt reprezentate. Matricea de adiacene A de dimensiune n n se
poate defini prin:
A[i,j] =
Reprezentarea prin matrice de adiacene permite un acces rapid la arcele (muchiile) grafului fiind
util n algoritmii n care se testeaz prezena sau absena unui arc oarecare. Ea este
dezavantajoas dac numrul de arce este mai mic dect n n, caz n care memoria necesar
pentru a pstra matricea este folosit ineficient.
Reprezentarea prin liste de adiacene folosete mai bine memoria, dar determin o cutare mai
anevoioas a arcelor. n aceast reprezentare, pentru fiecare nod se pstreaz lista arcelor ctre
nodurile adiacente. ntregul arbore poate fi reprezentat ca un tablou Cap, indexat dup noduri,
fiecare element Cap[I] fiind un pointer spre lista nodurilor adiacente lui i. Memoria necesar
reprezentrii este proporional cu suma dintre numrul de noduri i numrul de arce ale grafului.
Scriei un program C/C++ sau Java care citete de la tastatur un graf i l memoreaz
folosind reprezentarea prin matricea de adiacen.
40
7.2.2. Floyd
Algoritmul de aflare a cilor minime dintr-un punct poate fi repetat lund ca surs fiecare din
nodurile unui graf. Aceasta permite calculul unui tablou al drumurilor minime ntre toate perechile
de noduri ale grafului. O astfel de tehnic este cea datorat lui R. W. Floyd.
Implementarea algoritmului Floyd n C/C++:
1
2
3
4
5
6
7
8
9
10
Aceast tehnic se bazeaz pe utilizarea unui tablou A al distanelor minime, ale crui valori sunt
calculate n mai multe etape. Iniial,
A[i,j] =
41
ndrumar de laborator
Calculul distanelor minime se face n n iteraii. La iteraia k, A[i,j+ va avea ca valoare cea mai mic
distan ntre i i j, pe ci care nu conin vrfuri numerotate peste k (exceptnd capetele i i j ).
7.2.3. Arborele de acoperire minim algoritmul Kruskal
O metod utilizat n calcule este aceea a arborelui minim de acoperire (Kruskal). Algoritmul
construiete treptat mulimea T a muchilor arborelui minimal adugnd la fiecare pas muchia care
nu formeaz cicluri cu muchiile aflate deja n T.
Algoritmul Kruskal
T { }
ct timp T nu este arbore de acoperire
execut
{
selecteaz muchia (w,u) de cost minim din R
terge (w,u) din R
dac (w,u) nu creeaz un ciclu n T
atunci adaug (w,u) la T
}
O posibil implementare a algoritmului n limbajul de programare C/C++ este redat mai jos.
Implementarea algoritmului Kruskal n limbajul C/C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
42
#include<stdio.h>
#include<stdlib.h>
void printArray(int a[][100],int n)
{
int i,j;
for(i = 0; i < n;i++)
{
for(j = 0; j < n;j++)
{
printf("%d\t",a[i][j]);
}
printf("\n");
}
}
void GenereazaMatriceaDeAdiacenta(int a[][100], int n)
{
int i,j;
for(i = 0;i < n; i++)
{
for(j = 0;j < i; j++)
{
a[i][j] = a[j][i]= rand()%50;
if(a[i][j]>40)
{
a[i][j]=a[j][i]=999;
}
}
a[i][i] = 999;
}
printArray(a,n);
}
int root(int v,int p[])
{
while(p[v] != v)
{
v = p[v];
}
return v;
}
void union_ij(int i,int j,int p[])
{
if(j > i)
p[j] = i;
else
p[i] = j;
}
void kruskal(int a[][100],int n)
{
int count, i, p[100], min, j, u, v, k, t[100][100], sum;
count = k = sum = 0;
for(i = 0; i < n; i++)
{
p[i] = i;
}
while(count < n)
{
min = 999;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
if(a[i][j] < min)
{
min = a[i][j];
u = i;
v = j;
}
}
}
43
ndrumar de laborator
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
if(min != 999)
{
i = root(u, p);
j = root(v, p);
if (i != j)
{
t[k][0] = u;
t[k][1] = v;
k++;
sum += min;
union_ij(i,j,p);
}
a[u][v] = a[v][u] = 999;
}
count++;
}
if(count != n)
{
printf("Arborele de acoperire minim nu exista!\n");
}
else
{
printf("Arborele de acoperire minim este:\n");
for(k = 0; k < n-1 ; k++)
{
printf(" %d -> %d ",t[k][0],t[k][1]);
}
printf("\nCost = %d \n",sum);
}
}
void main()
{
int a[100][100],n;
printf("Introduceti numarul de noduri: ");
scanf("%d",&n);
GenereazaMatriceaDeAdiacenta(a,n);
kruskal(a,n);
}
Introducei mesaje de afiare n programul de mai sus pentru a evidenia paii parcuri n
obinerea arborelui de acoperire de cost minim.
44
{Bucl greedy}
ct timp U
execut
gsete {u,v} de cost minim astfel ca U
A
{{ u , v }}
{ u }
V \ U i v
returneaz A
45