Sunteți pe pagina 1din 7

Lectia nr.

10 Centru HaiLaOlimpiada Vaslui

Vector in STL

Containerul secvenţă vector

Un vector (tablou) este o structură de dată în care datele sunt stocate într-o
zonă contiguă de memorie. Această organizare a datelor permite accesul direct la
elementele vectorului prin operatorul []. Atunci când memoria obiectului de tip
vector nu mai este suficientă, se alocă o zonă mai mare de memorie contiguă, se
copiază elementele originale în noua zonă de memorie şi se dealocă vechea zonă de
memorie.
O caracteristică importantă a oricărui container este tipul de iterator pe care îl
suportă. Acesta determină ce algoritmi pot fi aplicaţi containerului. Un vector
suportă iteratorul random-access, adică poate fi folosit împreună cu orice operator
prezentat mai devreme. Toţi algoritmii STL pot opera asupra unui vector. Iteratorii
pentru un vector sunt implementaţi ca pointeri la elementele vectorului. Dacă un
algoritm are nevoie de un anumit tip de iterator pentru a funcţiona, atunci containerul
asupra căruia se aplică trebuie să poată lucra cel puţin cu acel tip de iterator.

Exemplul următor prezintă câteva dintre funcţionalităţile clasei template


vector, multe dintre aceste funcţii fiind însă utilizabile de orice container STL de tip
first-class.
Exemplu
test_vector.cpp
#include<iostream>
#include <vector>
using namespace std;
int main()
{
const int SIZE = 6;
int a[SIZE] = {1, 2, 3, 4, 5, 6};
vector<int> v;
cout << "Dimensiunea initiala a lui v este: " << v.size()
<< "\nCapacitatea initiala a lui v este: "
<< v.capacity();
v.push_back(2); //metoda push_back se gaseste
v.push_back(3); //in orice colectie secventa
v.push_back(4);
cout << "\nDimensiunea lui v este: " << v.size()
<< "\nCapacitatea lui v este: " << v.capacity();
cout << "\n\nContinutul tabloului folosind notatia pointer:
";
for(int *ptr = a; ptr != a + SIZE; ++ptr)
cout << *ptr << ' ';
cout << "\nContinutul vectorului v folosind iteratorul: ";
vector<int>::const_iterator p1;
for(p1 = v.begin(); p1 != v.end(); p1++)
cout << *p1 << ' ';
cout << "\nContinutul inversat al vectorului v: ";
vector<int>::reverse_iterator p2;
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

for(p2 = v.rbegin(); p2 != v.rend(); ++p2)


cout << *p2 << ' ';
cout << endl;
return 0;
}
Rulând acest program, obţinem următorul rezultat:

Dimensiunea initiala a lui v este: 0


Capacitatea initiala a lui v este: 0
Dimensiunea lui v este: 3
Capacitatea lui v este: 4
Continutul tabloului folosind notatia pointer: 1 2 3 4 5 6
Continutul vectorului v folosind iteratorul: 2 3 4
Continutul inversat al vectorului v: 4 3 2

Prin instrucţiunea
vector<int> v;
se declară obiectul v din clasa vector care poate păstra valori int. Odată cu
instanţierea acestui obiect, se creează un vector vid de dimensiune 0, adică numărul
de elemente stocate în acel moment este 0, şi capacitate 0, adică numărul de
elemente fără alocare de memorie este 0.

Apelurile de funcţii v.size() şi v.capacity() returnează aceste două valori.

Funcţia push_back inserează o valoare în container. Dacă se inserează o


valoare într-un vector plin, dimensiunea acestuia creşte automat. Un nou apel al
funcţiilor v.size() şi v.capacity() ilustrează această modificare a dimensiunii
şi a capacităţii vectorului.
Pentru afişarea conţinutului obiectului v în ordine, se instanţiază obiectul p1 de
tip const_iterator. Funcţia v.begin() returnează un const_iterator la
primul element din v, v.end() returnează un const_iterator care indică poziţia
de după ultimul element din v, iar operaţia p1++ poziţionează iteratorul pe următorul
element din v.
Afişarea valorii de la poziţia curentă a iteratorului se face prin dereferenţierea acestuia: *p1.
Afişarea valorilor în ordine inversă se face cu ajutorului obiectului p2 de tip
reverse_iterator.
Funcţia v.rbegin() returnează un iterator la punctul de start
pentru iterarea în ordine inversă , iar v.end() returnează un iterator la punctul de
terminare a iterării în ordine inversă.

