STL - Introducere și
vector
STL (Standard Template Library) este o colecție de structuri de date și algoritmi gata făcuți din C++. În loc să implementăm manual stive, cozi, sortări, STL ne oferă variante testate și optimizate.
#include <vector>
#include <algorithm>De ce STL?
- Nu mai reimplementăm structuri de date de la zero
- Codul devine mai scurt și mai clar
- Structurile sunt testate și optimizate
- La concursuri, economisim timp prețios
vector<T>
- vectorul dinamic
Un vector din STL este ca un vector clasic, dar
își ajustează dimensiunea automat. Nu trebuie
să declari dimensiunea dinainte.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v; // vector gol de int-uri
vector<int> w(5); // vector cu 5 elemente (toate 0)
vector<int> u(5, 3); // vector cu 5 elemente, toate egale cu 3
return 0;
}Operații de bază
vector<int> v;
v.push_back(3); // adaugă 3 la final: [3]
v.push_back(7); // adaugă 7 la final: [3, 7]
v.push_back(2); // adaugă 2 la final: [3, 7, 2]
cout << v[0]; // 3 (acces prin indice, ca la vector clasic)
cout << v[1]; // 7
cout << v.size(); // 3 (numărul de elemente)
v.pop_back(); // scoate ultimul: [3, 7]
cout << v.back(); // 7 (ultimul element)
cout << v.front(); // 3 (primul element)
cout << v.empty(); // 0 (false - nu e gol)Tabel operații
| Operație | Ce face | Complexitate |
|---|---|---|
push_back(x) |
Adaugă la final | O(1) amortizat |
pop_back() |
Scoate ultimul | O(1) |
v[i] |
Acces element i | O(1) |
size() |
Număr elemente | O(1) |
empty() |
Verifică gol | O(1) |
front() |
Primul element | O(1) |
back() |
Ultimul element | O(1) |
clear() |
Golește vectorul | O(n) |
Cum funcționează push_back intern?
Un vector alocă intern un bloc de memorie de o
anumită capacitate. Când adăugăm elemente cu
push_back, le pune în spațiul disponibil.
Când vectorul se umple (size == capacity), nu mai are loc. Atunci:
- Alocă un bloc de două ori mai mare (capacitatea se dublează)
- Copiază toate elementele vechi în blocul nou
- Eliberează blocul vechi
- Adaugă elementul nou
De aceea push_back este O(1)
amortizat: de cele mai multe ori e instantaneu, dar
ocazional (la dublare) copiază tot - O(n). Pe termen lung, media
e O(1) per operație.
Capacitate: 1 → 2 → 4 → 8 → 16 → 32 → ...
Dublare la: 1 2 4 8 16 32 elemente
push_back nr 1: alocă 1 → copiere 0 elemente
push_back nr 2: alocă 2 → copiere 1 element
push_back nr 3: alocă 4 → copiere 2 elemente
push_back nr 5: alocă 8 → copiere 4 elemente
push_back nr 9: alocă 16 → copiere 8 elemente
Putem verifica capacitatea cu v.capacity() și o
putem seta dinainte cu v.reserve(n) ca să evităm
realocările:
vector<int> v;
v.reserve(100000); // pre-alocă pentru 100000 elemente
// acum push_back nu va mai realloca până la 100000Parcurgerea unui vector
Cu indice (clasic)
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";Cu range-based for (modern)
for (int x : v)
cout << x << " ";x ia pe rând fiecare valoare din vector.
Cu referință (dacă vrem să modificăm)
for (int &x : v)
x = x * 2; // dublează fiecare elementFără &, x e o copie -
modificările nu se păstrează.
Citire și afișare
#include <fstream>
#include <vector>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
vector<int> v(n);
for (int i = 0; i < n; i++)
fin >> v[i];
for (int x : v)
fout << x << " ";
return 0;
}Atenție: vector folosește
indexare de la 0. v[0] e primul
element, v[n-1] e ultimul.
Sortarea unui vector
#include <algorithm>
vector<int> v = {3, 7, 2, 9, 4};
sort(v.begin(), v.end()); // crescător: 2 3 4 7 9
sort(v.begin(), v.end(), greater<int>()); // descrescător: 9 7 4 3 2v.begin() și v.end() sunt
iteratori - pointeri la începutul și sfârșitul
vectorului.
Inițializare cu valori
vector<int> v = {3, 7, 2, 9, 4}; // listă de inițializare
vector<int> w(10, 0); // 10 elemente, toate 0
vector<int> u; // gol, adăugăm cu push_backVector de vectori (matrice dinamică)
vector<vector<int>> mat(n, vector<int>(m, 0));
// matrice n x m, inițializată cu 0
mat[i][j] = 5; // acces ca la matrice clasicăExemplu complet: sortare și afișare
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
vector<int> v(n);
for (int i = 0; i < n; i++)
fin >> v[i];
sort(v.begin(), v.end());
for (int x : v)
fout << x << " ";
return 0;
}date.in:
5
3 7 2 9 4date.out:
2 3 4 7 9Vector clasic vs
vector<T>
Vector clasic (int v[100]) |
vector<int> |
|
|---|---|---|
| Dimensiune | Fixă, la declarare | Dinamică, crește automat |
| Indexare | De la 0 (sau 1) | De la 0 |
| Declarare | int v[100001]; |
vector<int> v; |
| Adăugare | Manual cu contor | push_back() |
| Dimensiune | Trebuie știută | v.size() |
| Sortare | sort(v+1, v+n+1) |
sort(v.begin(), v.end()) |
| Folosit la | Concursuri (simplu) | Concursuri + proiecte |
Când alegem vector<T> vs vector clasic?
La concursuri, ambele sunt acceptate. Vectorul clasic e puțin
mai rapid (fără overhead de redimensionare), dar
vector<T> e mai flexibil.
Folosește vector<T> când:
- Nu știi dimensiunea dinainte
- Ai nevoie de vector de vectori (liste de adiacență la grafuri)
- Vrei cod mai curat
Folosește vector clasic când:
- Dimensiunea e cunoscută și fixă
- Vrei performanță maximă
- Indexezi de la 1
Greșeli frecvente
1. Acces în afara limitelor
vector<int> v(5);
cout << v[10]; // EROARE! Doar 5 elemente2. size()
returnează unsigned
for (int i = 0; i < v.size(); i++) // warning: comparație signed/unsigned
for (int i = 0; i < (int)v.size(); i++) // CORECT3. Uitarea lui
#include <vector>
Fără include, vector nu e recunoscut.
Ce să reții
vector<T>e un vector cu dimensiune dinamică.push_back,pop_back,size,empty- operații de bază.- Parcurgere:
for (int x : v)sau cu indice. - Sortare:
sort(v.begin(), v.end()). - Indexare de la 0.
- Include:
<vector>și<algorithm>pentru sort.