Sunteți pe pagina 1din 26

Laborator de Structuri de Date si Algoritmi Lucrarea nr.

Recapitulare notiuni C/C++. Mediul Visual Studio 2008.


Complexitatea algoritmilor.
1. Recapitularea unor notiuni C. Limbajul C++.
2. Introducere mediul Visual C/C++ 6.0.
3. Complexitatea algoritmilor algoritmi de sortare.

Recapitularea unor notiuni C. Limbajul C++

Informatii introductive
La laboratorul de "Structuri de date si algoritmi" veti lucra sub mediul Visual C++ 6.0.
Programele vor fi scrise in C, dar vom folosi citeva facilitati specifice limbajului C++
care vor fi expuse in continuare. Fisierele sursa vor avea extensia .CPP (de exemplu
STUDENT.CPP).
Iata deja o prima simplificare: doua semne // arata inceputul unui comentariu. Acest tip
de comentariu incepe cu // si se termina la sfirsitul liniei.

Expresii si valori stinga


In C ne putem referi la un obiect folosind o expresie "valoare-stinga". O expresie valoarestinga" este o expresie care poate sa apara in STINGA unei atribuiri. De exemplu numele
unei variabile este o expresie valoare-stinga in timp ce rezultatul unei operatii aritmetice
nu este valoare-stinga.
int i;
int v[10];
Valori stinga:
Nu sint valori stinga:

i
v[i] v[i+1]
i+1 2*v[i]

In C prin combinarea numelor de variabile cu anumiti operatori se obtin valori-stinga:

Operatorul *

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Se aplica numai pointerilor, rezultatul fiind o valoare-stinga care se refera la obiectul a
carei adresa este continuta de catre pointer:
int* p;
p = &i;
*p=5;

// valori stinga:
// p
nume_variabila
// *p
* pointer

Observati ca expresia *p (rezulatata din combinatia numelui lui p cu operatorul *) apare


in stinga atribuirii.

Operatorul []
Se aplica numelor de tablouri si pointerilor, rezultatul fiind o valoare-stinga care se refera
la obiectul al n-lea din tablou:
int tab[10];
int* p = &tab[0];
tab[2] = 3;
p[2] = 4;

// valori stinga:
// nume_tablou [ index ]
// pointer [ index ]

Mai sus, pointerul p este initializat cu adresa primului element din vectorul tab. Expresia
p[2] va referi al doilea element din vectorul a carui adresa este memorata in pointerul p,
deci tab[2].

Operatorii . si ->
Apar in legatura cu structurile si vor fi tratati putin mai tirziu.

Structuri
1. Definire
O structura este un tip de date nou. Atunci cind definim o structura, trebuie sa specificam
numele structurii si cimpurile ei:
struct student {
char* nume;
int nota;
};
Am introdus tipul "struct student". Pentru a evita repetarea lui "struct" putem sa
introducem un pseudonim pentru tipul "struct student" si anume "Student" astfel:
typedef struct student Student;

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Cele doua declaratii de mai sus pot fi comprimate in una singura:
typedef struct {
char* nume;
int nota;
} Student ;
In C++, tipul definit de declaratia:
struct Student {
char* nume;
int nota;
};
poate fi denumit "struct Student" sau, doar simplu, "Student". In continuare ne vom folosi
de aceasta facilitate care mareste lizibilitatea programelor. Repetam: programele vor avea
extensia .CPP .
2. Obiecte de tip structura
Am definit structura Student avind CIMPURILE "nume" (adresa unui sir de caractere) si
"nota" de tip int.
Student

nume
nota

10
=

> 'R''a''d''u''\0'

Odata definit tipul, acesta poate fi folosit pentru a declara variabile:


Student s1, s2;
Student grupa[5];
Student* ps;
Student* idx[5];

// s1 si s2 sint doua variabile de tip Student


// un tablou de variable Student
// o variabila pointer la Student
// o variabila tablou de pointeri la Student

3. Operatorii . si ->
Folosirea cimpurilor unei structuri se face NUMAI CU REFERIRE LA UN OBIECT de
tipul respectiv. Obiectul este referit printr-o valoare stinga care semnifica obiectul
structura sau adresa obiectului structura.

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