Următorul program prezintă funcţii care permit diverse manipulări ale valorilor
unui obiect de tip vector.
Exemplu
test_vector_manipulation.cpp
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <exception>
using namespace std;
int main()
{
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

const int SIZE = 6;


int a[SIZE] = {1, 2, 3, 4, 5, 6};
vector<int> v(a, a+SIZE);
ostream_iterator<int> output(cout, " ");
cout << "Vectorul v contine: ";
copy(v.begin(), v.end(), output);
cout << "\nPrimul element din v: " << v.front()
<< "\nUltimul element din v: " << v.back();
v[0] = 7;
v.at(2) = 10;
v.insert(v.begin()+1, 22);
cout << "\nContinutul vectorului v dupa modificari: ";
copy(v.begin(), v.end(), output);
try
{
v.at(100) = 725;
}
catch(exception& e)
{
cout << "\nExceptie: " << e.what();
}
v.erase(v.begin());
cout << "\nContinutul vectorului v dupa stergere: ";
copy(v.begin(), v.end(), output);
v.erase(v.begin(), v.end());
cout << "\nDupa stergere, vectorul v "
<< (v.empty() ? "" : "nu ") << "este vid";
v.insert(v.begin(), a, a+SIZE);
cout << "\nContinutul vectorului v inainte de stergere: ";
copy(v.begin(), v.end(), output);
v.clear();
cout << "\nDupa stergere, vectorul v "
<< (v.empty() ? "" : "nu ") << "este vid";
cout << endl;
return 0;
}

Rulând acest program, obţinem următorul rezultat:


Vectorul v contine: 1 2 3 4 5 6
Primul element din v: 1
Ultimul element din v: 6
Continutul vectorului v dupa modificari: 7 22 2 10 4 5 6
Exceptie: vector::_M_range_check: __n (which is 100) >= this-
>size() (which is 7)
Continutul vectorului v dupa stergere: 22 2 10 4 5 6
Dupa stergere, vectorul v este vid
Continutul vectorului v inainte de stergere: 1 2 3 4 5 6
Dupa stergere, vectorul v este vid

Prin instrucţiunea
vector<int> v(a, a+SIZE);
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

se declară un vector v cu elemente de tip int. Se apelează un constructor


supraîncărcat al acestei clase care primeşte două argumente. Pointerii la un tablou
pot fi folosiţi ca iteratori, astfel că prin aceasta declaraţie vectorul v este iniţializat cu
conţinutul tabloului a, de la locaţia a până la a+SIZE, fără să o includă şi pe aceasta.
Prin ostream_iterator<int> output(cout, " ");
se defineşte un ostream_iterator cu numele output care poate fi folosit pentru
afişarea la cout a unor valori întregi separate prin spaţiu.

Algoritmul copy din STL este invocat prin apelul


copy(v.begin(), v.end(), output);
pentru a trimite către output conţinutul vectorul v dintre v.begin() şi v.end().
Primii doi parametri sunt iteratori ale căror valori sunt citite dintr-un container, iar
ultimul parametru este un iterator de ieşire. Pentru a putea apela această funcţie,
trebuie inclus fişierul header <algorithm> în codul sursă.
Liniile de program
v[0] = 7;
v.at(2) = 10;
ilustrează două moduri în care se pot accesa elementele unui obiect de tip vector.
Spre deosebire de operatorul [], funcţia at verifică dacă indicele de tablou care este
trimis ca parametru se încadrează între limitele acestuia. În caz contrar, este
generată o excepţie care poate fi tratată astfel:
try
{
v.at(100) = 725;
}
catch(exception& e)
{
cout << "\nExceptie: " << e.what();
}
Pentru a putea rula corect codul de tratare a excepţiei, programul trebuie să includă
fişierul header <exception>.

Funcţia
v.insert(v.begin()+1, 22);
este o variantă de insert care poate fi folosită pentru containerii secvenţă şi care
inserează valoarea 22 înainte de elementul a cărui poziţie este specificată prin
iteratorul din primul argument. În exemplul nostru, iteratorul pointează către al doilea
element din vectorul v, iar valoarea 22 este inserată pe a doua poziţie. Elementul
care se găsea înainte pe cea de-a doua poziţie devine astfel al treilea element din
vector, iar toate celelalte elemente sunt deplasate cu o poziţie. Instrucţiunea
v.insert(v.begin(), a, a+SIZE);
este o altă variantă a acestei funcţii care inserează o mulţime de valori dintr-o
secvenţă pentru care se precizează poziţia de început şi cea de sfârşit dintr-un alt
container. În exemplul nostru, se inserează elementele dintre a şi a+SIZE din tabloul
a.

