Introdu textul pe care doresti sa-l cauti:Aplicatia:
a IX-a A
Introducere în C++
Introducere în C++

Limbajul C++ a fost inventat de către Bjarne Stroustrup în 1979, ca o extindere a limbajului C. Limbajul C a fost inventat în 1969-1973 de către Dennis Ritchiepentru a realiza sistemul de operare Unix. Astfel, aproape toate programele scrise în C pot fi compilate în C++, eventual cu foarte puține modificări.

Limbaje de programare

Limbajele de programare sunt limbaje asemănătoare cu limbajul uman. Conțin cuvinte (destul de puține), semne de punctuație, operații matematice și au reguli de scriere. Programele care rulează pe orice calculator au fost scrise într-un limbaj de programare. Există numeroase limbaje programare, precum C, C++, Pascal, Java, Python, PHP, Javascript, etc.

Programul scris într-un limbaj de programare se numește program sursă și trebuie traduse într-un limbaj pe care îl înțelege procesorul, numit cod mașină, sau program executabil. Pentru anumite limbaje de programare operația de traducere se numește compilare (cazul lui C, C++, Pascal, etc.), pentru alte limbaje (PHP, Python, Javascript, etc.) operația de traducere se numește interpretare . Traducerea este realizată de un program specializat numit compilator  sau interpretor.

Limbajul C++ este un limbaj compilat. Etapele scrierii unui program în C++ sunt:

  • editarea programului C++; se obține fișierul sursă, cu extensia .cpp
  • compilarea fișierului sursă; aici se verifică corectitudinea sintactică a programului (corectitudinea cuvintelor folosite, prezența semnelor de punctuație, etc.); dacă programul este corect sintactic, se va obține fișierul obiect, cu extensia .o sau .obj
  • editarea de legături; se stabilesc legături între fișierul obiect curent și alte fișiere obiect, ale programatorului sau incluse în compilator; în urma acestei etape se obține programul executabil. În Windows, fișierele executabile au extensia .exe;
  • programul executabil poate fi lansat în execuție (rulat).

Primul program C++

Cum scriem un program C++? Avem nevoie cel puțin de un editor de text pentru scrierea sursei și de un compilator C++. Deși fișierul sursă poate fi realizat cu orice editor de text, de cel mai multe ori folosim un IDE . Un IDE pentru C/C++ foarte utilizat este Code::Blocks. Acest articol prezintă modul de instalare a pachetului Code::Blocks pe calculator, împreună cu compilatorul MinGW, iar acest articol prezintă pașii necesari pentru a realiza un program C++ în Code::Blocks.

Să considerăm un prim program C++:

view source

print?

// primul program C++

#include <iostream>

using namespace std;

int main()

{

/*

primul program C++

il scriem in Code::Blocks

*/

cout << "Hello world";

return 0;

}

Dacă vom compila și rula acest program, pe ecran va apărea:

Hello world

Să analizăm acest program. El este alcătuit din mai multe linii:

  1. // primul program C++
    Această linie reprezintă un 
    comentariu. Comentariile sunt texte explicative care nu influențează comportamentul programului. Ele sunt pentru programatori, pentru a înțelege mai repede semnificația programului. Acest comentariu începe de la cele două caractere slash // și se termină la sfârșitul liniei.
  2. #include <iostream>
    Liniile care încep cu 
    # se numesc directive preprocesor. Ele sunt interpretate înainte de compilarea propriu-zisă, de către un program numit preprocesor. În cazul nostru, directiva #include <iostream> cere preprocesorului să includă în sursă o secțiune a codului C++ standard, header-ul iostream, care permite realizarea operațiilor de citire și afișare – la noi afișarea mesajului Hello world pe ecran.
  3. int main()
    Această linie reprezintă declararea unei funcții. În esență, o functie este un grup de instructiuni care un un nume dat; în acest caz, funcția se numește 
    main și este alcătuită din toate instrucțiunile care urmează. Vom discuta pe larg despre functii mai târziu.
    Funcția numită 
    main este specială în toate programele C++; această funcție este apelată când se lansează în execuție programul și trebuie să apară în orice program C++.
  4. {
    Parantezele acolade de la linia 4 și 10 delimitează instrucțiunile care fac parte din funcția 
    main
  5. /*
  6. primul program C++
  7. il scriem in Code::Blocks
  8. */
    Și acesta este un comentariu. Textele cuprinse între 
    /* și */ nu influențează comportamentul programului. Ele pot să ocupe mai multe linii, sau pot să apară în interiorul unei linii.
  9. cout << "Hello world";
    Aceasta este o instrucțiune C++. O instrucțiune este o construcție (expresie, comandă) care face ceva. Instrucțiunile sunt “miezul” programelor, ele stabilind comportamentul acestora. Instrucțiunile dintr-un program se execută în ordine, una după alta.
    Această instrucțiune produce afișarea pe ecran a atextului 
    Hello world. Ea este alcătuită din trei părți. std::cout semnifică dispozitivul standard de ieșire (standard character output) – de cele mai multe ori ecranul calculatorului. A doua parte este operatorul de inserție <<, care indică faptul că ceea ce urmează este inserat în std::cout (trimis spre ecran). A treia parte este textul, "Hello world", cuprins între ghilimele, care va fi inserat în std::cout.
    Să observăm prezența caracterului 
    ; la sfârșitul instrucțiunii. Orice instructiune C++ trebuie să se termine cu ;, la fel cum orice propoziție în limba română se termină cu caracterul . (punct).

Una dintre cele mai frecvente erori de sintaxă este să uităm să scriem ; la finalul unei instrucțiuni.

  1. return 0;
    Această instrucțiune marchează finalul execuției funcției 
    main și a programului nostru. Valoarea 0 semnifica faptul că programul s-a încheiat cu succes! 
    Dacă în programul nostru ar fi fost și alte instrucțiuni după instrucțiunea 
    return 0;, acestea nu s-ar mai fi executat.
  2. }
    Acolada închisă 
    } reprezintă finalul funcției main.

Să reținem că nu toate liniile programului produc efecte la executarea programului. Unele linii (comentariile) sunt scrise numai pentru a ușura înțelegerea programului de către cel care îl citește/scrie. Mai mult, nu este obligatoriu ca fiecare instrucțiune să fie scrisă pe o singură linie. Următoarele trei exemple de funcție main au acelați efect:

view source

print?

int main()

{

cout << "Salut";

return 0;

}

view source

print?

int main() {  cout << "Salut"return 0; }

view source

print?

int main()

{

cout << "Salut";

return 0;

}

Instrucțiunea using namespace std;

În C++, identificatorii sunt grupați în spații de nume – namespaces. Există un spațiu de nume predefinit, cu numele std, din care fac parte toți identificatorii din biblioteca C++ standard.

Comentarii

Comentariile sunt texte care pot să apară în programul sursă și nu sunt luate în considerare la compilare. Ele sunt citite doar de către oameni, pentru a explica anumit secțiuni mai importante din program. Așa cum am văzut mai sus, în C++ sunt două tipuri de comentarii:

  • // comentariu pe o linie
  • /* comentariu de tip bloc */

Comentariul pe o linie începe de caracterele // și se termină la finalul liniei. Comentariul de tip bloc începe la /*, se termină la */ și se poate întinde pe mai multe linii.

Comentariile sunt importante! Trebuie să învățăm să scriem cod pe care să-l înțelegem și peste o zi sau un an, iar prezență comentariilor este un pas înainte.

 

Tipuri de date C/C++
Tipuri de date C/C++

Tipul de date reprezintă un concept foarte important în C/C++. Orice dată (constantă sau variabilă) este de un numit tip. Tipul datei precizează ce valori poate avea acea dată și ce operații se pot face cu ea.

În C/C++ tipurile de date sunt:

  1. Tipuri simple

o  Tipuri întregi

o    Tipuri reale

o    Tipul pointer

o  Tipul  bool

  1. Tipuri derivate

o  Tipul tablou

o    Tipul structură

o    Tipul enumerare

Acest articol se referă numai la tipurile simple.

Tipurile întregi

Tipurile întregi permit memorarea de valori întregi. Tipul de bază este int. O dată de tip int poate memora valori întregi cuprinse între -2^31  și 2^31-1.

Tipurile întregi diferă prin numărul de octeți necesari pentru memorarea datei, tipul datei (cu semn sau fără semn) și implicit intervalul de valori pe care le pate lua respectiva dată. Tipurile întregi sunt:

Denumire tip

Reprezentare

-

int

4 octeți cu semn

-2147483648 ...

2147483647

unsigned int

4 octeți fără semn

0 ... 4294967295

long int

4 octeți cu semn

-2147483648 ... 2147483647

unsigned

long int

4 octeți fără semn

0 ... 4294967295

short int

2 octeți cu semn

-32768 ... 32767

unsigned short int

2 octeți fără semn

0 ... 65535

 

long long int

8 octeți cu semn

unsigned long long int

8 octeți fără semn

char

1 octet cu semn

-128 ... 127

unsigned char

1 octet fără semn

0 ... 255

Tipurile char și  unsigned char  memorează valori întregi. La afișarea unei date de acest tip nu se va afișa numărul pe care îl memorează ci caracterul care are are codul ASCII egal cu acel număr. Operația de citire a unei date de acest tip este similară.

Tipurile reale – în virgulă mobilă

Memorează valori reale, reprezentate prin mantisă și exponent. În acest mod se pot reprezenta valori foarte mari, dar precizia reprezentării poate fi slabă – numărul de cifre semnificative memorate poate fi mult mai mic decât numărul de cifre din număr.

Tipurile reale sunt:

·         float – se reprezinta pe 4 octeți;

·         double – se reprezinta pe 8 octeți;

·         long ouble – se reprezinta pe 10 octeți;

Tipul pointer

O dată de tip pointer memorează o adresă de memorie – de exemplu adresa unei variabile. Vom reveni asupra tipului pointer mai târziu.

Tipul bool

Anumite operații care se fac cu datele au ca rezultat valori de adevăr: adevărat sau false. În anumite limbaje de programare există un tip de date care memorează exact aceste două valori.

În limbajele ,ai evoluate de C++ există tipul  

bool. Acest tip conține două valori: literalii true și false. De fapt, acestea sunt redenumiri ale valorilor 1 și 0.

Variabile și constante C/C++
Variabile și constante C/C++

Orice program prelucrează date. Acestea se află în memoria RAM a calculatorului, și pot fi variabile (valoarea datei se poate modifica) sau constante  (valoarea nu se poate modifica).

Variabile

variabilă reprezintă o locație de memorie unde se află o valoare de un anumit tip. Orice variabilă este caracterizată de:

  • adresa variabilei. Memoria RAM a calculatorului este adresată – fiecare octet (byte) din memorie are asociat un număr de ordine, începând de la 0. Acest număr reprezintă adresa acelui byte și se afișează implicit în baza 16.
  • identificatorul variabilei – reprezintă un nume pentru variabilă – legătura dintre variabilă si adresa ei. Identificatorul respectă următoarele reguli:
    • conține litere mari, mici ale alfabetului englez cifre și caracterul de subliniere '_' – underline. Literele mari sunt considerate diferite de cele mici, astfel că Raspunsraspuns și RASPUNS reprezintă identificatori diferiți.
    • primul caracter nu poate fi cifră. Deși este posibil ca un identificator să înceapă cu '_', nu este recomandat, pentru a evita anumite conflicte cu identificatori de sistem.
    • nu există limite legate de lungimea unui identificator, dar numai primele 31 de caractere sunt semnificative.
  • tipul variabilei – stabilește ce fel de valori poate să ia variabila, între ce limite sunt acestea, precum și ce operații pot fi realizate cu variabila. Citește aici despre tipurile de date!
  • domeniul de vizibilitate – reprezintă zona din program în care variabila există și poate fi utilizată. Variabilele pot fi globale sau locale.
    • variabilele locale se declară într-un bloc (între paranteze acolade {...}) și sunt vizibile doar în acel bloc. Au valori inițiale aleatorii.
    • variabilele globale se declară în exteriorul oricărui bloc și sunt vizibile în toate blocurile care urmează declarării. Sunt inițializate cu 0.

În C/C++, variabilele trebuie declarate, precizând tipul și identificatorul. Sintaxa este:

Tip_de_date Lista_identificatori ;

unde

Tip_de_date poate fi orice tip C++ corect (citește aici despre tipurile de date), iar Lista_identificatori  este alcătuită din cel puțin un identificator. Dacă sunt mai mulți, se vor separa prin caracterul virgulă ,.

Exemple:print?

int a , x;

S-au declarat două variabile, cu numele a și x ce vor putea memora valori numere întregi dintr-un interval pe care îl vom studia mai târziu.

Următorii identificatori C++ sunt corecți: anumarNumaralt_numara2b_suma – nerecomandat, un_nume_de_variabila_foarte_lung.

Următorii identificatori C++ sunt incorecți:

  • 2a – începe cu cifră. Identificatorii pot începe cu litere sau '_'
  • alt numar – conține caracter interzis: spațiu
  • un-numar – contine caracter interzis: minus
  • număr – conține litera ă. Identificatorii pot conține numai litere ASCII – din alfabetul englez.

Constante

Constantele sunt date care nu-și modifică valoarea în timpul execuției programului. Pot fi constante cu nume, sau constante literale, date direct prin valoarea lor.

Constante simbolice

Constantele simbolice (cu nume) pot fi precizate în două moduri:

  • prin directiva define. Exemplu:

#define MAX 101

  • se pot declara variabile cu modificatorul const , iar valoarea lor nu mai poate fi modificată. Exemplu:

const int MAX = 101;

Literali

Într-un program pot apărea valori constante, fie că sunt numere, caractere sau șiruri de caractere. Acestea se mai numesc constante literale sau  literali.

Constante întregi

Reprezintă numere întregi – fără parte fracționară. Pot fi:

  • Constante zecimale – în baza 10  : 176 -54 0. Pot conține cifrele: 0 1 2 3 4 5 6 7 8 9
  • Constante octale – în baza 8  : 015 062. Pot conține cifrele: 0 1 2 3 4 5 6 7 și încep întotdeauna cu 0.
  • Constante hexazecimale – în baza 16  : 0x15 0x6f 0xff. Pot conține cifrele: 0 1 2 3 4 5 6 7 8 9 A B C D E F și încep întotdeauna cu 0x.

Constante reale

Reprezintă numere reale și se mai numesc în virgulă mobilă. Separatorul zecimal este caracterul punct '.' și pot apărea în două forme:

  • scrierea standard: -1.5 14.974
  • scrierea științifică, cu mantisă și exponent. Numărul -0.567E+2  înseamnă  -0.567*10+2, adică -56.7-0567 reprezintă mantisa, iar -2 reprezintă exponentul.

Constante caracter – char

Sunt alcătuite dintr-un caracter, delimitat de apostroafe: '. De exemplu:  'a' 'B' '~' '?'. O categorie aparte de caractere este dată de secvențele ESCAPE. O secvență escape este alcătuită din două caractere, dintre care primul este backslash: \. Dintre secvențele escape amintim:

  • '\b' – Backspace
  • '\f' – Form feed
  • '\n' – Newline
  • '\r' – Return
  • '\t' – TAB orizontal
  • '\\' – Backslash
  • '\'' – Apostrof
  • '\"' – Ghilimele
  • '\?' – Semn de întrebare
  • '\0' -Caracterul nul

Constante șir de caractere

Sunt delimitate de ghilimele " . Pot să conțină secvențe escape. Exemple:

"numar""n = ""Am terminat.\n"

Cuvinte rezervate

Nu orice cuvânt poate fi utilizat pe post de identificator. Există în C++ o listă de cuvinte care au o semnificație bine determinată și nu pot fi utilizate în alt scop. Ele se numesc cuvinte rezervate (keywords) și sunt următoarele:

alignas
alignof
and
and_eq
asm
auto
bitand
bitor
bool
break
case
catch
char
char16_t
char32_t
class
compl
concept
const
constexpr
const_cast
continue
decltype
default
delete
do
double
dynamic_cast

else
enum
explicit
export
extern
false
float
for
friend
goto
if
inline
int
long
mutable
namespace
new
noexcept
not
not_eq
nullptr
operator
or
or_eq
private
protected
public
register
reinterpret_cast

requires
return
short
signed
sizeof
static
static_assert
static_cast
struct
switch
template
this
thread_local
throw
true
try
typedef
typeid
typename
union
unsigned
using
virtual
void
volatile
wchar_t
while
xor
xor_eq

 

Intrări/Ieșiri în C++
Intrări/Ieșiri în C++

Operațiile de de intrare/ieșire sunt operațiile prin care un program primește date sau afișează rezultate. Aceste operații trebuie privite din perspectiva programului

  • operații de intrare: datele intră în program, programul citește date
  • operații de ieșire: datele ies din program, programul afișează date

Practic, datele care intră în program/ies din program sunt șiruri de caractere pe care programul le primește/le trimite

Limbajul C++ oferă o modalitate uniformă de a realiza operațiile de intrare/ieșire, indiferent dacă se fac la consolă, în fișiere, sau cu alte dispozitive care prelucrează caractere. Este vorba despre vorba despre stream  sau flux. Stream-ul poate fi privit ca o înșiruire de caractere care sunt trimise într-o ordine bine determinată de la o sursă la o destinație. Programul va insera caractere în stream (dacă este un stream de ieșire, care afișează date) sau va extrage caractere din stream (dacă este un stream de intrare, din care se citesc date).

Biblioteca standard C++ permite lucrul cu mai multe categorii de stream-uri. Dintre acestea vom discuta în continuare despre stream-urile cu consola, dispozitivul standard de intrare-ieșire, altfel spus stream-uri care permit citirea de la tastatură și afișarea pe ecran. Obiectele care permit aceste operații sunt:

  • cin – stream standard de intrare
  • cout – stream standard de ieșire

În continuare vom vorbi despre cout și cin – stream-ul standard de ieșire și de intrare. 

Stream-ul de ieșire cout

În cele mai multe cazuri, dispozitivul standard de ieșire este ecranul și poate fi accesat cu stream-ul cout. Pentru aceasta,  cout se folosește împreună cu operatorul de inserție  <<, urmat de data care se va afișa:

view source

print?

cout << "Salut"; // afiseaza pe ecran Salut

cout << 17; // afiseaza numarul 17 pe ecran

cout << n; // afiseaza pe ecran valoarea variabilei n

Operatorul cout afișează în stream-ul din stânga valoarea din dreapta. Să observăm că "Salut" este delimitat de ghilimele, deoarece este o constantă literal de tip șir de caractere, iar x nu este delimitată de ghilimele, deoarece este o variabilă.

Dacă textul care urmează după << este între ghilimele, se va afișa ca atare. Dacă nu este între ghilimele, se consideră că este o variabilă, și se afișează valoarea ei.

view source

print?

