Sunteți pe pagina 1din 24

Tablouri unidimensionale

Un tablou unidimensional se declară în C++ astfel:

tipDeBază denumire[Dimensiune];

de exemplu:

int X[10];

Ne putem imagina tabloul declarat mai sus astfel (valorile elementelor sunt aleatorii):

Spunem că fiecare element are un indice. Indicii unui tablou sunt între 0 și Dimensiune-1, deci
în exemplul nostru între 0 și 9.

Observație: Nu este necesar la declarare tabloul să fie singura variabilă declarată în


instrucțiunea declarativă. Următoarea instrucțiune este corectă sintactic.

int n, X[10], m, Y[100], p;

Care sunt valorile inițiale ale elementelor tabloului? Regula este aceeași ca pentru alte variabile:

 elementele unui tablou declarat global (în afara oricărei funcții) sunt inițializate cu 0;
 elementele unui tablou declarat local (în interiorul unei funcții) sunt inițializate cu valori
aleatorii. Faptul că anumite implementări la compilatorului C++ inițializează și
variabilele local cu 0 nu este o regulă, ea nu este garantată de standardul C++.

Referirea unui element


Referirea unui element se face prin operatorul de indexare, [], care are prioritate maximă. De
exemplu:

X[0], X[5], X[i]

Aici X este identificatorul tabloului (denumirea), iar 0, 5 sau i sunt indicii. Este necesar ca


programatorul (adică TU!) să se asigure că valoarea indicelui se găsește în intervalul potrivit
pentru tabloul dat (în exemplul nostru între 0 și 9).

Un element al tabloului, referit prin indice este tratat ca o variabilă oarecare de tipul stabilit la
declarare. Următoarele expresii/instrucțiuni sunt corecte:
int X[10];
cin >> X[0];
X[0] = 17;
cout << X[0];
cout << X[0] / 5;

Observație: C++ nu verifică dacă valoarea indicelui face parte din intervalul stabilit prin
declararea tabloului. Dacă indicele are o valoare în afara acestui interval, comportamentul
programului este impredictibil.

Este necesar ca programatorul să se asigure că valorile indicilor sunt corecte.

Dimensiunea unui tablou unidimensional


La declararea unui tablou unidimensional se precizează o dimensiune pentru acesta. Aceasta
reprezintă o dimensiune fizică a tabloului – numărul maxim de elemente pe care le-ar putea
avea acesta, conform restricțiilor problemei.

De cele mai multe ori, în program nu se folosesc toate elementele tabloului. De regulă, enunțul
unei probleme cu tablouri este:

“Se citește un vector cu  n elemente, numere …. Să se …..”

Este deci necesar ca în program să avem o variabilă – de regulă se notează n, care să


reprezinte dimensiunea logică a tabloului – numărul de elemente ale tabloului care la un
moment dat sunt utilizate în program.

Parcurgerea unui tablou unidimensional


Parcurgerea unui tablou reprezintă referirea fiecărui element al tabloului, într-o anumită ordine.
Referirea elementului se face prin intermediul indicelui, cu ajutorul operatorului de indexare.

Următorul exemplu declară un tablou cu 100 de elemente și memorează în primele n elemente


ale tabloului valoarea 1. După cum știm deja, n trebuie să respecte relația n<=100. În caz
contrar, comportamentul programului devine impredictibil – foarte probabil execuția sa va fi
oprită de sistemul de operare.

int X[100], n;
//n = .... ;
for(int i = 0 ; i < n ; i ++)
X[i] = 1;

De regulă, parcurgerea tabloului se face în ordinea crescătoare a indicelor, de la 0 la n-1. Făcând


o analogie cu axa numerelor, putem spune că parcurgerea se face de la stânga spre dreapta.
Tabloul poate fi parcurs și de la dreapta la stânga, adică în ordinea descrescătoare a indicilor, de
la n-1 la 0:
for(int i = n - 1 ; i >= 0 ; i --)
X[i] = 1;

Citirea unui vector


int X[100], n;

De fapt, în cele mai multe cazuri nu se poate face citirea unui tablou unidimensional (vector),
adică

cin >> X;