Operatorul . cere in stinga sa o valoare stanga de tip structura iar in dreapta, numele
cimpului selectat, rezultatul fiind o valoare-stinga care se refera la cimpul selectat. De
exemplu, din declaratiile de mai sus, cimpurile variabilei grupa[3] vor fi denumite:
grupa[3].nume si grupa[3].nota
grupa[3]

grupa[3].nume
grupa[3].nota

grupa[3] . nota

variabila_structura . cimp
Operatorul -> cere in stinga sa o expresie de tip "pointer la structura" iar in dreapta
numele cimpului selectat, rezultatul fiind o valoare-stinga care se refera la cimpul
selectat:
ps

*ps

> ps->nume ps->nota

ps->nota

pointer_la_structura -> cimp
Exercitii
Avind urmatoarele declaratii:
int i, *pi;
Student s;
Student* ps;
Student ts[5];
Student* tps[5];
numiti tipurile urmatoarelor expresii. Decideti daca sint valori stinga sau nu:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


i+2
ts[2]
ps->nume
(ps+3)->nume
tps[2]->nume

pi
i
ts
ps
*pi
*(pi+2)
ps->nume[2]
tps
tps[2]->nume[2]

p+3
ps[2]
*pi+2
tps[2]

Pointeri
1. Definire
Pentru un tip de date T o variabila "pointer la T" se defineste astfel:
T* ptrT;

// ptrT este un pointer la T

O variabila pointer la T poate retine adresa unui obiect de tip T.


Exemple:
int* pi;
// pi este un pointer la int
char* tab;
// tab este un pointer la char
Nod *nou, *cap; // nou si cap sint pointeri la tipul Nod
2. Initializare
Prima operatie care se face cu un pointer este initializarea. Un "pointer la T" poate fi
initializat cu:
a) adresa unui T (care exista)
pi = &i;
pi
i

>

*pi
b) valoarea 0 (sau NULL) care semnifica adresa invalida
cap = 0;
cap

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

c) valoarea altui "pointer la T". De exemplu: daca p si q sint de tip T* si p contine


adresa unei variabile de tip T (a fost initializat in prealabil), atribuirea q = p va face ca
ambii pointeri sa indice aceeasi variabila.
p

>

>
q

d) adresa unui spatiu de memorie alocat in zona de alocare dinamica. Spatiul alocat poate
sa contina un singur obiect de tip T, acesta se exprima:
in C:
in C++:

p = (T*) malloc( sizeof(T) );


p = new T;

p
*p

>

sau poate sa contina mai multe (n) obiecte de tip T:


in C:
in C++:

p = (T*) malloc( n*sizeof(T) );


p = new T[n];

p
*p p[0] p[1] p[2] p[3] p[4]

>

Exprimarile din C++ sint in mod evident mult mai simple, si le vom folosi pe acestea in
continuare.
Iata un exemplu:
typedef Student* PStudent;
PStudent* ptps;
ptps = new PStudent[nr];

// pointer la tablou de pointeri la studenti

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

3. Dereferentierea
Este operatia prin care avind un "pointer la T" (care contine adresa unui T) obtinem o
valoare stinga care se refera la obiectul pointat (vezi operatorul *).
Pentru a obtine obiectul pointat folosim operatorul * astfel:
*pi = 5;

// obiectul pointat ia valoarea 5

!!! Atentie !!!


Aceasta operatie poate fi aplicata numai pointerilor care contin intr-adevar adresa unui
obiect. De exemplu nu puteti face dereferentierea unui pointer nul (cu valoare 0) sau a
unui pointer neinitializat. Este valabil si pentru operatorul -> care contine si el o
dereferentiere care se observa in scrierea echivalenta: ps->nota este echivalent cu
(*ps).nota
4. Pointeri si tablouri
Numele unui "tablou de T" este convertit automat la tipul "pointer la T", deci poate fi
folosit pentru a initializa un "pointer la T". Valoarea acestui pointer este adresa primului
element al tabloului:
T tab[10];
T* ptrT = tab;

// ptrT contine adresa primului element

