Responsabili:
În urma parcurgerii acestui laborator studentul va fi capabil să:
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).
Accesul la elementele stivei se face doar prin vârful acesteia.
Operații:
O structură de date definește un set de operații și funcționalitatea acestora.
Implementarea efectivă a unei structuri de date poate fi realizată în diverse moduri, cât timp funcționalitatea este păstrată.
O stivă poate fi implementată cu ajutorul unui vector sau cu liste înlănțuite.
În cadrul acestui laborator, ne vom concentra asupra implementării unei stive cu ajutorul unui vector de stocare.
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).
#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__
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:
5 + 4 | se scrie sub forma | 5 4 + |
---|
În cazul în care există mai multe operații, operatorul apare imediat după cel de-al doilea operand:
2 + 4 - 5 | se scrie sub forma | 2 4 + 5 - |
---|
Avantajul major al formei poloneze inverse este faptul că elimină parantezele din cadrul expresilor:
5 + (1 + 4) | se scrie sub forma | 5 1 4 + + |
---|
1. cât timp există elemente de citit 1.1 citește un element 1.2 dacă elementul este un număr, afișare (se adaugă la forma postfixată) 1.3 dacă elementul este o paranteză stângă, adaugă-l în stivă 1.4 dacă elementul este o paranteză dreaptă, extrage operatorii din stivă și adaugă-i la forma postfixată până când vârful stivei ajunge o paranteză stângă (care este extrasă, dar nu este adăugată la forma postfixată). !!! dacă stiva s-a golit fără să fie găsită o paranteză stângă, înseamnă că expresia inițială avea paranteze greșite 1.5 dacă elementul este un operator (fie el O1) 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ă) 1.5.2 adaugă O1 în stivă 2. când nu mai există elemente de citit, extrage toate elementele rămase în stivă și adaugă-le la forma postfixată (elementele trebuie să fie numai operatori; dacă este extrasă o paranteză stângă expresia inițială avea parantezele greșite).
Fie expresia:
1 - 7 * 2 /(3 + 5)^2^5
Element | Acțiune | Forma postfixată | Stiva | Observaţii |
---|---|---|---|---|
1 | Adaugă element la forma postfixată | 1 | ||
- | Pune elementul în stivă | 1 | - | |
7 | Adaugă element la forma postfixată | 1,7 | - | |
* | Pune elementul în stivă | 1,7 | * - | * are precedență mai mare decât - |
2 | Adaugă element la forma postfixată | 1,7,2 | * - | |
/ | Extrage element din stivă | 1,7,2* | - | / și * au aceeași prioritate |
Pune elementul în stivă | / - | / are precedență mai mare decât - | ||
( | Pune elementul în stivă | 1,7,2* | ( / - | |
3 | Adaugă element la forma postfixată | 1,7,2*3 | ( / - | |
+ | Pune elementul în stivă | 1,7,2*3 | + ( / - | |
5 | Adaugă element la forma postfixată | 1,7,2*3,5 | + ( / - | |
) | Extrage element din stivă | 1,7,2*3,5+ | ( / - | Se repeta până când se întâlnește ( |
repetă | / - | ( a fost ignorat | ||
^ | Pune elementul în stivă | 1,7,2*3,5+ | ^ / - | ^ are precedență mai mare decât / |
2 | Adaugă element la forma postfixată | 1,7,2*3,5+2 | ^ / - | |
^ | Pune elementul în stivă | 1,7,2*3,5+2 | ^ ^ / - | ^ este considerat asociativ-dreapta |
5 | Adaugă element la forma postfixată | 1,7,2*3,5+2,5 | ^ ^ / - | |
Final | Extrage toate elementele din stivă | 1,7,2*3,5+2,5^^/- |
1. cât timp există elemente de citit 1.1 citește un element 1.2 dacă elementul este o valoare 1.2.1 pune elementul în stivă altfel (elementul este un operator) 1.2.2 extrage 2 operanzi din stivă 1.2.3 dacă nu există 2 operanzi în stivă EROARE: forma postfixată nu este corectă 1.2.4 evaluează rezultatul aplicării operatorului asupra celor doi operanzi 1.2.5 pune rezultatul în stivă 2. dacă există o singură valoare în stivă 2.1 afișează valoarea ca rezultat final al evaluării expresiei altfel EROARE: forma postfixată nu este corectă
O coadă este o structură de date organizată după modelul FIFO (First In, First Out): primul element introdus va fi primul eliminat din buffer.
Metode generale disponibile pentru o clasă Queue:
O coadă se poate poate implementa folosind pe post de container lista înlănțuită, array de dimensiune fixă, array circular.
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.
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ă.
template <typename T, int N> class Queue { private: int head; int tail; int size; T queueArray[N]; public: // Constructor Queue() { // TODO } // Destructor ~Queue() { // TODO } // Adauga in coada void enqueue(T e) { // TODO } // Extrage din coada void dequeue() { // TODO } // Afla primul element T front() { // TODO } bool isEmpty() { // TODO } };
Î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).
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ă.
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.
Cozile pentru prima iteraţie vor fi:
* 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ă:
Noua secvenţă de sortat:
170, 090, 002, 802, 024, 045, 075, 066
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:
* 0: 002, 024, 045, 066, 075, 090 * 1: 170 * 2 - 7: nimic * 8: 802 * 9: nimic
Noua secvenţă:
002, 024, 045, 066, 075, 090, 170, 802 (sortată)
Atenție! Pentru acest laborator asistentul poate înlocui exercițiile publice complet.
1) [3p] Implementare stivă.
2) [3p] Implementare coadă.
3) [2p] Determinați dacă un șir format din caracterele (
, [
, {
, )
, ]
, }
este corect parantezat.
4) [2p] Implementaţi Radix Sort (sortare crescătoare).
5) [2p] Implementaţi problema Turnurilor din Hanoi folosind explicit stive. Jocul este format din trei tije și un număr variabil de discuri, de diferite mărimi, care pot fi poziționate pe oricare din cele trei tije. Jocul începe având discurile așezate în stivă pe prima tijă, în ordinea mărimii lor, astfel încât să formeze un turn. Scopul jocului este acela de a muta întreaga stivă de pe o tijă pe alta, respectând următoarele reguli:
6) [2p] Realizaţi conversia unui număr din baza 10 în baza 2, folosind stive.
7) [2p] Verificaţi dacă un număr este palindrom folosind un deque.
8) [2p] Implementaţi găsirea celui mai dens interval de puncte unidimensionale.
9) [2p] Inversaţi ordinea elementelor într-o coadă. Exemplu: dacă în coadă se află iniţial elementele [4 7 10], după inversare, coada va conţine [10 7 4].
10) [2p] Evaluare expresie aritmetică.
11) [2p] Primul caracter care nu se repetă într-un stream de caractere.
12) [2p] Implementaţi o coadă folosind două stive.
Observaţie Puteţi aborda problema în două feluri:
13) [2p] Implementaţi o stivă folosind două cozi.
Observaţie: Puteţi aborda problema în două feluri:
Această secțiune nu este punctată și încearcă să vă facă o oarecare idee a tipurilor de întrebări pe care le puteți întâlni la un job interview (internship, part-time, full-time, etc.) din materia prezentată în cadrul laboratorului.