Instrucțiunea de mai sus duce de regulă la eroare de sintaxă. În schimb, se pot citi elementele
tabloului, în ordine, cu ajutorul parcurgerii:

cin >> n;
for(int i = 0 ; i < n ; i ++)
cin >> X[i];

Afișarea unui vector


int X[100], n;

La fel ca în cazul citirii, în cele mai multe cazuri nu se poate face nici afișarea unui vector, adică

cout << X;

Spre deosebire de citire, afișarea unui tablou cu ajutorul operatorului de inserție << nu duce la
eroare de sintaxă, însă nu se vor afișa elementele tabloului, ci o adresă (de
exemplu 0x7ffc9711bcd0), reprezentând adresa primului element al tabloului. Elementele
tabloului se pot afișa prin parcurgere, în ordinea dorită:

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


cout << X[i] << ' ';

sau

for(int i = n - 1 ; i >= 0 ; i --)


cout << X[i] << ' ';

Indexare de la 0 și indexare de la 1
Orice tablou C++ are fizic elementele indexate de la 0 la Dimensiune-1. De exemplu, dacă
avem nevoie de un tablou cu n≤100 elemente întregi, îl vom declara:

int V[100];

iar elementele vor avea indici între 0 și 99. Astfel, primul element al tabloului este V[0], al
doilea este V[1], …, ultimul este V[n-1].

Dacă dorim, putem ignora elementul V[0] (pur și simplu nu îl folosim, el există însă în


continuare), și atunci toate operațiile (citire, afișare, parcurgere) vor utiliza
elementele V[1] (primul element), V[1] (al doilea element), …, V[n] (ultimul element).

Trebuie de asemenea să tratăm cu atenție declararea tabloului, dimensiunea fizică trebuind să fie
mai mare cu 1 decât valoarea precizată în problemă. Dacă de exemplu, n≤100, declararea va fi:

int V[101];

Acest lucru este necesar pentru a exista elementul V[100].

Inițializarea elementelor unui tablou


La declararea tabloului este posibil să inițializăm elementele sale:

int A[] = {10 , 20 , 30 , 40};


int B[10] = {10 , 20 , 30 , 40};
int C[10] = {0};

A va avea patru elemente, cu valorile 10, 20, 30, 40.


B va avea zece elemente; primele patru vor avea valorile v[0] = 10, v[1] = 20, v[2] =
30, v[3] = 40, celelalte vor avea valoarea 0!!!
C va avea zece elemente. Toate vor avea valoarea 0!!!

Atenție!!

 În declarația int A[10]={1};, numai A[0] primește valoarea 1, celelalte elemente


fiind egale cu 0!!
 Tablourile pot fi inițializate în acest mod numai la declarare. Următoarea secvență este
greșită sintactic:

int A[10];
A = {0};

Operațiile de ștergere a unui element dintr-un vector, sau de inserare a


unui element nou într-un vector sunt frecvente în practică: avem o listă
cu elevii dintr-o clasă și un elev pleacă, sau un alt elev vine. Cum
actualizăm lista?
Ștergerea
Să considerăm următoarea problemă :

Se dă un șir X cu n elemente întregi și un număr p. Să se


șteargă din șirul X elementul aflat pe poziția p.

Să considerăm următorul vector cu n=10 elemente și p=4.

Dorim să eliminăm din vector elementul de indice 4, cel cu


valoarea X[4] = 34. În urma eliminării vectorul trebuie să arate astfel:

Cum procedăm?

 elementele cu indici p+1, p+2, …, n-1 se mută spre stânga cu o


poziție
 dimensiunea n a tabloului se micșorează cu 1

Ștergerea se face astfel:

for(int i = p ; i < n - 1; i ++)


X[i] = X[i+1];
n --;

Ștergerea mai multor valori din șir


Considerăm următoarea problemă :
Considerăm un șir X cu n elemente întregi. Să se elimine din
șir toate elementele pare.

Rezolvare:

 parcurgem șirul și analizăm elementul curent  X[p];


 dacă elementul X[p] este par, aplicăm algoritmul de mai sus pentru
ștergerea elementului cu indicele p.

Este necesar să realizăm cu atenție parcurgerea. Următoarea


secvență:

for (int p = 0 ; p < n ; p ++)