Un "pointer la T" este deseori folosit pentru a se referi pe rind la elementele unui tablou.
Urmatoarele operatii semnifica:
ptrT++
ptrT--

// pointeaza la urmatorul element din tablou


// creeaza o valoare stinga!
// pointeaza la elementul precedent din tablou
// creeaza o valoare stinga!

5. Eliberarea spatiului alocat dinamic


Daca un pointer a fost initializat cu adresa unui spatiu din zona de alocare dinamica,
atunci cind nu mai avem nevoie de spatiul respectiv (adica nu mai avem nevoie de
obiectul din spatiul respectiv) vom elibera spatiul. El va putea fi astfel utilizat pentru
alocari ulterioare.
Daca p este un pointer care a fost initalizat printr-o alocare de memorie, eliberarea
memoriei alocate se exprima:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


in C: free(p);
in C++: delete p;
!!!Atentie!!!
Nici free() nici delete nu modifica valoarea pointerului p dar obiectul a carui adresa este
continuta de p nu trebuie sa fie referit dupa eliberare.
6. Observatie
In laboratoarele urmatoare, vor exista cazuri in care anumiti pointeri nu sint varibile
simple, ci vor fi componente ale unor structuri de date complexe. Toate regulile de mai
sus se pastreaza.

Referinte
Cind vrem ca o functie, atunci cind este apelata, sa modifice valoarea unei variabile din
functia apelanta, trebuie sa trimitem ca argument un pointer la acea varibila. De exemplu,
o functie care interschimba valoarea a doi "pointeri la student" va trebui sa primeasca ca
parametri doi "pointeri la pointeri la student":
void Schimba(Student** unu, Student** doi)
{
Student* trei;
trei = *unu;
*unu = *doi;
*doi = trei;
}
Daca argumentele au tipuri mai complicate, atunci sintaxa din interiorul functiei devine
greoaie. Pentru a rezolva aceasta problema putem trimite ca argumente REFERINTE. Un
argument referinta trebuie interpretat ca fiind un PSEUDONIM pentru argumentul pasat.
Orice modificare a referintei se va face asupra argumentului pasat:
void Schimba(Student*& unu, Student*& doi)
{
Student* trei;
trei = unu;
unu = doi;
doi = trei;
}

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Observati ca sintaxa din interiorul functiei a devenit mai simpla.

Crearea unui proiect in Microsoft Visual Studio 2008

Visual Studio 2008 este mediul de dezvoltare integrat (IDE Integrated


Development Environment) pe care il vom folosi pentru scrierea programelor C++.
El permite dezvoltarea de aplicatii si in alte limbaje de programare : C++, C#, Basic.
In cadrul laboratorului vom scrie programe C++ de tip consola. Pentru crearea
unei aplicatii in Visual Studio 2008 este necesara existenta unui proiect (nu se pot
compila fisiere de sine statatoare)
Pentru a crea un proiect:
1. Pornim VS2008
2. In meniul principal, selectam File New Project ca in figura 1

Fig. 1 Creare proiect nou

Fig. 2 Fereastra proiect nou

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

3. In fereastra New Project, in stanga avem casuta Project types. Alegem Other
languages Visual C++ :
4. In partea dreapta avem casuta Templates. Alegem Win32 Console
Application.
5. In partea de jos avem casutele Name si Location. Alegem un nume pentru
proiect. La Location selectam un director creat de noi in C:/temp sau dupa
caz. (vezi fig. 2)
6. Apasam OK.
7. Apare fereastra Welcome to the Win32 Application Wizard. Apasam Next,
NU Finish!

Fig. 3 Fereastra Welcome

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

Fig. 4 Fereastra Application Settings


8. In fereastra aparuta, rubrica Additional options, bifam Empty project.
Lasam celelalte setari neschimbate:
9. Apasam Finish.
Proiectul este creat si deschis in VS. Ar trebui sa vedem urmstoarele ferestre:
Solution explorer in partea stanga. De aici se pot crea sau deschide
fisierele proiectului.
Start Page in restul ecranului
Mediul de dezvoltare va arata in felul urmator:

Fig. 5 Proiectul creat

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

