Diferență între revizuiri ale paginii „SDA Lucrarea 3”
Linia 282: | Linia 282: | ||
printf("Second element: %d\n", numbers[1]); // se va afisa 4 | printf("Second element: %d\n", numbers[1]); // se va afisa 4 | ||
− | numbers | + | numbers.at(0) = 5; // in loc de 2, primul element devine 5 |
printf("All numbers:"); | printf("All numbers:"); |
Versiunea de la data 22 martie 2017 23:04
În acest laborator se vor implementa secvențe cu vectori și liste simplu și dublu înlănțuite.
Secvența (lista abstractă - Sequence)
Secvența este o structură de date abstractă care stochează datele sub forma unui șir de elemente de același fel.
Secvența are următoarele proprietăți:
- Datele sunt stocate într-o anumită ordine (se poate spune că un element este plasat înaintea sau după un alt element în structură).
- Numărul de elemente ce poate fi stocat de structură este nelimitat (a nu se confunda cu infinit, există întotdeauna limita dată de memoria disponibilă).
- Elementele stocate în secvență sunt de același fel.
Secvența suportă următoarele operații de bază:
- Interogarea numărului de elemente din secvență.
- Inserția unui element pe o anumită poziție în secvență.
- Ștergerea unui element de pe o anumită poziție din secvență.
- Căutarea unui element în secvență (căutare care va întoarce primul index pe care apare elementul în structură).
- Accesarea unui element de pe o anumită poziție.
- Dacă există o relație de ordine pe mulțimea din care fac parte elementele, se poate defini operația de sortare a secevenței care implică mutarea elementelor pe alte poziții, astfel încât un element considerat mai mic după relația de ordine folosită să fie plasat înaintea oricărui element considerat mai mare.
Implementarea secvenței cu vectori
Clasa din STL care implementează secvențe folosind array-uri cu redimensionare dinamică se numește std::vector
. Această clasă este un template care poate fi folosit pentru stocarea de elemente de diferite tipuri.
Crearea unui std::vector
Pentru crearea unei vector nou se pot utiliza doi constructori:
/**
* Constructorul aloca memorie si creeaza un nou vector cu o capacitate initiala nulă.
*/
vector();
/**
* Constructorul aloca memorie si creeaza un nou vector cu o dimensiune si o capacitate initiala.
* Vectorul va avea "initialSize" elemente neutre (0 pentru valori numerice).
* @param initialSize dimensiunea initiala a secventei.
*/
vector(uint32_t initialSize);
Exemplu:
#include <vector>
using namespace std;
int main() {
vector<int> intVector; // Foloseste primul constructor, dimensiune 0
/* intVector[0] = 0; */ // Aici s-ar genera un Segmentation Fault pentru ca nu exista alocata memorie pentru un element (dimensiune si capacitate 0).
intVector.push_back(10); // Aceasta linie functioneaza, pentru ca push_back adauga la sfarsitul vectorului, determinand redimensionarea acestuia.
vector<float> floatVector(10); // Foloseste al doilea constructor, dimensiunea initiala e 10.
floatVector[0] = 1; // Linia NU genereaza un SegFault pentru ca exista capacitatea necesata pentru stocarea valorii.
return 0;
}
Interogarea dimensiunii
Pentru interogarea dimensiunii, se definește următoarea metodă:
/**
* Metoda intoarce numarul de elemente valide din vector.
* @return numarul de elemente valide din vector.
*/
uint32_t size();
Exemplu:
#include <vector>
#include <cstdio>
using namespace std;
int main() {
vector<int> intVector;
intVector.push_back(10);
intVector.push_back(20);
intVector.push_back(5);
printf("Vectorul are %u elemente!\n", intVector.size()); // Programul va afisa valoarea 3.
return 0;
}
Inserția
Pentru inserția unui element value pe o anumită poziție index se definesc funcțiile de mai jos. Se observă că aceste funcții fac uz de iteratori. Acești iteratori sunt niște pointeri cu informații suplimentare. Există doi iteratori disponibili pentru orice vector: begin()
și end()
. Primul reprezintă adresa primului element din vector, iar al doilea reprezintă adresa DE DUPĂ ultimul element din vector.
/**
* Metoda insereaza elementul value pe pozitia position in vector. Daca capacitatea este insuficienta
* se face realocare.
* @param position pozitia pe care se va face insertia
* @param value valoarea ce trebuie inserata
* @return metoda intoarce un iterator la elementul inserat
*/
iterator insert(iterator position, T value);
/**
* Metoda insereaza count elemente cu valoarea value pe pozitia position in vector. Daca capacitatea este insuficienta
* se face realocare.
* @param position pozitia pe care se va face insertia
* @param count numarul de elemente ce trebuie introduse
* @param value valoarea ce trebuie inserata
* @return metoda intoarce un iterator la primul element inserat
*/
iterator insert(iterator position, uint32_t count, T value);
/**
* Metoda insereaza toate elementele dintre iteratorii first inclusiv si last exclusiv pe pozitia position in vector.
* Daca capacitatea este insuficienta se face realocare.
* @param position pozitia pe care se va face insertia
* @param first iteratorul de start
* @param last iteratorul de final
* @return metoda intoarce un iterator la primul element inserat
*/
iterator insert(iterator position, iterator first, iterator last);
/**
* Metoda adauga un element la sfarsitul vectorului. Daca capacitatea este insuficienta se face realocare.
* @param value valoarea ce trebuie inserata
*/
void push_back (T value);
Exemplu:
#include <vector>
#include <cstdio>
using namespace std;
int main() {
vector<int> intVector;
intVector.push_back(1);
intVector.push_back(2);
intVector.push_back(3);
// vectorul acum e: {1, 2, 3}
intVector.insert(intVector.begin(), 0); // se insereaza inaintea primului element din vector o valoare de 0
// vectorul acum e: {0, 1, 2, 3}
intVector.insert(intVector.begin() + 1, 3); // se insereaza inaintea celui de-al doilea element din vector o valoare de 3
// vectorul acum e: {0, 3, 1, 2, 3}
intVector.insert(intVector.end(), 10); // se insereaza dupa ultimul element din vector, o valoare de 10 (echivalent cu push_back)
// vectorul e acum: {0, 3, 1, 2, 3, 10}
vector<int> newVector;
newVector.push_back(-1);
newVector.push_back(-2);
newVector.push_back(-3);
// newVector e acum: {-1, -2, -3}
intVector.insert(intVector.begin(), newVector.begin(), newVector.end()); //Se insereaza inaintea primului element din intVector toate elementele din newVector
// intVector e acum: {-1, -2, -3, 0, 3, 1, 2, 3, 10}
return 0;
}
Ștergerea
Pentru ștergerea unuia sau mai multor elemente se definesc următoarele metode:
/**
* Metoda sterge elementul pe pozitia position din vector.
* @param position pozitia de la care se va face stergerea
* @return iterator la elementul ce urmeaza celui sters
*/
iterator erase(iterator position);
/**
* Metoda sterge toate elementele dintre pozitiile first si last din vector.
* @param first pozitia de unde incepe stergerea
* @param last pozitia unde se termina stergerea
* @return iterator la elementul ce urmeaza ultimului element sters
*/
iterator erase(iterator first, iterator last);
/**
* Metoda sterge ultimul element din vector (opusul lui push_back).
*/
void pop_back();
/**
* Metoda sterge toate elementele din vector.
*/
void clear();
Exemplu:
#include <vector>
#include <cstdio>
using namespace std;
int main() {
vector<int> intVector;
intVector.push_back(1);
intVector.push_back(2);
intVector.push_back(3);
intVector.push_back(4);
intVector.push_back(10);
// vectorul e acum: {1, 2, 3, 4, 10}
intVector.pop_back(); // se sterge ultimul element
// vectorul e acum {1, 2, 3, 4}
intVector.erase(intVector.begin() + 2); // se sterge elementul de pe indexul 2 (a treia valoare)
// vectorul e acum {1, 2, 4}
intVector.erase(intVector.begin() + 1, intVector.end()); // se sterg toate valorile dintre indexul 1 si sfarsit
// vectorul e acum {1}
intVector.clear();
// vectorul e acum gol: {}
return 0;
}
Accesarea elementului de pe o anumită poziție
Pentru accesarea unui element de pe poziția index dintr-o secvență se definesc următoarele metode:
/**
* Metoda intoarce elementul din vector de pe poziția index.
* @param index pozitia de la care se doreste citirea elementului.
* @return valoarea de la pozitia index
*/
T at(uint32_t index);
/**
* Metoda intoarce elementul din vector de pe poziția index.
* @param index pozitia de la care se doreste citirea elementului.
* @return valoarea de la pozitia index
*/
T operator[](uint32_t index);
Metoda operator[]
redefinește (supraîncarcă) utilizarea parantezelor pătrate astfel încât să poată fi folosite la fel ca la array-uri. Cele două metode sunt echivalente, singura diferență este că metoda at
face și verificarea limitelor vectorului pentru a evita un Segmentation Fault.
Exemplu:
#include <vector>
#include <cstdio>
int main() {
std::vector<int> numbers;
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(6);
numbers.push_back(8);
printf("Second element: %d\n", numbers[1]); // se va afisa 4
numbers.at(0) = 5; // in loc de 2, primul element devine 5
printf("All numbers:");
for (uint32_t i = 0; i < numbers.size(); i++) {
printf(" %d", numbers.at(i)); // se va afisa 5, 4, 6, 8
}
printf("\n");
return 0;
}
Căutarea
Pentru căutarea unui element value într-o secvență neordonată se definește următoarea funcție:
/**
* Metoda cauta elementul value in vector si intoarce prima pozitie pe care apare.
* Se intoarce -1 daca valoarea nu exista in vector.
* @param list lista in care se face cautarea.
* @param value valoarea care se cauta.
* @return indexul la care apare valoarea sau -1 daca nu exista in vector.
*/
int arrayListSearch(struct ArrayList * list, short value);
Căutarea unui element value într-o secvență neordonată implementată cu vectori, se face urmând următorul algoritm:
- Se iterează cu o variabilă i de la 0 la size - 1.
- Dacă elementul de pe poziția i din vector este egal cu value, se întoarce valoarea lui i.
- Se întoarce valoarea -1.
Sortarea unei secvențe implementate cu vectori - Bubble Sort
Pentru sortarea unei secvențe se definește următoarea funcție:
/**
* Functia sorteaza crescator elementele din secventa list folosind algoritmul Bubble Sort.
* @param list lista care se doreste sortata.
*/
void arrayListBubbleSort(struct ArrayList * list);
Sortarea unei secvențe implementată cu vectori se face urmând următorul algoritm:
- Se declară o variabilă done.
- Se atribuie lui done valoarea 1.
- Se iterează cu un contor i de la 0 la (size - 2)
- Dacă elementul de pe poziția i din vector este mai mare decât elementul de pe poziția (i + 1), acestea se interschimbă și lui done i se atribuie valoarea 0.
- După terminarea iterării peste vector, dacă done e 0, se sare la pasul 2 (se folosește o buclă do-while).
Căutarea într-o secvență sortată implementată cu vectori - Binary Search
Căutarea într-o secvență ordonată se poate realiza mai eficient decât iterând peste toate elementele cu o buclă în O(n), deoarece avem informații suplimentare legate de poziția cea mai probabilă a elementului căutat.
Algoritmul numit Binary Search, care face parte din clasa Divide-et-impera, funcționează căutând elementul la mijlocul intervalului în care se realizează căutare, și în funcție de rezultat întorcând poziția sau eliminând în întregime jumătate din interval.
Pentru căutarea unei valori value într-o secvență sortată implementată cu vectori, se definește următoarea funcție:
/**
* Functia cauta elementul value in secventa list sortata crescator.
* Daca secventa nu este sortata, comportamentul nu este definit.
* @param list lista in care se cauta.
* @param value valoarea cautata in secventa.
* @return indexul unde s-a găsit valoarea value sau -1 daca valoarea nu exista.
*/
int arrayListBinarySearch(struct ArrayList * list, short value);
Căutarea unei valori value într-o secvență sortată implementată cu vectori se face urmând următorul algoritm:
- Se inițializează o variabilă start cu valoarea 0 și o varibilă end cu valoarea (size - 1).
- Cât timp start este mai mic sau egal decât end se realizează următoarele operații:
- Se declară o variabilă middle și i se atribuie valoarea (start + end ) / 2.
- Dacă elementul din vector de pe poziția middle este egal cu value, se întoarce valoarea middle.
- Dacă elementul din vector de pe poziția middle este mai mare decât value, end va lua valoarea middle - 1 altfel,
- Dacă elementul din vector de pe poziția middle este mai mic decât value, start va lua valoarea middle + 1.
- Se sare la punctul 3.
- Aici se ajunge dacă start devine mai mare decât end, în care situație se întoarce -1.