cout << "Salut"; //afiseaza cuvantul Salut

cout << Salut; // afiseaza valoare variabilei Salut

Putem afișa mai multe valori în aceeași instrucțiune:

view source

print?

cout << "Ana " << "are" << " mere."; // se va afisa Ana are mere

sau

view source

print?

int nr_mere = 17;

cout << "Ana " << "are " << nr_mere << " mere."; // se va afisa Ana are 17 mere

cout nu adaugă automat sfârșit de linie sau spatii. De exemplu:

view source

print?

cout << "Aceasta este o";

cout << "propozitie mai lunga!";

va afișa:

Aceasta este opropozitie mai lunga!

Pentru a insera spațiu între o și  propoziție, îl precizăm explicit:

view source

print?

cout << "Aceasta este o "; // este un spatiu dupa o

cout << "propozitie mai lunga!";

sau

view source

print?

cout << "Aceasta este o";

cout << " propozitie mai lunga!"; // la inceput este un spatiu

Dacă vrem să afișăm pe linii diferite procedăm astfel:

view source

print?

cout << "Aceasta este o\n";

cout << "propozitie mai lunga!";

O altă variantă este să folosim manipulatorul endl pentru a întrerupe linia. De exemplu:

view source

print?

cout << "Aceasta este o" << endl;

cout << "propozitie mai lunga!";

Ambele variante de mai sus vor afișa:

Aceasta este o

propozitie mai lunga!

endl produce un caracter rând nou, exact ca și inserarea lui \n, dar mai face ceva: endl golește buffer stream-ului cout, adică forțează afișarea pe ecran tuturor caracterelor inserate în stream până în acest moment. endl produce întârzieri în execuția programului, deci trebuie folosit cu precauție.

Stream-ul de intrare cin

În cele mai multe cazuri, dispozitivul standard de intrare este tastatura și poate fi accesat cu stream-ul cin. Pentru aceasta,  cin se folosește împreună cu operatorul de extragere  >>, urmat de variabila în care se va memora valoarea extrasă (variabila care se va citi):

view source

print?

1.int n;

2.cin >> n;

Mai întâi se declară variabila n, apoi se citește o valoare pentru ea – se extrage din cin o valoare care se memorează în variabila n. La execuție, programul așteaptă să se introducă o valoare de la tastatură. De fapt, caracterele introduse sunt transmise programului numai când se apasă tasata ENTER.

Să considerăm următorul program:

view source

print?

#include <iostream>

using namespace std;

int main()

{

int n = 7;

cout << "n = ";

cin >> n;

cout << "n este " << n << endl;

cout << "patratul lui n este " << n * n << endl;

return 0;

}

Rezultatul său depinde de valoare introdusă pentru n. Ar putea fi:

n = 25

n este 25

patratul lui n este 625

Dar dacă se nu se introduce un număr?

n = salut

n este 0

patratul lui n este 0

La operația de extragere din cin contează tipul variabilei de după >>. Caracterele din stream sunt interpretate în funcție de tipul variabilei. Dacă aceste caractere nu corespund cu tipul variabilei, operația de extragere eșuează.

Se pot citi valorile a două sau mai multe variabile în aceeași instrucțiune:

view source

print?

cin >> x >> y;

este echivalent cu

view source

print?

cin >> x;

cin >> y;

În ambele cazuri se așteaptă introducerea a două valori; acestea pot fi separate/precedate prin orice fel de caractere albe: spații, TAB-uri, caractere rând nou.

Una dintre cele mai frecvente erori este inversarea operatorilor pentru stream-urile cin și cout, sau citirea valorii unei constante. Următoarele instrucțiuni sunt greșite:

view source

print?

cout >> "Salut";

cin << n;

cin >> "Salut";

 

Operatori C++
Operatori C++

Computerele prelucrează date! Le citesc de la tastatură, le memorează în variabile (sau constante), le afișează pe ecran. Și mai fac cu ele diverse operații. Noi suntem obișnuiți să facem operații aritmetice (adunări, scăderi, etc.), dar în C++ există multe alte operații.

operație este alcătuită din operanzi și  operator. Operanzii reprezintă datele cu care se fac operațiile, iar operatorul este simbolul care stabilește ce operație se face cu operanzii. Din punct de vedere a numărului de operanzi, operațiile (operatorii) pot fi:

  • unare – se aplică la un singur operator (de exemplu -7, operația de schimbare a semnului unui număr);
  • binare – se aplică la doi operatori (de exemplu adunarea numerelor, 2+5);
  • ternare – se aplică la trei operatori. Există un singur operator ternar în C++, operatorul condițional și va fi analizat mai jos.

Operanzii pot fi variabile, constante, literali, rezultatele unor funcții, rezultatele altor operații. O operație care are ca operanzi alte operații se numește expresie.

Fiecare operație C++ are un rezultat!

Acest articol analizează o parte a operatorilor C++, cei mai frecvent utilizați:

Operatorii aritmetici: +- */%

În exemplele de mai jos, considerăm variabilele:

  • N = 11 și M = 3 de tip int
  • X = 11 și Y = -3.5 de tip double.

Operatorii aritmetici unari: +-

În C++ există operațiile unare + și -+ returnează valoarea operandului, iar - returnează valoarea operandului cu semn schimbat.

Exemple

  •  + X = 11
  • - Y = 3
  • - + N = -11

Operatorii aritmetici binari: +-*/%

  • + : adunarea a două numere
  • - : scăderea a două numere
  • * : înmulțirea a două numere
  • / : împărțirea a două numere
  • % : restul împărțirii a două numere întregi (modulo)
  • În C++ nu există un operator pentru ridicarea la putere!

Adunarea, scăderea și înmulțirea se comportă conforma așteptărilor, ca la matematică. Operația de împărțire și operația modulo necesită niște explicații suplimentare.

Împărțirea întreagă și împărțirea zecimală

Operația de împărțire are două moduri de lucru, în funcție de tipul operanzilor.

  • Dacă operanzii sunt de tip întreg (intshortchar, etc.), se va realiza împărțirea întreagă, iar rezultatul operației / este câtul împărțirii întregi.
  • Dacă operanzii sunt de tip real (floatdoublelong double), se va realiza împărțirea zecimală, iar rezultatul operației / este rezultatul acestei împărțiri, “cu virgulă”.

Exemple

  • N / M = 3
  • X / Y = -3.14286
  • X / 2.0 = 5.5
  • M / 2 = 1
  • M / 2.0 = 1.5

Ultima împărțire este deosebită. Cei doi operanzi au tipuri diferite: M = 3 este de tip int, iar 2.0 este de tip double. Aici intervine operația de conversie implicită: în mod automat, operandul M se consideră ca fiind de tip double , împărțirea este împărțire reală și are rezultatul 1.5 .

Operatorul modulo %

Operația modulor are sens numai dacă ambii operanzi sunt de tip întreg – împărțirea cu rest are sens numai în această situație. Iată câteva exemple:

N % M = 2 : restul împărțirii lui 11 la 3 este 2
30 % 10 = 0

Operatorul modulo este util în multe situații. El poate fi utilizat pentru a afla ultima cifră a unui număr natural: ultima cifră a lui 276  este 276 % 10 adică  6, sau pentru a verifica dacă un număr N  este divizor al lui M. În caz afirmativ, M % N este  0.

Observații suplimentare

  • nu se poate face împărțire la 0.
  • dacă cel puțin un operand al împărțirii întregi sau al operației modulo este negativ, rezultatul operației depinde de versiunea compilatorului C++ folosit. O variantă de evaluare, utilizată în unele compilatoare, este efectuarea operației cu operanzii în valoare absolută și apoi aplicarea regulii semnelor.

Operatorii relaționali: <> <=>===!=

Un operatori relațional stabilește dacă între două numere (operanzii) are loc o anumită relație. Rezultatul acestei operații este adevărat  sau fals. Rezultatul operațiilor relaționale poate fi 0  sau 1:

  • rezultatul este 1 dacă relația este adevărată
  • rezultatul este 0 dacă relația este falsă

Fie N = 11 și M = 3. Operațiile relaționale sunt:

  • operații de comparare (comparații)
    • mai mic <N < M este fals, adică 0
    • mai mare >N > M este adevărat, adică 1
    • mai mic sau egal <=M <= N este 1
    • mai mare sau egal >=M >= N este 0
  • operația de egalitate == ; N == M este fals, adică 0
  • operația de neegalitate (diferit, not egal) != N != M este adevărat, adică 1 .

Un dintre cele mai frecvente erori este folosirea pentru operația de egalitate a operatorului =, în loc de ==. Operatorul = reprezintă operația de atribuire !

Operatorii logici: !||&&

Operatorii logici au operanzi de tip valori de adevăr și rezultat valori de adevăr. Istoric, operațiile logice sunt legate de numele matematicianului englez George Boole, cel care a pus bazele acestei ramuri a matematicii și a inventat algebra booleană și calculul propozițional.

În C++, operatorii logici pot fi aplicați oricăror valori numerice, și au ca rezultat una din valorile 0 sau 1. În exemplele de mai jos vom folosi literalii true și false, de tip bool.

Negația: !

  • ! true este false. Orice valoare nenulă negată devine 0.
  • ! false este true0 negat devine 1.

Disjuncția: ||

  • false || false → false
  • false || true → true
  • true || false → true
  • true || true → true

Conjuncția: &&

  • false && false → false
  • false && true → false
  • true && false → false
  • true && true → true

Legile lui De Morgan

Fie p și q două valori booleene (pot fi rezultatele unor expresii, de exemplu). Atunci:

  • !(p && q) == !p || !q
  • !(p || q) == !p && !q

Să luăm ca exemplu apartenența unei valori la un interval:

  • x[a,b]
  • expresia echivalentă C++ este x ≥ a && x ≤ b
  • conform legilor lui De Morgan, prin negare obținem: !(x ≥ a) || !(x ≤ b)
  • deoarece !(x ≥ a) este echivalent cu x<a, obținem:
  • x < a || x > b
  • folosind intervalele obținem: x (-∞,b) (a,+∞), adică x [a,b]

Operatorul de atribuire: =

Atribuirea este operația prin care o variabilă primește valoarea unei expresii:

variabila = expresie

Expresia poate avea orice fel de rezultat, dacă tipul său acesta este identic cu al variabilei, sau poate fi convertit la tipul variabilei. În cazul tipurile întregi, reale, bool, oricare dintre acestea poate fi convertit la la oricare altul, eventual cu pierderea unor date.

Exemple:

#include <iostream>

using namespace std;

int  main()

{

int  n , m; // valori aleatorii

double x , y; // valori aleatorii

n = 5; // valoare lui n devine 5

cout << n << endl;

m = n + 2; // valoare lui m devine 7

cout << m << endl;

n = n + 3; // valoarea lui n devine 5 + 3, adica 8

cout << n << endl;

x = m / 5; // valoarea lui x devine 8 / 5, adica 1. ATENTIE! este impartire intreaga

cout << x << endl;

y = 5; // valoarea lui y devine 5, de tip double. Are loc conversia lui 5 de tip int la double

cout << y << endl;

x = m / y; // valoarea lui x devine 1.4, deoarece impartirea este zecimala. Are loc conversia valorii lui m la double, apoi se face impartirea

cout << x << endl;

return  0;

}

Atribuirea este o operație, deci are rezultat! Rezultatul operației de atribuire este chiar variabila care primește valoare.

Nu confundați atribuirea cu operația de egalitate ==.

Este posibilă și realizarea unor atribuiri multiple, ca mai jos:

int a , b, c;

a = b = c = 10;

Toate variabilele vor primi valoarea 10.

Următoarea atribuire este mai interesantă:

n = n + 4;

Ea se efectuează astfel (să considerăm, ca exemplu, că valoarea inițială a lui n este 5):

  • mai întâi se calculează expresia din dreapta, în care se folosește valoarea curentă a lui nn + 4  este 5 + 4 adică 9
  • valoarea expresiei din dreapta se atribuie variabilei din stânga, deci n devine 9 .

Notă: În membru stâng al unei atribuiri poate fi nu doar o variabilă, ci o expresie de tip lvalue. Prin lvalue se înțelege left value, adică tocmai o expresie ce poate “în stânga” unei atribuiri. Variabilele sunt expresii lvalue, dar există și altfel de expresii, despre care vom vorbi mai târziu, care sunt lvalue.

Operatorii de atribuire compusă: +=-=*=/=%=>>= <<=&= ^=|=

În programare sunt foarte frecvente atribuirile de forma:

x = x * 5;

în care unei variabile i se aplică o anumită operație aritmetică (în exemplul de mai sus *) iar rezultatul se memorează chiar în acea variabilă. Pentru a facilita scrierea codului în aceste situații, în C++ există atribuirea compusă:

var OP= expresie, echivalentă cu var = var OP expresie

Astfel, atribuirea x = x * 5  este echivalentă cu x *= 5.

Operatorii de incrementare și decrementare: ++ --

Prin incrementarea unei variabile se înțelege mărirea valorii sale cu 1. Similar, prin decrementarea unei variabilă se înțelege micșorarea valorii sale cu 1.

Operația de incrementare a variabilei X poate fi:

  • postincrementareX ++. Efectul expresiei este mărirea valorii lui X cu  1, iar rezultatul expresiei este valoarea inițială a lui X.
  • preincrementare++ X. Efectul expresiei este mărirea valorii lui X cu  1, iar rezultatul expresiei este valoarea mărită a lui X.

Exemplu pentru postincrementare:

int  x = 5 , y = 10;

y = x ++; // y primeste valoare lui (x++), adica valoarea initiala a lui x

cout << x << " "  << y; // 6 5

Exemplu pentru preincrementare:

int  x = 5 , y = 10;

y = ++ x; // y primeste valoare lui (++x), adica valoarea marita a lui x

cout << x << " "  << y; // 6 6

Operația de decrementare  a variabilei X poate fi:

  • postdecrementareX ++. Efectul expresiei este micșorarea valorii lui X cu 1 , iar rezultatul expresiei este valoarea inițială a lui X.
  • predecrementare++ X. Efectul expresiei este micșorarea valorii lui X cu  1, iar rezultatul expresiei este valoarea micșorată a lui X.

Operatorul condițional: ?

Operatorul condițional este singurul operator ternar (cu trei operanzi) din C++. Sintaxa lui este:

expresie1 ? expresie2 : expresie3

și se evaluează astfel:

  • se evaluează expresie1
  • dacă rezultatul lui expresie1 este nenul (adevărat), se evaluează expresie2 și rezultatul acestei expresii va fi rezultatul operației ?
  • dacă rezultatul lui expresie1 este nul (fals), se evaluează expresie3 și rezultatul acestei expresii va fi rezultatul operației ?

expresie2 și  expresie3 trebuie să aibă rezultate de același tip, sau de tipuri compatibile

Exemplu:

int  x;

cin >> x;

cout << (x % 2 == 0? "par"  : "impar");

Operatorul virgulă: ,

În anumite situații, regulile de sintaxă ale limbajului C++ solicită prezența unei singure operații, dar logica programului cere prezența mai multor operații. Acestea pot fi grupate cu ajutorul operatorului ,. Sintaxa acestei operații este;

expresie1 , expresie2

Modul de evaluare este:

  • se evaluează mai întâi expresie1, apoi expresie2  – important, dacă în expresie2 apar variabile care se modifică în expresie1
  • rezultatul operației este rezultatul lui expresie2

Exemple:

int  x , y , z;

x = 1 , y = 2 , z = 3;

x ++, y = x + 2, z -= x; // este semnificativa ordinea in care s-au evaluat cele trei expresii

cout << x << " " << y << " " << z; // 2 4 1

Operatorii pe biți: &|^~<< >>

Operatorii pe biți reprezintă o temă avansată de programare. Ei permit manipularea directă și foarte rapidă a biților care formează reprezentarea în memorie a unei date. Vom trata această temă într-un alt articol.

Operatorul de conversie explicită

În anumite situații trebuie să considerăm o expresie de un anumit tip ca fiind de alt tip. Acest lucru poate fi realizat prin operatorul de conversie:

(tip_nou) expresie

Exemple:

int  x = 2;

cout << 7 / x << endl; // 3 - este impartire intreaga

cout << 7 / (double) x; // 3.5 - este impartire zecimala

char  p = 'a';

cout << (int)p << endl; // 97, codul ASCII al lui 'a'

cout << p - 32 << endl; // 65

cout << (char)(p - 32); // A - carcaterul cu codul ASCII 65

Alți operatori

Limbajul C++ conține și alți operatori, dintre care:

  • ( ) – modificarea priorității unei operații, apel de funcție
  • [ ] – indexarea unui tablou
  • .-> – acces la membrii unei structuri
  • &* – referențiere (determinarea adresei unei variabile), dereferențiere (accesare variabilei de la o adresă)
  • newdelete – alocare și dealocarea memoriei
  • <<>> – inserare și extragere din stream
  • :: operatorul de acces/rezoluție

Prioritatea operatorilor

Prioritatea operatorilor stabilește ordinea în care se evaluează o expresie care conține mai mulți operatori, de diverse feluri – ordinea în care se efectuează operațiile.

Asocierea operatorilor stabilește ordinea în care se evaluează o expresie ce conține mai mulți operatori cu aceeași prioritate. Poate fi de la stânga la dreapta sau de la dreapta la stânga.

Atât prioritatea, cât și asocierea operatorilor poate fi modificată folosind paranteze rotunde ()

 

Funcții C++ predefinite
Funcții C++ predefinite

funcție este un ansamblu de instrucțiuni care prelucrează un set de date de intrare, numite parametri sau argumente și obține un rezultat. Când folosim funcțiile, acestea apar în expresii ca operand, valoarea operandului fiind de fapt rezultatul funcției, obținut în urma prelucrării valorilor curente ale parametrilor.

De exemplu, în C++ nu există nicio operație prin care să calculăm rădăcina pătrată a unui număr real, de exemplu 5–√5. Acest lucru poate fi realizat folosind funcția sqrt, prin apelul sqrt(5); acesta trebuie realizat într-o expresie, de exemplu o afișare:

cout << sqrt(5);

Despre funcția sqrt (și de fapt despre orice funcții), trebuie cunoscute niște informații specifice, pentru a ști cum și când o putem folosi:

  • numele funcției
  • numărul parametrilor
  • tipul parametrilor
  • tipul rezultatului

Aceste informații sunt precizate printr-un mecanism de declarare a funcției, numit prototip. De exemplu funcția sqrt determină rădăcina pătrată dintr- un număr real (nenegativ) iar rezultatul său este de asemenea număr real. Prototipul său este:

double sqrt(double);

Prototipurile funcțiilor din aceeași categorie sunt grupate într-un fișier header. Acesta trebuie inclus în programul nostru, prin directiva #include. De exemplu, dacă folosim operațiile de de citire/scriere vom include header-ul iostream, iar dacă folosim funcțiile matematice vom include header-ul cmath.