2.1

Adaugarea unui fisier la proiect

Ca sa putem scrie cel mai simplu program in VS2008, trebuie sa adaugam un fisier
sursa. Pentru aceasta, urmati urmatorii pasi:
1. In Solution Explorer, click dreapta peste grupul Source Files Add New
Item
2. Apare fereastra Add New Item.
3. In casuta templates alegem C++ File (.cpp).
Atentie de aici putem alege tipul de fisier dorit din lista. Spre exemplu pentru fisiere
header alegem Header file (.h) iar pentru fisiere sursa alegem C++ File (.cpp)
4. La Name alegem numele fisierului. De obicei vom numi fisierul care contine
functia main() este creat dupa regula <numeProiect>Main. In cazul nostru
salutMain:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

Fig. 6-7 Adaugare fisier sursa


5. Apasam Add. Noul fisier este creat si deschis in editor

2.2

Scrierea primului program

Sa scriem un program simplu care afiseaza un mesaj la consola, dupa care asteapta
apasarea unei taste:

Fig. 8 Scrierea codului sursa


Observam in dreptul numelui fisierului salutMain.cpp, o steluta (*). Aceasta
inseamna ca fisierul contine modificari nesalvate. Liniile modificate sunt marcate cu

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


galben in stinga editorului. Apasam Ctrl+S pentru a salva fisierul. Observsm cum steluta
a disparut.
Functia _getch() la sfarsitul programului este echivalentul lui getch() din Borland
C. Ea asteapta apasarea unei taste fara sa o afiseze pe ecran.
In Visual Studio codul este identat in mod automat atunci cand il scriem. Chiar
daca programul nu este identat asa cum ne dorim, putem sa reformatam codul in orice
moment apasand Ctrl+A, apoi Ctrl +K, Ctrl +F. Este o practica buna sa avem codul tot
timpul formatat. Formatarea ne ajuta sa intelegem programul si sa detectam mai usor
erorile. Urmatorul exemplu ilustreaza facilitatea in practica (vezi figura 9):
1. In dreptul apelului printf() apasam tab pentru a-l identa in mod eronat la dreapta:
2. Apasam Ctrl+A pentru a selecta tot textul.
3. Apasam Ctrl +K, apoi Ctrl +F. Observam cum codul este formatat inapoi in mod
corect.

Fig. 9 Exemplu de cod neformatat

2.3

Compilarea si rularea proiectului. Detectarea erorilor.

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Pentru a compila proiectul apasam Ctrl + Shift + B, sau din meniul principal selectam
Build Build Solution.
Daca sunt detectate erori de compilare, in partea de jos a ecranului va aparea fereastra
Error List. Sa introducem intentionat o eroare pentru a vedea facilitatile acestei ferestre.
De exemplu, putem inlocui linia return 0 cu return x. La compilare, compilatorul
considera ca x este o variabila nedefinita si afiseaza fereastra error list ca in figura 10.

Fig. 10 Mesaj de eroare


Daca dam dublu-click peste mesajul de eroare, cursorul se va deplasa la linia de cod
continind eroarea. Utilizarea unei variabile fara sa fie declarata este una din cele mai
comune erori de programare. In VS2008 aceasta poate fi detectata siscorectata foarte
usor. Alte erori insa pot duce la mesaje de eroare mult mai obscure. In general s-a
observat urmatoarea regula eroarea trebuie cautata ori pe linia afisata in error list, ori cu
o linie mai sus. De exemplu, stergeti simbolul ; la sfarsitul uneia dintre linii. Veti
vedea ca mesajul de eroare ne duce cu o linie mai jos.
Sa corectam programul si sa il compilam din nou. Chiar daca fereastra error list nu s-a
ridicat dupa compilare, trebuie sa o verificam, pentru ca aceasta nu se ridica atunci cand
programul are doar warning-uri. De cele mai multe ori warning-urile sunt si ele erori de
program.
Pentru a executa programul apasam F5. Se deschide o fereastra consola in care
ruleaza programul.

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

2.4

Scriere / citire cu ajutorul cin i cout

In C++ s-a elaborat o modalitate mai simpla de scriere/citire la consola


