This shows you the differences between two versions of the page.
sd-ca:laboratoare:lab-06 [2022/04/07 11:07] emil.racec [Exerciţii] |
sd-ca:laboratoare:lab-06 [2025/04/06 14:50] (current) cristina.drinciu [Interviu] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 6 - Grafuri - Basics ====== | + | ====== Laborator 6 - Arbori Binari ====== |
- | Responsabili | + | Responsabili: |
- | * [[maximilian.banu@stud.acs.upb.ro|Maximilian Banu]] | + | * [[mailto:popaiarina13@gmail.com|Iarina-Ioana Popa]] |
- | * [[becheanudaniela04@gmail.com|Daniela Becheanu]] | + | * [[mailto:andreipirlea03@gmail.com|Andrei Pîrlea]] |
===== Obiective ===== | ===== Obiective ===== | ||
- | În urma parcurgerii acestui laborator, studentul va fi capabil să: | + | În urma parcurgerii articolului, studentul va fi capabil să: |
+ | * înţeleagă noţiunea de arbore şi structura unui arbore binar | ||
+ | * construiască, în limbajul C, un arbore binar | ||
+ | * realizeze o parcurgere a structurii de date prin mai multe moduri | ||
+ | ===== Noțiuni teoretice ===== | ||
- | * înțeleagă operațiile de parcurgere a grafurilor și diferențele dintre ele. | + | ==== Noțiunea de arbore. Arbori binari ==== |
- | * implementeze parcurgerile pe grafuri având la dispoziție structurile de date studiate. | + | Matematic, un arbore este un graf neorientat conex aciclic. |
- | * evalueze complexitatea parcurgerii grafurilor. | + | |
- | ===== Ce este un graf? ===== | + | În ştiinţa calculatoarelor, termenul de **arbore** este folosit pentru a desemna o structură de date care respectă definiţia de mai sus, însă are asociate un nod rădăcină şi o orientare înspre sau opusă rădăcinii. |
- | Un graf este o pereche de mulţimi ''G = (V, E)''. Mulțimea ''V'' conține nodurile grafului (**vertices**), iar mulțimea ''E'' conține muchiile sale (**edges**), fiecare muchie stabilind o relație de vecinătate între două noduri. Mulţimea ''E'' este inclusă în mulţimea ''VxV''. | + | Arborii sunt folosiţi în general pentru a modela o **ierarhie de elemente**. |
- | ==== Diferenţa între graf orientat şi graf neorientat ==== | + | Astfel, fiecare element (**nod**) poate deţine un număr de unul sau mai mulţi descendenţi, iar în acest caz nodul este numit **părinte** al nodurilor descendente (**copii** sau **fii**). |
- | Dacă pentru orice element al mulţimii ''E'', ''e = (u, v)'', elementul ''e' = (v, u)'' aparţine de asemenea mulţimii ''E'', atunci spunem că graful este **neorientat**. În caz contrar, graful este **orientat**. În cazul grafului orientat, muchiile se mai numesc şi arce. | + | Fiecare nod poate avea un **singur nod părinte**. |
+ | Un nod fără descendenţi este un **nod terminal**, sau **nod frunză**. | ||
- | {{sd-ca:laboratoare:neorientat.png}} {{sd-ca:laboratoare:orientat.png}} | + | În schimb, există un singur nod fără părinte, iar acesta este întotdeauna **rădăcina arborelui** (**root**). |
- | ===== Reprezentările grafurilor în memorie ===== | + | Un **arbore binar** este un caz special de arbore, în care fiecare nod poate avea maxim **doi descendenţi**: |
+ | *nodul stâng | ||
+ | *nodul drept. | ||
- | În funcţie de problemă şi de tipul grafurilor, avem 2 reprezentări: liste de adiacenţă sau matrice de adiacenţă. | + | În funcţie de elementele ce pot fi reprezentate în noduri şi de restricţiile aplicate arborelui, se pot crea structuri de date cu proprietăţi deosebite: heap-uri, arbori AVL, arbori roşu-negru, arbori Splay şi multe altele. O parte din aceste structuri vor fi studiate la curs şi în laboratoarele viitoare. |
- | ==== Liste de adiacenţă ==== | + | În acest articol ne vom concentra asupra unei utilizări comune a arborilor binari, şi anume pentru a reprezenta şi evalua expresii logice. |
+ | ==== Reprezentarea arborilor binari ==== | ||
- | Reprezentarea prin liste de adiacenţă constă într-un tablou ''Adj'' cu ''|V|'' liste, una pentru fiecare vârf din ''V''. Pentru fiecare ''u'' din ''V'', lista de adiacenţă ''Adj[u]'' conţine referinţe către toate vârfurile ''v'' pentru care există muchia ''(u, v)'' în ''E''. Cu alte cuvinte, ''Adj[u]'' este formată din totalitatea vârfurilor adiacente lui ''u'' în ''G''. | + | Arborii binari pot fi reprezentați în mai multe moduri. Structura din spatele acestora poate fi un simplu vector, alocat dinamic sau nu, sau o structură ce folosește pointeri, așa cum îi vom reprezenta în acest articol. |
- | Această reprezentare este preferată pentru grafurile rare ( ''|E|'' este mult mai mic decât ''|V|x|V|''). | + | <code c binary_tree.h> |
+ | typedef struct b_node_t b_node_t; | ||
+ | struct b_node_t { | ||
+ | /* left child */ | ||
+ | b_node_t *left; | ||
+ | /* right child */ | ||
+ | b_node_t *right; | ||
- | {{sd-ca:laboratoare:undirected_graph.gif}} | + | /* data contained by the node */ |
+ | void *data; | ||
+ | }; | ||
- | Pentru graful de mai sus, lista de adiacenţă este următoarea: | + | typedef struct b_tree_t b_tree_t; |
+ | struct b_tree_t { | ||
+ | /* root of the tree */ | ||
+ | b_node_t *root; | ||
+ | |||
+ | /* size of the data contained by the nodes */ | ||
+ | size_t data_size; | ||
+ | }; | ||
+ | </code> | ||
- | * **0**: 1->2 | + | Structura nodului de mai sus este clară: |
- | * **1**: 0->2->3->4 | + | * pointer către fiul stâng |
- | * **2**: 0->1->3 | + | * pointer către fiul drept |
- | * **3**: 1->2->4 | + | * pointer către date |
- | * **4**: 1->3 | + | |
- | ==== Matrice de adiacenţă ==== | + | Pentru a ne reaminti cum alocăm/dealocăm memorie: |
+ | <code c> | ||
+ | /** | ||
+ | * Creates a new binary tree | ||
+ | * @data_size: size of the data contained by the tree's nodes | ||
+ | * @return: pointer to the newly created tree | ||
+ | */ | ||
+ | b_tree_t *b_tree_create(size_t data_size); | ||
- | Reprezentarea prin matrice de adiacenţă a unui graf constă într-o matrice ''A[i][j]'' de dimensiune ''|V|x|V|'' astfel încât: | + | /** |
- | *''A[i][j] = 1'', dacă muchia ''(i,j)'' aparţine lui ''E'' | + | * Clear the whole memory used by the tree and its nodes |
- | *''A[i][j] = 0'', în caz contrar. | + | * @b_tree: the binary tree to be freed |
+ | * @free_data: function used to free the data contained by a node | ||
+ | */ | ||
+ | void b_tree_free(b_tree_t *b_tree, void (*free_data)(void *)); | ||
+ | </code> | ||
- | Această reprezentare este preferată pentru grafurile dense ( ''|E|'' este aproximativ egal cu ''|V|x|V|''). | + | De exemplu, dacă dorim să creem un arbore binar ce conține elemente de tip *char*, codul arată astfel: |
+ | <code c> | ||
+ | b_tree_t *char_tree = b_tree_create(sizeof(char)); | ||
- | Pentru graful de mai sus, matricea de adiacenţă este următoarea: | + | b_tree_free(char_tree, free); |
+ | </code> | ||
+ | ==== Parcurgerea arborilor ==== | ||
- | ^ ^0^1^2^3^4^ | + | Se implementeaza foarte usor recursiv: |
- | ^0|0|1|1|0|0| | + | |
- | ^1|1|0|1|1|1| | + | |
- | ^2|1|1|0|1|0| | + | |
- | ^3|0|1|1|0|0| | + | |
- | ^4|0|1|0|1|0| | + | |
+ | ==== 1. Preordine ==== | ||
+ | *Se parcurge **rădăcina** | ||
+ | *Se parcurge subarborele **stâng** | ||
+ | *Se parcurge subarborele **drept** | ||
- | <note important> | + | ==== 2. Inordine ==== |
- | În general, preferăm reprezentarea prin liste de adiacență deoarece au o complexitate mai bună în cazul parcurgerilor (cea mai comună operație pe grafuri). Totuși, există situații în care alegerea reprezentării prin matrice de adiacență simplifică mult rezolvarea unei probleme. Un exemplu ar fi algoritmul [[https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm|Floyd-Warshall]] care se bazează pe faptul că putem obține ușor distanța dintre două noduri pe baza matricei în care reținem, adițional, și costurile muchiilor. | + | |
- | </note> | + | |
- | ===== Parcurgerea grafurilor ===== | + | *Se parcurge subarborele **stâng** |
+ | *Se parcurge **rădăcina** | ||
+ | *Se parcurge subarborele **drept** | ||
- | ==== Parcurgerea în lăţime ==== | + | Exemplu: |
+ | <code c> | ||
+ | void b_tree_print_inorder(b_node_t *b_node, void (*print_data)(void *)) { | ||
+ | if (!b_node) | ||
+ | return; | ||
- | Parcurgerea în lățime (**Breadth-first Search - BFS**) presupune vizitarea nodurilor în următoarea ordine: | + | b_tree_print_inorder(b_node->left, print_data); |
- | * nodul sursă (considerat a fi pe nivelul 0) | + | print_data(b_node->data); |
- | * vecinii nodului sursă (aceștia constituind nivelul 1) | + | b_tree_print_inorder(b_node->right, print_data); |
- | * vecinii încă nevizitați ai nodurilor de pe nivelul 1 (aceștia constituind nivelul 2) | + | |
- | * vecinii încă nevizitați ai nodurilor de pe nivelul 2 | + | |
- | * ş.a.m.d. | + | |
- | + | ||
- | Caracteristica esențială a acestui tip de parcurgere este, deci, că se preferă explorarea **în lățime**, a nodurilor de pe același nivel (aceeași depărtare față de sursă) în detrimentul celei **în adâncime**, a nodurilor de pe nivelul următor. | + | |
- | + | ||
- | === Pași de execuție === | + | |
- | + | ||
- | * colorarea nodurilor. Pe parcurs ce algoritmul avansează, se colorează nodurile în felul următor: | + | |
- | ***alb** - nodul este nedescoperit încă | + | |
- | ***gri** - nodul a fost descoperit și este în curs de procesare | + | |
- | ***negru** - procesarea nodului s-a încheiat | + | |
- | * păstrarea informațiilor despre distanța până la nodul sursă. | + | |
- | * pentru fiecare nod în ''d[u]'' se reține distanța până la nodul sursă (poate fi util în unele probleme) | + | |
- | * obținerea arborelui BFS. | + | |
- | * în urma aplicării algoritmului BFS se obține un arbore de acoperire (prin eliminarea muchiilor pe care nu le folosim la parcurgere). Pentru a putea reconstitui acest arbore, se păstrează pentru fiecare nod dat informația despre părintele său în ''p[u]''. | + | |
- | + | ||
- | === Pseudocod === | + | |
- | + | ||
- | Pentru implementarea BFS se utilizează o coadă (''Q'') în care inițial se află doar nodul sursă. Se vizitează pe rând vecinii acestui nod şi se pun și ei în coada. În momentul în care nu mai există vecini nevizitați, nodul sursă este scos din coadă. | + | |
- | + | ||
- | <code> | + | |
- | // Inițializări | + | |
- | pentru fiecare nod u din V | + | |
- | { | + | |
- | culoare[u] = alb | + | |
- | d[u] = infinit | + | |
- | p[u] = null | + | |
- | } | + | |
- | + | ||
- | culoare[sursa] = gri | + | |
- | d[sursa] = 0 | + | |
- | enqueue(Q,sursa) // Punem nodul sursă în coada Q | + | |
- | + | ||
- | // Algoritmul propriu-zis | + | |
- | cât timp coada Q nu este vidă | + | |
- | { | + | |
- | v = dequeue(Q) // Extragem nodul v din coadă | + | |
- | pentru fiecare u dintre vecinii lui v | + | |
- | dacă culoare[u] == alb | + | |
- | { | + | |
- | culoare[u] = gri | + | |
- | p[u] = v | + | |
- | d[u] = d[v] + 1 | + | |
- | enqueue(Q,u) // Adăugăm nodul u în coadă | + | |
- | } | + | |
- | culoare[v] = negru // Am terminat de explorat toți vecinii lui v | + | |
} | } | ||
</code> | </code> | ||
+ | ==== 3. Postordine ==== | ||
- | Dacă graful are mai multe componente conexe, algoritmul, în forma dată, va parcurge doar componenta din care face parte nodul sursă. Pe grafuri cu mai multe componente conexe se va aplica în continuare algoritmul pentru fiecare nod rămas nevizitat și astfel se vor obține mai mulți arbori, câte unul pentru fiecare componentă. | + | *Se parcurge subarborele **stâng** |
+ | *Se parcurge subarborele **drept** | ||
+ | *Se parcurge **rădăcina** | ||
- | === Exemplu === | + | ==== 4. Lățime ==== |
- | {{sd-ca:laboratoare:bf1.jpg}} | + | Se folosește o coadă, iar la fiecare pas se extrage din această coadă câte un nod și se adăugă înapoi în coadă nodul stâng, respectiv drept al nodului scos. Acest algoritm continuă până când coada devine goală. |
- | + | ||
- | {{sd-ca:laboratoare:bf2.jpg}} | + | |
- | Arborele obținut în urma execuției este următorul: | + | <note important> |
+ | Nodurile frunză nu au descendenţi -> nodul stâng şi nodul drept pointează la **NULL** şi nu trebuie adăugate în coadă. | ||
+ | </note> | ||
- | {{sd-ca:laboratoare:bf3.jpg}} | + | ==== Arbori asociați expresiilor ==== |
- | ==== Parcurgerea în adâncime ==== | + | O expresie matematică este un şir de caractere compus din: |
+ | *variabile | ||
+ | *constante | ||
+ | *operatori | ||
+ | *paranteze (eventual). | ||
- | Parcurgerea în adâncime (**Depth-First Search - DFS**) presupune explorarea nodurilor în următoarea ordine: | + | Fiecărei expresii i se poate asocia un arbore binar, în care: |
- | * nodul sursă | + | *nodurile interioare reprezintă **operatorii** |
- | * primul vecin nevizitat al nodului sursă (îl vom numi ''V1'') | + | *frunzele reprezintă **constantele** şi/sau **variabilele**. |
- | * primul vecin nevizitat al lui ''V1'' (îl vom numi ''V2'') | + | |
- | * primul vecin nevizitat al lui ''V2'' | + | |
- | * s.a.m.d. | + | |
- | * în momentul în care am epuizat vecinii unui nod ''Vn'', continuăm cu următorul vecin nevizitat al nodului anterior, ''Vn-1'' | + | |
- | Așadar, spre deosebire de BFS, acest tip de parcurgere pune prioritate pe explorarea **în adâncime** (la distanțe tot mai mari față de nodul sursă), în detrimentul celei **în lățime** (pe același nivel). | + | În terminologia limbajelor formale şi a compilatoarelor, acest arbore se mai numeşte şi **Abstract Syntax Tree (AST)**. |
- | === Pași de execuție === | + | Pentru expresia **(a+1)*(b+10)+25/c** , arborele asociat este prezentat mai jos: |
+ | {{ sd-ca:laboratoare:imagine-ast.png?nolink&600 |}} | ||
- | * colorarea nodurilor. Pe parcurs ce algoritmul avansează, se colorează nodurile in felul următor: | + | <hidden> |
- | ***alb** - nodul este nedescoperit încă | + | ==== Evaluarea expresiilor ==== |
- | ***gri** - nodul a fost descoperit și este în curs de procesare | + | |
- | ***negru** - procesarea nodului s-a încheiat | + | |
- | * păstrarea informațiilor despre timp. Fiecare nod are două momente de timp asociate: | + | |
- | *''tDesc[u]'' - momentul descoperirii nodului (și a schimbării culorii din alb în gri) | + | |
- | *''tFin[u]'' - momentul în care procesarea nodului s-a încheiat (și culoarea acestuia s-a schimbat din gri în negru) | + | |
- | * obținerea arborelui DFS. | + | |
- | * în urma aplicării algoritmului DFS asupra fiecărei componente conexe a grafului, se obține pentru fiecare dintre acestea câte un arbore de acoperire (prin eliminarea muchiilor pe care nu le folosim la parcurgere). Pentru a putea reconstitui acest arbore, păstram pentru fiecare nod dat informația despre părintele său în ''p[u]''. | + | |
- | === Pseudocod === | + | Următorul pseudo-cod reprezintă în linii mari algoritmului de evaluare a expresiilor reprezentate sub formă de arbori binari: |
- | + | ||
- | <code> | + | <code c++> |
- | // Inițializări | + | Evalueaza(Node nod) { |
- | pentru fiecare nod u din V | + | // Daca nu este nod terminal... |
- | { | + | if (nod->left || nod->right) { |
- | culoare[u] = alb | + | // Evaluam expresiile subarborilor... |
- | p[u] = NULL | + | res1 = Evalueaza(nod->left); |
- | tDesc[u] = 0 | + | res2 = Evalueaza(nod->right); |
- | tFin[u] = 0 | + | |
- | } | + | // ... si combinam rezultatele aplicand operatorul |
- | contor_timp = 0 | + | return AplicaOperator(nod->op, res1, res2); |
- | + | } else { | |
- | // Funcţie de vizitare a nodului | + | // Daca nodul terminal contine o variabila, atunci intoarcem valoarea variabilei |
- | vizitare(nod) | + | if (nod->var) { |
- | { | + | return Valoare(nod->var); |
- | contor_timp = contor_timp + 1 | + | } else { // Avem o constanta |
- | tDesc[nod] = contor_timp | + | return nod->val; |
- | culoare[nod] = gri | + | } |
- | printeaza nod; | + | } |
- | } | + | |
- | + | ||
- | // Algoritmul propriu-zis | + | |
- | DFS(nod) | + | |
- | { | + | |
- | stiva s; | + | |
- | | + | |
- | viziteaza nod; | + | |
- | s.introdu(nod); | + | |
- | + | ||
- | cât timp stiva s nu este goală | + | |
- | { | + | |
- | nodTop = nodul din vârful stivei | + | |
- | + | ||
- | vecin = află primul vecin nevizitat al lui nodTop. | + | |
- | dacă vecin există | + | |
- | { | + | |
- | p[v] = nodTop | + | |
- | viziteaza v; | + | |
- | s.introdu(v); | + | |
- | } | + | |
- | altfel | + | |
- | { | + | |
- | contor_timp = contor_timp + 1 | + | |
- | tFin[nodTop] = contor_timp | + | |
- | culoare[nodTop] = negru | + | |
- | s.scoate(nodTop); | + | |
- | } | + | |
- | } | + | |
} | } | ||
</code> | </code> | ||
+ | </hidden> | ||
+ | ==== Cel mai mic strămoș comun ==== | ||
- | === Exemplu === | + | O problemă importantă în analiza arborilor este determinarea celui mai mic strămoș comun (LCA - Lowest Common Ancestor). LCA-ul a două noduri, u si v, este nodul cel mai depărtat de rădăcină care îi are pe u și v ca descendenți. |
- | {{sd-ca:laboratoare:df1.jpg}} | + | {{:sd-ca:laboratoare:diagram1-2.png?600|}} |
- | Nodul de pornire este I, iar pentru simplificare vecinii sunt aleși în ordine alfabetică. În stânga nodului este notat ''tDesc'', iar în dreapta ''tFin''. Dacă se afișează nodurile, în urma parcurgerii se obține următorul output: **I, E, B, A, C, D, G, F, H** | + | Spre exemplu, cel mai mic strămoș comun al nodurilor 1 și 12 este 0, în timp ce pentru nodurile 4 și 7, acesta este 1. |
- | Arborele obținut în urma parcurgerii este următorul: | ||
- | {{sd-ca:laboratoare:df2.jpg}} | + | ===== Schelet ===== |
- | + | ||
- | ==== Complexitate ==== | + | |
- | + | ||
- | Pentru ambele tipuri de parcurgeri, complexitatea este ''O(|E|+|V|)'' - unde ''|E|'' este numărul de muchii, iar ''|V|'' este numărul de noduri. | + | |
- | + | ||
- | **Explicație**: în cazul cel mai defavorabil, vor fi explorate toate muchiile și toate nodurile (când graful este liniarizat). | + | |
<note important> | <note important> | ||
- | De remarcat faptul că, pentru ambele tipuri de parcurgeri, complexitatea este cea menționată ''O(|E|+|V|)'' **numai în cazul în care grafurile sunt reținute ca liste de adiacență**. În acest caz, lista corespunzătoare nodului x reține numai vecinii nodului x. | + | Daca folositi **Github Classroom**, va rugam sa va actualizati scheletul cu cel de pe Devmind. Cel din repo-ul clonat initial nu este la cea mai recenta versiune. |
- | În cazul matricei de adiacență, pentru a parcurge vecinii unui nod x, trebuie să parcurgem toate nodurile. Această limitare duce la o complexitate de ''O(|V|^2)'' | + | |
</note> | </note> | ||
- | ===== Algoritmul Floyd-Warshall ===== | + | <hidden> |
- | Algoritmul Floyd-Warshall este un algoritm folosit pentru căutarea celor mai scurte căi într-un graf orientat ce are cost pe fiecare muchie (costul poate fi pozitiv sau negativ, dar nu pot exista cicli negativi). | + | {{:sd-ca:laboratoare:lab8_2022.zip|Scheletul de laborator}} |
- | Algortimul compară toate căile posibile din graf între toate perechile de noduri. | + | </hidden> |
- | === Pseudocod === | + | ===== Exerciții ===== |
- | <code> | + | |
- | + | ||
- | dist[V][V] // Matricea de distanțe minime, inițializate cu INFINIT | + | |
- | pentru fiecare nod v | + | |
- | dist[v][v] = 0 | + | |
- | + | ||
- | pentru fiecare pereche de noduri (u, v) | + | |
- | dist[u][v] = cost(u, v) | + | |
- | + | ||
- | pentru k între 0 și |V| – 1 | + | |
- | pentru i între 0 și |V| – 1 | + | |
- | pentru j ]ntre 0 și |V| – 1 | + | |
- | dacă (dist[i][k] + dist[k][j]) < dist[i][j] | + | |
- | dist[i][j] = dist[i][k] + dist[k][j] | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | ===== Schelet ===== | + | |
- | <note important> | + | |
- | Daca folositi **Github Classroom**, va rugam sa va actualizati scheletul cu cel de mai jos. Cel din repo-ul clonat initial nu este la cea mai recenta versiune. | + | |
- | </note> | + | |
- | {{:sd-ca:laboratoare:lab06_2022.zip|Scheletul de laborator}} | + | |
- | ===== Exerciţii ===== | + | |
<note> | <note> | ||
- | Trebuie să vă creați cont de [[https://lambdachecker.io | Lambda Checker]], dacă nu v-ați creat deja, pe care îl veți folosi la SD pe toată durata semestrului. Aveti grija sa selectati contestul corect la submit, si anume **[[https://lambdachecker.io/contest/37 |SD-CA-LAB-06 Grafuri - basics]]** | + | Trebuie să vă creați cont de [[https://code.devmind.ro | Devmind]], dacă nu v-ați creat deja, pe care îl veți folosi la SD pe toată durata semestrului. |
</note> | </note> | ||
+ | 1) [**4p**] Implementarea arborelui binar. Problema **Binary Tree** pe Devmind. | ||
- | 1) [**2.5p**] Implementați, plecând de la scheletul de cod, graful reprezentat prin matrice de adiacență (**Matrix Graph Implementation** pe Lambda Checker). | + | 2) [**3p**] Problema asociata grupei voastre: **SD-CA-LAB06-31XCA**. |
- | 2) [**2.5p**] Implementați, plecând de la scheletul de cod, graful reprezentat prin liste de adiacență (**List Graph Implementation** pe Lambda Checker). | + | 3) [**Bonus**] 1p bonus pe fiecare problema in plus pe care o rezolvati, maxim 2p bonus. |
- | 3) [**1p**] Implementați parcurgerea in adâncime (DFS) a unui graf implementat cu matrice de adiacență (**Matrix Graph DFS** pe Lambda Checker). | ||
- | |||
- | 4) [**1p**] Implementați parcurgerea in lățime (BFS) a unui graf implementat cu listă de adiacență (**List Graph BFS** pe Lambda Checker). | ||
- | |||
- | 5) [**2p - bonus**] Implementați algoritmul Floyd-Warshall pe un graf reprezentat printr-o matrice de adiacență. (pentru a primi punctajul, implementați soluția local, apoi urcați codul pe GitHub) | ||
===== Interviu ===== | ===== Interviu ===== | ||
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. | 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. | ||
- | Cum multe din companiile mari folosesc date stocate sub formă de grafuri (Facebook Open Graph, Google Social Graph şi Page Rank, etc.) la angajare vor dori să vadă ca ştiţi grafuri: | + | *Ce este un arbore? |
- | + | *Cum poate fi reprezentat un nod dintr-un arbore binar? | |
- | * cum se reprezintă grafurile | + | *Daţi exemplu de un tip (mai multe tipuri) de parcurgere al arborilor binari. Descrieţi modul de funţionare al acestuia (acestora). |
- | * cum funcţionează şi cum se implementează parcurgerile (BFS, DFS) | + | *Daţi exemplu de un mod de utilizare al arborilor binari. |
- | + | *Ce complexitate medie / worst-case au funcţiile de inserare / ştergere / căutare pentru un arbore binar, BST, AVL, etc. (mai multe despre complexitatea algoritmilor şi structurilor de date veţi învăţa în anul 2: Analiza Algoritmilor şi Proiectarea Algoritmilor). | |
- | Puteţi căuta mai multe întrebări pe https://leetcode.com/, http://www.careercup.com/ şi http://www.glassdoor.com/ | + | * (Bonus) Binary Tree Paths - afisati toate drumurile root-frunza. [[https://leetcode.com/problems/binary-tree-paths/description/ | (link)]] |
+ | * (Bonus) Binary Tree Right Side View - Întoarceți valorile nodurilor pe care le puteți vedea când vă uitați la un arbore binar din partea dreaptă [[https://leetcode.com/problems/binary-tree-right-side-view/description/ | (link)]] | ||
===== Bibliografie ===== | ===== Bibliografie ===== | ||
- | - [[http://en.wikipedia.org/wiki/Breadth-first_search | BFS]] | + | - [[http://en.wikipedia.org/wiki/Binary_tree | Binary Tree]] |
- | - [[http://en.wikipedia.org/wiki/Depth-first_search | DFS ]] | + | - [[http://en.wikipedia.org/wiki/AVL_tree | AVL ]] |
- | - [[https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm | Algoritmul Floyd-Warshall ]] | + | - [[http://en.wikipedia.org/wiki/Red%E2%80%93black_tree | Red-Black Tree ]] |
- | - [[https://ro.wikipedia.org/wiki/Algoritmul_lui_Kruskal | Algoritmul lui Kruskal ]] | + | - [[https://en.wikipedia.org/wiki/Segment_tree | Arbori de Intervale ]] |
+ | - [[https://en.wikipedia.org/wiki/Fenwick_tree | Arbori Indexati Binar ]] | ||
+ | - [[http://en.wikipedia.org/wiki/Splay_tree | Splay Tree ]] | ||
+ | - [[http://en.wikipedia.org/wiki/Abstract_syntax_tree | AST ]] | ||
+ | - [[http://en.wikipedia.org/wiki/Disjunctive_normal_form | DNF ]] |