Documente Academic
Documente Profesional
Documente Cultură
2
Profesor: Siminițchi Gheorghe, profesor la disciplinile de specialitate
Disciplina: Programarea calculatorului
Tema: Prelucrarea tipurilor de date stivă coadă
Scopul lucrării:
Înțelegerea principiului de funcționare a tipurilor de date stivă și coadă,
Implimentarea tipurilor de date stivă și coadă.
Considerații teoretice
Stivă (Stack)
O stivă este o instanță a unui tip de date abstract ce formalizează conceptul de colecţie cu acces
restricționat. Restricția respectă regula LIFO (Last In, First Out).
Operații:
void push(E element) – adaugă un element (entitate) în stivă. Adăugarea se poate face doar
la vârful stivei.
E pop() – șterge un element din stivă și îl returnează. Ștergerea se poate face doar la vârful
stivei.
E peek() – consultă (întoarce) elementul din vârful stivei fără a efectua nicio modificare
asupra acesteia.
bool isEmpty() – întoarce 1 dacă stiva este goală; 0 dacă are cel puțin un element
Implementare
La nivel de implementare, stiva este reprezentată printr-o clasă ce folosește (pe lângă operațiile ce pot fi
efectuate asupra ei) un vector de stocare (stackArray) de o dimensiune maximă dată (NMAX) și un
indice ce indică vârful stivei(topLevel).
Stack.h
#ifndef __STACK__H__
#define __STACK__H__
// Primul argument al template-ului este tipul de date T
// Al doilea argument este dimensiunea maxima a stivei N
template<typename T, int N>
class Stack {
private:
// Vectorul de stocare
T stackArray[N];
// Pozitia in vector a varfului stivei
int topLevel;
public:
// Constructor
Stack() {
// TODO: initializari
}
// Destructor
~Stack() {
// TODO: eliberare resurse, daca este cazul
}
// Operator de adaugare
void push(T x) {
// TODO: verificari, implementare
}
// Operatorul de stergere
void pop() {
// TODO: verificari, implementare
}
// Operatorul de consultare
T peek() {
// TODO: verificari, implementare
}
// Operatorul de verificare dimensiune
bool isEmpty() {
// TODO: implementare
}
};
#endif //__STACK__H__
Exemplu de utilizare
Forma poloneză inversă este o notație matematică în care fiecare operator urmează dupa toți operanzii
săi.
Cel mai simplu exemplu de notație postfixată este cel pentru doi operanzi și un operator:
se scrie sub
5+4 54+
forma
În cazul în care există mai multe operații, operatorul apare imediat după cel de-al doilea operand:
2+4-
se scrie sub forma 2 4 + 5 -
5
Avantajul major al formei poloneze inverse este faptul că elimină parantezele din cadrul expresilor:
postfixată)
!!! dacă stiva s-a golit fără să fie găsită o paranteză stângă,
1.5.1 cât timp există un alt operator în vârful stivei (fie el O2)
ȘI precedența lui O1 este MAI MICA SAU EGALA decât cea a lui O2,
extrage O2
din stivă, afișare (se adaugă la forma postfixată)
operanzi
altfel
Coadă (Queue)
O coadă este o structură de date organizată după modelul FIFO (First In, First Out): primul element
introdus va fi primul eliminat din buffer.
void enqueue(E element) – adaugă elementul ”element” la sfârșitul cozii. Adăugarea se face doar
la sfârșitul cozii.
E dequeue() – șterge un element din coadă și îl returnează. Ștergerea se poate face doar la
începutul cozii.
E peek() – întoarce primul element din coada fără a-l scoate din aceasta.
bool isEmpty() – întoarce true dacă coada este goală; false dacă are cel puțin un element
Variante de implementare
Vom avea doi indici (head și tail) ce vor reprezenta începutul, respectiv sfârșitul cozii în cadrul
vectorului. Apare însă următoarea problemă din punctul de vedere al spațiului neutilizat: întotdeauna
spațiul de la 0 la head-1 va fi nefolosit, iar numărul de elemente ce pot fi stocate în coadă va scădea
(având inițial N elemente ce pot fi stocate, după ce se extrage prima oară un element, mai pot fi stocate
doar N-1 elemente). Vrem ca întotdeauna să putem stoca maxim N elemente.
Soluția: vector circular.
void enqueue(E element) - presupune adăugarea noului element la sfârșitul vectorului. Se
verifică în prealabil dacă dimensiunea vectorului mai permite adăugarea unui element. Se
incrementează tail. Complexitate: O(1)
E dequeue() - “șterge” și întoarce primul element din vector. Se incrementează head.
Complexitate: O(1)
E peek() - întoarce primul element din vector. Complexitate: O(1)
bool isEmpty() - true dacă vectorul nu conține niciun element, false în caz contrar. Complexitate:
O(1)
Reprezentare internă cu vector circular
La nivel de implementare, coada este reprezentată printr-o clasă template ce folosește (pe lângă
operațiile ce pot fi efectuate asupra ei) un vector de stocare (queueArray) de o dimensiune maximă
specificată ca al doilea argument al template-ului (N), doi indici ce indică începutul (head) şi sfârşitul
cozii (tail). De asemenea, se reţine şi dimensiunea curentă a cozii (size) pentru a putea spune când
aceasta este plină sau vidă.
void enqueue(E element) - adaugă noul element la sfârșitul vectorului. Se incrementează modulo
(dimensiune container) indicele tail. Complexitate: O(1)
E dequeue() - șterge și întoarce primul element din vector. Se incrementează modulo
(dimensiune container) indicele head. Complexitate: O(1)
E peek() - întoarce primul element din vector. Complexitate: O(1)
bool isEmpty() - la fel ca în celelalte cazuri. Complexitate: O(1)
bool isFull() - se verifică dacă următorul index după tail (circular) este egal cu head. Dacă da,
returnează true, returnează false în caz contrar. Complexitate: O(1)
Queue.h
Într-o coadă obișnuită accesul la elemente este de tip FIFO - elementele sunt introduse pe la un capăt și
scoase la celălalt capăt. În cazul unei Dequeue, se permit ambele operații, la ambele capete. Astfel, în
capătul head se pot atât introduce, cât și extrage elemente. La fel și în cazul capătului tail. Se observă că
cele două structuri prezentate în acest laborator (stiva și coada) sunt particularizări ale structurii de date
Dequeue. Dintre cele 4 operații de adaugare/ștergere puse la dispoziție de o dequeue, atât stiva cât și
coada folosesc doar 2 (addFront() și removeFront() în cazul stivei, respectiv addRear() și removeFront()
în cazul cozii).
Priority Queue
Este o coadă în care un dequeue() / peek() va întoarce primul element din acea coadă în funcție de un
anumit criteriu. Exemplu: pentru o coadă cu priorități care organizează elementele în funcție de valoarea
maximă, un peek() va întoarce valoarea maximă stocată. Similar, în cazul unei cozi cu priorități de
minim, peek() va întoarce valoarea minimă stocată.
Exemple de utilizare
Radix Sort
Radix Sort este un algoritm de sortare care ţine cont de cifre individuale ale elementelor sortate. Aceste
elemente pot fi nu doar numere, ci orice altceva ce se poate reprezenta prin întregi. Majoritatea
calculatoarelor digitale reprezintă datele în memorie sub formă de numere binare, astfel că procesarea
cifrelor din această reprezentare se dovedeşte a fi cea mai convenabilă. Există două tipuri de astfel de
sortare: LSD (least significant digit) şi MSD (most significant digit). LSD procesează reprezentările
dinspre cea mai puţin semnificativă cifră spre cea mai semnificativă, iar MSD invers.
O versiune simplă a radix sort este cea care foloseşte 10 cozi (câte una pentru fiecare cifră de la 0 la 9).
Aceste cozi vor reţine la fiecare pas numerele care au cifra corespunzătoare rangului curent. După
această împărţire, elementele se scot din cozi în ordinea crescătoare a indicelui cozii (de la 0 la 9), şi se
reţin într-un vector (care devine noua secvenţă de sortat). Exemplu:
Secvenţa iniţială:
170, 45, 75, 90, 2, 24, 802, 66
Numere sunt introduse în 10 cozi (într-un vector de 10 cozi), în funcţie de cifrele de la dreapta la stânga
fiecărui număr.
* 0: 170, 090
* 1: nimic
* 2: 002, 802
* 3: nimic
* 4: 024
* 5: 045, 075
* 6: 066
* 7 - 9: nimic
a. Se face dequeue pe toate cozile, în ordinea crescătoare a indexului cozii, şi se pun numerele într-un
vector, în ordinea astfel obţinută:
b. A doua iteraţie:
Cozi:
* 0: 002, 802
* 1: nimic
* 2: 024
* 3: nimic
* 4: 045
* 5: nimic
* 6: 066
* 7: 170, 075
* 8: nimic
* 9: 090
Noua secvenţă:
002, 802, 024, 045, 066, 170, 075, 090
c. A treia iteraţie:
Cozi:
* 1: 170
* 2 - 7: nimic
* 8: 802
* 9: nimic
Noua secvenţă: