This shows you the differences between two versions of the page.
|
poo-is:laboratoare:11 [2020/09/30 22:10] eduard.ciurezu [Biblioteca <algorithm>] |
— (current) | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ===== Laboratorul 11: STL ===== | ||
| - | In cadrul acestui laborator vom aprofunda cunostintele legate de componentele parametrizate, functii clase template, si vom vorbi despre **STL** (**Standard Template Library**). Aceasta biblioteca generalizata ne va permite sa folosim algoritmi, structuri de date deja implementate. | ||
| - | |||
| - | Ca referinte externe, recomandam urmatorul capitol din [[https://discourse-production.oss-cn-shanghai.aliyuncs.com/original/3X/2/3/2380479dcb8e375425884a10da232730bbc7f88d.pdf|Absolute C++]]: | ||
| - | * Capitolul 19 (Chapter 19: Standard Template Library, pag. 859-912) | ||
| - | ===== Introducere ===== | ||
| - | **STL** (**Standard Template Library**) face parte din **C++ Standard Library** si este un set de clase template ce implementeaza structuri de date cunoscute si metode asociate lor. | ||
| - | |||
| - | <note> | ||
| - | **STL** contine trei componente cheie: | ||
| - | * **Clase Container** | ||
| - | * **Iteratori** | ||
| - | * **Algoritmi** | ||
| - | </note> | ||
| - | |||
| - | **Containerul** reprezinta un obiect ce contine o colectie de alte obiecte. Clasele **container** implementeaza structuri de date clasice sub forma template, astfel ofera flexibilitate in utilizare. De asemenea, se ocupa de managementul spatiului de memorie pentru elemente si pun la dispozitie functii membre pentru accesul la obiecte. | ||
| - | |||
| - | **Iteratorii** permit accesul la elementele dintr-un container sau ajuta la parcurgerea acestora, independent de modul in care sunt stocate. **Iteratorii** reprezinta o **generalizare a pointerilor**, fiind obiecte ce indica alte obiecte (elementele din container). | ||
| - | <note>Fiecare **clasa container** are definiti **iteratorii** proprii.</note> | ||
| - | **STL** puna la dispozitie **algoritmi** pentru procesarea elementelor din colectii (cautare, partitionare, ordonare, lucru cu multimi, gasire minim/maxim, etc.). **Algoritmii** au ca parametrii iteratori si reprezinta functii template ce nu sunt metode ale claselor container. | ||
| - | ===== Containere ===== | ||
| - | Exista mai multe tipuri de containere: | ||
| - | * **Containere secventiale (sequence containers)** – contin o succesiune de elemente de acelasi tip T, iar fiecare element are o pozitie in container. Pozitia depinde de locul sau momentul cand obiectul a fost inserat in structura. Clase: **vector**, **deque**, **list**; C++11: **array**, **forward_list**. | ||
| - | |||
| - | * **Adaptoare de containere (container adaptors)** – clase ce pun la dispozitie o interfata specifica, bazandu-se pe un obiect container de baza, cum ar fi deque sau list. Clase: **stack**, **queue**, **priority_queue**. | ||
| - | |||
| - | * **Containere asociative (associative containers)** – contin colectii de elemente in care pozitia unui element depinde de valoarea lui in functie de criteriul de sortare. Acestea permit accesul rapid la un element prin intermediul unei chei. Clase: **set**, **multiset**, **map**, **multimap**. | ||
| - | |||
| - | * **Containere aosciative neordonate (unordored associative containers)** – C++11: **unordered_set**, **unordered_multiset**, **unordered_map**, **unordered_multimap**. | ||
| - | |||
| - | ^Structura de date implementata ^ Nume STL ^ #include ^ | ||
| - | |Tablou dinamic | vector | <vector> | | ||
| - | |Lista dublu inlantuita | list | <list> | | ||
| - | |Stiva | stack | <stack> | | ||
| - | |Coada cu doua capete | queue | <queue> | | ||
| - | |Arbore binar/Multime | set | <set> | | ||
| - | |Multime (se pot repeta valorile) | multiset | <set> | | ||
| - | |Hash table | map | <map> | | ||
| - | |Hash table (se pot repeta valorile) | multimap | <map> | | ||
| - | |Max-heap | priority_queue | <queue> | | ||
| - | |||
| - | <note> | ||
| - | **Toate containerele** sunt **template**. <code>Exemplu: vector<int> vect;</code> | ||
| - | |||
| - | **Toate containerele** au **iteratori** asociati, iar tipul lui e specificat prin: <code>container<T>::iterator</code> | ||
| - | |||
| - | **Toate containerele** au urmatoarele **functii membre**: | ||
| - | ^Functie membra ^ Rezultat ^ | ||
| - | |int size() | returneaza numarul de elemente din container | | ||
| - | |iterator begin() | returneaza iterator la primul element | | ||
| - | |iterator end() | returneaza iterator la nodul santinela (ultimul element al containerului) | | ||
| - | |bool empty() | returneaza true daca obiectul container este gol | | ||
| - | </note> | ||
| - | |||
| - | ==== Vector ==== | ||
| - | Clasa **vector** permite realizarea unui vector alocat dinamic, ce poate contine elemente de orice tip. | ||
| - | |||
| - | Clasa permite acces usor la orice element prin operatorul de indexare sau prin iteratori. | ||
| - | |||
| - | Implementarea din **STL** pune la dispozitie **constructori**, **operator=**, **operator[]**, diferite metode pentru **manipularea obiectelor** din vector, **realocarea** spatiului, etc. | ||
| - | |||
| - | == Exemplu apel constructori: == | ||
| - | <code c++> | ||
| - | #include <vector> | ||
| - | using namespace std; | ||
| - | |||
| - | vector<int> a; //Vector gol cu elemente de tip int | ||
| - | vector<double> b(10); //Vector cu 10 elemente de tip double | ||
| - | vector<int> c(5,9); //Vector cu 5 elemente de tip int initializate cu valoarea 9 | ||
| - | vector<int> d(a); //Copia vectorului a | ||
| - | </code> | ||
| - | |||
| - | <note> | ||
| - | Cateva metode din clasa **vector**:\\ | ||
| - | **push_back()** – adauga un element la finalul vectorului si creste dimensiunea acestuia cu 1\\ | ||
| - | **pop_back()** – scoate ultimul element din vector si reduce dimensiunea acestuia cu 1\\ | ||
| - | **clear()** – scoate toate elementele din vector si seteaza dimensiunea acestuia la 0\\ | ||
| - | **empty()** – returneaza true daca vectorul este gol (false altfel)\\ | ||
| - | **resize()** – modifica dimensiunea vectorului\\ | ||
| - | **capacity()** – returneaza capacitatea setata prin constructor sau cu functia resize\\ | ||
| - | **insert()** – insereaza un element pe pozitia specificata; creste dimensiunea\\ | ||
| - | **erase()** – scoate elementul de pe pozitia specificata; scade dimensiunea\\ | ||
| - | </note> | ||
| - | |||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasa **vector**, recomandam urmatoarea documentatie: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/vector/vector/]]</note> | ||
| - | |||
| - | ==== List ==== | ||
| - | Containerul **List** implementeaza o **lista dublu inlantuita**, ale carei elemente sunt imprastiate prin memorie si conectate prin pointeri. Lista poate fi parcursa in ambele sensuri, atat inainte, cat si inapoi. | ||
| - | |||
| - | Putem adauga sau elimina elemente atat de la inceputul listei, cat si de la finalul acesteia folosind metodele: **push_back**, **push_front**, **pop_back**, **pop_front**. Complexitatea acestor operatii este **O(1)**. | ||
| - | |||
| - | Pentru a adauga sau sterge elemente de pe anumite pozitii se folosesc **iteratori**. | ||
| - | |||
| - | Principalul dezavantaj al listelor este ca **NU** se pot accesa imediat valori de pe o anumita pozitie. | ||
| - | |||
| - | Inserarea, mutarea si stergea unui element se face cu **costuri mici**, indiferent de pozitie. | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <list> | ||
| - | #include <iterator> | ||
| - | using namespace std; | ||
| - | |||
| - | //Functie pentru afisarea elementelor din container | ||
| - | void showlist(list<int> g) | ||
| - | { | ||
| - | //Iterator lista | ||
| - | list<int>::iterator it; | ||
| - | | ||
| - | //Parcurgere container | ||
| - | for(it = g.begin(); it != g.end(); ++it) { | ||
| - | cout << *it << ' '; | ||
| - | } | ||
| - | cout << endl; | ||
| - | } | ||
| - | |||
| - | int main() { | ||
| - | list<int> myList; | ||
| - | |||
| - | myList.push_back(5); //Adauga elementul 5 la finalul listei | ||
| - | myList.push_front(3); //Adauga elementul 3 la inceputul listei | ||
| - | myList.push_back(10); //Adauga elementul 10 la finalul listei | ||
| - | myList.push_front(1); //Adauga elementul 1 la inceputul listei | ||
| - | |||
| - | showlist(myList); //Afiseaza 1 3 5 10 | ||
| - | |||
| - | return 0; | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasa **list**, recomandam urmatoarea documentatie: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/list/list/]]</note> | ||
| - | |||
| - | ==== Stack si Queue (Stiva si Coada) ==== | ||
| - | |||
| - | Containerul **stack** implementeaza o **stiva**, adica o lista simplu inlantuita de tip **LIFO** (**Last In First Out**). Varful stivei este singurul obiect al containerului care poate fi accesat. | ||
| - | |||
| - | Containerul **queue** implementeaza o **coada**, adica o lista simplu inlantuita de tip **FIFO** (**First In First Out**). Avem acces atat la inceputul cozii, cat si la finalul acesteia. | ||
| - | |||
| - | In ambele containere o sa se adauge elemente folosind metoda **push** si vor fi eliminate elemente folosind metoda **pop**. | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <queue> | ||
| - | #include <stack> | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | //Stiva cu elementele de tip char | ||
| - | stack<char> s; | ||
| - | //Coada cu elementele de tip int | ||
| - | queue<int> q; | ||
| - | |||
| - | //Adaugare elemente in varful stivei folosind push | ||
| - | s.push('a'); | ||
| - | s.push('b'); | ||
| - | s.push('c'); | ||
| - | |||
| - | //Adaugare elemente la sfarsitul cozii folosind push | ||
| - | q.push(0); | ||
| - | q.push(1); | ||
| - | q.push(2); | ||
| - | |||
| - | //Cat timp stiva nu este goala | ||
| - | while (!s.empty()) { | ||
| - | cout << s.top() << ' '; //Afisam elementul din varful stivei | ||
| - | s.pop(); //Eliminam elementul din varful stivei | ||
| - | } | ||
| - | cout << endl; | ||
| - | //Se va afisa: | ||
| - | //c b a | ||
| - | |||
| - | //Cat timp coada nu este goala | ||
| - | while (!q.empty()) { | ||
| - | cout << q.front() << ' '; //Afisam elementul de la inceputul cozii | ||
| - | q.pop(); //Eliminam elementul de la inceputul cozii | ||
| - | } | ||
| - | //Se va afisa: | ||
| - | //0 1 2 | ||
| - | |||
| - | return 0; | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | <note warning>Stivele si cozile **NU** au iteratori disponibili!</note> | ||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasele **stack** si **queue**, recomandam urmatoarele documentatii: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/stack/stack/]] | ||
| - | [[http://www.cplusplus.com/reference/queue/queue/]] | ||
| - | </note> | ||
| - | |||
| - | ==== Priority Queue (Heap) ==== | ||
| - | Containerul **priority_queue** implementeaza **o coada cu prioritati**, adica o structura de tip **max-heap**, in care cea mai mare valoare este intotdeauna in varf. | ||
| - | |||
| - | Avand in vedere faptul ca se fac comparatii intre valorile tipurilor de date prezente in container, trebuie sa definim mereu **operator<** pentru tipul respectiv de date. | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <queue> | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | //Container cu elemente de tip int | ||
| - | priority_queue<int> pq; | ||
| - | |||
| - | //Adaugam elemente in coada cu prioritati | ||
| - | pq.push(10); | ||
| - | pq.push(20); | ||
| - | pq.push(5); | ||
| - | //Eliminam elementul din varf, adica elementul 20 (cu cea mai mare prioritate) | ||
| - | pq.pop(); | ||
| - | |||
| - | cout << pq.top() << endl; //Se va afisa 10 | ||
| - | |||
| - | return 0; | ||
| - | } | ||
| - | |||
| - | </code> | ||
| - | |||
| - | <note warning>Nici coada cu prioritati **NU** are iteratori disponibili!</note> | ||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasa **priority_queue**, recomandam urmatoarea documentatie: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/queue/priority_queue/]]</note> | ||
| - | ==== Set (Arbori Binari) ==== | ||
| - | Containerele de tip **set** realizeaza un **arbore binar balansat (de cautare)**, deci cautarea unui element va fi eficienta. | ||
| - | |||
| - | Pentru a se crea o multime, tipul de date trebuie sa aiba **operator<** definit. | ||
| - | |||
| - | Toate elementele din containerul **set** sunt **distincte** (in caz contrar se foloseste containerul **multiset**). | ||
| - | |||
| - | Obiectele din container sunt **ordonate** si nu mai pot fi modificate, acestea pot fi doar sterse sau se pot adauga noi obiecte. | ||
| - | |||
| - | |||
| - | <note> | ||
| - | Operatii de baza pentru containerul **set**: | ||
| - | * **insert(element)** | ||
| - | * **erase(iterator)** | ||
| - | * **erase(valoare element)** | ||
| - | * **iterator find(valoare element)** | ||
| - | </note> | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <set> | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | //Multime cu elemente de tip int | ||
| - | set<int> mySet; | ||
| - | |||
| - | //Adaugam elemente in containerul set | ||
| - | mySet.insert(1); | ||
| - | mySet.insert(4); | ||
| - | mySet.insert(2); | ||
| - | mySet.insert(5); | ||
| - | mySet.insert(3); | ||
| - | | ||
| - | //Iterator pentru parcurgere set | ||
| - | set<int>::iterator it; | ||
| - | | ||
| - | //Parcurgem containerul set | ||
| - | for (it = mySet.begin(); it != mySet.end(); it++) { | ||
| - | cout << *it <<' '; //Afisare element | ||
| - | } | ||
| - | cout << endl; | ||
| - | //Se va afisa: | ||
| - | //1 2 3 4 5 | ||
| - | |||
| - | return 0; | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasa **set**, recomandam urmatoarea documentatie: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/set/set/]]</note> | ||
| - | ==== Map (Hash Table) ==== | ||
| - | Un container de tip **map** ne permite sa cautam intr-o maniera rapida un obiect folosind **o cheie unica** (spre exemplu cautam o persoana dupa id-ul acesteia). | ||
| - | |||
| - | Un astfel de container foloseste **o pereche** de tipuri template: | ||
| - | <code>map<K, T></code> | ||
| - | **K** reprezinta tipul de date al cheii, iar **T** reprezinta tipul de date pe care vrem sa le stocam. | ||
| - | |||
| - | Containerul **map** va contine elementele indexate cu indecsi de tipul **K** al cheii. | ||
| - | |||
| - | Pentru tipul de date **K** al cheii este nevoie ca **operator<** sa fie definit, altfel se va folosi un container de tip **unordered_map**. | ||
| - | |||
| - | Elementele containerului sunt mereu **sortate** in functie de cheie (similar cu un **arbore binar de cautare**). | ||
| - | <note> | ||
| - | Functia **insert** pentru containerul **map** asteapta un argument de tip **pair**. | ||
| - | |||
| - | **Pair** este o structura template care are atributele **first** si **second**. | ||
| - | <code c++> | ||
| - | map<int, char> myMap; | ||
| - | |||
| - | pair<int, char> obj(10, 'x'); | ||
| - | myMap.insert(obj); | ||
| - | </code> | ||
| - | </note> | ||
| - | |||
| - | <note tip> | ||
| - | Se poate folosi functia **operator[]** pentru inserarea in map. | ||
| - | <code c++> | ||
| - | map<char, double> myMap; | ||
| - | |||
| - | myMap['a'] = 20.189; | ||
| - | myMap['b'] = 3.1415; | ||
| - | </code> | ||
| - | </note> | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <map> | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | //Map cu tipurile de date char, double | ||
| - | map<char, double> myMap; | ||
| - | //Iterator pentru map | ||
| - | map<char, double>::iterator it; | ||
| - | | ||
| - | //Adaugare element folosind operator[] | ||
| - | myMap['a'] = 18.91; | ||
| - | | ||
| - | //Adaugare element folosind insert() si pair | ||
| - | pair<char, double> obj('b', 3.1415); | ||
| - | myMap.insert(obj); | ||
| - | | ||
| - | it = myMap.find('a'); //Cautam elementul cu cheia 'a' | ||
| - | cout << it->second << endl; //Se va afisa 18.91 | ||
| - | | ||
| - | it = myMap.find('b'); //Cautam elementul cu cheia 'b' | ||
| - | cout << it->second << endl; //Se va afisa 3.1415 | ||
| - | | ||
| - | return 0; | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | <note tip>Pentru **toate metodele** puse la dispozitie de clasa **map**, recomandam urmatoarea documentatie: | ||
| - | |||
| - | [[http://www.cplusplus.com/reference/map/map/]]</note> | ||
| - | |||
| - | ==== Ce si cand folosim? ==== | ||
| - | <note> | ||
| - | * Daca dorim sa cautam in mod rapid date pe baza unei chei, o sa folosim **map**. | ||
| - | * Elementele sunt **unice**, au **operator<** definit si sunt de interes cautari rapide, utilizam **set**. | ||
| - | * Daca este nevoie in principal de elementul cu **cea mai mare** valoare, folosim **priority_queue**. | ||
| - | * Daca este de interes adaugarea rapide de elemente si nu sunt accesate des, o sa utilizam **list**. | ||
| - | * Daca depindem de accesul rapid la elemente stocate pe o anumita pozitie, recomandat este **vector**. | ||
| - | </note> | ||
| - | ===== Iteratori ===== | ||
| - | <note> | ||
| - | Urmatoarele **operatii de pointeri** sunt definite pentru aproape toti **iteratorii**: | ||
| - | * <code>it++ //muta iteratorul it cu o pozitie inainte</code> | ||
| - | * <code>it-- //muta iteratorul it cu o pozitie inapoi</code> | ||
| - | * <code>it1 = it2 //atribuie iteratorului it1 iteratorul it2</code> | ||
| - | * <code>it1 == it2 si it1 != it2 //compara pozitiile dintre iteratorii it1 si it2</code> | ||
| - | </note> | ||
| - | |||
| - | <note tip> | ||
| - | In clasa **vector** iteratorul poate fi mutat cu **mai multe** pozitii intainte sau inapoi. | ||
| - | <code>it = it + 4;</code> | ||
| - | sau | ||
| - | <code>it = it - 3;</code> | ||
| - | </note> | ||
| - | |||
| - | Toate containerele **STL** contin metodele **begin()** si **end()** care returneaza iteratori. | ||
| - | |||
| - | Metoda **begin()** pointeaza la **primul** element al containerului. | ||
| - | |||
| - | Metoda **end()** pointeaza la nodul **santinela** (care nu contine niciun element, se afla imediat dupa ultimul element al containerului). | ||
| - | |||
| - | <note important> | ||
| - | Cand o functie de cautare **NU** gaseste elementul pe care il cauta returneaza iteratorul **end()**. | ||
| - | </note> | ||
| - | |||
| - | ===== Biblioteca <algorithm> ===== | ||
| - | Contine o serie de **functii** care sunt utile pentru lucrul cu elementele din containere: | ||
| - | * **find** | ||
| - | * **remove** | ||
| - | * **count** | ||
| - | * **shuffle** | ||
| - | * **replace** | ||
| - | |||
| - | De obicei, functiile primesc ca parametri **iteratori** pentru pozitiile de inceput si final ale intervalului unde vrem sa realizam operatia. | ||
| - | <note warning>**NU** sunt functii membre ale claselor!</note> | ||
| - | |||
| - | == Exemplu: == | ||
| - | <code c++> | ||
| - | #include <iostream> | ||
| - | #include <vector> | ||
| - | #include <algorithm> | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | //Container vector cu elemente de tip int | ||
| - | vector<int> vect; | ||
| - | | ||
| - | //Adaugam elemente | ||
| - | vect.push_back(10); | ||
| - | vect.push_back(30); | ||
| - | vect.push_back(15); | ||
| - | vect.push_back(20); | ||
| - | vect.push_back(10); | ||
| - | | ||
| - | //Iterator pentru parcurgere vector | ||
| - | vector<int>::iterator it; | ||
| - | | ||
| - | for (it = vect.begin(); it != vect.end(); it++) { | ||
| - | cout << *it << ' '; //Afisam elementele | ||
| - | } | ||
| - | cout << endl; | ||
| - | //Se va afisa: | ||
| - | //10 30 15 20 10 | ||
| - | | ||
| - | //Sortam vectorul folosind functia sort | ||
| - | sort(vect.begin(), vect.end()); | ||
| - | | ||
| - | for (it = vect.begin(); it != vect.end(); it++) { | ||
| - | cout << *it << ' '; //Afisam elementele | ||
| - | } | ||
| - | cout << endl; | ||
| - | //Se va afisa: | ||
| - | //10 10 15 20 30 | ||
| - | | ||
| - | cout << count(vect.begin(), vect.end(), 10) << endl; //Se va afisa 2 (numarul de aparitii al lui 10) | ||
| - | | ||
| - | return 0; | ||
| - | } | ||
| - | </code> | ||