if(X[p] % 2 == 0) {
for(int i = p ; i < n - 1; i ++)
X[i] = X[i+1];
n --;
}

nu funcționează corect dacă în șir sunt elemente consecutive cu


proprietatea dorită (de a fi pare), deoarece al doilea element par nu va
fi analizat, deci nu va fi eliminat din șir. O solu ție bun ă este s ă
parcurgem elementele în ordine inversă:

for (int p = n - 1 ; p >= 0 ; p --)


if(X[p] % 2 == 0) {
for(int i = p ; i < n - 1; i ++)
X[i] = X[i+1];
n --;
}

Adăugarea unui element într-un vector


Adăugarea unui element într-un vector înseamnă mărirea dimensiunii
logice n a vectorului și memorarea în ultimul element a noii valori.
Următoarele secvențe adaugă o valoare într-un vector indexat de la  0.

X[n] = val;
n ++;

sau, mai condensat:


X[n++] = val;

Următoarele secvențe adaugă o valoare într-un vector indexat de la  1.

n ++;
X[n] = val;

sau, mai condensat:

X[++n] = val;

Inserarea unui element într-un vector


Considerăm următoarea problemă :

Se dă un șir X cu n elemente întregi, o valoare


întreagă val și un număr p. Să se insereze pe poziția p în șir
valoarea val.

Similar cu algoritmul de ștergere a unui element dintr-un vector, și cel


de inserare presupune modificarea elementelor din dreapta lui X[p].
De data aceasta elementele vor fi mutate spre dreapta, începând cu
ultimul. Elementul X[p] se înlocuiește cu noua valoare, iar
dimensiunea logică a vectorului crește, fără a depăși însă dimensiunea
fizică.

for(int i = n - 1 ; i >= p ; i --)


X[i+1] = X[i];
X[p] = val;
n ++;

Inserarea mai multor valori în șir


Se dă un vector cu n elemente naturale. Să se insereze după
fiecare element par, jumătatea sa.

Principial, procedăm astfel:


 parcurgem șirul
 dacă elementul curent X[p] este par
o inserăm pe poziția p+1 valoarea X[p]/2

Dacă parcurgerea se face de la stânga spre dreapta, exist ă riscul unor


inserări suplimentare, ca în acest exemplu:

Se pot formula foarte multe probleme în care se cere să se verifice


dacă elementele unui vector respectă diverse proprietăți, dar toate se
pot reduce în cele din urmă la una dintre următoarele:

 să se verifice dacă toate elementele unui vector dat respectă o


anumită regulă;
 să se verifice dacă într-un vector dat există elemente care
respectă o anumită regulă.

O rezolvare ar putea fi să numărăm elementele care respectă regula.


La final:

 dacă numărul de elemente care respectă regula este egal cu


numărul totale de elemente din vector, atunci toate elementele
respectă regula
 dacă numărul de elemente care respectă regula este nenul,
atunci există elemente care respectă regula.

Altă rezolvare, mai bună, ne permite să oprim parcurgerea când


suntem siguri că vectorul respectă sau nu proprietatea dorită. Vom
folosi o variabilă booleană (cu valori true sau false, 1 sau 0, …):

 dacă la final variabila are valoarea true, atunci vectorul respectă


regula,
 dacă la final variabila are valoare false, atunci vectorul nu
respectă regula.

Toate elementele respectă regula


 inițializăm variabila cu true
 parcurgem vectorul
o dacă elementul curent nu respectă regula dorită
 variabila devine false
 parcurgerea vectorului poate opri

Secvențe C++:

bool OK = true;
for(int i = 0 ; i < n && OK ; i ++)
if(X[i] - nu respectă regula)
OK = false;

sau

bool OK = true;
for(int i = 0 ; i < n ; i ++)
if(X[i] - nu respectă regula)
{
OK = false;
break;
}

sau

bool OK = true;
int i = 0;
while(i < n && OK)
{
if(X[i] - nu respectă regula)
OK = false;
else
i ++;
}

Există elemente care respectă regula


 inițializăm variabila cu false
 parcurgem vectorul
o dacă elementul curent respectă regula dorită
 variabila devine true
 parcurgerea vectorului poate opri