Câteva funcții cu caracter matematic

Denumire

Header

Prototip

Rezultat

abs

cstdlib

int double(int x)

Valoarea absolută a argumentului, |x||x|, număr întreg

abs

cmath

double double(double x)

Valoarea absolută a argumentului, |x||x|, număr real

sqrt

cmath

double sqrt(double x)

Rădăcina pătrată a argumentului, x−−√x

pow

cmath

double pow(double x, double y)

xyxy

sin

cmath

double sin(double x)

sinxsinx

cos

cmath

double cos(double x)

cosxcosx

tan

cmath

double tan(double x)

tanxtanx

floor

cmath

double floor(double x)

Cel mai mare întreg mai mic sau egal cu x

ceil

cmath

double ceil(double x)

Cel mai mic întreg mai mare sau egal cu x

 

Structura liniară
Structura liniară

Structura liniară este reprezentată de instrucțiuni care se execută la fel la fiecare executare a programului (sau a secvenței de program), indiferent care sunt valorile variabilelor cu care se lucrează.

Instrucțiunea expresie

Instrucțiunea expresie este cel mai frecvent folosit tip de instrucțiune dintr-un program C++. O expresie devine instrucțiune dacă este urmată de ;.

Sintaxa:

Expresie ;

Exemple:

view source

print ?

x = 2;

x ++;

cout << x;

Notă: În C++, operațiile de intrare/ieșire folosind stream-uri sunt și ele operații. În exemplul de mai sus, cout și x sunt operanzii, iar << este operatorul. Rezultat operației este o adresă de memorie, a stream-ului cout.

Instrucțiunea declarativă

Printr-o instrucțiune declarativă se pot declara identificatori de un anumit tip. Identificatorii pot fi variabile, dar vom vedea mai târziu că pot fi și funcții.

Sintaxa este:

Tip_de_date Lista_identificatori ;

unde

Tip_de_date poate fi orice tip C++ corect (intdouble, etc.), iar  Lista_identificatori este alcătuită din cel puțin un identificator. Dacă sunt mai mulți, se vor separa prin caracterul virgulă ,.

Exemple:

view source

print ?

int x, y , z;

double a;

Instrucțiunea compusă

Instrucțiunea compusă sau blocul este o grupare de declarații și instrucțiuni închise între acolade {}. Ele au fost introduse cu scopul de a folosi mai multe instrucțiuni acolo unde sintaxa cere o singură instrucțiune. Instrucţiunea compusă sau blocul sunt echivalente sintactic cu o singură instrucţiune.

Blocul determină și un domeniu de vizibilitate pentru identificatori. Mai precis, identificatorii declarați într-un bloc vor fi eliminați la terminarea acestuia.

După acolada închisă } nu se scrie ;!

Exemple:

view source

print ?

#include <iostream>

using namespace std;

int main(){

int x = 5;

{

int x = 7;

cout << x << endl; // se va afisa 7

}

cout << x << endl; // se va afisa 5

return 0;

}

Instrucțiunea return

O instrucţiune return permite ieşirea dintr-o funcţie și transmiterea controlului apelantului funcției. O funcţie poate returna valori apelantului său, prin intermediul unei instrucţiuni return.

Sintaxă:

return;

sau

return expresie ;

În primul caz valoarea returnată nu este definită. În al doilea caz valoarea expresiei este returnată apelantului funcţiei.

Instrucțiunea vidă

În numite situații, sintaxa limbajului C++ cere prezența unei instrucțiuni într-un anumit punct al programului, dar logica acestuia nu cere acest lucru. Aici intervine instrucțiunea vidă, cu următoarea sintaxă:

;

La întâlnirea instrucțiunii vide nu se va executa nicio acțiune.

Structura alternativă
Structura alternativă

În anumite situații, este necesară executarea unor instrucțiuni în cadrul unui program numai în anumite condiții. Numite și structuri de decizie, structurile alternative permit rezolvarea unor asemenea situații.

Instrucțiunea if

Instrucțiunea if este cea mai utilizată structură alternativă.

Sintaxa ei are două forme:

Varianta 1

if ( Expresie ) Instrucțiune1;
          else Instrucțiune2;

Varianta 2

if ( Expresie ) Instrucțiune1;

Mod de execuție:

Instrucțiunea if se execută în felul următor:

  • se evaluează Expresia
  • dacă valoarea ei este nenulă
    • se execută Instrucțiune1;
    • se continuă cu instrucțiunea care urmează după if
  • dacă valoare expresiei este nulă
    • dacă există clauza else
      • se execută Instrucțiune2;
      • se continuă cu instrucțiunea care urmează după if
    • dacă nu există clauza else, se continuă cu instrucțiunea care urmează după if

Observații:

  • Varianta 2 (fără clauza else) a instrucțiunii if este echivalentă cu următoarea, în care Instructiune2; este o instrucțiune vidă:

if ( Expresie ) Instrucțiune1;
else ;

  • Instrucțiune1; se execută numai dacă Expresie este nenulă (condiție adevărată). Instrucțiune2; se execută numai dacă Expresie este nulă (condiție falsă). În nicio situație nu se execută ambele instrucțiuni!
  • Instrucțiune1; și Instrucțiune2; pot fi orice fel de instrucțiuni, inclusiv instrucțiunea vidă și inclusiv o altă instrucțiune if.
  • Dacă logica programului o cere, Instrucțiune1; și/sau Instrucțiune2; pot fi instrucțiuni compuse, care să conțină mai multe instrucțiuni.
  • if testează valoarea numerică pentru Expresie, nu valoarea de adevăr. De aceea, scrierile:
    if(Expresie) ...
    și
    if(Expresie != 0) ...
    sunt echivalente. La fel și scrierile:
    if(! Expresie) ...
    și
    if(Expresie == 0) ...

Exemple:

Următoarea secvență decide dacă un număr întreg citi este par sau nu:

view source

print ?

int x;

cin >> x;

if(x % 2 == 0)

cout << x << " este par";

     else

cout << x << " este impar";

Următoarea secvență citește două numere n m și stabilește dacă m este divizor al lui  n, tratând cazul m=0. Este un exemplu de instrucțiuni if imbricate (una în alta).

view source

print ?

int n , m;

cin >> n >> m;

if(m == 0)

cout <<"nu putem imparti la zero!";

else

if(n % m == 0)

cout << m << " divide pe " << n << endl;

else

cout << m << " nu divide pe " << n << endl;

Următoarea secvență testează egalitatea cu 0 a unei expresii în forma scurtă, fără a folosi operatorii de egalitate:

view source

print ?

if( n )

cout << n << " nenul";

else

cout << n << " este nul";

Instrucțiunea switch

Instrucțiunea switch permite executarea unor instrucțiuni, în funcție de egalitatea unei expresii cu anumite valori numerice constante:

Sintaxa:

switch ( Expresie )
{
case Constanta_1: Grup_Instructiuni_1;

break;

case Constanta_2: Grup_Instructiuni_2;

break;

 …

case Constanta_N: Grup_Instructiuni_N;

 break;

default: Grup_Instructiuni_default;

break;
}

Mod de execuție:

  • se evaluează Expresie
  • dacă valoarea expresiei este egală cu una dintre valorile constante din clauzele case, se execută instrucțiunile din grupul de instrucțiuni corespunzător, apoi se trece la instrucțiunea de după switch
  • dacă valoarea expresiei nu este egală cu niciuna dintre valorile constante din clauzele case, se verifică existența clausei default;
    • dacă există clauza default, se execută instrucțiunile din grupul de instrucțiuni corespunzător clauzei default, apoi se trece la instrucțiunea de după switch
    • dacă nu există clauza default, se trece la instrucțiunea de după switch

Observații:

  • Valorile din clauzele case trebuie să fie constante întregi.
  • În fiecare grup de instrucțiuni pot să apară oricâte instrucțiuni, de orice tip, eventual niciuna. Nu este necesară utilizarea instrucțiunii compuse.
  • Prezență instrucțiunii break; nu este obligatorie, dar lipsa ei modifică modul de execuție al instrucțiunii. În exemplul de mai jos:

view source

print ?

switch(n)

{

case 1:

cout << "n = 1\n";

case 2:

cout << "n = 2\n";

break;

case 3:

cout << "n = 3\n";

break;

}

dacă valoarea lui n este 1, se va afișa:

n = 1

n = 2

Mai exact, se execută toate instrucțiunile de la clauza case corespunzătoare valorii expresiei până la prima instrucțiune break; întâlnită.

Exemple:

În exemplul de mai jos se afișează (aproximativ) numele zilei din săptămână în funcție de numărul ei.

view source

print ?

#include <iostream>

using namespace std;

int main(){

int zi;

cin >> zi;

switch(zi)

{

case 1:

cout << "Luni\n"; break;

case 2:

cout << "Marti\n"; break;

case 3:

cout << "Miercuri\n"; break;

case 4:

cout << "Joi\n"; break;

case 5:

cout << "Vineri\n"; break;

case 6:

case 7:

cout << "WEEKEND!!!\n"; break;

default:

cout << "Numarul zilei este incorect\n"; break;

}

return 0;

}

Observați mai sus că în clauza case 6: nu avem instrucțiuni. Dacă variabila zi are valoarea 6 sau 7 se va afișa:

 

Structuri repetitive
Structuri repetitive

Structurile repetitive execută o instrucțiune de un anumit număr de ori, sau cât timp o condiție este adevărată. Se mai numesc și bucle sau cicluri.

Structurile repetitive pot fi:

  • cu număr cunoscut de pași (iterații) – se cunoaște de la început de câte ori se va execută instrucțiunea
  • cu număr necunoscut de pași (iterații). Instrucțiunea se execută cât timp o condiție este adevărată. La fiecare pas se va evalua condiția, iar dacă aceasta este adevărată se va executa instrucțiunea.

Structurile repetitive cu număr necunoscut de pași pot fi:

  • cu test inițial: mai întâi se evaluează condiția; dacă este adevărată se execută instrucțiunea și procesul se reia.
  • cu test final: mai întâi se execută instrucțiunea, apoi se evaluează condiția; Dacă este adevărată, procesul se reia.

Instrucțiunea care se execută în mod repetat poartă numele de corp al structurii repetitivecorp al cicluluicorp al buclei și de foarte multe ori este o instrucțiune compusă.

Instrucțiunea while

Instrucțiunea while este o structură repetitivă cu număr necunoscut de pași și test inițial.

Sintaxa:

while (Expresie) Instructiune;

Mod de execuție:

  1. Se evaluează Expresie
  2. Dacă Expresie este nenulă
    • Se execută Instructiune;
    • Se reia pasul 1.
  3. Dacă Expresie este nulă, se trece la instrucțiunea de după while.

Observații

  • Instructiune; se execută cât timp Expresie este nenulă – condiție adevărată.
  • Dacă Expresie este de început vidă, Instructiune; nu se execută deloc.
  • Instructiune; poate fi orice fel de instrucțiune, dar una singură. Dacă sunt necesare mai multe instrucțiuni, se va folosi instrucțiunea compusă.
  • Este necesar ca cel puțin o variabilă care apare în Expresie să-și modifice valoarea în Instructiune;. Altfel se obține o buclă infinită.

Exemplu:

Următorul program citește valoarea variabilei n și calculează suma primelor n numere naturale. Rulați-l analizând rezultatul pentru diverse valori ale lui n, inclusiv 0.

view source

print ?

#include <iostream>

using namespace std;

int main ()

{

int n;

cin >> n;

int S = 0;

int i = 1;

while(i <= n)

{

S += i;

i ++;

}

cout << S << endl;

return 0;

}

Instrucțiunea do ... while

Instrucțiunea do ... while este o structură repetitivă cu număr necunoscut de pași și test final.

Sintaxa:

do Instructiune;
while ( Expresie );

Mod de execuție:

  1. Se execută Instructiune;
  2. Se evaluează Expresie
  3. Dacă Expresie este nenulă, se reia pasul 1.
  4. Dacă Expresie este nulă, se trece la instrucțiunea de după do ... while.

Observații

  • Instructiune; se execută cât timp Expresie este nenulă – condiție adevărată.
  • Dacă Expresie este de început vidă, Instructiune; se execută exact o dată. În orice situație, Instructiune se execută cel puțin o dată.
  • Instructiune; poate fi orice fel de instrucțiune, dar una singură. Dacă sunt necesare mai multe instrucțiuni, se va folosi instrucțiunea compusă.
  • Este necesar ca cel puțin o variabilă care apare în Expresie să-și modifice valoarea în Instructiune;. Altfel se obține o buclă infinită.

Exemplu:

Următorul program citește valoarea variabilei n și calculează suma primelor n numere naturale. Rulați-l analizând rezultatul pentru diverse valori ale lui n, inclusiv 0.

#include <iostream>

using namespace std;

int main ()

{

int n;

cin >> n;

int S = 0;

int i = 1;

do

{

S += i;

i ++;

}

while(i <= n);

cout << S << endl;

return 0;

}

Instrucțiunea for

Instrucțiunea for este o structură repetitivă cu număr necunoscut de pași și test inițial, echivalentă cu while.

Sintaxa:

for ( Expresie_de_Initializare ; Expresie_de_Testare ; Expresie_de_Continuare ) Instructiune;

Mod de execuție:

  1. Se evaluează Expresie_de_Initializare
  2. Se evaluează Expresie_de_Testare
  3. Dacă Expresie_de_Testare este nenulă:
    • Se execută Instructiune;.
    • Se evaluează Expresie_de_Continuare.
    • Se revine la pasul 2.
  4. Dacă Expresie_de_Testare este nulă, se trece la instrucțiunea de după for.

Observații

  • Instrucțiunea for este echivalentă cu instrucțiunea while. Sintaxa descrisă mai sus este echivalentă cu:

Expresie_de_Initializare ;
while ( Expresie_de_Testare )
{
Instructiune;

  Expresie_de_Continuare ;
}

  • Instructiune; se execută cât timp Expresie_de_Testare este nenulă – condiție adevărată.
  • Dacă Expresie_de_Testare este de început vidă, Instructiune; nu se execută deloc, iar Expresie_de_Continuare nu se mai evaluează.
  • Instructiune; poate fi orice fel de instrucțiune, dar una singură. Dacă sunt necesare mai multe instrucțiuni, se va folosi instrucțiunea compusă.
  • Este necesar ca cel puțin o variabilă care apare în Expresie_de_Testare să-și modifice valoarea în Instructiune; sau la evalurea Expresiei_de_Continuare. Altfel se obține o buclă infinită.
  • Cele trei expresii, de_Initializare_de_Testare și _de_Continuare sunt separate prin caracterul ; – obligatoriu!
  • Oricare dintre cele trei expresii, de_Initializare_de_Testare și _de_Continuare, eventual toate, poate să lipsească. În acest caz avem expresii vide. Dacă Expresie_de_Testare este vidă, rezultatul său este nenul!
  • Expresie_de_Initializare se execută o singură dată. Poate să conțină și declararea unor variabile. În acest caz, variabilele vor exista numai în instrucțiunea for.

Exemplu:

Următorul program citește valoarea variabilei n și calculează suma primelor n numere naturale. Rulați-l analizând rezultatul pentru diverse valori ale lui n, inclusiv 0.

#include <iostream>

using namespace std;

int main ()

{

int n;

cin >> n;

int S = 0;

for(int i = 1; i <= n ; i ++)

S += i;

cout << S << endl;

return 0;

}

Instrucțiunea break

Instrucțiunea break are sens și poate fi folosită numai în instrucțiunile switchwhiledo ... while și break.

Sintaxa:

break ;

Mod de execuție

Am văzut semnificația instrucțiunii break atunci când apare în instrucțiunea switch.

Efectul instrucțiunii break când apare într-o instrucțiune repetitivă este întreruperea execuției acesteia și trecerea la instrucțiunea care urmează celei repetitive.

Exemplu:

?

#include <iostream>

using namespace std;

int main ()

{

int n;

cin >> n;

int S = 0;

for(int i = 1; i <= n ; i ++)

{

S += i;

if(i == 5)

break;

}

cout << S << endl;

return 0;

}

  • Dacă valoarea lui n este cel mult 5, se va afișa suma numerelor de la 1 la n.
  • Dacă n >= 5 se va afișa întotdeauna 15, deoarece execuția lui for se întrerupe, datorită lui break, când i este 5.
a X-a A
Tablouri unidimensionale
Tablouri unidimensionale

Un tablou unidimensional se declară în C++ astfel:

tipDeBază denumire[Dimensiune];

de exemplu:

int X[10];

Ne putem imagina tabloul declarat mai sus astfel (valorile elementelor sunt aleatorii):

Spunem că fiecare element are un indice. Indicii unui tablou sunt între 0 și Dimensiune-1, deci în exemplul nostru între 0 și 9.

Observație: Nu este necesar la declarare tabloul să fie singura variabilă declarată în instrucțiunea declarativă. Următoarea instrucțiune este corectă sintactic.

int n, X[10], m, Y[100], p;

Care sunt valorile inițiale ale elementelor tabloului? Regula este aceeași ca pentru alte variabile:

  • elementele unui tablou declarat global (în afara oricărei funcții) sunt inițializate cu 0;
  • elementele unui tablou declarat local (în interiorul unei funcții) sunt inițializate cu valori aleatorii. Faptul că anumite implementări la compilatorului C++ inițializează și variabilele local cu 0 nu este o regulă, ea nu este garantată de standardul C++.

Referirea unui element

Referirea unui element se face prin operatorul de indexare[], care are prioritate maximă. De exemplu:

X[0], X[5], X[i]

Aici X este identificatorul tabloului (denumirea), iar 05 sau i sunt indicii. Este necesar ca programatorul (adică TU!) să se asigure că valoarea indicelui se găsește în intervalul potrivit pentru tabloul dat (în exemplul nostru între 0 și 9).

Un element al tabloului, referit prin indice este tratat ca o variabilă oarecare de tipul stabilit la declarare. Următoarele expresii/instrucțiuni sunt corecte:

int X[10];
cin >> X[0];
X[0] = 17;
cout << X[0];
cout << X[0] / 5;

Observație: C++ nu verifică dacă valoarea indicelui face parte din intervalul stabilit prin declararea tabloului. Dacă indicele are o valoare în afara acestui interval, comportamentul programului este impredictibil.

Este necesar ca programatorul să se asigure că valorile indicilor sunt corecte.

Dimensiunea unui tablou unidimensional

La declararea unui tablou unidimensional se precizează o dimensiune pentru acesta. Aceasta reprezintă o dimensiune fizică a tabloului – numărul maxim de elemente pe care le-ar putea avea acesta, conform restricțiilor problemei.

De cele mai multe ori, în program nu se folosesc toate elementele tabloului. De regulă, enunțul unei probleme cu tablouri este:

“Se citește un vector cu n elemente, numere …. Să se …..”

Este deci necesar ca în program să avem o variabilă – de regulă se notează n, care să reprezinte dimensiunea logică a tabloului – numărul de elemente ale tabloului care la un moment dat sunt utilizate în program.

Parcurgerea unui tablou unidimensional

Parcurgerea unui tablou reprezintă referirea fiecărui element al tabloului, într-o anumită ordine. Referirea elementului se face prin intermediul indicelui, cu ajutorul operatorului de indexare.

Următorul exemplu declară un tablou cu 100 de elemente și memorează în primele n elemente ale tabloului valoarea 1. După cum știm deja, n trebuie să respecte relația n<=100. În caz contrar, comportamentul programului devine impredictibil – foarte probabil execuția sa va fi oprită de sistemul de operare.

int X[100], n;
//n = .... ;
for(int i = 0 ; i < n ; i ++)
    X[i] = 1;

De regulă, parcurgerea tabloului se face în ordinea crescătoare a indicelor, de la 0 la n-1. Făcând o analogie cu axa numerelor, putem spune că parcurgerea se face de la stânga spre dreapta. Tabloul poate fi parcurs și de la dreapta la stânga, adică în ordinea descrescătoare a indicilor, de la n-1 la 0:

for(int i = n - 1 ; i >= 0 ; i 
--)
    X[i] = 1;

Citirea unui vector

int 
X[100], n;

De fapt, în cele mai multe cazuri nu se poate face citirea unui tablou unidimensional (vector), adică

cin >> X;

Instrucțiunea de mai sus duce de regulă la eroare de sintaxă. În schimb, se pot citi elementele tabloului, în ordine, cu ajutorul parcurgerii:

cin >> n;
for(int i = 0 ; i < n ; i ++)
    cin >> X[i];

Afișarea unui vector

int 
X[100], n;

La fel ca în cazul citirii, în cele mai multe cazuri nu se poate face nici afișarea unui vector, adică

cout <<  X;

Spre deosebire de citire, afișarea unui tablou cu ajutorul operatorului de inserție << nu duce la eroare de sintaxă, însă nu se vor afișa elementele tabloului, ci o adresă (de exemplu 0x7ffc9711bcd0), reprezentând adresa primului element al tabloului. Elementele tabloului se pot afișa prin parcurgere, în ordinea dorită:

for(int i = 0 ; i < n ; i ++)
    cout << X[i] << ' ';

sau

for(int i = n - 1 ; i >= 0 ; i --)
    cout << X[i] << ' ';

Indexare de la 0 și indexare de la 1

Orice tablou C++ are fizic elementele indexate de la 0 la Dimensiune-1. De exemplu, dacă avem nevoie de un tablou cu n≤100 elemente întregi, îl vom declara:

int V[100];

iar elementele vor avea indici între 0 și 99. Astfel, primul element al tabloului este V[0], al doilea este V[1], …, ultimul este V[n-1].

Dacă dorim, putem ignora elementul V[0]  (pur și simplu nu îl folosim, el există însă în continuare), și atunci toate operațiile (citire, afișare, parcurgere) vor utiliza elementele V[1] (primul element), V[1] (al doilea element), …, V[n]  (ultimul element).

Trebuie de asemenea să tratăm cu atenție declararea tabloului, dimensiunea fizică trebuind să fie mai mare cu 1 decât valoarea precizată în problemă. Dacă de exemplu, n≤100, declararea va fi:

int V[101];

Acest lucru este necesar pentru a exista elementul V[100].

Tablouri unidimensionale / Ștergeri și inserări de elemente
Tablouri unidimensionale / Ștergeri și inserări de elemente

Operațiile de ștergere a unui element dintr-un vector, sau de inserare a unui element nou într-un vector sunt frecvente în practică: avem o listă cu elevii dintr-o clasă și un elev pleacă, sau un alt elev vine. Cum actualizăm lista?

Ștergerea

Să considerăm următoarea problemă :

Se dă un șir X cu n elemente întregi și un număr p. Să se șteargă din șirul X elementul aflat pe poziția p.

Să considerăm următorul vector cu n=10 elemente și p=4.

Dorim să eliminăm din vector elementul de indice 4, cel cu valoarea X[4] = 34. În urma eliminării vectorul trebuie să arate astfel:

Cum procedăm?

  • elementele cu indici p+1p+2, …, n-1 se mută spre stânga cu o poziție
  • dimensiunea n a tabloului se micșorează cu 1

Ștergerea se face astfel:

for(int i 
= p ; i < n - 1; i ++)
    X[i] = X[i+1];
n --;

Ștergerea mai multor valori din șir

Considerăm următoarea problemă :

Considerăm un șir X cu n elemente întregi. Să se elimine din șir toate elementele pare.

Rezolvare:

  • parcurgem șirul și analizăm elementul curent X[p];
  • dacă elementul X[p] este par, aplicăm algoritmul de mai sus pentru ștergerea elementului cu indicele p.

Este necesar să realizăm cu atenție parcurgerea. Următoarea secvență:

for (int p = 0 ; p < n ; p ++)
    if(X[p] % 2 == 0) {
        for(int i = p ; i < n - 1; i ++)
            X[i] = X[i+1];
        n --;
    }

nu funcționează corect dacă în șir sunt elemente consecutive cu proprietatea dorită (de a fi pare), deoarece al doilea element par nu va fi analizat, deci nu va fi eliminat din șir. O soluție bună este să parcurgem elementele în ordine inversă:

for (int p = n - 1 ; p >= 0 ; p --)
    if(X[p] % 2 == 0) {
        for(int i = p ; i < n - 1; i ++)
            X[i] = X[i+1];
        n --;
    }

Adăugarea unui element într-un vector

Adăugarea unui element într-un vector înseamnă mărirea dimensiunii logice n a vectorului și memorarea în ultimul element a noii valori. Următoarele secvențe adaugă o valoare într-un vector indexat de la 0.

X[n] = val;
n ++;

sau, mai condensat:

X[n++] = val;

Următoarele secvențe adaugă o valoare într-un vector indexat de la 1.

n ++;
X[n] = val;

sau, mai condensat:

X[++n] = val;

Inserarea unui element într-un vector

Considerăm următoarea problemă :

Se dă un șir X cu n elemente întregi, o valoare întreagă val și un număr p. Să se insereze pe poziția p în șir valoarea val.

Similar cu algoritmul de ștergere a unui element dintr-un vector, și cel de inserare presupune modificarea elementelor din dreapta lui X[p]. De data aceasta elementele vor fi mutate spre dreapta, începând cu ultimul. Elementul X[p] se înlocuiește cu noua valoare, iar dimensiunea logică a vectorului crește, fără a depăși însă dimensiunea fizică.

for(int i = n - 1 ; i >= p ; i --)
    X[i+1] = X[i];
X[p] = val;
n ++;

Inserarea mai multor valori în șir

Se dă un vector cu n elemente naturale. Să se insereze după fiecare element par, jumătatea sa.

Principial, procedăm astfel:

  • parcurgem șirul
  • dacă elementul curent X[p] este par
    • inserăm pe poziția p+1 valoarea X[p]/2

Dacă parcurgerea se face de la stânga spre dreapta, există riscul unor inserări suplimentare, ca în acest exemplu:

Tablouri unidimensionale / Verificarea unor proprietăți
Tablouri unidimensionale / Verificarea unor proprietăți

Se pot formula foarte multe probleme în care se cere să se verifice dacă elementele unui vector respectă diverse proprietăți, dar toate se pot reduce în cele din urmă la una dintre următoarele:

  • să se verifice dacă toate elementele unui vector dat respectă o anumită regulă;
  • să se verifice dacă într-un vector dat există elemente care respectă o anumită regulă.

O rezolvare ar putea fi să numărăm elementele care respectă regula. La final:

  • dacă numărul de elemente care respectă regula este egal cu numărul totale de elemente din vector, atunci toate elementele respectă regula
  • dacă numărul de elemente care respectă regula este nenul, atunci există elemente care respectă regula.

Altă rezolvare, mai bună, ne permite să oprim parcurgerea când suntem siguri că vectorul respectă sau nu proprietatea dorită. Vom folosi o variabilă booleană (cu valori true sau false1 sau 0, …):

  • dacă la final variabila are valoarea true, atunci vectorul respectă regula,
  • dacă la final variabila are valoare false, atunci vectorul nu respectă regula.

Toate elementele respectă regula

  • inițializăm variabila cu true
  • parcurgem vectorul
    • dacă elementul curent nu respectă regula dorită
      • variabila devine false
      • parcurgerea vectorului poate opri

Secvențe C++:

bool OK = true;
for(int i = 0 ; i < n && OK ; i ++)
    if(X[i] - nu respectă regula)
        OK = false;

sau

bool OK = true;
for(int i = 0 ; i < n ; i ++)
    if(X[i] - nu respectă regula)
    {
        OK = false;
        break;
    }

sau

bool OK = true;
int i = 0;
while(i < n && OK)
{
    if(X[i] - nu respectă regula)
        OK = false;
    else
        i ++;
}

Există elemente care respectă regula

  • inițializăm variabila cu false
  • parcurgem vectorul
    • dacă elementul curent respectă regula dorită
      • variabila devine true
      • parcurgerea vectorului poate opri

Secvențe C++:

bool OK = false;
for(int i = 0 ; i < n && !OK ; i ++)
    if(X[i] - respectă regula)
        OK = true;

sau

bool OK = false;
for(int i = 0 ; i < n ; i ++)
    if(X[i] - respectă regula)
    {
        OK = true;
        break;
    }

sau

bool OK = false;
int i = 0;
while(i < n && !OK)
{
    if(X[i] - respectă regula)
        OK = true;
    else
        i ++;
}
Tablouri unidimensionale / Sortarea tablourilor
Tablouri unidimensionale / Sortarea tablourilor

Sortarea unui tablou reprezintă o rearanjare a elementelor astfel încât valorile acestora să fie într-o anumită ordine. De regulă ordinea cerută este cea crescătoare sau descrescătoare.

Există numeroase metode de sortare, conform Wikipedia .

Din punct de vedere al eficienței, avem:

algoritmi mai puţin eficienţi

    1. metoda bulelor
    2. sortarea prin selecție
    3. sortarea prin inserție
    4. metoda minimului
    5. etc.
  • algoritmi eficienți,
  1. QuickSort
  2. MergeSort
  3. HeapSort
Ordonarea unui sir - Metoda "bulelor" (Bubble Sort)
Ordonarea unui sir - Metoda "bulelor" (Bubble Sort)

Se citeşte un sir de numere întregi si se cere sa se ordoneze elementele acestui sir crescâtor

A: 7 , 4 , 5 , 3 , 9 , 1    (înainte)

A: 1 , 3 , 4 , 5 , 7 , 9     (după)

Ideea care stă la baza acestei metode este următoarea: se parcurge sirul, comparându-se elementele consecutive din şir două câte două. Dacă acestea nu sunt în relatia dorita ("³" sau "£") se schimba valorile elemntelor.

Printr-o parcurgere a şirului şi efectuarea comparaţilor obţinem:            


 4, 7, 5, 3, 9, 1

 4, 5, 7, 3, 9, 1

 4, 5, 3, 7, 9, 1

 4, 5, 3, 7, 1, 9

 Comparăm  A[i] > A[i+1]   dacă da, atunci cele două valori se interschimbă.

Se observă că, după o parcurgere a vectorului, şirul obţinut nu este ordonat deci este necesară reluarea procedeului descris anterior.


int n, 
v[100];
//citire v[] cu n elemente
int sortat = 0;
while (sortat==0)
{
  sortat = 1;
  for(int i = 0 ; i < n ; i ++)
    if(v[i] > v[i+1])
    {
      int aux = v[i];
      v[i] = v[i+1];
      v[i+1] = aux;
      sortat = 0;
    }
}


Video Bubble Sort

Sortarea prin selecţie sau Select Sort
Sortarea prin selecţie sau Select Sort
Algoritmul constă în alegerea celui mai mic element dintr-un vector şi aşezarea lui pe prima poziţie, repetată pentru şiruri din ce în ce mai scurte. Metoda necesită un timp de lucru care depinde  de numărul de elemente din vector, iar algoritmul metodei se reprezintă prin structuri repetitive cu
număr cunoscut de paşi.
În cazul unui vector sortat crescător, primul element (cu indice 1) este cel mai mic dintre cele cu indici de la 1 la n, cel de-al doilea este cel mai mic dintre cele cu indici de la 2 la n ş.a.m.d. Să considerăm un vector în care elementele cu indici de la 1 la i-1 sunt deja sortate. Pentru a continua procesul de sortare, dintre elementele rămase (cu indici de la i până la n) trebuie găsit cel mai mic (cu indice isel) şi adus în poziţia i.


Pas 1 : Se citeste vectorul

Pas 2 : Se parcurge vecorul de la prima pana la penultima pozitie ( 1 => n-1 )

Pas 2.1 : Se considera valoarea minima cea din pozitia i si se pastreaza valoarea intr-o variabila k

Pas 2.2 : Parcurgem de la elementul urmatorul pana la sfarsit si cautam valoarea minima

Pas 2.2.1 : Pastram valoarea nou gasita in variabila min si schimbam pozitia.

Pas 2.3 : Interschimbam cele 2 valori ( i cu k )

Pas 3 : Se reia de la pasul 2 pana la penultima valoare

Pas 4 : Se afiseaza vectorul



#include <iostream>
using namespace std;
int main()
{
int v[100],i,n,min,k,aux,j;
cout<<"n= ";cin>>n;
for(i=1;i<=n;i++)
{             
   cout<<"v["<<i<<"]= ";
   cin>>v[i];
}
for(i=1;i<=n-1;i++)
{
                min=v[i];
                k=i;
                for(j=i+1;j<=n;j++)
                    if(v[j]<min)
                    {
                       min=v[j];
                       k=j;
                    }
                aux=v[i];
                v[i]=v[k];
                v[k]=aux;
}
cout<<"Vectorul sortat este: ";
for(i=1;i<=n;i++)
      cout<<v[i]<<" ";
return 0;
}


Video Select Sort

Sortarea prin inserție (Insertion Sort)
Sortarea prin inserție (Insertion Sort)

Sortarea prin inserție  (Insertion Sort) se bazează pe următoarea idee:

  • fie un vector  X[] cu n elemente;
  • dacă secvența cu indici 01, …,  i-1 este ordonată, atunci putem insera elementul  X[i] în această secvență astfel încât să fie ordonată secvența cu indici 01, …, i-1i.
  • luăm pe rând fiecare element X[i] și îl inserăm în secvența din stânga sa
  • la final întreg vectorul va fi ordonat

O reprezentare a algoritmului este:

  • parcurgem vectorul cu indicele i
    • inserăm pe X[i] în secvența din stânga sa; pentru inserare se mută unele elemente din secvență spre dreapta

Exemplu: Să ordonăm următorul vector, în care n=5:

sortarea prin inserție presupune următoarele transformări ale vectorului:

int n, X[100];
//citire X[] cu n elemente
for(int i = 1 ; i < n ; i ++)
{
    int x = a[i];
    int p = i - 1;
    while(p >= 0 && a[p] > x)
      {  a[p + 1] = a[p]; p --; }
    a[p + 1] = x;
}
Video Insertion Sort
Sortare stabilind poziţia definitivă prin numărare
Sortare stabilind poziţia definitivă prin numărare

Sortare stabilind  pozia  definitivă prin   numărare

Această metodă constă în construirea unui nou tablou B care are aceeaşi dimensiune ca şi tabloul

în care depunem elementele din  A, ordonate crescător.