comparativ cu funciile scanf/printf din C. La inceputul executiei fiecarui program sunt
instantiate automat 2 variabile globale speciale - cin i cout. Ele sunt folosite pentru
citire, respectiv scriere la consola.
Pentru a citi o variabila de la consola, se folosete urmatoarea sintaxa:
int a;
cin >> a;

Operatorul >> are un rol special pentru variabila cin. Expresia


cin >> a;

semnifica faptul ca de la consola este citita o valoare ai depozitata in variabila a. Tipul


variabilei din dreapta poate fi de orice tip simplu int, char, float, double sau sir de
caractere char*. Pentru fiecare tip citirea se va face in mod corect.
Pentru a scrie o variabila la consola, folosim sintaxa:
char str[] = "abc";
cout << str;

In mod similar, operatorul << are o semnificatie speciala pentru variabila cout.
Expresia semnifica faptul ca variabila str este scrisa la consola. Variabilele scrise pot fi
de aceleasi tipuri ca si cele citite cu cin.
cout << str;

Variabilele cin si cout sunt definite in header-ul <iostream>. Pentru ca sa le putem


folosi, trebuie sa includem la inceputul programului urmatoarele linii:
#include <iostream>
using namespace std;

Iata un exemplu complet de scriere/citire folosind cin/cout:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


// exemplu: cin si cout
#include<conio.h>
#include <iostream>
using namespace std;

Introduceti un numar: 12
Si un sir de caractere: abc
Numarul este: 12
Sirul este: abc

int main() {
int iVal;
char sVal[30];
cout << "Introduceti un numar: ";
cin >> iVal;
cout << "Si un sir de caractere: ";
cin >> sVal;
cout << "Numarul este: " << iVal << "\n"
<< "Sirul este: " << sVal << endl;
_getch();
return 0;
}

Un aspect nou este cuvantul endl. Acesta este o variabila globala tip sir de
caractere cu valoarea "\n" (sfarit de linie).
Instructiunea:
cout << endl;

este echivalenta cu
cout << \n;

dar are avantajul ca este mai lizibila.


Atat expresiile cu cin cat si cele cu cout pot fi inlantuite. Expresia
cout << a << " " << b;

este echivalenta cu
cout << a;
cout << " ";
cout << b;

Comparativ cu functiile printf / scanf din C, expresiile cu cin si cout sunt mai simple
si mai usor de inteles.

2.5

Operatorii new i delete

In C++, lucrul cu memoria dinamica este facilitat de doi operatori speciali - new
i delete.

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Alocarea dinamica de memorie se face cu operatorul new. El returneaza un
pointer catre inceputul blocului de memorie proaspat alocat. Sintaxa operatorului este
urmatoarea:
TIP *p, *pvector;
p = new TIP;
pvector = new TIP[nr_elemente];

De exemplu, pentru pointeri la variabile simple:


int *pi;
pi = new int;
char *pch;
pch = new char;

si pentru pointeri la vectori:


int *vi;
vi = new int[10];
char *psir;
psir = new char[80];

Atunci cand nu mai avem nevoie de o variabila alocata dinamic, aceasta trebuie
dealocata. Memoria dealocata devine disponibila pentru noi cereri de alocare. Pentru
dealocari se folosesc operatorii delete pentru variabile simple, si delete[] pentru
vectori. Exemplu:
delete vi;
delete[] psir;

2.6

Debug

Debug este facilitatea oferita de mediile de dezvoltare de a analiza procesul de


executie a unui program in scopul de a detecta erorile. Este posibil de a rula programul
instructiune cu instructiune, urmarind valoarea unor variabile dupa fiecare instructiune.
Sau este posibil de a stabili anumite linii de cod la care dorim ca programul sa se
opreasca, si sa vizualizam valoarea variabilelor alese de noi doar in acele puncte din
program. Aceste linii in care dorim sa intrerupem executia programului se numesc puncte
de oprire (breakpoint).
De exemplu, sa presupunem ca in programul de mai jos:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


