STL în C++

STL (Standard Template Library) reprezintă structuri, funcții și algoritmi care pot fi de ajutor atunci când avem nevoie să creăm algoritmi rapizi. Câteva librării din STL sunt <vector>, <set>, <unordered_multiset>, <map>, însă în acest articol vom discuta doar despre primele două.

Folosind librăria <vector>, putem face un vector care vine automat cu câteva funcții ajutătoare. Foarte important este că vectorul pe care îl știe toată lumea (int v[50]) se numește de fapt array și nu este la fel cu vectorul din STL. Pentru a include această librărie trebuie să introducem #include <vector> la începutul sursei, iar ca să declarăm un vector scriem vector<int> v;. Această scriere va crea un vector numit v care va conține doar elemente de tip int. Observăm că, la declararea vectorilor în STL, nu trebuie specificată dimensiunea vectorului, spre deosebire de declararea unui array. Pentru a accesa elemente și a atribui valori în vector, sintaxa este la fel ca la un array.

Exemplu de algoritm care folosește vectorii din STL
Exemplu de date de intrare și afișarea datelor de ieșire pentru algoritmul de mai sus

Înainte de descrierea algoritmului de mai sus, trebuie să știți că vectorii din STL trebuie să pornească de la indicele 0 pentru a funcționa corespunzător. Dacă prima valoare din vector este pe poziția 1, unele funcții nu vor mai returna rezultatul dorit. La începutul sursei, se includ librăriile <iostream> și <vector> pentru a putea citi și afișa pe ecran și pentru a folosi vectorii din STL. În funcția main, se declară un vector în STL v care va avea doar elemente de tipul int, și 3 variabile de tipul int: n, i și x. Citim n de la tastatură, care va reprezenta numărul de elemente pe care le vom citi în continuare. Apoi, de la i=0 până la i=n-1, vom citi un număr în variabila x, și îl vom pune la sfârșitul vectorului folosind funcția v.push_back(x). După ce se termină bucla anterioară, eliminăm ultimul element din vector folosind funcția v.pop_back() și adăugăm la sfârșitul vectorului valoarea 5. Practic, înlocuim ultima valoare cu 5. După aceasta, elementului de pe poziția 2, adică elementului al treilea din vector (pentru că indexarea începe de la 0) i se va atribui valoarea 1, iar apoi se va face afișarea elementelor vectorului astfel: „pentru fiecare element nr din vectorul v, afișează nr”. Această scriere poate fi folositoare, și uneori obligatorie. auto nr înseamnă că nu i-am specificat tipul de dată variabilei nr și compilatorul trebuie să își dea seama singur ce tip de dată are. Sigur că auto nr putea fi înlocuit cu int nr, doar că acea scriere este mai folosită, mai ales atunci când avem de-a face cu iteratori.

La fiecare tip de dată din STL există o funcție numită size() care returnează numărul de elemente din acea structură (v.size()).

Un set este tot o structură de date care memorează valori, însă aceste valori sunt ordonate automat la introducerea lor în acea structură; de asemenea, dacă un element introdus există deja în set, nu va mai fi introdus. Cu alte cuvinte, set memorează valori ordonate și distincte. Există și unordered_set (memorează valori distincte, neordonate), multiset (memorează toate valorile, ordonate) și unordered_multiset (memorează toate valorile, neordonate).

La structura STL set, accesarea și atribuirea elementelor nu se mai poate face cu indici (v[5] = 62), ci se face doar prin funcții și prin iteratori.

Un iterator reprezintă o porțiune din memorie care poate să succede sau să precede în memorie. Iteratorii sunt specifici pentru fiecare structură și fiecare tip de dată din structură, și se declară cu următoarea formă: structurăstl<tipdedată>::iterator numeiterator;. Pentru a crea un iterator pentru structura set și tipul de date int, scriem set<int>::iterator itr;.

Exemplu de algoritm care folosește structura set din STL
Exemplu de date de intrare și afișarea datelor de ieșire la programul din exemplul de mai sus

La început, algoritmul de mai sus include librăriile iostream și set. Apoi, în main, se declară un set v care conține valori int, un iterator itr pentru acest set și 3 variabile int: n, x și i. După ce se citește n de la tastatură, mergem cu un for de la i=0 până la i=n-1, citim x de la tastatură și îl inserăm în set folosind funcția v.insert(x). Atunci când se inserează un element într-un set, elementul este așezat automat într-o poziție astfel încât numerele să fie ordonate. După această buclă for, afișăm n și v.size() pentru a compara numărul de elemente de la început și numărul de elemente de la sfârșit. La afișarea datelor de ieșire, observăm că n este diferit de v.size(). Aceasta se datorează faptului că există 2 numere identice în datele de intrare care, odată cu încercarea introducerii amândurora în set, unul nu se va mai insera. Putem vedea acest lucru și la afișarea elementelor din set, numărul 56 nefiind afișat de două ori. Înapoi la algoritm — observăm un for care atribuie valoarea v.begin() lui itr, compară valoarea lui itr cu v.end() și incrementează pe itr cu 1. Mai jos este o imagine care reprezintă elementele din set după ce au fost inserate, precum și unde se află v.begin() și v.end(). Un lucru important de reținut este că v.begin() și v.end() nu sunt valori. Acestea reprezintă doi iteratori cu ajutorul cărora putem parcurge set-ul, adică două puncte în memorie.

Atribuim iteratorului nostru itr valoarea v.begin(), adică îi arătăm în memorie de unde să înceapă for-ul. Iteratorii pot fi incrementați cu 1 folosind operatorul ++, deci începem de la v.begin(), adunăm iteratorul cu 1 de fiecare dată, și trebuie să ne oprim cu această parcurgere atunci când itr ajunge la v.end(), deci, ca condiție pentru for, itr != v.end(). Forma finală a for-ului este prezentată în imaginea cu codul sursă de mai sus. Pentru a afișa valoarea iteratorului la care suntem în for, folosim asterisk-ul (*) înaintea iteratorului. Folosind această notație, compilatorul va ști că vrem valoarea numerică din iterator, și nu locul în memorie unde se află iteratorul. De aceea, în for dăm cout la *itr, adică la numărul din locul unde se află iteratorul.

Foarte important de cunoscut este și structura map. Există mult mai multe tipuri de structuri de date în STL, o listă cu ele o puteți găsi aici.

STL-ul este foarte rapid ca timp de execuție și este important să știți să îl folosiți când vă aflați la olimpiade sau concursuri de informatică, sau chiar atunci când faceți probleme de informatică de acasă. Pentru că acesta nu se predă, de obicei, în școli, trebuie să citiți și să învățați de pe internet aceste noțiuni elementare ale limbajului C++. Spor la învățat!