Secvențe C++:

bool OK = false;
for(int i = 0 ; i < n && !OK ; i ++)
if(X[i] - respectă regula)
OK = true;

sau

bool OK = false;
for(int i = 0 ; i < n ; i ++)
if(X[i] - respectă regula)
{
OK = true;
break;
}

sau

bool OK = false;
int i = 0;
while(i < n && !OK)
{
if(X[i] - respectă regula)
OK = true;
else
i ++;
}
Sortarea tablourilor

Sortarea unui tablou reprezintă o rearanjare a elementelor astfel încât


valorile acestora să fie într-o anumită ordine. De regulă ordinea cerut ă
este cea crescătoare sau descrescătoare.

Există numeroase metode de sortare

Din punct de vedere al eficienței, avem:

 algoritmi neeficienți, de complexitate O(n2)O(n2):


o metoda bulelor
o sortarea prin selecție
o sortarea prin inserție
o metoda piticului
o etc.
 algoritmi eficienți, de complexitate O(n⋅logn)O(n⋅log⁡n):
o QuickSort
o MergeSort
o HeapSort

Pentru structuri de date particulare există și algoritmi de


complexitate O(n)O(n). De asemenea, există algoritmi exponențiali,
de complexitate O(n!)O(n!), fără utilitate practică.

Cunoscută și sub numele BubbleSort, metoda bulelor se bazează pe


următoare idee:
 fie un vector X[] cu n elemente
 parcurgem vectorul și pentru oricare două elemente învecinate
care nu sunt în ordinea dorită, le interschimbăm valorile
 după o singură parcurgere, vectorul nu se va sorta, dar putem
repeta parcurgerea
 dacă la o parcurgere nu se face nicio interschimbare, vectorul
este sortat

O reprezentare a algoritmului este:

 cat timp vectorul nu este sortat


o presupunem că vectorul este sortat
o parcurgem vectorul
 dacă două elemente învecinate nu sunt în ordinea
dorită
 le interschimbăm
 schimbăm presupunerea inițială

Exemplu: Să ordonăm următorul vector, în care n=5:


După prima parcurgere vectorul După o nouă parcurgere, Îl parcurgem din nou:
are următoarea structură: vectorul este:

Deoarece am făcut Și acum am făcut Deoarece nu am făcut nicio


interschimbări, nu știm dacă interschimbări, nu știm dacă interschimbare, decidem că vectorul
vectorul este ordonat. vectorul este ordonat (chiar este ordonat.
dacă el este).
O secvență C++:

int n, v[100];
//citire v[] cu n elemente
bool sortat;
do
{
sortat = true;
for(int i = 0 ; i < n - 1 ; i ++)
if(v[i] > v[i+1])
{
int aux = v[i];
v[i] = v[i+1];
v[i+1] = aux;
sortat = false;
}
}
while(!sortat);

Observație: La fiecare parcurgere cel puțin un element ajunge pe poziția sa final ă:

 la prima parcurgere cel mai mare element al vectorului ajunge pe pozi ția sa final ă;
 la a doua parcurgere următorul cel mai mare element ajunge pe poziția final ă;

Observăm că nu mai are rost să parcurgem aceste elemente, fixate. Astfel, putem parcurge vectorul numai
până la indicele unde s-a făcut ultima interschimbare la parcurgerea anterioară.

Exemplu:
Parcurgere 1 Parcurgere 2 Parcurgere 3

O secvență C++:
int n, v[100];
//citire v[] cu n elemente
bool sortat;
int m = n;
do
{
sortat = true;
int p = m;
for(int i = 0 ; i < p - 1 ; i ++)
if(v[i] > v[i+1])
{
int aux = v[i];
v[i] = v[i+1];
v[i+1] = aux;
sortat = false;
m = i + 1;
}
}
while(!sortat);

Sortarea prin selecție (Selection Sort) se bazează pe următoarea idee:

 fie un vector X[] cu n elemente;
 plasăm în X[0] cea mai mică valoare din vector;
 plasăm în X[1] cea mai mică valoare rămasă;
 etc.

O descriere a algoritmului este:


 parcurgem vectorul cu indicele i