int main()
{
int a;
int b;
float med;

Introduceti primul numar: 6


Introduceti al doilea numar: 2
Media: 7

cout << "Introduceti primul numar: ";


cin >> a;
cout << "Introduceti al doilea numar: ";
cin >> b;

med = a+ b / 2;
cout << "Media: ";
cout << med;
_getch();
return 0;

vom dori sa verificam de ce media nu este 4 asa cum ne-am dori. In primul rand
putem verifica daca citirea a fost facuta corect, si in caz afirmativ, daca operatia de
medie a fost facuta corect. Pentru aceasta, vom plasa 2 breakpoint-uri in functia main()
unul dupa citirea celor doua numere si unul dupa calcularea medieie. Pentru a plasa un
breakpoint, ducem cursorul la linia de cod unde dorim breakpoint-ul, si apasam F9. Tot
cu F9 se poate scoate un breakpoint. In partea dreapta a editorului de text va aparea o
bulina rosie pentru fiecare breakpoint:

Fig 11. Breakpoint-uri


Acum apasam F5 pentru a rula programul in mod debug. Vom observa ca
programul isi incepe executia normal, ne cere datele de intrare, iar dupa ce le introducem
consola se blocheaza. In acel moment ne intoarcem in Visual Studio si observam ca

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


aranjamentul ferestrelor s-a schimbat, iar in dreptul primului breakpoint a aparut o
sageata:

Fig 12. Oprirea executiei la un breakpoint


Sageata indica instructiunea la care s-a oprit executia programului.
Atentie! Instructiunea la care se afla sageata inca nu s-a executat, dar instructiunea
anterioara a fost executata!
Acum, putem sa vizualizam valoarea unor variabile. In mod evident, ne va
interesa valoarea celor 2 variabile definite in main() a si b. Pentru a le vedea, mergem
in meniul principal Debug Windows Watch Watch 1.
Apare fereastra Watch 1 in partea de jos. In aceasta fereastra dam click pe coloana Name
si intruducem numele variabilelor ce dorim sa le vedem intai a, apasam enter, pe urma
b, enter:
In cea de-a 2-a coloana Value, va fi afisata valoarea variabilelor, iar in cea de-a 3-a
Type tipul.
Fereastra watch ne permite insa sa vizualizam nu doar variabile, ci si orice
expresie. Putem sa vizualizam elementele unui vectorului, pe care VS va putea sa le
afiseze intr-o forma mai relevanta.
Mai jos sunt prezentate combinatiile de taste utile in VS.
Combinatia de taste
Ctrl + C
Ctrl + V
Ctrl + A
Ctrl + K, F
Ctrl + A, K, F

Efect
Copy
Paste
Select all
Format selected
Format all

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Ctrl + Shift + B
F5
F10
F11
F9

2.7

Build all
Debug, continue after breakpoint
Executa codul linie cu linie fr a intra n
metode.
Executa codul linie cu linie i va intra n
metodele care vor fi apelate
Insert / remove breakpoint

Documentatie
Sursa recomandata de documentatie este situl http://www.cplusplus.com/ :
http://www.cplusplus.com/reference/clibrary/ - documentatia completa a tuturor
header-elor din C, si a functiilor din ele. Vedeti meniul din stanga. Similar cu
documentatia din Borland C.
http://www.cplusplus.com/reference/iostream/ - documentatia claselor ce
realizeaza lucrul cu fisierele. Inclusiv obiectele cin si cout.
http://www.cplusplus.com/doc/tutorial/ - un material didactic alternativ despre
limbajul C++.

Exercitiu
Sa se scrie un program care citeste studentii dintr-o grupa si ii afiseaza. Programul va fi
impartit in trei module:
Modulul Student
Interfata acestui modul va fi STUDENT.H :
#ifndef _STUDENT_
#define _STUDENT_
struct Student {
char* nume;
int nota;
};
void InitStudent (Student&);
void AfisStudent (Student);
void StergeStudent (Student&);
#endif
Implementarea (fisierul STUDENT.CPP) cuprinde:

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

InitStudent citeste numele studentului (pentru care va aloca spatiu cu malloc sau new)
si nota.
AfisStudent afiseaza cimpurile structurii.
StergeStudent va elibera spatiul de memorie ocupat de nume (cu free sau delete).
Modulul Grupa
Interfata acestui modul va fi GRUPA.H :
#ifndef _GRUPA_
#define _GRUPA_
#include "student.h"
struct Grupa {
Student* tab;
int nr;
int id;
// numarul grupei, de exemplu 1105
};
void InitGrupa (Grupa&);
void AfisGrupa (Grupa);
void StergeGrupa (Grupa&);
#endif
Implementarea (fisierul GRUPA.CPP) cuprinde:
InitGrupa citeste numarul grupei si numarul de studenti, dupa care va aloca spatiu cu
malloc pentru acestia. Fiecare student va fi apoi initializat cu InitStudent.
AfisGrupa afiseaza studentii.
StergeGrupa va elibera spatiul de memorie ocupat de cei nr studenti.
Modulul Program principal
Se va gasi in fisierul MAIN.CPP:
#include <stdio.h>
#include "student.h"
#include "grupa.h"
void main()
{

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Grupa g;
InitGrupa(g);
AfisGrupa(g);
StergeGrupa(g);

// citeste studentii
// afiseaza grupa
// elibereaza spatiul

}
Fisierele STUDENT.CPP, GRUPA.CPP, INDEX. CPP si MAIN.CPP se introduc intr-un
proiect in Visual C++ 6.0. Programul demonstrativ este LAB1.EXE.

Complexitatea algoritmilor

3.1. Consideratii teoretice


La evaluarea (estimarea) algoritmilor secventiali se pune in evidenta necesarul de timp si
de spatiu de memorare al lor.
Studierea complexitatii presupune analiza completa in cadrul algoritmului a urmatoarelor
3 apecte:
1.
configuratia de date cea mai defavorabila (cazurile degenerate);
2.
configuratia de date cea mai favorabila;
3.
comportarea medie.
Comportarea medie presupune probabilitatea de aparitie a diferitelor configuratii de date
la intrare. Aspectul 1 este cel mai studiat si este folosit, de obicei, pentru compararea
algoritmulor.
Complexitatea unui algoritm secvential se exprima de regula in limbajul ordinului O.
Definitie
Fie f : N->N si g : N->N doua functii.
Spunem ca f apartine O(g) si notam f = O(g) daca si numai daca exista o constanta
reala c si un numar natural n0 astfel incat pentru n> n0 => f(n) < c * g(n)
Observatie:
f : N->N este o functie f(n) cu n dimensiunea datelor de intrare.
f(n) reprezinta timpul de lucru al algoritmului exprimat in "pasi".
Lema 1
Daca f este o functie polinomiala de grad k atunci f = O(nk ).
Concluzie: f = O(nk), si ordinul O exprima viteza de variatie a functiei, functie de
argument.

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


Proprietati:
1) Fie f, g : N->N.
Daca f = O(g)

