Vectori (tablouri unidimensionale)
Până acum, am prelucrat numere „din mers” - le citeam pe rând și le foloseam imediat, fără a le păstra pe toate.
Dar sunt probleme în care avem nevoie de toate valorile simultan:
- să afișăm numerele în ordine inversă;
- să căutăm o valoare după ce am citit tot șirul;
- să comparăm elemente aflate la distanță;
- să sortăm numerele.
Pentru aceste situații, folosim vectori.
Ce este un vector?
Un vector este o colecție de valori de același tip, stocate una lângă alta în memorie, fiecare având un indice (un număr de ordine).
Imaginează-ți un șir de cutii numerotate:
indice: | 0 | 1 | 2 | 3 | 4 |
+-----+-----+-----+-----+-----+
valoare: | 3 | 7 | 2 | 9 | 4 |
+-----+-----+-----+-----+-----+
Fiecare cutie are:
- un indice (poziția) - începe de la
0; - o valoare - numărul stocat acolo.
Declararea unui vector
int v[100];Aceasta creează un vector cu cel mult 100 de
elemente de tip int.
Putem folosi oricâte din cele 100 de poziții - de obicei,
folosim primele n.
Convenție: declarăm vectorul suficient de
mare (de exemplu v[1001] dacă n poate
fi cel mult 1000). Pozițiile nefolosite nu ne
deranjează.
Accesarea elementelor
Fiecare element se accesează prin indice,
folosind paranteze pătrate []:
v[0] = 3; // primul element
v[1] = 7; // al doilea element
v[2] = 2; // al treilea element
cout << v[1]; // afișează 7Atenție la indici
În C++, indicii încep de la 0.
Un vector cu n elemente are indicii de la
0 la n - 1.
Indexare de la 1
Mulți elevi preferă să lucreze cu indici de la 1
la n. Acest lucru este perfect valid - declarăm
vectorul cu o poziție în plus (v[1001] în loc de
v[1000]) și pur și simplu nu folosim
v[0].
La concursuri, indexarea de la 1 este foarte
frecventă.
Citirea unui vector din fișier
De obicei, pe prima linie primim n (numărul de
elemente), iar pe a doua linie cele n valori.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001];
int n;
int main()
{
fin >> n;
for (int i = 1; i <= n; i++) {
fin >> v[i];
}
return 0;
}date.in:
5
3 7 2 9 4După citire, vectorul arată așa (indexat de la 1):
v[1] = 3, v[2] = 7, v[3] = 2, v[4] = 9, v[5] = 4
Afișarea unui vector
for (int i = 1; i <= n; i++) {
fout << v[i] << " ";
}Output:
3 7 2 9 4Afișarea în ordine inversă
Acesta este un exemplu clasic pentru care avem nevoie de vector - nu putem afișa invers dacă nu avem toate valorile stocate.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001];
int n;
int main()
{
fin >> n;
for (int i = 1; i <= n; i++) {
fin >> v[i];
}
for (int i = n; i >= 1; i--) {
fout << v[i] << " ";
}
return 0;
}date.in:
5
3 7 2 9 4date.out:
4 9 2 7 3Suma elementelor unui vector
int suma = 0;
for (int i = 1; i <= n; i++) {
suma = suma + v[i];
}
fout << suma;Observație: suma se poate calcula și fără vector (citind din mers). Dar dacă avem vectorul deja citit, este la fel de simplu.
Maximul și minimul dintr-un vector
int maxim = v[1];
int minim = v[1];
for (int i = 2; i <= n; i++) {
if (v[i] > maxim) {
maxim = v[i];
}
if (v[i] < minim) {
minim = v[i];
}
}
fout << "Max: " << maxim << endl;
fout << "Min: " << minim << endl;Căutarea unei valori în vector
Problema: avem un vector și un număr x. Vrem să
aflăm dacă x există în vector.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001];
int n;
int main()
{
fin >> n;
for (int i = 1; i <= n; i++) {
fin >> v[i];
}
int x;
fin >> x;
int gasit = 0;
for (int i = 1; i <= n; i++) {
if (v[i] == x) {
gasit = 1;
}
}
if (gasit == 1) {
fout << "DA";
} else {
fout << "NU";
}
return 0;
}date.in:
5
3 7 2 9 4
7date.out:
DANumărarea aparițiilor unei valori
int x;
fin >> x;
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (v[i] == x) {
cnt++;
}
}
fout << cnt;Modificarea elementelor unui vector
Putem modifica valorile direct:
// Dublăm fiecare element
for (int i = 1; i <= n; i++) {
v[i] = v[i] * 2;
}Sau putem înlocui o valoare specifică:
// Înlocuim toate aparițiile lui 0 cu 1
for (int i = 1; i <= n; i++) {
if (v[i] == 0) {
v[i] = 1;
}
}Inserarea unui element
Vrem să inserăm o valoare val pe poziția
poz. Trebuie să mutăm toate elementele de la
poz la n cu o poziție la dreapta.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001];
int n;
int main()
{
fin >> n;
for (int i = 1; i <= n; i++) {
fin >> v[i];
}
int poz, val;
fin >> poz >> val;
// Mutăm elementele la dreapta
for (int i = n; i >= poz; i--) {
v[i + 1] = v[i];
}
v[poz] = val;
n++;
for (int i = 1; i <= n; i++) {
fout << v[i] << " ";
}
return 0;
}date.in:
5
3 7 2 9 4
3 5date.out:
3 7 5 2 9 4Pas cu pas (inserăm
5 pe poziția 3)
Înainte: 3 7 2 9 4
- Mutăm
v[5]→v[6]:3 7 2 9 4 4 - Mutăm
v[4]→v[5]:3 7 2 9 9 4 - Mutăm
v[3]→v[4]:3 7 2 2 9 4 - Punem
v[3] = 5:3 7 5 2 9 4 ndevine6
Important: mutarea se face de la
dreapta la stânga (de la n spre
poz). Dacă am face invers, am suprascrie valorile
înainte de a le muta.
Ștergerea unui element
Vrem să ștergem elementul de pe poziția poz.
Mutăm toate elementele de la poz + 1 la
n cu o poziție la stânga.
int poz;
fin >> poz;
for (int i = poz; i < n; i++) {
v[i] = v[i + 1];
}
n--;Exemplu
Vector: 3 7 2 9 4, ștergem poziția
2.
v[2] = v[3]:3 2 2 9 4v[3] = v[4]:3 2 9 9 4v[4] = v[5]:3 2 9 4 4ndevine4
Rezultat: 3 2 9 4
Exemplu complet: elementele distincte
Afișăm fiecare valoare o singură dată (eliminăm duplicatele).
Ideea: pentru fiecare element, verificăm dacă a mai apărut înainte de el.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001];
int n;
int main()
{
fin >> n;
for (int i = 1; i <= n; i++) {
fin >> v[i];
}
for (int i = 1; i <= n; i++) {
int aparut = 0;
for (int j = 1; j < i; j++) {
if (v[j] == v[i]) {
aparut = 1;
}
}
if (aparut == 0) {
fout << v[i] << " ";
}
}
return 0;
}date.in:
8
3 7 2 7 4 3 9 2date.out:
3 7 2 4 9De ce două bucle?
Bucla exterioară (i) parcurge fiecare element.
Bucla interioară (j) verifică toate elementele
dinaintea lui i.
Dacă găsește o valoare egală cu v[i], înseamnă
că acea valoare a mai apărut - deci nu o mai afișăm.
Aceasta este o tehnică frecventă cu vectori: două bucle imbricate pentru a compara perechi de elemente.
Declararea vectorului - unde și cum
La concursuri, vectorul se declară global
(înainte de main), exact ca fin și
fout:
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int v[1001]; // global
int n; // global
int main()
{
// ...
}De ce global?
- Vectorii mari (peste ~1000 de elemente) pot cauza probleme
dacă sunt declarați în
main(spațiu limitat pe stivă). - Variabilele globale sunt inițializate automat cu
0. - Este mai simplu - accesibile oriunde.
Greșeli frecvente
1. Depășirea limitelor vectorului
int v[100];
v[100] = 5; // GREȘIT! Indicii sunt de la 0 la 99Dacă vectorul are 100 de poziții (indici
0 la 99), accesarea
v[100] este în afara vectorului și
poate cauza erori grave.
2. Uitarea citirii lui
n
for (int i = 1; i <= n; i++) { // n nu a fost citit!
fin >> v[i];
}Variabila n trebuie citită
înainte de buclă.
3. Confuzia între indice și valoare
v[3]este valoarea de pe poziția33este indicele (poziția)
// Greșit: afișăm indicele în loc de valoare
cout << i;
// Corect: afișăm valoarea
cout << v[i];4. Inserare/ștergere în direcția greșită
La inserare, mutăm de la dreapta la stânga. La ștergere, mutăm de la stânga la dreapta.
Dacă inversăm direcția, suprascriem valori înainte de a le salva.
5. Vectorul prea mic
Dacă n poate fi cel mult 1000,
vectorul trebuie declarat v[1001] (cu o poziție în
plus pentru indexarea de la 1).
Ce să reții
- Un vector stochează mai multe valori de același tip, accesibile prin indice.
- Indicii în C++ încep de la
0, dar la concursuri se folosește frecvent indexarea de la1. - Declarăm vectorul global, suficient de mare.
- Citirea: buclă
forde la1lan, cufin >> v[i]. - Cu vectori putem: afișa invers, căuta, număra, insera, șterge, elimina duplicate.
- Atenție la limitele vectorului - nu depăși indicele maxim.
- Inserarea se face de la dreapta la stânga, ștergerea de la stânga la dreapta.