Vom lua fiecare element şi îl vom compara cu fiecare  alt element din şir pentru a  putea reţine în variabila k  numărul elementelor care sunt mai mici decât elementul considerat. Astfel, vom afla poziţia pe care trebuiesă-l punem pe acesta în şirul B. Da în problemă avem nevoie de  şirul ordonat tot în tabloul A, vom copia  în A întreg tabloul B.

  Subalgoritm Numărare(n,A

   1: pentru i =1,n execută

2:      k = 0

3:      pentru j=1,n execută

4:          dacă (A[i] < A[j]) atunci

5:                k = k +  1           { numărăm câte elemente sunt mai mici det A[i] 

              sfarsit daca

          sfarsit pentru

6:          B[k1] = A [i]          { pe A[i] îl punem pe poziţia co respunzătoare din  B 

     sfarsit pentru

7:                A = B        copiem peste şirul A întreg şirul B }

 Exemplu

 Fie tabloul  A cu 4 ele mente: 7, 2, 3, –1.

i

j

Relaţia

k

bk +  1

1

1

=  j

0

 

1

2

7 > 2

1

 

1

3

7 >  3

2

 

1

4

7 >  –1

3

b4 =  7

2

1

2 <  7

0

 

2

2

= j

0

 

2

3

2 < 3

0

 

2

4

2 >  –1

1

b2 = 2

3

1

3 < 7

0

 

3

2

3 >  2

1

 

3

3

=  j

1

 

3

4

3 > –1

2

b3 =  3

4

1

–1 <  7

0

 

4

2

–1 < 2

0

 

4

3

–1 <  3

0

 

4

4

= j

0

b1 = –1

Algoritmul funcţionea în această formă dacă elementele tabloului A sunt distincte altfel în B vor răne elemente necopiate din A, deoarece două elemente egale se vor copia în B pe  acee aşi poziţie. 

Tablouri unidimensionale / Interclasarea tablourilor
Tablouri unidimensionale / Interclasarea tablourilor

Considerăm două tablouri unidimensionale cu elemente numere întregi ordonate crescător. Se dorește construirea unui alt tablou, care să conțină valorile din cele două tablouri, în ordine.

O soluție foarte eficientă este interclasarea:

  • considerăm două tablouri, cu n, respectiv m elemente, ordonate crescător
  • cele două tablouri se parcurg concomitent;
  • se alege valoarea mai mică dintre cele două elemente curente
    • se adaugă în al treilea tablou
    • se avansează numai în tabloul din care am ales valoarea de adăugat
  • parcurgerea unuia dintre cele două tablouri se încheie
  • toate elementele din celălalt tablou, neparcurse încă, sunt adăugate în tabloul destinație
  • tabloul destinație are p = n + m elemente

int n,a[100000], m , b[100000], p, c[200000];

//citire a[] cu n elemente
//citire b[] cu m elemente

int i = 0 , j = 0;
p = 0;
while(i < n && j < m)
    if(a[i] < b[j])
        c[p ++] = a[i ++];
    else
        c[p ++] = b[j ++];
while(i < n)
    c[p ++] = a[i ++];
while(j < m)
    c[p ++] = b[j ++];
Observație: Doar una dintre instrucțiunile while(i < n)... și while(j < m)... se va 
executa, deoarece exact una dintre condițiile i < n și j < m este adevărată. În prima 
structură repetitivă, la fiecare pas, doar una dintre variabilele i și j se mărește, deci la final una dintre 
condiții este adevărată și una este falsă.
a XI-a A
Tablouri bidimensionale (matrice)
Tablouri bidimensionale (matrice)

Tablouri bidimensionale – matrice

 Un tablou cu două dimensiuni se numeşte matrice.

Declarare
                     int a[50][100], n, m, i, j;
S-a declarat:
– o matrice cu maxim 50 de linii şi 100 de coloane, numerotarea se face de la 0 sau de la 1.
– n- numarul efectiv de linii;
– m-numarul efectiv de coloane;
– i- contor pentru linie
– j- contor pentru coloane
Citire:
                    for(i=1; i<=n;i++)
                       for(j=1;j<=m;j++)
                        {   cout<<”a[“<<i<<”][“<<j<<”]=”;

cin>>a[i][j];

  }

Afisare:
                  for(i=1;i<=n;i++)
                         {
                           for(j=1;j<=j;j++)
                                 cout<<a[i][j]<<” “;
                                       cout<<“ ”;
                          }

 Matrice patratica

Într-o matrice pătratică numarul de linii= numarul de coloane (n=m).

Într-o matrice pătratică avem:

  •                                 Diagonala principala elementele a[i][i], cu i=1,n        sau         a[i][i], cu i=0,n-1
  •                                 Diagonala secundara elementele a[i][n-i+1], i=1,n        sau         a[i][n-i-1], i=0,n-1

Zonele determinate de diagonale:
I.
               Pe diagonala principală i=j
               Sub diagonala principala: i>j
               Deasupra diagonalei principale: i<j
II.
               Pe diagonala secundară j=n-i+1
               Sub diagonala secundara: j>n-i+1
               Deasupra diagonalei secundare:j<n-i+1


Siruri de caractere (citire, tiparire)
Siruri de caractere (citire, tiparire)

ȘIRURI DE CARACTERE

Limbajul C/C++ permite initializarea unui tablou de caractere printr-o constanta sir, care include automat caracterul  NULL

Caracterul NULL '\0' se adauga atutomat dupa ultimul caracter din sir.

'\n' - caracterul linie noua     deci cout<<endl;    chivalent cu  cout<<'\n';

 

Exemplu :

char vect[11]=”calculator”;

char vect[]=”calculator”; (compilatorul face calculul numarului de octeti necesari)

char vect[100]=”calculator”; (s-au rezervat mai multi octeti decat era necesar)

1               Sirurile de caractere sunt de fapt tablouri de caractere, care au ca ultim element un terminator de sir, caracterul NULL. Exemplu:

char tc[5] = {’a’, ’b’, ’c’, ’d’, ’e’};     // tablou de caractere

char sc[5] = {’a’, ’b’, ’c’, ’d’, ’\0’};    // sir de caractere cu elementele abcd Ultima initializare este echivalenta cu:

char sc[5] = ”abcd”;      //sau char sc[] = ”abcd”; char sc1[5] = ”abcd”;

char s[10];

cout<<sc<<endln;    //afiseaza abcd
cout<<tc<<endl;     //eroare: tabloul de caractere nu contine terminatorul de sir, deci nu poate fi afisat ca sir
cout<<s<<endl;         // eroare: tablou neinitializat 
cout<<sc1[0];        // afiseaza primul  caracter din  sirul sc1 
cout<<sc1[2];       / / afiseaza al treilea element din sirul  sc1
sc1[1]=’K’;             // elementului din sir de indice 1 i se atribuie valoarea K;

 

CITIREA / AFISAREA SIRURILOR DE CARACTERE

Sirurile de caractere pot fi initializate inca de la declarare sau citite pe parcursul programului.

a.          Citirea unui sir de caractere se poate face ca citirea oricarui tablou, intr-un for, caracter cu caracter

(desi nu este recomandata). In acest caz, terminatorul de sir nu este memorat automat, el trebuie pus explicit dupa ultimul caracter din sir.

 

Exemplu:

char c[20];

for(int i=0;i<=5;i++)

cin>>c[i];

cout<<c<<endl; //se va afisa sirul format din cele 6 caractere, urmat de caractere reziduale”,

//initializate implicit la compilare, din cauza ca n-a fost pus terminatorul de sir

c[6]='\0';

cout<<c<<endl; //a fost pus terminatorul de sir, deci sirul va fi afisat corect

  b.      Se poate face pur si simplu, folosind cin>>. Caracterul nul este adaugat automat. Dezavantajul este ca in acest fel nu se pot citi siruri care contin mai multe cuvinte separate prin spatii. Citirea sirului se sfarseste la intalnirea primului caracter blank (de ex, daca se citeste ora de informatica, variabila c va retine numai ora”).

Exemplu char c[30]; cin>>c; cout<<c;

  c.    Se poate folosi o functie speciala pentru citirea sirurilor de caractere, inclusa in bibliotecstring.h (varianta recomandata).

Exemplu

char a[30],x;int  nr;             cin.get(a,nr,x );

Functia cin.get citeste un sir de caractere sau pana cand au fost citite nr-1 caractere, sau daca s-a

intalnit caracterul x. Al treilea parametru poate lipsi, caz in care el este implicit caracterul \n (new line). Sunt citite si caracterele albe, caracterul NULL este inserat automat iar caracterul transmis ca ultim parametru nu este inserat in sir.

E xemplu

char a[30];

cin.get(a,5,’s’);         //daca se citeste sirul maimuta, variabila a va retine  maim” 

cin.get(a,15,’s’);  //daca se citeste sirul maimuta, variabila a va retine  maimuta” 

cin.get(a,15,’t’);   //daca se citeste sirul maimuta, variabila a va retine  maimu”

2                   cin.get(a,4,’t’);         //daca se citeste sirul maimuta, variabila a va retine  mai

cin.get(a,10);      //daca se citeste sirul maimuta, variabila a va retine  maimuta”

                   Functia cin.get( ) fara parametri are rolul de a citi un caracter (alb sau nu).

 

Observatie : In cazul utilizarii repetate a functiei cin.get(a,nr,x), dupa fiecare folosire trebuie citit caracterul de la sfarsitul fiecarui sir , adica ‟\n‟ (in caz contrar, acest caracter va fi incarcat la inceputul urmatorului sir, a carui citire se termina la caracterul Enter, deci citirea celui de-al doilea sir se termina inainte de a incepe, iar al doilea sir va fi sirul vid). Aceasta citire a caracterului \n‟ se realizeaza folosind cin.get() fara parametri.

 

Exemplu

char a[30],b[30];

cin.get(a,15);

cin.get(b,10);

Daca se incearca citirea sirurilor sarbatoare si vacanta, se observa ca a=sarbatoare, b=

(nici nu apucam sa citim sirul b). Varianta corecta este:

cin.get(a,15);

cin.get();

cin.get(b,10);

 

Afisarea unui sir de caractere se face folosind cout.

                                                     cout<<a;

Se poate afisa si caracter cu caracter, ca in cazul tablourilor, dar aceasta varianta nu este recomandata.

FUNCTII PENTRU OPERATII CU SIRURI DE CARACTERE
FUNCTII PENTRU OPERATII CU SIRURI DE CARACTERE

Functiile pentru operatii cu siruri se gasesc in header-ul .

Ø Functia strlen

int strlen (nume_sir); returneaza lungimea efectiva a unui sir (fara a numara terminatorul de sir).

Ex emplu:

char a[50]=”ora de informatica”;  =>   strlen(a) = 18

Ø Functia strcpy

strcpy(sir_destinatie,sir_sursa); copiaza sirul sir_ sursa in sir_destinatie (se simuleaza atribuirea a=b) .

ATENTIE!! Nu este permisa atribuirea intre doua siruri de caractere folosind operatorul = . Atribuirea se  face folosind functia strcpy .

Exemplu:

char a[50]=”primul sir”,b[40]=”al doilea sir”;

a=b;  //eroare

strcpy(a,b);  =>     a = ”al doilea sir”; b=”al doilea sir”;

 

Ø Functia strcat

strcat(dest,sursa); adau ga sirului dest sirul sursa. Sirul sursa ramane nemodificat. Operatia se numeste concatenare si nu este comutativa.

Exemplu:

char *a=”vine ”,*b=”vacanta?”; strcat(a,b);   =>  a = ”vine vacanta?”;

 

Ø Functia strncat

strncat(dest,sursa,nr); adauga dest primele nr caractere din sirul sursa. Sirul sursa ramane

1                             nemodificat.

E xem plu:

char *a=”vine ”,*b=”vacanta?”; strncat(a,b,4); =>  a = ”vine vaca”;

 

Ø Functia strchr

strchr(sir,c); are rolul de a cauta caracterul c in sirul sir. Cautarea se face de la stanga la dreapta, iar functia intoarce adresa subsirului care incepe cu prima aparitie a caracterului c. Daca nu este gasit caracterul, functia returneaza 0. Diferenta dintre adresa sirului initial si cea a subsirului returnat reprezinta chiar pozitia caracterului cautat in sirul dat.

Exemplu:

char *a=”acesta este un sir”, b=’t’, c=’x’, *d;      

                        cout<  =>       se tipareste   ”ta este un sir”;

cout< => nu se tipareste nimic (se tipareste 0 daca se face o conversie la int a lui strchr(a,c) ;

d= strchr(a,b);

cout<<”Caracterul apare prima data la pozitia ”<a;

 

Ex: Sa se afiseze toate pozitiile unui caracter intr-un sir

#include

#include using namespace std; int main()

{char a[100],*p,c;

cin.get(a,100); cin>>c; p=strchr(a,c); while (p)

{cout<<"Pozitia "<

p=strchr(p,c);}

return 0;}

Ø Functia strrchr

                     strrchr(sir,c); are acelasi rol cu strchr, cu deosebirea ca returnea za adresa ultimei aparitii a caracterului (cautarea se face de la dreapta spre stanga; r = right)

                       Ø Functia st rcmp

int strcmp(sir1,sir2); are rolul de a compara doua siruri de caract ere. Valoarea returnata este <0 (daca sir1< sir2), =0 (daca sir1=sir2) si >0 (daca sir1>sir2). Functia strcmp face distinctie intre literele mari si cele mici ale alfabetului.

Obs : Functia strcmp returneaza diferenta dintre codurile ASC II ale primelor caractere care nu coincid

  • Functia stricmp

int stricmp(sir1,sir2); – are acelasi rol cu strcmp, cu deosebirea ca nu face distinctie intre literele mari si cele mici ale alfabetului (i = ignore).

 Functia strstr

strstr(sir1,sir2); – are rolul de a identifica daca sirul sir2 este subsir al sirului sir1. Daca este, functia returneaza adresa de inceput a subsirului sir2 in sirul sir1, altfel returneaza adresa 0. In cazul in care sir2 apare de mai multe ori in sir1, se returneaza adresa de inceput a primei aparitii. Cautarea se face de la stanga la dreapta

 Functia strtok

  • strtok(sir1,sir2); – are rolul de a separa sirul sir1 in mai multe siruri (cuvinte) separate intre ele prin unul sau mai multe caractere cu rol de separa Sirul sir2 este alcatuit din unul sau mai multe caractere

cu rol de separator.

Functia strtok actioneaza in felul urmator:

o Primul apel trebuie sa fie de forma strtok(sir1,sir2); Functia intoarce adresa primului caracter al primei entitati. Dupa prima entitate, separatorul este inlocuit automat prin caracterul nul.

o Urmatoarele apeluri sunt de forma strtok(NULL,sir2); De fiecare data, functia intoarce adresa de inceput a urmatoarei entitati, adaugand automat dupa ea caracterul nul.

o Cand sirul nu mai contine entitati, functia returneaza adresa nula.

 Exemplu:

//Sa se separe cuvintele dintr-un text.

#include <iostream.h>

#include <string.h>

int main()

{char text[100],cuv[10][10],*p,*r,separator[]=",. !?";int i=0,nr=0; clrscr();

cout<<"Dati sirul:";cin.get(text,100); strcpy(p,text);  p=strtok(p,separator);

while (p)

 {strcpy(cuv[++nr],p); p=strtok(NULL,separator);}

cout<<"Sunt "<<nr<<" cuvinte:"<<endl; for (i=1;i<=nr;i++) cout<<cuv[i]<<endl; return 0;}

 

  • Functia strspn cu forma generala

int strspn(sir1,sir2); – are rolul de a returna numarul de caractere ale sirului sir1 (caractere consecutive care incep obligatoriu cu primul caracter) care se gasesc in sirul sir2.

Exemplu:

strspn(“AB2def”,”1B3AQW”); à returneaza 2, pentru ca primele 2 caractere „A‟ si „B‟ din sir1 se gasesc in sir2.

strspn(“FAB2def”,”16A32BF”); à returneaza 0, deoarece caracterul „F‟ cu care incepe sir1 nu se gaseste in sir2.

 Functia strcspn cu forma generala

int strspn(sir1,sir2); – are rolul de a returna numarul de caractere ale sirului sir1 (caractere consecutive care incep obligatoriu cu primul caracter) care nu se gasesc in sirul sir2.

 

Exemplu:

strspn(“AB2def”,”123”); à returneaza 2, pentru ca primele 2 caractere din sir1 nu se gasesc in

sir2.

 

 

//Se citeste un sir de caractere care nu contine caractere albe. Sa se decida daca sirul este alcatuit exclusiv din caractere numerice.

#include <iostream>

#include <string.h> using namespace std; int main()

{char text[100],cifre[]="0123456789"; cout<<"Dati sirul:";cin.get(text,100);

  • if (strcspn(cifre,text)==strlen(text)) cout<<"exclusiv numeric";

else

cout<<”nenumeric”;

return 0;}

 

  • Functia strlwr cu forma generala

strlwr(sir); – are rolul de a converti toate literele mari din sir in litere mici. Restul caracterelor raman neschimbate.

 

  • Functia strupr cu forma generala

strupr(sir); – are rolul de a converti toate literele mici din sir in litere mari. Restul caracterelor raman neschimbate

 Functia strbrk cu forma generala

strpbrk(sir1,sir2); – actioneaza in felul urmator:

o Cauta primul caracter al sirului sir1 in sir2. Daca este gasit, returneaza adresa sa din cadrul sirului sir1 si executia se termina. Altfel, se trece la pasul urmator.

o Cauta al doilea caracter al sirului sir1 in sir2. Daca este gasit, returneaza adresa sa din cadrul sirului sir1 si executia se termina. Altfel, se trece la pasul urmator.

o …

o Daca nici un caracter al sirului sir1 nu apartine sirului sir2, functia returneaza adresa nula.

 

Functia atof cu forma generala

double atof(sir); – converteste un sir catre tipul double. Daca aceasta conversie esueaza (se intalneste un caracter nenumeric), valoarea intoarsa este 0. Aceasta functie (ca si cele similare) necesita includerea librariei stdlib.h.

 Functia atoi cu forma generala

int atoi(sir); – converteste un sir catre tipul int. Daca aceasta conversie esueaza (se intalneste un caracter nenumeric), valoarea intoarsa este 0.

 Functia atol cu forma generala

long atol(sir); – converteste un sir catre tipul long. Daca aceasta conversie esueaza (se intalneste un caracter nenumeric), valoarea intoarsa este 0.

 Functia itoa cu forma generala

itoa(int valoare,sir,int baza); – converteste o valoare de tip int in sir, care este memorat in variabila sir. Baza retine baza de numeratie catre care sa se faca conversia. In cazul bazei 10, sirul retine si eventualul semn -.

  • Functia ltoa cu forma generala

ltoa(long valoare,sir,int baza); – converteste o valoare de tip long int in sir, care este memorat in variabila sir.

  • Functia ultoa cu forma generala

ultoa(unsigned long valoare,sir,int baza); – converteste o valoare de tip unsigned long in sir, care este memorat in variabila sir.

SUBPROGRAME
SUBPROGRAME

Subprogramele sunt părţi ale unui program, identificabile prin nume, care se pot activa (apela) la cerere prin intermediul acestor nume.

Prezenţa subprogramelor implică funcţionarea în strânsă legătură a două noţiuni: definiţia unui subprogram şi apelul unui subprogram.

Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul variabilelor virtuale (parametri formali) iar apelul unui subprogram nu este altceva decât execuţia procesului de calcul pentru cazuri concrete (cu ajutorul parametrilor reali, (efectivi, actuali) ).

 

Structura unui subprogram C++

Un subprogram (funcţie) are o definiţie şi atâtea apeluri câte sunt necesare.

 

Definiţia unei funcţii are forma generală:

 

tip_returnat   nume_funcţie    (lista parametrilor formali)

{

instrucţiune;      // corpul funcţiei

return valoare;

}

Tip_returnat

Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi: int, char, char*, long, float, void, etc.

În cazul în care tipul rezultatului este diferit de void, corpul funcţiei trebuie să conţină cel puţin o instrucţiune return. Înstrucţiunea return va specifica valoarea calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat.

Nume_funcţie

Reprezintă numele dat funcţiei de către cel ce o defineşte, pentru a o putea apela.

Lista_parametrilor_formali

Reprezintă o listă de declaraţii de variabile separate prin virgulă. Această listă poate să fie şi vidă.

Instrucţiune

Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă.

Există două tipuri de subprograme:

  ü  De tip funcție – returnează o valoare

  ü  De tip procedură (void)  - nu returnează direct o valoare, dar poate returna prin intermediul parametrilor săi.

În cazul subprogramului de tip procedură Tip_returnat este void, instrucțiunea return nu apare.

Apelul unei funcţii . Revenirea dintr-o funcţie

 

Apelul unui subprogram care nu returnează o valoare (procedură)  are forma generală:

 

nume_funcţie (lista parametrilor actuali);

 

parametru efectiv = parametru actual = parametru real = parametru de apel

lista parametrilor actuali = fie vidă, fie o expresie sau mai multe despărţite prin virgulă

O funcţie care returnează o valoare poate fi apelată fie printr-o instrucţiune de apel simplă (cazul funcţiilor care nu returnează valori) şi în plus poate fi apelată ca operand al unei expresii. În cazul în care funcţia se apelază print-o instrucţiune de apel simplă, rezultatul funcţiei se pierde. Când funcţia se apelează ca operand, valoarea returnată va fi utilizată în expresie.

La apelul unei funcţii, valorile parametrilor efectivi se atribuie parametrilor formali corespunzători. În cazul în care unul din tipul unui paramatru efectiv diferă de tipul parametrului formal corespunzător, parametrul efectiv va fi convertit spre parametru formal (dacă este posibil, altfel compilatorul generează eroare).

În momentul în care se întâlneşte un apel de funcţie, controlul execuţiei programul este transferat primei instrucţiuni din funcţie, urmând a se executa secvenţial instrucţiunile funcţiei.

Apel funcție

Apel procedură

Z = nume_funcţie (lista parametrilor actuali);

 

cout<<(nume_funcţie (lista parametrilor actuali);

 

if (nume_funcţie (lista parametrilor actuali)….)

 

while (nume_funcţie (lista parametrilor actuali)….)

 

 

nume_funcţie (lista parametrilor actuali);

 

STRUCTURA UNUI SUBPROGRAM

Variabilele dintr-un program C++ pot fi clasificate în:

1. Variabile globale                 2. Variabile locale                   3. Parametri formali

1. Variabile globale

·         Se declară în afara oricărei funcţii din program

·         Sunt alocate static, în segmentul de date al programului

·         Sunt iniţializate implicit, cu valoarea 0

·         Au domeniul de vizibilitate tot fişierul sursă, adică pot fi folosite din locul în care au fost definite şi până la sfârşitul fişierului

·         Au alocat spaţiu în memorie tot timpul rulării programului

 

2. Variabile locale

·         Se declară doar în interiorul unei funcţii din program, inclusiv în funcţia main ()

·         Nu sunt iniţializate implicit, dacă nu sunt iniţializate explicit de programator, reţin o valoare oarecare, numită valoare reziduală

·         Au domeniul de vizibilitate la nivelul blocului în care au fost declarate, adică pot fi folosite doar în cadrul acelui bloc de instrucţiuni

·         Au alocat spaţiu în memorie numai în timpul rulării blocului respectiv de instrucţiuni

 

Regula de omonimie

Ø În cazul în care există o variabilă locală care are acelaşi nume cu o variabilă globală, aceste două variabile se numesc variabile omonime.

Ø Variabilele locale sunt prioritare (ascund) variabilele globale omonime.

int n=10;

void f1() { int n=2; cout << n; }

void main () { f1(); cout << n; }

Întrebare. Cum gestionează compilatorul cele două variabile omonime ?

Răspuns:

Ø Variabilelor globale li se rezervă spaţiu de memorare la începutul execuţiei programului, într-o zonă de memorie numită “zonă de date”. Acest spaţiu va fi ocupat până la încheierea execuţiei programului.

Ø Variabilelor locale li se rezervă spaţiu într-o zonă specială de memorie numită “stiva”. La încheierea execuţiei subprogramului, conţinutul stivei este eliberat.

Din acest motiv, variabilele globale sun vizibile doar în interiorul subprogramului în care au fost declarate.

3. Parametri formali – reprezintă o cale de comunicare între modulul apelant şi funcţia apelată. Pot fi:

Ø Parametri de intrare – corespund datelor de intrare din analiza problemei

- Sunt valori transmise de modulul apelant către funcţia apelată

- Se transmit prin valoare

- Se declară ca orice variabilă prin tip idvar

Ø Parametri de ieşire (rezultate) – corespund datelor de ieşire din analiza problemei

 

- Sunt valori transmise de funcţia apelată către modulul apelant - Se transmit prin referinţă, este specificat prin tip& idvar

Parametri de intrare şi ieşire – sunt parametri formali transmişi prin adresă, dar care sunt folosiţi şi pentru a transmite date de intrare

Parametrii formali şi actuali

ü  Parametrii care sunt declaraţi în antetul unei funcţii se numesc parametri formali, iar cei care se găsesc în instrucţiunea de apel se numesc parametri efectivi (actuali sau de apel).

ü  Legătura între parametrii formali şi cei actuali este dată de regulile următoare:

ü  Parametrii actuali trebuie să coincidă ca număr, tip şi ordine cu parametri formali

ü  Transmiterea parametrilor are efectul unei atribuiri a valorii parametrului actual către parametrul formal corepsunzător 

 

Metode de transmitere a parametrilor unui subprogram
Metode de transmitere a parametrilor unui subprogram

 Transmiterea prin valoare se foloseşte atunci când funcţia primeşte acea valoare ca o dată de intrare, fără a transmite în modulul apelant valoarea modificată în subprogram.

Pot fi transmise prin valoare:

a) valorile reţinute de variabile

b) valoarea unei expresii, care pot conţine inclusiv apeluri de funcţii; expresiile sunt evaluate înainte de transfer

 Transmiterea prin referinţă se foloseşte atunci când în urma apelului dorim ca variabila transmisă să reţină valoarea stabilită în timpul execuţiei subprogramului 

Exemplu:

Exemplul 1

Exemplul 2

# include <iostream>

using namespace std;

void f1 ()

{

cout << "abc";

}

int main ()

{

f1();

return 0;

}

# include <iostream>

using namespace std;

void f1 (int k)

{

for (int i=1; i<=k ; i++)

cout << "abc"<< " ";

}

int main ()

{

f1(3);

return 0;}

Se va afisa:

Abc

Se va afişa:

abc abc abc

Funcţia nu returnează o valoare

Funcţia nu are parametri

Apelul funcţiei este o instrucţiune de apel simplă

Funcţia nu returnează o valoare

Funcţia are un parametru formal de tip int

Apelul funcţiei este o instrucţiune de apel sinplă şi se face cu ajutorul unui parametru actual care este de acelaşi tip cu tipul parametrului formal corespunzător

 

Exemplul 3.3

Exemplul 3.4

# include <iostream>

# include <math.h>

using namespace std;

 

int prim (int x)

{

int nr_div=0;

for (int i=2; i<=x; i++)

if (x%i==0)

nr_div++;

if (nr_div==0)

return 1;

else

return 0;

}

int main ()

{

int N;

cout << "N="; cin >> N;

if (prim(N))

cout << "PRIM";

else

cout << "NU E PRIM";

return 0;

}

# include <iostream>

# include <math.h>

using namespace std;

 

int prim (int x)

{

for (int i=2; i<=x; i++)

if (x%i==0)

return 0;

return 1;

}

int main ()

{

int N;

cout << "N="; cin >> N;

if (prim(N))

cout << "PRIM";

else

cout << "NU E PRIM";

return 0;

}

Funcţia returnează o valoare de tip int

Funcţia are un parametru de tip int

Rezultatul funcţiei este este utilizat în cadrul unei expresii.

În cazul în care se întâlneşte un divizor a lui x se execută instrucţiunea return 0. Astfel apelul funcţiei se încheie. Dacă x este număr prim, instrucţiunea return 0 nu se execută niciodată şi în acest caz, după terminarea execuţiei instrucţiunii for, se execută instrucţiunea return 1 (care determină încheierea execuţiei funcţiei).

    !!!   Tablourile(sirurile) sunt transmire implicit prin referinta. Asta insemna ca ele isi vor pastra valorile pe care le vor primi in subprogram.
SUBPROGRAME RECURSIVE
SUBPROGRAME RECURSIVE

Ce este recursivitatea ?

    • Recursivitatea este un concept matematic care implică definirea unui concept prin referirea la același concept.
    • Astfel, mulțimea numerelor naturale se poate defini:
      • 1 este număr natural
      • orice succesor al unui număr natural este de asemenea un număr natural
      • În definiția de mai sus, observăm că avem o valoare inițială (1), iar restul valorilor se obțin adunând 1 la valoarea anterioară

Spunem despre un subprogram că este recursiv dacă conține în definirea sa un apel la el însuși.

Astfel putem avea două tipuri de recursivitate:

  • Directă (atunci când subprogramul conține un apel la el însuși)
  • Indirectă (atunci când subprogramul P apelează un subprogram Q care la rândul să conține în definirea sa un apel la subprogramul P).

Structura unei funcţii recursive

tip nume_funcţie (lista de parameri) {

 .................

nume_funcţie (lista de parametri);       

//funcţia conține un apel la ea însăși

 .................. 

 

                Structura unei funcţii recursive indirect apelate prin                                  intermediul unei alte funcţii:

          tip B(lista de parameri) {

                 .................

            A(lista de parametri); //funcţia B apelează funcţia A ..................

           }

          tip A(lista de parameri) //funcţia A este recursivă şi apelează funcţia B                             {.................

                                  if(condiţie de continuare)

                                           .................

                                 B(lista de parametri);

                           .................. }

Majoritatea algoritmilor repetitivi se pot implementa atât în varianta nerecursivă, folosind cicluri, cât și în varianta recursivă. Varianta recursivă este recomandată în special pentru problemele definite prin relații de recurentă, care permit o formulare a rezultatelor mult mai clară și mai concisă.

Un subprogram recursiv se caracterizează prin proprietatea că se auto- apelează, adică din interiorul lui se apelează pe el însuși. Din afara subprogramului facem un prim apel al acestuia, după care subprogranul se auto-apelează de un anumit număr de ori: la fiecare nouă auto-apelare a subprogramului, se execută din nou secvența de instrucțiuni ce reprezintă corpul său, eventual cu alte date, creându-se un așa-numit lanț de auto-apeluri recursive.

Putem spune că un subprogram recursiv are același efect ca și un ciclu: repetă execuția unei anumite secvențe de instrucțiuni. Dar, la fel ca în cazul unui ciclu, este necesar ca repetarea să nu aibă loc la infinit. De aceea în corpul subprogramului trebuie să existe cel puțin o testare a unei condiții de oprire, la îndeplinirea căreia se întrerupe lanțul de auto-apeluri.

int f(int n)

{

     if (n==0) return 1;

    else return n*f(n-1);

}

int main()

{  int n;

    cout<<“n=“; cin>>n ;

    cout << n <<“! = “<<f(n));

}

Funcţia factorial este scrisă după definiţia recursivă, respectiv varianta iterativă:


 

int f (int n)

{ int i,P=1;

   for (i=1; i<= n; i++) P=P*i;

     return P;

}

  • Cum afişăm numerele naturale de la 1 la n printr-o procedură recursivă ?
  • Rezolvare: În acest caz, nu avem o formulă de recurenţă pentru scrierea unui subprogram recursiv. Pentru a rezolva problema o descompunem în mai multe subprobleme de acelaşi tip:
  • Subproblema p(i): pentru valoarea i a parametrului vom tipări i după care apelăm (recursiv) p pentru i+1
  • Apelul iniţial este p(1) : începem cu 1
  • Condiţia de oprire este să ajungem la n, când tipărim doar n fără alte apeluri
 
 

int n ;                                     // câte numere tipărim

void p(int i)                         // procedura p cu parametrul i

{

    if (i==n) cout<<n ;    // rezolvarea directă (condiţia de STOP)

     else {

            cout<<i<<;           // Subproblema p(i): tipărim i

            p(i+1);                     ;  // trecem la subproblema p(i+1)

             }

}

int main()

{  cout<<“n=“;  cin>>n;      // citim valoarea lui n

  p(1);                              // apelul iniţial

}

Cum scriem un subprogram recursiv ?

     1. Trebuie să formulăm problema în termeni recursivi

                - stabilim formula de recurenţă

                - identificăm soluţia în cazul rezolvării directe, dată de obicei sub forma condiţiilor iniţiale

                - formulăm subproblemele de rezolvat în cazul în care nu dispunem de o formulă de recurenţă

  1. Scriem subprogramul recursiv:

                - soluţia directă se scrie sub forma condiţiei de oprire

                - apelul recursiv rezultă din formula de recurenţă

                - la fiecare apel problema parţială trebuie rezolvată complet

Metoda Divide et Impera
Metoda Divide et Impera

Metoda Divide et Impera (Împarte si Stăpânește) este o metoda de programare care se aplica problemelor care pot fi descompuse in subprobleme independente, similare problemei inițiale, de dimensiuni mai mici si care pot fi rezolvate foarte ușor.  Procesul se reia pana când (in urma descompunerilor repetate) se ajunge la probleme care admit rezolvare imediata.

 

            Metoda presupune:

  • Descompunerea problemei Pb curente in subprobleme independente SubPbi.
    • In cazul in care subproblemele SubPbi. admit o rezolvare imediata se compun solutiile si se rezolva problema Pb
    • Altfel se descompun in mod similar si subproblemele SubPbi

 

Evident, nu toate problemele pot fi rezolvate prin utilizarea acestei tehnici. Majoritatea algoritmilor de Divide et Impera presupun prelucrări pe tablouri (dar nu obligatoriu).

 

Aceasta metoda (tehnica) se poate implementa atât iterativ dar si recursiv. Dat fiind ca problemele se împart în subprobleme in mod recursiv, de obicei împărțirea  se realizează pana când șirul obținut este de lungime 1, caz in care rezolvarea subproblemei este foarte ușoară, chiar banala.

 

Spre exemplu fie un vector X=[x1, x2, x3, …xi  xp… xj, …xn] asupra căruia se aplica o prelucrare. Pentru orice secvența din vector delimitata de indecșii  i si j, i<j  exista o valoare p astfel încât prin prelucrarea secvențelor :

                       xi, xi+1, xi+2, xi+3, …xp  si xp+1, xp+2, xp+3, …xj

 se obțin soluțiile corespunzătoare celor doua subșiruri care prin compunere conduc la obținerea soluției prelucrării secvenței:

                               xi, xi+1, xi+2, xi+3, …xj  

 

Aplicație:

Ne propunem sa determinăm suma elementelor unui vector de întregi utilizând metoda Divide et Impera:

 

2

3

4

1

5

6

8

9

1

10

3

4

4

5

2

6

 

Se împarte vectorul in doi vectori:

2

3

4

1

5

6

8

9

 

1

10

3

4

4

5

2

6

 

Fiecare dintre cei doi vectori se  poate împarți in continuare in alți doi vectori:

2

3

4

1

 

5

6

8

9

 

1

10

3

4

 

4

5

2

6

 

La fel se procedează in continuare:

2

3

 

4

1

 

5

6

 

8

9

 

1

10

 

3

4

 

4

5

 

2

6

 

Apoi:

 

2

   

3

 

4

 

1

 

5

 

6

 

8

 

9

 

1

 

10

 

3

 

4

 

4

 

5

 

2

 

6

 

Dat fiind ca s-au obținut secvențe de vector de lungime 1, nu se mai realizează descompunerea.

Se compun soluțiile subsecventelor si se determina soluțiile corespunzătoare:

2

+

3

 

4

+

1

 

5

+

6

 

8

+

9

 

1

+

10

 

3

+

4

 

4

+

5

 

2

+

6

 

Si in continuare:

5

+

5

 

11

+

17

 

11

+

7

 

9

+

8

 

Apoi:

10

+

28

 

18

+

17

 

La fel:

38

+

35

 

La sfârșit:

73

 

Iată o soluție de implementare:

 

#include<iostream>

using namespace std;

 

int v[20],n;

 

int divide(int v[20], int li, int ls) // functia primeste ca parametric extremitatile unei secvente din vector

{int mij, d1 ,d2; //mijlocul, d1 si d2 retin sumele pe extr. Stanga respectiv dreapta

 if(li != ls)            //algoritmul se autoapeleaza daca secventele au lungime mai mare de 1

     {mij=(li+ls)/2;

      d1=divide(v, li, mij);

      d2=divide(v, mij+1, ls);

       return d1+d2;

      }

 else

    return v[li];

}

 

int main()

{

 cout<<"n=";

 cin>>n;

 for(int i=1;i<=n;i++)

   {cout<<"v["<<i<<"]=";

    cin>>v[i];}

cout<<"suma celor "<<n<<" elemente ale vectorului "<<divide(v, 1, n);

return 0;

}

 Se citește un vector cu n componente numere întregi (numerele se presupun ordonate crescător) și o valoare întreagă ("nr"). Să se decidă dacă nr se găsește sau nu printre numerele citite, iar în caz afirmativ să se tipărească indicele componentei care conține această valoare. Algoritm:

- Funcția care va fi implementată va decide dacă valoarea căutată se găsește printre numerele aflate pe poziții de indice cuprins între i și j (inițial, i=1, j=n). Pentru aceasta, se va proceda astfel:

ü  dacă mai sunt elemente de analizat (adică i<=j, deci nu au fost verificate toate pozițiile necesare), problema se descompune astfel:

ü  dacă nr coincide cu valoarea de la mijloc, aflată pe poziția de indice (i+j)/2, se treturnează indicele și se revine din apel (problema a fost rezolvată).

ü  în caz contrar, dacă nr este mai mic decât valoarea testată (din mijloc), înseamnă că nu se poate afla pe pozițiile din partea dreaptă, întrucât acestea sunt cel puțin mai mari decât valoarea testată. Nr se poate găsi doar printre componentele cu indice între i și (i+j)/2 - 1, caz în care se reapelează funcția cu acești parametri;

ü  dacă nr este mai mare decât valoarea testată (din mijloc), înseamnă că nu se poate afla în stânga; se poate găsi doar printre componentele cu indicele între (i+j)/2 + 1 și j, caz în care se reapelează funcția cu acești parametri.

ü  dacă nu mai sunt alte elemente de analizat (pentru că i>j se concluzionează că nr nu apare în cadrul vectorului.

 

Această problemă nu mai necesită analiza tuturor subproblemelor în care se descompune, ea se reduce la o singură subproblemă, iar partea de combinare a soluțiilor dispare. În linii mari, această rezolvare este tot de tip "divide et impera". #include <iostream>

 using namespace std;

 int v[100], n, nr;

int caut(int v[100], int i, int j) {

if (i<=j)  {

int m = (i+j)/2;

 if (nr==v[m])  return m;

else

if (nr<v[m]) return caut(v, i, m-1);

else     return caut(v, m+1,  j); }

else     return 0;

}

 int main( ) {

cout<<”n=”; cin>>n;

for (int i=1; i<=n; i++)

{cout<<”v[“<<i<<”]=”; cin>>v[i]; }

 cout<<”nr=”; cin>>nr;

if (caut (v, 1,n))

cout<< caut (v, 1,n);

else

cout<<” nu s-a gasit ”;

return 0;

}

 

 

Probleme propuse:

  1. Sa se determine produsul a n numere întregi
  2. Sa se determine maximul (minimul) a n numere întregi
  3. Sa se determine cel mai mare divizor comun a n valori dintr-un vector
  4. Sa se caute o valoare intr-un vector. Daca se găsește se va afișa poziția pe care s-a găsit, altfel se va afișa un mesaj.
  5. Sa se caute o valoare intr-un vector ordonat crescător
  6. Sa se numere cate valori sunt egale cu x dintr-un sir de numere întregi citite.
METODA BACKTRACKING
METODA BACKTRACKING

Aceasta tehnica se foloseste in general la rezolvarea problemelor care indeplinesc urmatoarele conditii:

Ø  Solutia lor poate fi pusa sub forma unui vector S= x1, x2, …, xn cu x1 apartine lui A1, x2 apartine lui A2 …

Ø  Multimile A1, A2, …, A n sunt multimi finite, iar elementele lor se considera intr-o relatie de ordine bine stabilita.

Ø  Nu se dispune de o alta metoda de rezolvare, mai rapida.

Obs: In multe din probleme multimile A1, A2, …, An  coincid.

La intalnirea unei astfel de probleme, daca nu cunoastem aceasta tehnica, suntem tentati sa generam toate elementele produsului cartezian A1* A2* …* An si fiecare element sa fie testat daca este solutie.

Ex. Consideram generarea permutarilor multimii {1, 2, 3, …, n} unde n este un numar natural.

In cazul de fata A1  =A2 =… = An = {1, 2, 3, …, n} = A (notatie). Trebuie determinata o multime x1, x2, …, x n cu propietatea ca x1 , x2, …, xn apartin lui A. Elementul 1 * 1 * … * 1 este un element al produsului cartezian A * A *… * A dar nu este o permutare a multimii A fiind ca nu respecta propietatea unei pertutari si anume ca toate elementele sunt distincte intre ele. Deci daca s-a ales un element i care apartine lui A atunci celalat element va trebui sa fie diferit de i.

Elementele vectorului solutiei se vor completa pe rand. Astfel x2 va primi o valoare doar dupa ce x1  a primit o valoare si va trebui sa fie diferita de aceasta.

In general xk+1 va primi o valoare daca xk  a primit o valoare si daca x1 <> x2 <> … <>xk <> xk+1

x1 va primi valoarea primului element al multimii A. Se vor verifica conditiile de continuare adica in sirul x al solutiilor sa nu fie doua elemente egale, dar fiind ca sirul contine un singur element acestea sunt indeplinite. x2 va primi la randul sau valoarea primului element din A numai daca sunt indeplinite conditiile de continuare. Deoarece atat x1 are deja valoarea primului element al multimii A aceste conditii nu sunt indeplinite si deci va trebui sa se aleaga o alta valoare pentru x2 si anume urmatorul element din A.

Daca xk  a primit o valoare atunci xk+1 va primi primul element din A daca sunt indeplinite conditiile de continuare.

Daca k = n si sunt indeplinite conditiile de continuare atunci s-a determinat o solutie si se va afisa solutia

 Daca primul element din A se afla deja printre elementele x1, x2, …, xk  atunci se va alege urmatorul element din A.

Daca in A nu se mai afla nici un element care sa se aleaga si sa respecte conditiile de continuare atunci se revine la xk  si se alege urmatorul element din A de dupa elementul xk

Algoritmul Backtraking este:

K=1

X1= 1

Cat timp k>0 executa

    cat timp k < n  si sunt indeplinite Conditiile de continuare executa

        k =  k+1

        xk = 1

    Sf. Cat timp

     Daca k=n si sunt indeplinite Conditiile de continuare  atunci

                       Tipareste Solutia – vectorul X

     Sf Daca

    Cat timp  Nu mai sunt Elemente in A de ales  executa

            k = k –1

    Sf Cat timp

    Daca  k >0 atunci

              x k = xk + 1

    Sf Daca

Sf Cat timp                          

Teoria Grafurilor
Teoria Grafurilor

Teoria Grafurilor.

Notiuni introductive.

 Def. Se considera multimile finite X = { x1, x2, …, xn} si multimea U, care este produsul cartezian al multimii X cu ea insasi.

Se numeste graf o pereche ordonata de multimi (X, U), X fiind o multime finita si nevida de elemente numite varfuri sau noduri, iar U o multime de perechi (submultimi cu cate doua elemete) din X,  numite muchii sau arce.

Multimea X se numeste multimea nodurilor sau varfurilorr, iar multimea U se numeste multimea muchiilor sau arcelor.

Un graf va fi notat G = (X, U), iar in figurarea sa nodurile se vor desena prin numere sau litere, iar muchiile prin linii neorientate si arcele prin linii orientate.

Def. Se spune ca multimea U are propietatea de simetrie daca si numai daca avand [xk, xi] in U rezulta si [xi, xk] tot in U.

Def. Daca multimea U are propietatea de simetrie se spune ca graful este un graf neorientat.

Def. Daca multimea U nu are propietatea de simetrie se spune ca graful este un graf orientat sau directionat sau digraf.

Pentru un arc (xi, xj), xi este numit baza arcului sau originea sau extremitatea initiala iar xj este numit varful grafului sau extremitatea finala sau terminala. Se mai spune ca xi si xj sunt adiacente. Astfel arcul (xi, xj) este incident spre exterior cu xi si incident spre interior cu xj.

Daca un varf nu este nici extremitate initiala si nici extremitate finala pentru nici un arc atunci el este varf izolat.

Def. Se considera graful G= (X, U). Daca multimea U este vida atunci graful se numeste nul si reprezentarea lui se reduce la figurarea unor puncte izolate.

Def. Se numeste gradul unui varf x al unui graf numarul de muchii incidente cu x si se noteaza cu d(x).

Daca un varf are gradul 0 atunci el este varf izolat iar daca are gradul 1 atunci el este varf terminal.

Conventie:  Fie un graf G = (X, U). Atunci  unde n este numarul de varfuri iar m este numarul de muchii al grafului.

Corolar. Fiecare graf are un numar par de varfuri cu grad impar.

Def. Fie graful G=(X, U) si V     U. Graful Gp= (X, V)  se numeste graf partial al grafului G.

Numărul de grafuri parţiale ale lui G este  2m.

Soluţie: Numerotăm muchiile grafului de la 1 la m. Fiecărui graf parţial îi putem asocia în mod biunivoc o funcţie f:{1, 2, 3, …, m} à {0, 1}  astfel: dacă muchia numerotată i aparţine grafului parţial va avea valoarea 1 și 0 dacă nu aparține.

Numărul de grafuri parţiale este egal cu numărul de funcţii definite, adică 2m (considerăm că un graf este parţial al său).

Def. Fie graful G=(X, U). se numeste graf complementar al grafului G, graful Gc=(X, U’) cu prop. ca doua varfuri sunt adiacente in Gc daca ele nu sunt adiacente in G.

Def. Fie graful G=(X, U) si Y  inclusa in X. Fie Vinclusa in U, unde V contine toate muchiile din U care au extremitatile in multimea Y. Graful Gs=(Y, V)  se numeste graf subgraf al grafului G.

Def. Se considera U = X x X – D. Atunci graful G=(X, U) se numeste graf complet si se noteaza Kn (n fiind numarul de varfuri al grafului). Si D = { x     x | x  apartine lui X }

Prop. Kn este graf neorintat cu n varfuri are  n(n-1)/2 muchii.

Def. Graful G se numeste bipartit daca exista doua multimi nevide A si B astfel incat X = A U B si A Ç B =  Æ si orice muchie u a lui U are o extremitate in A si cealalta in B.

Def. Graful G bipartit se numeste bipartit complet  daca intre orice varf xi din  A si orice varf xk din B exista muchia [xi, xk].

Metode de reprezentare

 Reprezentarea geometrica.

Fie graful G = (X, U) unde X = {1, 2, 3, 4, 5, 6}

                             U = {[1, 2]; [2, 3]; [1, 4], [4, 5]; [2, 6]}

Reprezentarea varfurilor se va face printr-un cerculet in interiorul caruia se va trece numarul corespunzator varfului sau o litera .

MEMORAREA UNUI GRAF
MEMORAREA UNUI GRAF

 1.        Matricea de adiacenta.

Fie un graf G = (X, U), X = {x1, x2, …, xn}. Asociem lui 1 pe x1, 2 pe x2, …

Astfel reprezentarea grafului va fi o matrice patratica de dimensiune n.

A[i][j]   primeste valoarea 1 daca I si j sunt adiacente

                                         0  in caz contrar

Ex.  Pentru graful de mai sus: G = (X, U) unde X = {1, 2, 3, 4, 5, 6}

                                     U = {[1, 2]; [2, 3]; [1, 4],  [4, 5]; [2, 6]}






2.            Lista de adiacenta

Pentru fiecare nod din graf se pastreaza cate o lista care contine nodurile adiacente cu acesta.

Ex: Pentru graful de mai sus


Metoda constă în crearea / memorarea a doi vectori alfa şi beta definiţi astfel:

       alfa[1] = 1

       alfa[i] = 1 + suma gradelor nodurilor 1, 2, 3, ..., i- 1   sau  

                                                              alfa [i] =  alfa [i-1] + grad (i)

               alfa = (1, 3, 6, 7, 9, 10, 11)

        beta reprezintă înşiruirea nodurilor din coloana din dreapta a tabelului alăturat.

                beta = (2, 4, 1, 3, 6, 2, 1, 5, 4, 2)


3.       Matricea costurilor

Fiecarui muchie i se va atribui un numar Real mai mare ca 0, reperezentand costul muchiei respective

c : U -->  R+  

Astfel c este o matrice patratica de dimensiune n definita astfel:

C[i][j]   primeste valoarea v daca i si j sunt adiacente, iar v reprezinta costul muchiei

                                        ;    0  in caz contrar

4.       Matricea de incidenta

Se noteaza muchiile grafului cu m1, m2, …, mm. Metoda consta in alcatuirea unei matrici B cu n linii si m coloane (n- nr. de varfuri si m – nr. de muchii)

B[i][j]  primeste valoarea 1 daca daca nodul I este incident cu muchia mj.

                                            0 in rest








5. Cu ajutorul a doua siruri

Se vor construi doua siruri X si Y astfel incat muchia mi are prima extremitate in sirul xi iar a doua in sirul yi

Parcurgerea grafurilor in latime (Breadth First)
Parcurgerea grafurilor in latime (Breadth First)

Derularaea algoritmului presupune alegerea la un moment dat, dintre vecinii unui varf, pe acela ce nu a fost vizitat inca. Acest lucru este posibil prin folosirea unui vector VIZITAT de dimensiune n ale carui componente se definesc astfel:

Vizitat[i] = 1 daca nodul I a fost vizitat

                  = 0 in rest

Se va folosi o structura de tip coada.

Algoritmul parcurgerii in latime

Pas. 1.   Se prelucreaza varful initial k

              Pas. 1.1.  Se adauga varful k in coada

              Pas. 1.2. Varful k se considera vizitat

Pas. 2.  Cat timp coada este nevida se executa:

               Pas. 2.1. Pentru toti vecinii j nevizitati inca ai varfului k

                     Pas. 2.1.1. Se adauga varful j in coada

                Pas. 2.1.2. Varful j se considera vizitat

                Pas. 2.2. Se reia de la Pas. 2.1. (varful j devine varful k)

void Parc_Lat()

{

prim = 1;

ultim = 1;

Cd[1] = 1;

cout<<endl<<Cd[1];

Viz[1] = 1;

while (prim<=ultim)

{

            nod = Scoate(Cd, prim);

            for (j=1 ; j<= n; j++)

              if ((a[nod][j] == 1) && (Viz[j] == 0))

                {

                  Add(Cd, ultim, j);

                  cout<<"   "<<j;

                  Viz[j]=1;

                }

}

}

Cd – coada

Viz – vectorul VIZITAT

Scoate – functie care returneaza primul element din coada

Add – adauga un element in coada

Prim – indicele primului element din coada (la scoatere creste cu o unitate)

Ultim – indicele ultimului element din coada (la adaugare creste cu o unitate)

In coada vor fi elemente atata timp cat

                    &a mp;nb sp;                             prim <=ultim

Parcurgerea grafurilor in adancime (Depth First)
Parcurgerea grafurilor in adancime (Depth First)

Derularaea algoritmului presupune vizitarea unui varf apoi a varfului adiacent cu acesta si asa mai departe , pana se revine la un alt varf si se alege un alt  varf nevizitat inca. Acest lucru este posibil prin folosirea unui vector VIZITAT de dimensiune n ale carui componente se definesc astfel:

Se va folosi o structura de tip stiva.

Algoritmul parcurgerii in adancime

Pas. 1.   Se adauga varful  k in stiva si se considera vizitat

Pas. 2.   Se analizeaza varful k

         Pas. 2.1. Se cauta primul din vecinii nevizitati

                Pas. 2.1.1.  Daca exista (fie j acesta) se dauga in stiva si    se considera vizitat

                Pas. 2.1.2. Daca nu exista se scoate urmatorul nod din stiva (fie j  acesta)

           Pas. 2.2. Varful j devine varful ce trebuie analizat (varful k)

Pas. 3. Cat timp stiva este nevida se executa Pas. 2.

void Parc_Ad()  {

ultim = 1;

St[1] = 1;

cout<<endl<<St[1];

Viz[1] = 1;

while (ultim>0)  {

            nod = Scoate(St, ultim);

            j=0;

            ok=0;

            for (j=1; ((j<=n) && (ok==0)); j++)

               if((a[nod][j] == 1) && (Viz[j] == 0)) {

                  Add(St, ultim, j);

                  cout<<"   "<<j;

                  Viz[j]=1;

                  ok=1;

                }

            while ((ok==0) && (j<=n));

            if (ok==0) ultim--;

}

}

 

St – stiva

Viz – vectorul VIZITAT

Scoate – functie care returneaza primul element din stiva

Add – adauga un element in stiva

Ultim – indicele ultimului element din stiva (la adaugare creste cu o unitate)

Ok – indica daca a fost gasit un alt varf adiacent cu cel prelucrat. ( daca e 1 inseamna ca s-a gasit)

In stiva vor fi elemente atata timp cat    ultim > 0


CONEXITATE IN GRAFURI
CONEXITATE IN GRAFURI

Def:  Fie graful G=(X, U). Numim lant o succesiune de varfuri L = {x1, x2, …, xp} cu propietatea ca oricare doua varfuri succesive sunt adiacente, adica [x1, x2] Î U, [x2, x 3] Î U, …, [xp-1, xp] Î U.

Varfurile x1 si xp  se numesc extremitatile lantului, iar numarul de muchii ce il compun se numeste lungimea lantului.

Def: Daca varfurile x1, x2, …, xp sunt distincte doua cate doua, lantul  se numeste lant elementar. In caz contrar lantul se numeste neelementar.

Def: Daca intr-un lant, toate muchiile sunt diferite intre ele, lantul se numeste simplu, in caz contrar lantul se numeste compus.

Def:  Daca varfurile x1 si xp coincid lantul se numeste ciclu.

Daca un ciclu are o lungime para, el se numeste par, altfel impar

Def: Daca intr-un ciclu, toate varfurile sunt distincte intre ele, cu exceptia primului si ultimului atunci ciclul se numeste ciclu elementar.

Def: Un graf se numeste conex daca oricare ar fi doua varfuri x si y, exista un lant ce le leaga.

Def: Fie graful G=(X, U). Un subgraf C = (X1, U1) al sau conex se numeste componenta conexa a grafului G daca are in plus propietatea ca nu exista nici un lant in G sa lege un varf din X1 cu un nod din X – X1.

Teorema: Componentele conexe Xi ale unui graf genereaza o partitie a lui X, adica multimile Xi au propietatile:

                1. Xi ¹ Æ,  pentru orice i Î N

                2. Xi ¹ Xj  Þ  Xi Ç Xj = Æ , pentru orice i Î N

                3. reuniunea lor este chiar X

Teorema:  Un graf este conex numai si numai daca are o singura componenta conexa.

Def:  Fie graful G=(X, U). Daca exista varfuri intre care exista mai mult de o muchie, graful este numit multigraf.

Def: Fie graful G=(X, U). Daca exista un lant elementar care contine toate varfurile grafului, lantul se numeste lant hamiltonian.

Def:  Daca intr-un lant hamiltonian varful initial si varful final coincid lantul se numeste ciclu hamiltonian iar despre graf spunem ca se numeste graf hamiltonian

Teorema:  Kn (graful complet) este un graf hamiltonian

Def: Fie graful G=(X, U). Daca exista un lant care contine toate muchiile grafului o singura data fiecare, lantul se numeste lant eulerian.

Def:  Daca intr-un lant eulerian varful initial si varful final coincid lantul se numeste ciclu eulerian iar despre graf spunem ca se numeste graf eulerian

Teorema:  Un graf G fara varfuri izolate este eulerian daca si numai daca este conex si gradul fiecarui varf este par.

Determinarea numarului de componente conexe ale unui graf
Determinarea numarului de componente conexe ale unui graf

Se da un graf G=(X, U). Sa se determine numarul de componente conexe ale grafului.

Graful se va citi c o succesiune de muchii (doua varfuri). Numarul de muchii este m.

Odata cu citirea unei muchii se stabileste apartenenta celor doua varfuri la o componenta conexa. Astfel avem 3 cazuri:

  1. Daca varfurile nu apartin de nici o componenta conexa atunci se va crea una noua care sa contina cele doua varfuri
  2. Daca unul din varfuri apartine de o componenta conexa atunci se va introduce si celalalt sau ambele varfuri apartin de o componenta conexa, atunci nu se va face nimic.
  3. Daca unul din varfuri apartine la o componenta conexa si celalalt la o alta componenta conexa atunci cele doua componente conexe se vor uni si va contine toate varfurile de la cele doua si numarul de componente conexe va scade cu 1.

Algoritmul de determinare a nr. de componente conexe.

Pentru I = 1 la m executa

  Citeste x[I], y[I];

  k=0;

  Pentru j = 1 la nc executa

   Daca ((x[I] apartine CompCon[j])  sau (y[I] apartine CompCon[j]))   atunci

            k = k+1;

            u[k] = j;

   SfDaca

 SfPentru

  Daca (k =0) atunci

      nc = nc + 1;

      CompCon[nc] = {x[I], y[I]}

      Altfel    Daca (k=1) atunci

                         CompCon[u[1]] = CompCon[u[1]] U {x[I], y[I]}

                    &a mp;nb sp;        Altfel

                          CompCon[u[1]] = CompCon[u[1]] U CompCon[u[2]];

                          nc = nc –1;

                     SfDaca

    SfDaca

SfPentru

           m – numarul de muchii

x[I] – sir ce contine prima extremitate a muchiei

y[I] - sir ce contine a doua extremitate a muchiei

nc – numarul de componente conexe

CompCon[j] – vector ce memoreaza varfurice ce fac parte din componenta conexa j.

u[k] – memoreaza numarul componentei conexe din care fac parte cele doua varfuri

k – ne spune in ce situatie ne aflam (1, 2, sau 3)

Excelenta A
Reprezentarea în memorie a valorilor întregi
Reprezentarea în memorie a valorilor întregi

Reprezentarea în memorie a valorilor întregi

Valorile întregi se reprezintă în memorie ca o secvență de biți (cifre binare, 0 și 1). Acestă secvență poate avea 8 16 32 sau 64 de biți.

Reprezentarea în memorie a datelor de tip întreg se face în mod similar pentru toate tipurile cu semn (charshort int,  int long long int) și similar pentru toate tipurile fără semn (unsigned char unsigned short int unsigned intunsigned long long int).

Tip de date

Reprezentare

Înțeles

signed int

sau int

4 octeți cu semn

La fel ca  int. Valori întregi din [−231,231−1][−231,231−1], adică  [−2147483648,2147483647][−2147483648,2147483647].

unsigned int

4 octeți fără semn

Valori naturale din [0,232−1][0,232−1], adică  [0,4294967295][0,4294967295].

Long  sau int

4 octeți cu semn

La fel ca  int. Echivalent cu long int.

unsigned long

4 octeți fără semn

La fel ca unsigned int. Echivalent cu unsigned long int.

Short

Sau short int

2 octeți cu semn

Valori întregi mici din [−215,215−1][−215,215−1], adică  [−32768,32767][−32768,32767]. Echivalent cu short int.

unsigned short sau

unsigned short int.

2 octeți fără semn

Valori naturale mici din [0,216−1][0,216−1], adică  [0,65535][0,65535]. Echivalent cu unsigned short int.

long long

8 octeți cu semn

Valori întregi foarte mari din [−263,263−1][−263,263−1]. Echivalent cu long long int

unsigned long long

8 octeți fără semn

Valori naturale foarte mari din [0,264−1][0,264−1]. Echivalent cu unsigned long long int

signed char  sau char

1 octet cu semn

Caractere. Valorile numerice sunt din [−27,27−1][−27,27−1], adică  [−128,127][−128,127].

unsigned char

1 octet fără semn

Caractere. Valorile numerice sunt din [0,28−1][0,28−1], adică  [0,255][0,255].

long double

10, 12, 16

Memorează numere reale mari. Reprezentarea depinde de compilator, dar trebuie să ocupe cel puțin la fel ca double.



În exemplele care urmează vom folosi tipurile reprezentate pe 16 biți:  short intrespectiv  unsigned short int,

short int.

semn

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1 sau 0

Primul octet

Al doilea octet

 

111111111111111(2) = 32767(10)

 

unsigned short int,

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

 

Primul octet

Al doilea octet

 

1111111111111111(2) = 65535(10)

Reprezentarea în memorie a valorilor de tip unsigned short int

Tipul unsigned short int memorează valori mai mari sau egale cu 0. Acestea se reprezintă în memorie astfel:

  • se transformă numărul în baza 2 și se memorează, adăugând la început cifre de 0 nesemnificative, atâtea câte sunt necesare până la completarea celor 16 biți.
  • dacă reprezentarea în baza 2 a numărului are mai mult de 16 cifre, se vor memora numai ultimele 16 cifre – numărul se va trunchia.

Astfel, valorile fără semn care se pot reprezenta pe 16 biți sunt cuprinse între 0 și 216-1, adică  0 și  65535.

  • 0 se reprezintă 0000000000000000
  • 65535 se reprezintă 1111111111111111
  • 5 se reprezintă 0000000000000101
  • 133 se reprezintă 0000000010000101

Reprezentarea în memorie a valorilor de tip short int

Tipul short int memorează atât valori pozitive, cât și valori negative. Astfel, dintre cei 16 biți disponibili, cel mai din stânga (numit bit de semn) stabilește semnul numărului. Dacă acest bit este 0, numărul este pozitiv, dacă acest bit este 1, numărul este negativ. Astfel, se pot memora  32768 valori negative, de la -32768 la -1, și  32768 pozitive sau zero, de la 0 la 32767.

Reprezentarea numerelor pozitive se face exact ca mai sus: se transformă numărul în baza 2 și se completează cu zerouri nesemnificative.

Nu la fel se face reprezentarea numerelor întregi negative. Această reprezentare se face conform pașilor următori, numită reprezentare în cod complementar:

  1. se determină reprezentarea în memorie a numărului ce reprezintă valoarea absolută a numărului inițial. Aceasta are bitul de semn 0.
  2. se determină complementul față de 1 a reprezentării de la pasul anterior – fiecare bit 1 devine 0 și fiecare bit 0 devine 1.
  3. se adună 1 la valoarea obținută

De exemplu, pentru reprezentarea în memorie a numărului -133  (considerat de tip short int) se procedează astfel:

  1. se determină reprezentarea în memorie a lui 133 și se obține:
    10000101
  2. se obține complementul față de 1:
    101111010
  3. se adună 1 și se obține:
    101111011

Mecanismul de memorare numerelor este același pentru toate tipurile întregi. Diferă numai numărul de biți folosiți pentru reprezentare și implicit intervalul din care fac parte valorile reprezentate.

Operatori pe biți

Operațiile pe biți se aplică numai datelor de tip întreg, și presupun manipularea directă a biților din reprezentarea în memorie a operanzilor.

Operatorul de negație ~

Este un operator unar care are ca rezultat numărul obținut prin complementarea față de 1 a biților din reprezentarea numărului inițial (biții 0 devin  1, biții  1 devin 0).

Exemplu:

~ 133 == -134

Reprezentarea lui 133 este  10000101. Prin complementare se obține 101111010. Aceasta este reprezentarea în memorie a lui -134.

Pentru a verifica, îl reprezentăm conform celor de mai sus pe -134:

  1. reprezentarea lui 134 este 10000110
  2. prin complementare se obține 101111001
  3. adunăm 1 și obținem 101111010

Operatorul de conjuncție biți &

Este un operator binar care are ca rezultat numărul obținut prin conjuncția fiecărei perechi de biți ce apar în reprezentare în memorie a operanzilor:

0 & 0 == 0

0 & 1 == 0

1 & 0 == 0

1 & 1 == 1

Exemplu:

Să calculăm 13 & 151.

Reprezentarea lui 13 este  0000000000001101. Reprezentarea lui 151 este 0000000010010111:

0000000000001101 &
0000000010010111

Se obține:

0000000000000101, adică  5

Deci: 13 & 151 == 5

Operatorul de disjuncție pe biți |

Este un operator binar care are ca rezultat numărul obținut prin disjuncția fiecărei perechi de biți ce apar în reprezentare în memorie a operanzilor:

0 | 0 == 0

0 | 1 == 1

1 | 0 == 1

1 | 1 == 1

Exemplu:

Să calculăm 13 | 151.

Reprezentarea lui 13 este  0000000000001101. Reprezentarea lui 151 este 0000000010010111:

0000000000001101 |
0000000010010111

Se obține:

0000000010011111, adică  159

Deci: 13 | 151 == 159

Operatorul de disjuncție exclusivă ^

Este un operator binar care are ca rezultat numărul obținut prin disjuncția exclusivă fiecărei perechi de biți ce apar în reprezentare în memorie a operanzilor:

0 ^ 0 == 0

0 ^ 1 == 1

1 ^ 0 == 1

1 ^ 1 == 0

Exemplu:

Să calculăm 13 ^ 151.

Reprezentarea lui 13 este  0000000000001101. Reprezentarea lui 151 este 0000000010010111:

0000000000001101 ^
0000000010010111

Se obține:

0000000010011010, adică  2 + 8 + 16 + 128 = 154.

Deci: 13 ^ 151 == 154

Operatorul de deplasare spre stânga – shift left <<

Este un operator binar care are ca rezultat numărul obținut prin deplasare spre stânga a biților din reprezentarea în memorie a primului operand cu un număr de poziții egal cu al doilea operand.

Să calculăm 13 << 3.

Reprezentarea lui 13 este  0000000000001101. Deplasând toți biții spre stânga cu 3 poziții se obține:  0000000001101000, adică 104.

Să observăm că 104 este egal cu 13 * 23. În general n << k este n * 2k.

Pentru a calcula 2n putem folosi operația 1 << n.

Operatorul de deplasare spre dreapta – shift right >>

Este un operator binar care are ca rezultat numărul obținut prin deplasare spre dreapta a biților din reprezentarea în memorie a primului operand cu un număr de poziții egal cu al doilea operand.

Să calculăm 133 >> 3.

Reprezentarea lui 133 este  0000000010000101. Deplasând toți biții spre dreapta cu 3 poziții se obține:  0000000000010000 adică 16.

Să observăm că 16 este egal cu 133 / 23. În general n >> k este n / 2k.

Metode de sortare
Metode de sortare

QuickSort sau Sortarea rapidă este o metodă eficientă de sortare a unui tablou, descoperită în 1960 de programatorul britanic C.A.R. Hoare

Algoritmul este de tip divide et impera; el sortează o secvență a tabloului (inițial întreg tabloul), astfel:

  1. se alege un element special al listei, numit pivot;
  2. se ordonează elementele listei, astfel încât toate elementele din stânga pivotului să fie mai mici sau egale cu acesta, și toate elementele din dreapta pivotului să fie mai mari sau egale cu acesta;
  3. se continuă recursiv cu secvența din stânga pivotului și cu cea din dreapta lui.

 Exemplu:


 

p=1

 

 

 

 

 

 

 

u=9

24

6

76

43

7

9

12

42

19

Piv=24

[p<u] 1<9  DA

       [ x[p]>x[u] ] 24>19  DA    

19

6

76

43

7

9

12

42

24

        X[p] == Piv  NU     [p++]  p=2

[p<u] 2<9  DA

19

6

76

43

7

9

12

42

24

       [ x[p]>x[u] ] 6>24  NU    

X[p] == Piv  NU     [p++]  p=3

[p<u] 3<9  DA

19

6

76

43

7

9

12

42

24

       [ x[p]>x[u] ] 76>24  DA   

19

6

24

43

7

9

12

42

76

                X[p] == Piv  DA     [u--]  u=8

[p<u] 3<8  DA

19

6

24

43

7

9

12

42

76

       [ x[p]>x[u] ] 24>42  NU   

                X[p] == Piv  DA     [u--]  u=7

[p<u] 3<7  DA

19

6

24

43

7

9

12

42

76

       [ x[p]>x[u] ] 24>12  DA   

19

6

12

43

7

9

24

42

76

                X[p] == Piv  NU     [p++]  p=4

[p<u] 4<7  DA

19

6

12

43

7

9

24

42

76

       [ x[p]>x[u] ] 43>24  DA   

19

6

12

24

7

9

43

42

76

 

                X[p] == Piv  DA     [u--]  u=6

[p<u] 4<6  DA

19

6

12

24

7

9

43

42

76

       [ x[p]>x[u] ] 24>9  DA   

19

6

12

9

7

24

43

42

76

 

                X[p] == Piv  NU     [p++]  p=5

[p<u] 5<6  DA

19

6

12

9

7

24

43

42

76

       [ x[p]>x[u] ] 7>24  NU   

                X[p] == Piv  NU     [p++]  p=6

[p<u] 6<6  NU

 




int poz(int x[50], int p,int u)

{int piv,aux,k;

 piv=x[p];

 while (p<u)

    { if (x[p]>x[u]) {aux=x[p];

                              x[p]=x[u];

                              x[u]=aux;

                              }

       if (x[p]==piv)

              u--;

        else p++;

    }

 k=p;

 return k;

}

 

 

Următoarea funcție C++ realizează sortarea unui tablou, transmis ca parametru:

int poz(int x[50], int p,int u)

{int piv,aux,k;

 piv=x[p];

 while (p<u)

    { if (x[p]>x[u]) {aux=x[p];

                             x[p]=x[u];

              x[u]=aux;

              }

       if (x[p]==piv)

              u--;

        else p++;

    }

 k=p;

 return k;

}

 

void quick(int x[50], int p,int u)

{int k;

 if (p<u) {k=poz(x, p,u);

                quick(x, p,k-1);

                quick(x, k+1,u);}

}

Observații:

  • în timpul pivotării:
    • la fiecare iterație, doar una dintre variabilele p și u se modifică: sau crește p, sau scade u
    • pivotul este elementul cu indicele care nu se modifică
  • algoritmul descris mai sus realizează ordonarea crescătoare a tabloului; pentru ordonarea descrescătoarea algoritmul este asemănător: prin pivotare, elementele din stânga pivotului devin mai mari decât acesta, cele din dreapta devin mai mici;
  • algoritmul este cu atât mai rapid cu cât la fiecare etapă cele două secvențe delimitate de pivot au lungimi cât mai apropiate (ideal egale);

Analiza complexitatii unui algoritm in C++
Analiza complexitatii unui algoritm in C++

De ce este utila aceasta notiune?

In acest tutorial vom discuta despre ce este complexitatea. Acest concept te va ajuta in viitor sa iti dai seama cat timp ii ia programului tau sa ruleze atunci cand numarul de date este foarte mare.

Timpul de rulare poate fi influentat de mai multi factori, precum: ce procesor ai, cata memorie RAM, ce viteza de scriere si citire are sistemul de stocare, s.a.m.d. Asadar, programatorilor le trebuie o metoda comuna prin care sa stabileasca cat de eficienti sunt algoritmii.

Eficienta unui algoritm

Pentru a intelege mai bine de ce este nevoie sa cunoastem aceasta notiune, vom lua urmatoarea problema simpla drept exemplu: Catalin si Paul doresc sa stie daca un numar dat (N) este prim. Din pacate, cei doi au un mod diferit de a determina daca un numar este prim, sau nu.

Ce este un numar prim? Un numar prim este un numar care se imparte doar la 1 și  la el insusiAtentie: 1 nu este numar prim (de ce? citeste propozitia urmatoare mai atent).

Metoda lui Catalin:  foloseste un for ca sa ia toate numerele de la 2 pana la N / 2 si verifica daca are loc impartirea exacta (fara rest). In momentul cand aceasta are loc, inseamna ca numarul nu este prim.

bool ePrim = true;
for(int div = 2; div <= N / 2; div++)
{
if (N % div == 0)
{
ePrim = false;
break;
}
}
cout << "N este prim: " << ePrim;

Metoda lui Paul: foloseste un for ca sa ia toate numerele de la 2 pana la radical din N sii verifica daca are loc impartirea exacta (fara rest). In momentul cand aceasta are loc, inseamna ca numarul nu este prim.

bool ePrim = true;
for(int div = 2; div <= sqrt (N); div++)
{
if( N % div == 0)
{
ePrim = false;
break;
}
}
cout << "N este prim: " << ePrim;

Sa presupunem faptul ca domnul Calculator are nevoie de  1 ms (milisecunda) pentru a calcula o operatie de genul N % div. Vom analiza eficienta acestui algoritm pentru cel mai rau caz (cazul in care nu gasim nici un divizor, iar numarul este prim).

Pentru N = 11 / N = 101 / N = 10,000,000,019 (1010 + 19)

  • Catalin: 5 ms / 50 ms / 5 * 109 ms (~115 zile)
  • Paul: 3 ms / 10 ms / 105 ms (~2 min)

Haideti sa reprezentam acest timp de rulare folosind un grafic.

Puteti vedea cum timpul de rulare pentru algoritmul lui Catalin „creste” mai mult pe masura ce numarul primit este mai mare. In schimb, algoritmul lui Paul nu se „departeaza” foarte mult de orizontala.

Ce este complexitatea unui algoritm?

In informatica, complexitatea unui algoritm masoara cat de repede creste timpul pe masura ce marimea input-ului se mareste. Analizand graficul de mai sus, un algoritm care se aproprie foarte mult de axa Oy este considerat ineficient, iar un algoritm care este mai aproape de axa Ox este considerat eficient.

Complexitatea poate fi:

  • constanta – O(1)
  • logaritmica – O(log2n)
  • liniara – O(n)
  • patratica – O(n2)
  • polinomiala – O(nk)

Complexitatea unui algoritm in timp

Pentru a intelege mai bine, vom atribuii urmatorilor algoritmi cate „1 punct” pentru fiecare operatie. Mai departe vom descrie procedeul prin care calculezi complexitatea in timp:

  1. Aduni „toate punctele” (explicatie mai amanuntita in video)
  2. Selectezi termenul care creste cel mai rapid
  3. Stergi coeficientul

De exemplu, daca, Tr (timpul de rulare) este:

  • T = a  (unde a este constant), atunci complexitatea va fi O(1)
  • T = an + b (unde a si b sunt constante), atunci complexitatea va fi O(n) (pentru ca doar n este o necunoscuta ce creste pe masura ce primim valori)
  • T = bn2 + an + c  (unde a, b si c sunt constante), atunci complexitatea algoritmului va fi O(n2) (pentru ca n2 creste cel mai rapid dintre toti)

Complexitate constanta – O(1)

Sa luam functia care calculeaza suma a doua numere, a si b.

int a, b;
cin >> a >> b;
cout << a + b;

Conform regulii de mai sus, acest algoritm va avea „1 punct. deoarece a facut o operatie. Daca aveam 10 numere (a, b, c, d, …) atunci algoritmul aveam „9 puncte”. Aplicand regula de inainte, calculam complexitatea, dar nu avem nici un termen care ar influenta rata de crestere a algoritmului, asadar acesta este constant.

Complexitate liniara – O(n)

Sa luam acum, un algoritm putin mai diferit, si vom calcula suma elementelor unui vector.

int n = 7, suma = 0;
int V[] = {42, 86, 17, 25, 15, 63, 12};
for(int i = 0; i < n; i++)
suma = suma + V[i];
cout << "Suma este: " << suma;

In momentul cand parcurgem n elemente dintr-un vector, vom efectua n operatii, asadar avem „n puncte”. Aplicand regulile dinainte, vom avea complexitatea O(n) unde n reprezinta numarul total de elemente din vector.

Sa luam alt algoritm in schimb, sa presupunem ca avem doi vectori.

int n = 7, m = 5, suma = 0;
int V[] = {42, 86, 17, 25, 15, 63, 12 } ;
for(int i = 0; i < n; i++)
suma = suma + V [i];
int U[] = {420, 18, 58, 12, 1};
for(int i = 0; i < m; i++)
suma = suma + U[i];
cout << "Suma este: " << suma;

In acest caz, dupa ce calculam punctele obtinem O(n + m), dar aplicand cele trei reguli de mai sus, scoatem numarul m din calcul (deoarece trebuie sa selectez doar cel care creste cel mai rapid – iar amandoi cresc la fel de rapid), si obtinem complexitatea O(n).

Complexitate patratica – O(n2)

Iar acum vom lua un alt exemplu, sa presupunem ca dorim sa aflam produsul cartezian a doi vectori. Vom avea urmatorul algoritm:

int n = 3, m = 2;
int V[] = {42, 86, 17};
int U[] = {420, 18};
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++ )
cout << "(" << V[i] << ", " << U [j] << ")\n";
}

Adunand punctele, observam ca avem un total de n * m, asadar complexitatea acestui algoritm este O(n*m) , iar daca m = n, complexitatea devine una patratica – adica  O(n2) .

In concluzie, pentru a calcula complexitatea unui algoritm, trebuie sa calculam pe rand cate puncte „obtine” fiecare particica mai mica din acesta. Pe masura ce spargem problema noastra in probleme mai mici, vom calcula „totalul punctelor”, iar mai apoi vom aplica regulile de mai sus pentru a determina complexitatea in timp a acestuita. Asadar, de-acum in-colo cand cineva o sa va intrebe: „cat de rapid este algoritmul tau?” ii vei raspunde „are o complexitate liniara in timp” cu in loc de „merge sub 2 secunde”.