k * f = O(g)
f = O(k * g) , k R constant.

2) Fie f, g, h : N->N.
si: f = O(g)
g = O(h)
atunci
3) Fie f1, f2, g1, g2 : N->N.
si:
f1 = O(g1) ==>
f2 = O(g2) ==>

f = O(h)
f1 + f2 = O( g1 + g2 )
f1 * f2 = O( g1 * g2 )

Aceasta proprietate permite ca, atunci cand avem doua bucle imbricate (de complexitati
diferite), complexitatea totala sa se obtina inmultindu-se cele doua complexitati. Cele
doua complexitati se aduna, daca buclele sunt succesive.
Teorema:
Oricare ar fi doua constante c > 0, a > 1, si f : N->N, o functie monotona strict
crescatoare, atunci:
c f(n)
(f(n)) = O(a )
Intre clasa functiilor logaritmice, si cea a functiilor polinomiale exista relatia:
O(nc ) inclusa in O(an ).
Au loc urmatoarele incluziuni:
O(1) in O(log n) in O(n) in O(nlog n) in O(n2) in ... in O(nk log n) in O(nk+1 ) in O(2n)
Pentru calculul complexitatii se va incerca incadrarea in clasa cea mai mica de
complexitate din acest sir:
O(1)
O(log n)
O(n)
O(nlog n)
O(n2 )
O(nklog n)
O(nk+1)
O(2n)