Funcţiile
v.erase(v.begin());
v.erase(v.begin(), v.end());
indică faptul că elementul de la locaţia specificată sau elementele dintre cele două
locaţii sunt şterse din colecţie.
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

Funcţia
v.clear();
şterge întreg conţinutul vectorului v.

Implementarea tablourilor bidimensionale în STL


1. STL nu defineste tablouri bidimensionale, însa prin combinarea containerelor
secventiale, cum este vector sau deque, se pot obtine matrici alocate dinamic. Iata
cum puteti define si un vector de vector de int, care sa poat_a fi folosit în maniera
unei matrici. Inserarea de elemente în aceasta “matrice” se va face cu
push_back().

vector<int> v; // auxiliar - vector vid


vector<vector<int> > m; // vector vid
// se creaza in v pe rând, fiecare linie
int k;
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 3; j++ )
{
cin >> k;
v.push_back(k);
}
m.push_back(v);
v.clear(); // sterge toate elementele din v
}
Dezavantaje multiple: construirea si distrugerea de obiecte temporare si realocari ale
vectorilor.
2. O cale mai simpla de a crea tablouri bidimensionale, este de a folosi constructorii
vector(n, x) si vector(n), care creaza un vector cu n elemente de valoare x,

respectiv un vector de dimensiune n. În acest mod se evita si realocarea, ca si


construirea de obiecte temporare. Declaratii necesare:
cin >> m >> n;
vector<vector<int> > v(m, n); // *
// vector<vector<int> > v(m, vector<int>(n)) //
echivalent cu *
for (int i = 0; i < m; i++ )
for ( int j = 0; j < n; j++ )
cin >> v[i][j];
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

3. Memorarea grafurilor cu ajutorul matricei de adiacenta, are pentru grafurile rare


neajunsul consumului mare de memorie. Se poate economisi memorie prin folosirea
unui vector de vector de bool:
cin >> n;
vector<vector<bool> > a(n, n);
Declaratia este echivalenta cu
vector<vector<bool> > a(n,vector<bool>(n));
4. Cum se declara un tablou tridimensional, folosind constructorii clasei vector ?
cin >> m >> n >> k;
vector<vector<vector<float> > >
V(m, vector<vector<float> >(n, vector<float>(k) ) );

Observatie:
Declaratia: vector<vector<vector<float> > > v(m, n, k); este invalida.
5. Un alt procedeu, care permite construirea de matrice cu ajutorul clasei vector,
este declararea unui vector de vectori vid, apoi redimensionarea cu resize(). Iata
declaratiile necesare:
vector<vector<int> > v;
cin >> m >> n;
v.resize(m); // m – linii
for ( int i = 0; i < n; i++ )
v[i].resize(n); // fiecare linie are n elemente
Acest procedeu evita de asemenea realocarea si crearea de temporare.
6.Clasa sablon map, supraîncarca operatorul [], astfel încât un map se poate folosi
ca un sir asociativ: m[key] = val; Aceasta caracteristica poate fi exploatata pentru
implementarea de tablouri bidimensionale. Operatorul [][] nu exista, deci se aplica
de doua ori operatorul []. Vom declara un map de map-uri, asa ca mai jos:
#include <map>
#include <iostream>
#include <iomanip>
using namespace std;
map<int, map<int, int> > M; // cheia are tipul int. Valoarea
// asociat_ este un map<int, int>
int main()
{
int m, n;
cin >> m >> n;
for (int i = 0; i < m; i++ )
for ( int j = 0; j < n; j++ )
cin >> M[i][j];
Lectia nr. 10 Centru HaiLaOlimpiada Vaslui

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


{
for ( int j = 0; j < n; j++ )
cout << setw(4) << M[i][j];
cout << '\n';
}
}
Observa_ie
- Accesarea valorii asociate unei chei folosind operatorul de indexare [] este o
operatie O(log N) , unde N este numarul perechilor din container. Prin urmare, M[i]
[j] se executa în O(log N * log M).

Tema pe pbinfo

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