o parcurgem cu indicele j elementele din dreapta lui X[i]
 dacă elementele X[i] și X[j] nu sunt în ordinea dorită, le interschimbăm

Exemplu

Să ordonăm următorul vector, în care n=5:

i = 0 i = 1 i = 2 i = 3 La final
Secvență C++
int n, X[100];
//citire X[] cu n elemente
for(int i = 0 ; i < n - 1 ; i ++)
for(int j = i + 1 ; j < n ; j ++)
if(X[i] > X[j])
{
int aux = X[i];
X[i] = X[j];
X[j] = aux;
}

Algoritmul descris mai sus se mai numește  sortare prin selecție generală, sau implicită. O altă variantă este
următoarea, în care pentru fiecare secvență i ... n-1 se determină explicit minimul și se interschimbă
cu X[i].

int n, X[100];
//citire X[] cu n elemente
for(int i = 0 ; i < n - 1 ; i ++)
{
int p = i;
for(int j = i + 1 ; j < n ; j ++)
if(X[j] < X[p])
p= j;
int aux = X[i];
X[i] = X[p];
X[p] = aux;
}

Sortarea prin inserție (Insertion Sort) se bazează pe următoarea idee:

 fie un vector X[] cu n elemente;
 dacă secvența cu indici 0, 1, …, i-1 este ordonată, atunci putem insera elementul X[i] în această secvență
astfel încât să fie ordonată secvența cu indici 0, 1, …, i-1, i.
 luăm pe rând fiecare element X[i] și îl inserăm în secvența din stânga sa
 la final întreg vectorul va fi ordonat

Algoritm
O reprezentare a algoritmului este:
 parcurgem vectorul cu indicele i
o inserăm pe X[i] în secvența din stânga sa; pentru inserare se mută unele elemente din secvență spre
dreapta

Exemplu

Să ordonăm următorul vector, în care n=5:

sortarea prin inserție presupune următoarele transformări ale vectorului:


Secvență C++

În secvențele următoare considerăm că tabloul are elementele indexate de la  0 la n-1:

int n, X[100];
//citire X[] cu n elemente
for(int i = 1 ; i < n ; i ++)
{
int x = a[i];
int p = i - 1;
while(p >= 0 && a[p] > x)
a[p + 1] = a[p], p --;
a[p + 1] = x;
}

sau:

for(int i = 1 ; i < n ; i ++)


{
int p = i;
while(p > 0 && a[p] < a[p-1])
{
int aux = a[p];
a[p] = a[p-1];
a[p-1] = aux;
p --;
}
}
Considerăm două tablouri unidimensionale cu elemente numere întregi  ordonate crescător. Se dorește
construirea unui alt tablou, care să conțină valorile din cele două tablouri, în ordine.

O soluție foarte eficientă este interclasarea:

 considerăm două tablouri, cu n, respectiv m elemente, ordonate crescător


 cele două tablouri se parcurg concomitent;
 se alege valoarea mai mică dintre cele două elemente curente
o se adaugă în al treilea tablou
o se avansează numai în tabloul din care am ales valoarea de adăugat
 parcurgerea unuia dintre cele două tablouri se încheie
 toate elementele din celălalt tablou, neparcurse încă, sunt adăugate în tabloul destina ție
 tabloul destinație are p = n + m elemente

Secvență C++:

int n,a[100000], m , b[100000], p, c[200000];

//citire a[] cu n elemente


//citire b[] cu m elemente

int i = 0 , j = 0;
p = 0;
while(i < n && j < m)
if(a[i] < b[j])
c[p ++] = a[i ++];
else
c[p ++] = b[j ++];
while(i < n)
c[p ++] = a[i ++];
while(j < m)
c[p ++] = b[j ++];

Observație: Doar una dintre instrucțiunile while(i < n)... și while(j < m)... se va executa, deoarece exact
una dintre condițiile i < n și j < m este adevărată. În prima structură repetitivă, la fiecare pas, doar una
dintre variabilele i și j se mărește, deci la final una dintre condiții este adevărată și una este falsă.

Algoritmul de interclasare este foarte eficient. El are complexitate O(n+m). De asemenea, este posibilă și


interclasarea valorilor din două fișiere, singura condiție este ca valorile să fie ordonate.

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