clasa algoritmilor constanti;


clasa algoritmilor logaritmici;
clasa algoritmilor liniari;
clasa algoritmilor polilogaritmici;
clasa algoritmilor patratici;
clasa algoritmilor polilogaritmici;
clasa algoritmilor polinomiali;
clasa algoritmilor exponentiali.

3.2. Algoritmul INSERTION SORT

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1

Algoritmul INSERTION_SORT considera ca in pasul k, elementele A[1k-1] sunt


sortate, iar elementul k va fi inserat, astfel incat, dupa aceasta inserare, primele elemente
A[1k] sa fie sortate.
Pentru a realiza inserarea elementului k in secventa A[1k-1], aceasta presupune:
- memorarea elementului intr-o varibila temporara;
- deplasarea tuturor elementelor din vectorul A[1k-1] care sunt mai mari decat A[k], cu
o poziie la dreapta (aceasta presupune o parcurgere de la dreapta la stanga);
- plasarea lui A[k] in locul ultimului element deplasat.
Complexitate: O(n)
PseudoCod:
insertion_sort(A,n)
{
for k = 2 to n do
temp = A[k]
i=k-1
while (i >= 1) and (A[i] > temp) do A[i+1] = A[i]
i = i-1
A[i+1] = temp
}
Cazul cel mai dafavorabil: situatia in care deplasarea (la dreapta cu o pozitie in vederea
inserarii) se face pana la inceputul vectorului, adica sirul este ordonat descrescator.
Exprimarea timpului de lucru:
T(n) = 3 (n - 1) + 3 (1 + 2 + 3+ ... + n - 1) = 3(n-1) + 3n * (n - 1)/2
Rezulta complexitatea: T(n) = O(n2) functie polinomiala de gradul II.
Observatie: Cand avem mai multe bucle imbricate, termenii buclei celei mai interioare
dau gradul polinomului egal cu gradul algoritmului.
Bucla cea mai interioara ne da complexitatea algoritmului.

3.3. Cautarea binara (Binary Search)


Fie A, de ordin n, un vector ordonat crescator. Se cere sa se determine daca o valoare b se
afla printre elementele vectorului. Limita inferioara se numeste low, limita superioara se
numeste high, iar mijlocul virtual al vectorului, mid (de la middle).
PseudoCod:
Binary_search(A,n,b)

Laborator de Structuri de Date si Algoritmi Lucrarea nr. 1


{
low = 1
high = n
while low <= high do
mid = [(low + high)/2]
// partea intreaga
if A[mid] = b then return mid
else if A[mid] > b then high = mid-1 // restrang cautarea
// la stanga
else low = mid+1
// restrang cautarea
// la dreapta
return(0)
}
Calculul complexitatii algoritmului consta in determinarea numarului de ori pentru care
se executa bucla while. Se observa ca, la fiecare trecere, dimensiunea zonei cautate se
injumatateste.
Cazul cel mai defavorabil este ca elementul cautat sa nu se gaseasca in vector.
Pentru simplitate se considera n = 2k , unde k este numarul de injumatatiri.
Rezulta k = log2 n si facand o majorare, T(n) <= log2 n + 1, a.i.
2 * 2k <= n < 2k+1

Rezulta complexitatea acestui algoritm: este O(log2 n). Dar, baza logaritmului se poate
ignora, deoarece: logax = logab * logbx si logab este o constanta, deci ramane O(log n),
adica o functie logaritmica.

TEMA
Sa se construiasca un modul (fisierle .H si .C (.CPP) ) care sa contina tipurile de date si
functiile care implementeaza algoritmii analizati anterior si calculeaza numarul de
operatii (atribuiri si comparatii) executate pentru diferite instante de intrare.
Sa se construiasca un program C (C++) care cu ajutorul unui meniu simplu sa permita
urmatoarele:
- introducerea datelor
- sortarea prin metoda insertiei si afisarea numarului de operatii
- cautarea prin metoda cautarii binare a unei valori introduse de la tastatura si afisarea
numarului de operatii

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