This shows you the differences between two versions of the page.
sda-aa:laboratoare:07 [2021/04/12 06:42] cristian.rusu |
sda-aa:laboratoare:07 [2021/04/12 06:55] (current) cristian.rusu [4. Exerciții] |
||
---|---|---|---|
Line 26: | Line 26: | ||
* Este recomandat ca funcţia de indexare să existe explicit (să fie definită ca subprogram) atunci când are o formă complicată. Dacă are o formă simplă (cum ar fi o singură operaţie), această parte poate fi omisă. | * Este recomandat ca funcţia de indexare să existe explicit (să fie definită ca subprogram) atunci când are o formă complicată. Dacă are o formă simplă (cum ar fi o singură operaţie), această parte poate fi omisă. | ||
- | ===== 2.3 Operații pe biți ===== | + | === 2.3 Operații pe biți === |
Menţionăm următoarele operaţii pe biţi ce se pot folosi în C/C++ : | Menţionăm următoarele operaţii pe biţi ce se pot folosi în C/C++ : | ||
Line 47: | Line 47: | ||
* n >> p == n / k | * n >> p == n / k | ||
* n & (k - 1) == n % k | * n & (k - 1) == n % k | ||
- | * | + | |
Apar diferenţe în cazul numerelor negative.</note> | Apar diferenţe în cazul numerelor negative.</note> | ||
- | === 3. Algoritmii de sortare === | + | ==== 3. Algoritmii de sortare ==== |
- | == 3.1 Heap Sort == | + | === 3.1 Heap Sort === |
* Timp mediu: O(N log N) | * Timp mediu: O(N log N) | ||
* Timp la limită: O(N log N) | * Timp la limită: O(N log N) | ||
Line 120: | Line 120: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | |||
+ | === 3.2 Radix Sort === | ||
+ | |||
+ | * Timp mediu: O(N * k) | ||
+ | * Timp la limită: O(N * k) | ||
+ | * Memorie: O(N + k) | ||
+ | * Stabil: DA | ||
+ | k = lungimea cuvântului/cheii (word size) | ||
+ | |||
+ | Vom prezenta varianta LSD (Least Signifiant Digit) a algoritmului de sortare. | ||
+ | |||
+ | Descriere: | ||
+ | LSD Radix Sort este una dintre cele mai rapide metode de sortare.Aceasta se bazează pe sortarea în funcţie de cea mai nesemnificativă „cifră“. | ||
+ | * Radix Sort nu are la bază o tehnică de comparare. Fiecare element din vector (sau atribut al unui element, în cazul structurilor) după care facem sortarea va fi numit cheie. | ||
+ | * Cheile sunt gândite ca şiruri de „caractere“ (unde un „caracter“ poate fi un bit, o cifra, o literă, …). | ||
+ | |||
+ | Algoritmul trece prin următorii paşi: | ||
+ | * pornind de la poziţia celui mai nesemnificativ „caracter“, numără de câte ori apare fiecare „caracter“ pe poziţia respectivă, apoi împarte un vector auxiliar în secţiuni (imaginare). Numărul de secţiuni este numărul de „caractere“ diferite ce pot exista în vector, adică fiecărei secţiuni îi este asociat un „caracter“, dimensiunea unei secţiuni depinde de numărul de apariţii ale „caracterului“ asociat; | ||
+ | * pune fiecare element (în vectorul auxiliar) în secţiunea corespunzătoare, apoi copiază vectorul auxiliar înapoi în vectorul ce trebuie sortat. Se obţine un vector sortat până la poziţia curentă; | ||
+ | * trece la poziţia următoare şi repetă paşii, ultima poziţie fiind cea a celui mai semnificativ „caracter“. | ||
+ | |||
+ | Implementare: | ||
+ | Exemplul prezentat foloseşte un octet pe post de „caracter“. Putem interpreta acest exemplu şi ca scriere a numerelor în baza 256, valoarea fiecărui octet fiind o „cifră“. | ||
+ | |||
+ | <code C> | ||
+ | #define BYTE 8 | ||
+ | #define COUNT_BYTE 256 | ||
+ | int obtineOctetul(int n,int byteNr) | ||
+ | { //cautam octetul de la pozitia byteNr | ||
+ | //octetul de pe pozitia 0 este LSD = octetul cel mai din dreapta(pentru int) | ||
+ | int bitsNr = BYTE * byteNr; | ||
+ | int mask = COUNT_BYTE - 1; | ||
+ | return (n >> bitsNr) & mask; | ||
+ | } | ||
+ | void rad(int *a,int *b, int byteNr,int n) | ||
+ | { //sortare dupa octetul de pe pozitia byteNr, | ||
+ | // pe pozitia 0 este LSD = octetul cel mai din dreapta | ||
+ | int i, | ||
+ | count[COUNT_BYTE] = {0}, //numaram cate elemente au "car." i pe pozitia byteNr | ||
+ | index[COUNT_BYTE] = {0}; //pozitia la care vom pune urmatorul element cu "car." i | ||
+ | for(i = 0; i < n;i++) { | ||
+ | int car = obtineOctetul(a[i],byteNr); | ||
+ | count[car]++; | ||
+ | } | ||
+ | for(i = 1;i < COUNT_BYTE;i++) //sectionam vectorul b | ||
+ | index[i] = index[i-1] + count[i-1]; | ||
+ | for(i = 0; i < n; i++) { //umplem sectiunile | ||
+ | int car = obtineOctetul(a[i],byteNr); | ||
+ | b[index[car]++] = a[i]; | ||
+ | } | ||
+ | } | ||
+ | void radixSort(int *a,int n) | ||
+ | { | ||
+ | int *b = new int[n], //vector folosit la copiere | ||
+ | byteNr, //pozitia curenta | ||
+ | k = sizeof(a[0]); //numarul de "caractere" | ||
+ | for(byteNr = 0; byteNr < k; byteNr += 2) { | ||
+ | rad(a, b, byteNr, n); //in loc sa copiem b inapoi in a la fiecare pas | ||
+ | rad(b, a, byteNr + 1, n); //copiem doar o data la 2 pasi | ||
+ | } | ||
+ | delete []b; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | <note important>Exemplul prezentat funcţionează bine pentru întregi cu acelaşi semn.</note> | ||
+ | |||
+ | <note important>Performanţa de timp poate fi influenţată prin schimbarea lungimii cuvântului (k), adică prin schimbarea „bazei“ folosite.</note> | ||
==== 4. Exerciții ==== | ==== 4. Exerciții ==== | ||
- | - Se dă un vector cu n întregi (citit din fișierul input.txt, câte un număr pe fiecare linie). Scrieţi o funcţie care să creeze un arbore binar de căutare cu valorile din vector. Scrieţi o funcţie care verifică dacă arborele este binar de căutare. Scrieţi o funcţie care verifică dacă o valoare (generată aleator) dată se află în arbore (căutare). Acelaşi arbore – inserare (şi să rămână arbore de căutare). Acelaşi arbore – ştergere (şi să rămână arbore de căutare). [80% nota] | + | - Generaţi un vector de n întregi cu n = 1e6. Sortaţi vectorul cu cei doi algoritmi şi comparaţi rezultatele (aici ne intereseaza viteza sortării, vezi https://stackoverflow.com/questions/5248915/execution-time-of-c-program). [80% notă] |
- | - Același enunț ca ex. 1 dar acum păstrați arborele echilibrat (AVL). [20% nota] | + | - Introduceţi o variabilă globală cu care să contorizaţi numărul de apelări ale funcţiei „cerne“. Afişaţi numărul de apelări necesare pentru construirea heap-ului (makeHeap) şi numărul de apelări necesare pentru tot algoritmul (heapSort). [10% notă] |
+ | - Cu Radix Sort, încercaţi să sortaţi un vector cu orice numere întregi (pozitive şi negative). Verificaţi rezultatul şi adăugaţi un pas în algoritm pentru a aşeza corect elementele. [10% notă] | ||
==== 5. Probleme opționale, de interviu ==== | ==== 5. Probleme opționale, de interviu ==== | ||
- | - Se dă V (un vector de n întregi) şi P (un vector de taţi de lungime n). Verificaţi dacă se poate construi un arbore binar de căutare cu valorile din V şi legăturile copil-părinte din P. | + | - Se dă un vector cu n întregi, unde toate valorile din vector sunt cuprinse între 0 şi n^2 - 1. Sortaţi vectorul în timp O(n). Hint: încercaţi să folosiţi altă „bază“ decât 256 pentru algoritm. |
- | - Propuneți soluții eficiente pentru a programa un program de tip "auto-complete" pentru o listă de cuvinte (în timp ce tastați se afișează cele mai probabile 3 cuvinte la care vă referiți). Hint: verificați/căutați trie sau arbori de prefixe. | + |