Diferență între revizuiri ale paginii „SDA Lucrarea 6”

De la WikiLabs
Jump to navigationJump to search
 
(Nu s-au afișat 56 de versiuni intermediare efectuate de același utilizator)
Linia 3: Linia 3:
 
= Structura de date asociativă - Map =
 
= Structura de date asociativă - Map =
  
  Structura de date asociativă este o structură de date abstractă care stochează perechi de elemente cheie-valoare,  
+
  Structura de date asociativă (map) este o structură de date abstractă care stochează perechi de elemente cheie-valoare,  
  astfel încât nu pot exista două perechi cu aceeași cheie, într-o ordine arbitrară stabilită de structură.
+
  într-o ordine arbitrară stabilită de structură, astfel încât nu pot exista două perechi cu aceeași cheie în map.
  
În esență, un ''map'' este o mulțime (set) de chei care este întotdeauna stocată împreună cu o valoare.
+
În esență, un ''map'' este o mulțime (set) de chei care sunt întotdeauna stocate împreună cu o valoare.
  
 
[[Fișier:map.png]]
 
[[Fișier:map.png]]
Linia 13: Linia 13:
 
# Datele sunt plasate într-o ordine oarecare stabilită arbitrar de structură.
 
# Datele sunt plasate într-o ordine oarecare stabilită arbitrar de structură.
 
# Numărul de elemente ce poate fi stocat de structură este nelimitat.
 
# Numărul de elemente ce poate fi stocat de structură este nelimitat.
# Elementele stocate în mulțime sunt de același fel.
+
# Elementele stocate în map sunt de același fel.
 
# Tipul de date al cheii și tipul de date al valorii pot fi diferite.
 
# Tipul de date al cheii și tipul de date al valorii pot fi diferite.
 
# Map-ul poate conține doar chei unice, în baza unei funcții definite de egalitate. Altfel spus, dacă două chei sunt egale din punctul de vedere al map-ului, ele nu pot fi ambele prezente în perechi cheie-valoare în map.
 
# Map-ul poate conține doar chei unice, în baza unei funcții definite de egalitate. Altfel spus, dacă două chei sunt egale din punctul de vedere al map-ului, ele nu pot fi ambele prezente în perechi cheie-valoare în map.
Linia 20: Linia 20:
 
# Interogarea numărului de elemente din map.
 
# Interogarea numărului de elemente din map.
 
# Verificarea dacă map-ul este gol.
 
# Verificarea dacă map-ul este gol.
# Adăugarea perechi cheie-valoare (''put'') - dacă cheia există deja în mulțime, perechea respectivă este înlocuită de noua valoare.
+
# Adăugarea perechi cheie-valoare (''insert'') - dacă cheia există deja în map, aceasta nu se înlocuiește.
# Verificarea dacă o chieie există în mulțime (''hasKey'').
+
# Verificarea dacă o cheie există în map (''count'').
# Căutarea unei valori după o cheie dată (''get'');
+
# Căutarea unei valori după o cheie dată (''at'');
# Eliminarea unei chei din mulțime (''remove'').
+
# Eliminarea unei chei din map (''erase'').
<!--
 
== Implementarea de mulțimi cu funcții hash  ==
 
  
O funcție hash este o funcție care ia ca argument un șir de bytes de orice dimensiune și întoarce un șir de bytes
+
== Implementarea de structuri asociative cu funcții hash  ==
  de dimensiune fixă ce poartă numele de valoare de hash, cod hash sau pur și simplu hash.
 
  
Definiția unei funcții hash este strâns legată de definiția funcției <code>equals</code> astfel încât dacă două elemente e1 și e2 sunt considerate egale (<code>equals(e1, e1) == 1</code>), atunci obligatoriu <code>hash(e1) == hash(e2)</code>.
+
Definirea funcțiilor hash și a proprietăților lor s-a făcut în laboratorul 5 ([[SDA Lucrarea 5#Implementarea de mulțimi cu funcții hash]]).
  
 
=== Tipul de date și funcțiile de bază ===
 
=== Tipul de date și funcțiile de bază ===
  
În continuare vom prezenta un exemplu de mulțime pentru elemente de tip "persoană" implementată cu funcții hash. În primul rând vom defini structura ce memorează datele legate de o persoană:
+
În continuare vom prezenta un exemplu de map de la CNP (cheia) la elemente de tip <code>Person</code> (valoarea), implementată cu funcții hash. În primul rând vom defini structura ce memorează datele legate de o persoană:
  
<syntaxhighlight lang="C">
+
<syntaxhighlight lang="cpp">
 
struct Person {
 
struct Person {
     char firstName[30];
+
     std::string firstName;
     char lastName[30];
+
     std::string lastName;
     char idNumber[9]; // seria si numarul de buletin
+
     std::string idNumber; // seria si numarul de buletin
     char address[255];
+
     std::string address;
     char birthday[11];
+
     std::string birthday;
     char cnp[14];
+
     std::string cnp;
 
};
 
};
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Pentru a implementa o funcție hash pentru variabilele de tip <code>struct Person</code>, avem întâi nevoie să definim operația de egalitate. O persoană își poate schimba numele, buletinul și adresa, și pot exista mai multe persoane cu aceeași dată de naștere, dar ceea ce identifică clar o persoană este codul numeric personal (CNP). Deci vom defini funcția <code>equals</code> să întoarcă 1 dacă cnp-urile celor două argumente de tip <code>struct Person</code> sunt identice:
+
Funcțiile de hash și de egalitate pentru clasa <code>std::string</code> sunt deja implementate în STL. Dacă totuși aveți nevoie de a folosi niste funcții proprii, acestea se scriu în același fel ca cele pentru mulțimi împlementate cu funcții hash (vezi [[SDA Lucrarea 5]]).
  
<syntaxhighlight lang="C">
+
=== Clasa <code>std::unordered_map</code> ===
/**
 
* Functia intoarce 1 daca CNP-urile celor doua persoane sunt egale.
 
* @param person1 prima persoana de comparat.
 
* @param person2 a doua persoana de comparat.
 
* @return 1 daca CNP-urile celor doua persoane sunt identice.
 
*/
 
char equals(struct Person person1, struct Person person2);
 
</syntaxhighlight>
 
  
Mai departe definim funcția hash pentru o Persoană.
+
Pentru stocarea datelor necesare map-ului, În STL se definește clasa <code>std::unordered_map</code> în header-ul <code>unordered_map</code>. Aceasta este o clasă de tip template care se parametrizează cu două tipuri de date: tipul pentru cheie și tipul pentru valoare:
  
Pentru a putea implementa o mulțime cu funcții hash trebuie ca hash-ul maxim să fie o valoare rezonabilă pentru a putea aloca memorie cu dimensiunea valorii respective.
+
<syntaxhighlight lang="cpp">
 +
#include <unordered_map>
 +
#include <string>
  
Din motivul de mai sus, și pentru a micșora posibilitatea unei coliziuni de hash ce va micșora performanța structurii, ieșirea funcției hash va fi un număr între 0 și 999999 obținut din ultimele 6 cifre ale CNP-ului convertite din text în număr (pentru conversie puteți folosi funcția [http://www.cplusplus.com/reference/cstdlib/atoi/ atoi]):
+
int main() {
 +
    std::unorder_map<std::string, Person> myMap; // un map de la string la Person
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
<syntaxhighlight lang="C">
+
=== Crearea unui <code>std::unordered_map</code>===
 +
 
 +
Pentru crearea unui <code>std::unordered_map</code>, se definește următorul constructor:
 +
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functie hash pentru structurile de tip Person.
+
  * Constructorul creeaza un map nou și inițializează structurile de date interne.
* @param person peroana pentru care se doreste codul hash.
 
* @return codul hash al persoanei person
 
 
  */
 
  */
unsigned hash(struct Person person);
+
unordered_map();
 
</syntaxhighlight>
 
</syntaxhighlight>
  
În continuare vom utiliza pentru implementarea unui Hash Set o structură de date de tip secvență împlementată cu liste (LinkedList) și vom folosi funcțiile deja definite de la laboratoarele anterioare:
+
Exemplu:
* [[Fișier:linkedList.h]]
+
<syntaxhighlight lang="cpp" highlight="5">
* [[Fișier:linkedList.c]]
+
#include <unordered_map>
 +
#include <string>
  
=== Structura <code>HashSet</code> ===
+
int main() {
 +
    std::unorder_map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap["190010112345"] = person;
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
Pentru stocarea datelor necesare mulțimii, definim următoarea structură:
+
=== Interogarea numărului de elemente din map ===
  
<syntaxhighlight lang="C">
+
Pentru interogarea numărului de elemente din map se definesc:
  
#define MAX_HASH 1000000
+
<syntaxhighlight lang="cpp">
struct HashSet {
+
/**
    struct LinkedList array[MAX_HASH]; // un vector de liste, una pentru fiecare valoare posibila de hash
+
* Metoda intoarce numarul de elemente din map.
    unsigned size; // numarul de elemente din multime
+
* @return numarul de elemente din map.
};
+
*/
</syntaxhighlight>
+
uint64_t size();
  
=== Crearea unui <code>HashSet</code>===
 
 
Pentru crearea unui <code>HashSet</code>, se definește următoarea funcție:
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia creeaza un hashSet nou si intoarce adresa de memorie alocata.
+
  * Metoda intoarce true dacă map-ul nu conține nici un element.
  * @return un pointer la o structura de tip HashSet.
+
  * @return true dacă map-ul este gol, false în rest.
 
  */
 
  */
struct HashSet * createHashSet();
+
bool empty();
 +
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Pentru crearea unui <code>HashSet</code> se urmează următorii pași:
+
Exemplu:
# Se alocă memorie pentru un element de tip <code>struct HashSet</code> folosind funcția corectă pentru reseta memoria la alocare (scrierea ei cu 0).
+
<syntaxhighlight lang="cpp" highlight="9,10">
# Se inițializează <code>size</code> cu 0.
+
#include <unordered_map>
# Se întoarce adresa elementului de tip <code>struct HashSet</code>.
+
#include <string>
 +
#include <cstdio>
  
* Complexitate în '''timp''': O(1)
+
int main() {
* Complexitate în '''spațiu''': O(MAX_HASH)
+
    std::unorder_map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap["190010112345"] = person;
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
=== Interogarea numărului de elemente din mulțime ===
+
=== Adăugarea unei perechi cheie-valoare în map ===
  
Pentru interogarea numărului de elemente din mulțime definim:
+
Pentru operația de adăugare se definesc următoarele metode:
  
<syntaxhighlight lang="C">
+
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functia intoarce numarul de elemente din multime.
+
  * Metoda adauga perechea data in map daca cheia nu exista.  
  * @param set multimea a carei dimensiuni este ceruta.
+
  * @param keyValuePair o pereche cheie-valoare
  * @return numarul de elemente din multime.
+
  * @return o pereche iterator-bool, unde iteratorul identifică perechea
 +
*  introdusă în map, sau cea existentă cu aceeași cheie, iar bool spune daca
 +
*  perechea a fost introdusa sau nu (pentru ca exista deja).
 
  */
 
  */
unsigned hashSetSize(struct HashSet * set);
+
pair<iterator, bool> insert(pair<K, V> keyValuePair);
</syntaxhighlight>
 
 
 
Funcția va întoarce valoarea din câmpul <code>size</code> din structură.
 
 
 
* Complexitate în '''timp''': O(1)
 
* Complexitate în '''spațiu''': O(1)
 
  
Pentru a afla dacă mulțimea este goală definim:
 
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia intoarce 1 dacă mulțimea nu conține nici un element.
+
  * Metoda ia ca argument o cheie și intoarce o referinta (LVALUE) la
  * @param set multimea de interes.
+
*  valoarea asociată. Dacă cheia nu există în map, se adaugă cu o valoare
  * @return 1 dacă mulțimea este goală, 0 în rest.
+
*  neutră.
 +
  * @param key cheia ce se dorește adăugată (sau interogată).
 +
  * @return o referință la valoarea asociată cheii.
 
  */
 
  */
char hashSetIsEmpty(struct HashSet * set);
+
V & operator[](K key);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Funcția va întoarce 1 dacă valoarea din câmpul <code>size</code> este 0.
+
Exemplu:
 +
<syntaxhighlight lang="cpp" highlight="8-9">
 +
#include <unordered_map>
 +
#include <string>
 +
#include <cstdio>
 +
 +
int main() {
 +
    std::unorder_map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap.insert({"191010112345", person});
 +
    myMap["190010112345"] = person;
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
* Complexitate în '''timp''': O(1)
+
=== Eliminarea unei chei din map ===
* Complexitate în '''spațiu''': O(1)
 
  
=== Adăugarea unui element în mulțime ===
+
Pentru operația de eliminare se definește următoarea metodă:
  
Pentru operația de adăugare se definește următoarea funcție:
+
<syntaxhighlight lang="cpp">
 
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia adauga elementul dat in multime daca acesta nu exista. Daca
+
  * Metoda elimina cheia data din map daca acesta exista. Daca
  *  exista deja, functia nu are nici un efect.
+
  *  nu exista, functia nu are nici un efect.
  * @param set multimea in care trebuie adaugat elementul.
+
  * @param key cheia ce trebuie eliminata.
* @param person elementul ce trebuie adaugta.
 
 
  */
 
  */
void hashSetPut(struct HashSet * set, struct Person person);
+
void erase(K key);
</syntaxhighlight>
 
  
Adăugarea se realizează în felul următor:
 
# Se aplică funcția hash pe variabila <code>person</code>.
 
# Folosind valoarea obținută se accesează lista de pe poziția hash-ului din <code>array</code>.
 
# Se folosește funcția <code>linkedListSearch</code> pentru a verifica dacă <code>person</code> există în listă (funcția <code>linkedListSearch</code> trebuie re-implementată pentru a se folosi de funcția <code>equals</code> pentru compararea elementelor).
 
# Dacă valoarea întoarsă este -1 atunci <code>person</code> se adaugă în listă și <code>size</code> se incrementează cu 1.
 
 
* Complexitate în '''timp''': O(1) best case (fără coliziuni), O(1) average case pentru număr mai mic de 1000000 de persoane, O(n) worst case (același hash pentru poate persoanele).
 
* Complexitate în '''spațiu''': O(1)
 
 
<div class="regula"><span style="color: red; font-weight: bold">Atenție:</span> Deoarece vectorul <code>array</code> conține variabile de tip <code>struct LinkedList</code> și nu pointeri, pentru a folosi funcțiile definite în '''linkedList.h''' e necesară obținerea adresei variabilei de tip <code>struct LinkedList</code> folosind operatorul &.</div>
 
 
=== Eliminarea unui element din mulțime ===
 
 
Pentru operația de eliminare se definește următoarea funcție:
 
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia elimina elementul dat din multime daca acesta exista. Daca
+
  * Metoda sterge toate elementele din map.
*  nu exista, functia nu are nici un efect.
 
* @param set multimea din care trebuie eliminat elementul.
 
* @param person elementul ce trebuie eliminat.
 
 
  */
 
  */
void hashSetRemove(struct HashSet * set, struct Person person);
+
void clear();
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Eliminarea se realizează în felul următor:
+
Exemplu:
# Se aplică funcția hash pe variabila <code>person</code>.
+
<syntaxhighlight lang="cpp" highlight="10,13,16">
# Folosind valoarea obținută se accesează lista de pe poziția hash-ului din <code>array</code>.
+
#include <unordered_map>
# Se folosește funcția <code>linkedListSearch</code> pentru a verifica dacă <code>person</code> există în listă.
+
#include <string>
# Dacă valoarea întoarsă este diferită de -1 atunci se folosește funcția <code>linkedListDelete</code> pentru a șterge elementul de pe poziția respectivă și size se decrementează cu 1.
+
#include <cstdio>
 +
 +
int main() {
 +
    std::unorder_map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap.insert({"191010112345", person});
 +
    myMap["190010112345"] = person;
 +
    myMap.erase("098764321987");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.erase("191010112345");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.clear();
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
  
* Complexitate în '''timp''': O(1) best case (fără coliziuni), O(1) average case pentru număr mai mic de 1000000 de persoane, O(n) worst case (același hash pentru poate persoanele).
+
    return 0;
* Complexitate în '''spațiu''': O(1)
+
}
 +
</syntaxhighlight>
  
=== Verificarea dacă un element există în mulțime ===
+
=== Verificarea dacă o cheie există în map ===
  
Pentru a verifica dacă un element există în mulțime, se definește următoarea funcție:
+
Pentru a verifica dacă o cheie există în map, se definește următoarea funcție:
  
<syntaxhighlight lang="C">
+
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functia intoarce 1 daca elementul specificat exista in multime.
+
  * Functia intoarce 1 daca cheia specificata exista in map, 0 in rest.
* @param set multimea in care se cauta elementul.
+
  * @param key cheia cautata.
  * @param person elementul cautat.
+
  * @return 1 daca cheia exista in map, 0 daca nu.
  * @return 1 daca elementul exista in multime, 0 daca nu.
 
 
  */
 
  */
char hashSetContains(struct HashSet * set, struct Person person);
+
uint64_t count(K key);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Verificarea unui element dacă este sau nu în mulțime se realizează în felul următor:
+
Exemplu:
# Se aplică funcția hash pe variabila <code>person</code>.
+
<syntaxhighlight lang="cpp" highlight="14,15">
# Folosind valoarea obținută se accesează lista de pe poziția hash-ului din <code>array</code>.
+
#include <unordered_map>
# Se folosește funcția <code>linkedListSearch</code> pentru a verifica dacă <code>person</code> există în listă.
+
#include <string>
# Dacă valoarea întoarsă este diferită de -1 atunci se întoarce 1, altfel se întoarce 0.
+
#include <cstdio>
 +
 +
int main() {
 +
    std::unorder_map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap.insert({"191010112345", person});
 +
    myMap["190010112345"] = person;
 +
    myMap.erase("098764321987");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.erase("191010112345");
 +
    printf("Cheia 123 apare in map: %d\n", myMap.count("123"));
 +
    printf("Cheia 190010112345 apare in map: %d\n", myMap.count("190010112345"));
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.clear();
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
* Complexitate în '''timp''': O(1) best case (fără coliziuni), O(1) average case pentru număr mai mic de 1000000 de persoane, O(n) worst case (același hash pentru poate persoanele).
+
=== Căutarea unei valori după cheie ===
* Complexitate în '''spațiu''': O(1)
 
  
=== Ștergerea unui <code>HashSet</code>===
+
Pentru a căuta o valoare după cheie, se definește următoarele metode:
  
Pentru dezalocarea unui <code>HashSet</code>, se definește următoarea funcție:
 
 
<syntaxhighlight lang="C">
 
<syntaxhighlight lang="C">
 
/**
 
/**
  * Functia sterge hash set-ul specificat.
+
  * Metoda intoarce a referinta la valoarea asociată cheii key.
  * @param set hash set-ul ce trebuie sters.
+
  * @param key cheia cautata.
 +
* @return valoarea asociată cheii, dacă aceasta există,
 +
*  sau se generează o eroare daca cheia nu exista in map.
 
  */
 
  */
void deleteHashSet(struct HashSet * set);
+
V& at(K key);
</syntaxhighlight>
 
 
 
Pentru ștergerea unui <code>HashSet</code> se urmează următorii pași:
 
# Se iterează peste tot <code>array</code>-ul, ștergându-se toate nodurile din toate listele.
 
# Se șterge memoria alocată pentru structura de tip <code>HashSet</code>.
 
 
 
* Complexitate în '''timp''': O(MAX_HASH)
 
* Complexitate în '''spațiu''': O(1)
 
-->
 
  
== Implementarea map-urilor cu arbori binari de căutare (Binary Search Trees - BST) ==
 
 
Pentru a putea plasa elemente într-un map implementat cu arbori binari de căutare, pe mulțimea cheilor
 
trebuie să existe definită o relație de ordine.
 
 
Map-urile implementate cu arbori binari de căutare, în plus față proprietățile map-urilor definite mai sus, garantează
 
faptul că elementele sunt plasate în ordinea cheilor în structură (Ordered Map).
 
 
Din acest motiv trebuie definită o funcție care să compare două chei. Vom numi această funcție <code>compare</code> și se ca comporta similar cu funcția <code>strcmp</code>: va întoarce o valoare pozitivă dacă primul element e mai mare, o valoare negativă dacă al doilea element e mai mare și 0 dacă elementele sunt egale:
 
 
<syntaxhighlight lang="c">
 
 
/**
 
/**
  * Compara value1 cu value2 si intoarce o valoare corespunzătoare.
+
  * Operatorul intoarce o referinta la valoarea asociată cheii key.
  * @param value1 prima valoare de comparat.
+
  * @param key cheia cautata.
* @param value2 a doua valoare de comparat.
+
  * @return valoarea asociată cheii, dacă aceasta există,  
  * @return o valoare pozitivă dacă primul element e mai mare, o
+
  *  sau o valoare neutra daca nu există (care se si adauga in map).
  *  valoare negativă dacă al doilea element e mai mare și 0 dacă
 
*  elementele sunt egale.
 
 
  */
 
  */
int compare(K value1, K value2);
 
</syntaxhighlight>
 
  
=== Tipul de date și funcțiile de bază ===
+
V& operator[](K key);
 
 
În continuare vom prezenta un exemplu de mulțime pentru elemente de tip "server" implementată cu arbori binari de căutare. În primul rând vom defini structura ce memorează datele legate de un server:
 
<syntaxhighlight lang="c">
 
struct Server {
 
    /* Numele server-ului in retea - sir de caractere */
 
    char hostname[30];
 
   
 
    /* Adresa IP a server-ului o secvență de 4 valori numerice între 0 și 255 (ex: 192.168.1.10) */
 
    unsigned char ipv4[4];
 
 
 
    /* Adresa hardware pentru adaptorul de retea - o secventa de 6 bytes, in hexa (ex: 60:57:18:6e:a8:e8). */
 
    char hardwareAddress[6];
 
 
 
    /* Tipul procesorului - sir de caractere. */
 
    char cpuType[10];
 
 
 
    /* Frecventa procesorului in Gigahertz. */
 
    float cpuFrequencyGhz;
 
 
 
    /* Cantitatea de memorie RAM, in Gigabytes. */
 
    float ramMemoryGigaBytes;
 
 
 
    /* Capacitatea discului, in Terabytes. */
 
    float diskCapacityTeraBytes;
 
};
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Pentru a implementa o mulțime de servere folosind BST, avem întâi nevoie să definim funcția de comparare a două servere. Vom considera că din toate datele definite în structura de mai sus, cea care se schimbă cel mai greu este adresa fizică (hardware) a adaptorului de rețea care de cele mai multe ori este plasat pe placa de bază. Astfel, vom defini funcția compare astfel încât să compare cele două <code>hardwareAddress</code>:
+
Exemplu:
<syntaxhighlight lang="c">
+
<syntaxhighlight lang="cpp" highlight="10,11">
/**
+
#include <unordered_map>
* Compara server1 cu server2 si intoarce o valoare corespunzătoare.
+
#include <string>
  * @param server1 primul server de comparat.
+
#include <cstdio>
* @param server2 al doilea server de comparat.
+
   
* @return o valoare pozitivă dacă primul server are o adresa hardware mai mare, o
+
int main() {
*  valoare negativă dacă al doilea server are o adresa hardware mai mare și 0 dacă
+
    std::unorder_map<std::string, Person> myMap;
*  adresele hardware sunt egale.
+
    Person person;
*/
+
    person.firstName = "Ghita";
int compare(struct Server server1, struct Server server1);
+
    myMap.insert({"191010112345", person});
 +
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap.at("191010112345").fullName.c_str());
 +
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap["191010112345"].fullName.c_str());
 +
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Structurile <code>TreeSet</code> și <code>TreeNode</code> ===
 
  
Pentru stocarea datelor necesare mulțimii, definim următoarele structuri:
+
== Implementarea de structuri asociative cu arbori binari de căutare  ==
  
<syntaxhighlight lang="C">
+
Definirea arborilor binari de căutare și a proprietăților lor s-au făcut în laboratorul 5 ([[SDA Lucrarea 5#Implementarea de mulțimi cu arbori binari de căutare (BST)]]).
struct TreeNode {
 
    struct TreeNode * parent;
 
    struct TreeNode * left;
 
    struct TreeNode * right;
 
    struct Server value;
 
};
 
  
struct TreeSet {
+
=== Clasa <code>std::map</code> ===
    struct TreeNode * root;
 
    unsigned size;
 
};
 
</syntaxhighlight>
 
  
=== Crearea unui <code>TreeSet</code> ===
+
Pentru stocarea datelor necesare map-ului, În STL se definește clasa <code>std::map</code> în header-ul <code>map</code>. Aceasta este o clasă de tip template care se parametrizează cu două tipuri de date: tipul pentru cheie și tipul pentru valoare:
  
Pentru a crea un <code>TreeSet</code>, definim următoarea funcție:
+
<syntaxhighlight lang="cpp">
 +
#include <map>
 +
#include <string>
  
<syntaxhighlight lang="C">
+
int main() {
/**
+
    std::map<std::string, Person> myMap; // un map de la string la Person
* Functia aloca memorie si intoarce un pointer la un nou TreeSet.
+
    return 0;
* @return adresa noului TreeSet.
+
}
*/
 
struct TreeSet * createTreeSet();
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Funcția trebuie să realizeze următorii pași:
+
=== Crearea unui <code>std::map</code>===
# Se alocă memorie pentru un nou <code>struct TreeSet</code> ce trebuie inițializată automat cu 0.
 
# Se întoarce adresa alocată.
 
  
* Complexitate în '''timp''': O(1)
+
Pentru crearea unui <code>std::map</code>, se definește următorul constructor:
* Complexitate în '''spațiu''': O(1)
+
<syntaxhighlight lang="cpp">
 
 
=== Interogarea numărului de elemente din mulțime ===
 
 
 
Pentru interogarea numărului de elemente din mulțime definim:
 
 
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia intoarce numarul de elemente din multime.
+
  * Constructorul creeaza un map nou și inițializează structurile de date interne.
* @param set multimea a carei dimensiuni este ceruta.
 
* @return numarul de elemente din multime.
 
 
  */
 
  */
unsigned treeSetSize(struct TreeSet * set);
+
map();
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Funcția va întoarce valoarea din câmpul <code>size</code> din structură.
+
Exemplu:
 +
<syntaxhighlight lang="cpp" highlight="5">
 +
#include <map>
 +
#include <string>
  
* Complexitate în '''timp''': O(1)
+
int main() {
* Complexitate în '''spațiu''': O(1)
+
    std::map<std::string, Person> myMap;
 
+
    Person person;
Pentru a afla dacă mulțimea este goală definim:
+
    myMap["190010112345"] = person;
 
+
    return 0;
<syntaxhighlight lang="C">
+
}
/**
 
* Functia intoarce 1 dacă mulțimea nu conține nici un element.
 
* @param set multimea de interes.
 
* @return 1 dacă mulțimea este goală, 0 în rest.
 
*/
 
char treeSetIsEmpty(struct TreeSet * set);
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Funcția va întoarce 1 dacă valoarea din câmpul <code>size</code> este 0.
+
=== Interogarea numărului de elemente din map ===
 
 
* Complexitate în '''timp''': O(1)
 
* Complexitate în '''spațiu''': O(1)
 
 
 
=== Adăugarea unui element în mulțime ===
 
  
Pentru operația de adăugare se definește următoarea funcție:
+
Pentru interogarea numărului de elemente din map se definesc:
  
<syntaxhighlight lang="C">
+
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functia adauga elementul dat in multime daca acesta nu exista. Daca
+
  * Metoda intoarce numarul de elemente din map.
*  exista deja, functia nu are nici un efect.
+
  * @return numarul de elemente din map.
* @param set multimea in care trebuie adaugat elementul.
 
  * @param server elementul ce trebuie adaugat.
 
 
  */
 
  */
void treeSetPut(struct TreeSet * set, struct Server server);
+
uint64_t size();
</syntaxhighlight>
 
 
 
Adăugarea se realizează în felul următor:
 
# Dacă dimensiunea mulțimii este 0, se alocă memorie pentru un nod nou <code>newNode</code> în care:
 
#* <code>value</code> va lua valoarea lui <code>server</code>
 
#* <code>parent</code>, <code>left</code> și <code>right</code> vor lua valoarea <code>NULL</code>.
 
# Câmpul <code>root</code> ia valoarea <code>newNode</code>, <code>size</code> se incrementează și funcția se încheie.
 
# Altfel, se definește o variabilă de tip nod numită <code>tmpNode</code> care se inițializează cu valoarea câmpului <code>root</code>.
 
# Într-o buclă infinită se realizează următorii pași:
 
## Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este 0 (elementele sunt egale), funcția se încheie.
 
## Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este mai mare ca 0 și <code>tmpNode->left</code> este diferit de <code>NULL</code>, atunci <code>tmpNode</code> ia valoarea <code>tmpNode->left</code>.
 
## Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este mai mare ca 0 și <code>tmpNode->left</code> este egal cu <code>NULL</code>, atunci se alocă memorie pentru un nod <code>newNode</code> nou după regula de la 1, <code>newNode->parent</code> ia valoarea lui <code>tmpNode</code>, <code>tmpNode->left</code> ia valoarea lui <code>newNode</code>, <code>size</code> se incrementează cu 1 și funcția se încheie.
 
## Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este mai mică ca 0 și <code>tmpNode->right</code> este diferit de <code>NULL</code>, atunci <code>tmpNode</code> ia valoarea <code>tmpNode->right</code>.
 
## Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este mai mică ca 0 și <code>tmpNode->right</code> este egal cu <code>NULL</code>, atunci se alocă memorie pentru un nod <code>newNode</code> nou după regula de la 1, <code>newNode->parent</code> ia valoarea lui <code>tmpNode</code>, <code>tmpNode->right</code> ia valoarea lui <code>newNode</code>, <code>size</code> se incrementează cu 1 și funcția se încheie.
 
 
 
* Complexitate în '''timp''': O(1) best case (element adăugat imediat sub rădăcină), O(log<sub>2</sub>n) average case (arbore echilibrat), O(n) worst case (arbore dezechilibrat).
 
* Complexitate în '''spațiu''': O(1)
 
 
 
=== Eliminarea unui element din mulțime ===
 
  
Pentru operația de eliminare se definește următoarea funcție:
 
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia elimina elementul dat din multime daca acesta exista. Daca
+
  * Metoda intoarce true dacă map-ul nu conține nici un element.
nu exista, functia nu are nici un efect.
+
  * @return true dacă map-ul este gol, false în rest.
  * @param set multimea din care trebuie eliminat elementul.
 
* @param server elementul ce trebuie eliminat.
 
 
  */
 
  */
void treeSetRemove(struct TreeSet * set, struct Server server);
+
bool empty();
 +
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Eliminarea se realizează în felul următor (o descriere în imagini poate fi găsită [http://www.algolist.net/Data_structures/Binary_search_tree/Removal aici] - nu încercați să folosiți codul de pe acea pagină deoarece structurile din platformă diferă și în plus limbajul este C++ și nu va compila):
+
Exemplu:
# Folosind un pointer la nod <code>tmpNode</code>, se folosește tehnica de la căutarea unui element în mulțime pentru a identifica nodul care memorează serverul <code>server</code>. Dacă funcția ajunge la frunze fără a găsi server-ul căutat, funcția se încheie (server-ul nu există în mulțime). Altfel, <code>tmpNode</code> va fi pointer la nodul ce trebuie eliminat. În plus, se va defini o variabilă de tip char <code>direction</code> care la fiecare avansare în arbore va lua valoarea -1 dacă avansarea s-a făcut spre stânga și 1 dacă s-a făcut spre dreapta, astfel încât atunci când <code>tmpNode</code> este pointer la nodul ce trebuie șters, <code>direction</code> va fi 1 sau -1 în funcție de poziția nodului <code>tmpNode</code> față de nodul părinte.
+
<syntaxhighlight lang="cpp" highlight="9,10">
# Dacă <code>tmpNode->left</code> și <code>tmpNode->right</code> sunt ambele <code>NULL</code> (nodul ce trebuie șters este frunză), atunci, dacă <code>direction</code> este -1, <code>tmpNode->parent->left</code> ia valoarea <code>NULL</code>, altfel <code>tmpNode->parent->right</code> ia valoarea <code>NULL</code>, nodul <code>tmpNode</code> este dezalocat, <code>size</code> se decrementează și funcția se încheie.
+
#include <map>
# Dacă <code>tmpNode->left</code> este <code>NULL</code> și <code>tmpNode->right</code> este diferit de <code>NULL</code> (nodul are un singur copil), atunci, dacă <code>direction</code> este -1, <code>tmpNode->parent->left</code> ia valoarea <code>tmpNode->right</code>, altfel <code>tmpNode->parent->right</code> ia valoarea <code>tmpNode->right</code>, nodul <code>tmpNode</code> este dezalocat, <code>size</code> se decrementează și funcția se încheie.
+
#include <string>
# Dacă <code>tmpNode->left</code> este diferit de <code>NULL</code> și <code>tmpNode->right</code> este <code>NULL</code> (nodul are un singur copil), atunci, dacă <code>direction</code> este -1, <code>tmpNode->parent->left</code> ia valoarea <code>tmpNode->left</code>, altfel <code>tmpNode->parent->right</code> ia valoarea <code>tmpNode->left</code>, nodul <code>tmpNode</code> este dezalocat, <code>size</code> se decrementează și funcția se încheie.
+
#include <cstdio>
# Dacă <code>tmpNode->left</code> și <code>tmpNode->right</code> sunt ambele diferite de <code>NULL</code> (nodul ce trebuie șters are doi copii), atunci:
 
#* Se foloște un al doilea pointer la nod numit <code>minSubtreeNode</code> cu care se caută cea mai mică valoare din subarborele din dreapta nodului <code>tmpNode</code>.
 
#* <code>tmpNode->value</code> ia valoarea <code>minSubtreeNode->value</code> apoi se șterge <code>minSubtreeNode</code> după aceleași reguli de mai sus.
 
  
* Complexitate în '''timp''': O(1) best case (element găsit imediat sub rădăcină), O(log<sub>2</sub>n) average case (arbore echilibrat), O(n) worst case (arbore dezechilibrat).
+
int main() {
* Complexitate în '''spațiu''': O(1)
+
    std::map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap["190010112345"] = person;
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
=== Verificarea dacă un element există în mulțime ===
+
=== Adăugarea unei perechi cheie-valoare în map ===
  
Pentru a verifica dacă un element există în mulțime, se definește următoarea funcție:
+
Pentru operația de adăugare se definesc următoarele metode:
  
<syntaxhighlight lang="C">
+
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functia intoarce 1 daca elementul specificat exista in multime.
+
  * Metoda adauga perechea data in map daca cheia nu exista.  
  * @param set multimea in care se cauta elementul.
+
  * @param keyValuePair o pereche cheie-valoare
  * @param server elementul cautat.
+
  * @return o pereche iterator-bool, unde iteratorul identifică perechea
  * @return 1 daca elementul exista in multime, 0 daca nu.
+
  *   introdusă în map, sau cea existentă cu aceeași cheie, iar bool spune daca
 +
*  perechea a fost introdusa sau nu (pentru ca exista deja).
 
  */
 
  */
char treeSetContains(struct TreeSet * set, struct Server server);
+
pair<iterator, bool> insert(pair<K, V> keyValuePair);
</syntaxhighlight>
 
  
Verificarea unui element dacă este sau nu în mulțime se realizează în felul următor:
 
# Se definește un pointer la nod numit <code>tmpNode</code> care se inițializează cu valoarea lui <code>root</code>.
 
# Cât timp <code>tmpNode</code> este diferit de <code>NULL</code>:
 
#* Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este 0, se întoarce valoarea 1 (elementul a fost găsit).
 
#* Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este pozitiv, <code>tmpNode</code> ia valoarea lui <code>tmpNode->left</code>.
 
#* Dacă rezultatul funcției <code>compare</code> cu argumentele <code>tmpNode->value</code> și <code>server</code> este negativ, <code>tmpNode</code> ia valoarea lui <code>tmpNode->right</code>.
 
# Când s-a ieșit din buclă, se întoarce 0 (elementul nu a fost găsit).
 
 
* Complexitate în '''timp''': O(1) best case (element găsit în rădăcină), O(log<sub>2</sub>n) average case (arbore echilibrat), O(n) worst case (arbore dezechilibrat).
 
* Complexitate în '''spațiu''': O(1)
 
 
=== Ștergerea unui <code>TreeSet</code>===
 
 
Pentru dezalocarea unui <code>TreeSet</code>, se definește următoarea funcție:
 
<syntaxhighlight lang="C">
 
 
/**
 
/**
  * Functia sterge tree set-ul specificat.
+
  * Metoda ia ca argument o cheie și intoarce o referinta (LVALUE) la
  * @param set tree set-ul ce trebuie sters.
+
*  valoarea asociată. Dacă cheia nu există în map, se adaugă cu o valoare
 +
*  neutră.
 +
  * @param key cheia ce se dorește adăugată (sau interogată).
 +
* @return o referință la valoarea asociată cheii.
 
  */
 
  */
void deleteTreeSet(struct TreeSet * set);
+
V & operator[](K key);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Pentru ștergerea unui <code>TreeSet</code> se urmează următorii pași:
+
Exemplu:
# Se realizează o parcurgere în post-ordine (stânga-dreapta-rădăcină) și se șterg toate nodurile din arbore.
+
<syntaxhighlight lang="cpp" highlight="8-9">
# Se șterge memoria alocată pentru structura de tip <code>TreeSet</code>.
+
#include <map>
 
+
#include <string>
* Complexitate în '''timp''': O(n)
+
#include <cstdio>
* Complexitate în '''spațiu''': O(1)
+
 
+
int main() {
= Exerciții =
+
    std::map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap.insert({"191010112345", person});
 +
    myMap["190010112345"] = person;
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
== Săptămâna 1 ==
+
=== Eliminarea unei chei din map ===
  
<ol>
+
Pentru operația de eliminare se definește următoarea metodă:
<li> Dându-se fișierele [[Fișier:linkedList.h]], [[Fișier:linkedList.c]] și header-ele <code>hashSet.h</code> și <code>person.h</code> de mai jos, implementați toate funcțiile definite în două fișiere sursă numite <code>person.c</code> și <code>hashSet.c</code>:
 
<ul><li> <code>person.h</code>:
 
<syntaxhighlight lang="C">
 
#ifndef PERSON_H
 
#define PERSON_H
 
 
 
struct Person {
 
    char firstName[30];
 
    char lastName[30];
 
    char idNumber[9]; // seria si numarul de buletin
 
    char address[255];
 
    char birthday[11];
 
    char cnp[14];
 
};
 
  
 +
<syntaxhighlight lang="cpp">
 
/**
 
/**
  * Functia intoarce 1 daca CNP-urile celor doua persoane sunt egale.
+
  * Metoda elimina cheia data din map daca acesta exista. Daca
  * @param person1 prima persoana de comparat.
+
  * nu exista, functia nu are nici un efect.
  * @param person2 a doua persoana de comparat.
+
  * @param key cheia ce trebuie eliminata.
* @return 1 daca CNP-urile celor doua persoane sunt identice.
 
 
  */
 
  */
char equals(struct Person person1, struct Person person2);
+
void erase(K key);
  
 
/**
 
/**
  * Functie hash pentru structurile de tip Person.
+
  * Metoda sterge toate elementele din map.
* @param person peroana pentru care se doreste codul hash.
 
* @return codul hash al persoanei person
 
 
  */
 
  */
unsigned hash(struct Person person);
+
void clear();
 +
</syntaxhighlight>
  
#define T struct Person
+
Exemplu:
 +
<syntaxhighlight lang="cpp" highlight="10,13,16">
 +
#include <map>
 +
#include <string>
 +
#include <cstdio>
 +
 +
int main() {
 +
    std::map<std::string, Person> myMap;
 +
    Person person;
 +
    myMap.insert({"191010112345", person});
 +
    myMap["190010112345"] = person;
 +
    myMap.erase("098764321987");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.erase("191010112345");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.clear();
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
  
#endif
+
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</li>
 
<li> <code>hashset.h</code>:
 
<syntaxhighlight lang="C">
 
#ifndef HASH_SET_H
 
#define HASH_SET_H
 
  
#include "linkedList.h"
+
=== Verificarea dacă o cheie există în map ===
#include "person.h"
 
  
#define MAX_HASH 1000000
+
Pentru a verifica dacă o cheie există în map, se definește următoarea funcție:
  
struct HashSet {
+
<syntaxhighlight lang="cpp">
    struct LinkedList array[MAX_HASH]; // un vector de liste, una pentru fiecare valoare posibila de hash
 
    unsigned size; // numarul de elemente din multime
 
};
 
 
 
/**
 
/**
  * Functia creeaza un hashSet nou si intoarce adresa de memorie alocata.
+
  * Functia intoarce 1 daca cheia specificata exista in map, 0 in rest.
  * @return un pointer la o structura de tip HashSet.
+
* @param key cheia cautata.
 +
  * @return 1 daca cheia exista in map, 0 daca nu.
 
  */
 
  */
struct HashSet * createHashSet();
+
uint64_t count(K key);
+
</syntaxhighlight>
/**
+
 
* Functia intoarce numarul de elemente din multime.
+
Exemplu:
* @param set multimea a carei dimensiuni este ceruta.
+
<syntaxhighlight lang="cpp" highlight="14,15">
* @return numarul de elemente din multime.
+
#include <map>
*/
+
#include <string>
unsigned hashSetSize(struct HashSet * set);
+
#include <cstdio>
 
/**
 
* Functia intoarce 1 dacă mulțimea nu conține nici un element.
 
* @param set multimea de interes.
 
* @return 1 dacă mulțimea este goală, 0 în rest.
 
*/
 
char hashSetIsEmpty(struct HashSet * set);
 
 
/**
 
* Functia adauga elementul dat in multime daca acesta nu exista. Daca
 
*  exista deja, functia nu are nici un efect.
 
* @param set multimea in care trebuie adaugat elementul.
 
* @param person elementul ce trebuie adaugta.
 
*/
 
void hashSetPut(struct HashSet * set, struct Person person);
 
 
/**
 
* Functia elimina elementul dat din multime daca acesta exista. Daca
 
*  nu exista, functia nu are nici un efect.
 
* @param set multimea din care trebuie eliminat elementul.
 
* @param person elementul ce trebuie eliminat.
 
*/
 
void hashSetRemove(struct HashSet * set, struct Person person);
 
 
/**
 
* Functia intoarce 1 daca elementul specificat exista in multime.
 
* @param set multimea in care se cauta elementul.
 
* @param person elementul cautat.
 
* @return 1 daca elementul exista in multime, 0 daca nu.
 
*/
 
char hashSetContains(struct HashSet * set, struct Person person);
 
 
   
 
   
/**
+
int main() {
* Functia sterge hash set-ul specificat.
+
    std::map<std::string, Person> myMap;
* @param set hash set-ul ce trebuie sters.
+
    Person person;
*/
+
    myMap.insert({"191010112345", person});
void deleteHashSet(struct HashSet * set);
+
    myMap["190010112345"] = person;
 +
    myMap.erase("098764321987");
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.erase("191010112345");
 +
    printf("Cheia 123 apare in map: %d\n", myMap.count("123"));
 +
    printf("Cheia 190010112345 apare in map: %d\n", myMap.count("190010112345"));
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 +
    myMap.clear();
 +
    printf("Map-ul contine %lu elemente.\n", myMap.size());
 +
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 
   
 
   
#endif
+
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</li>
 
</ul>
 
</li>
 
<li> Scrieți o altă sursă <code>main.c</code> în care să definiți o funcție <code>struct Person readFromKeyboard()</code> și o altă funcție <code>main</code> care să citească persoane de la tastatură până când numele introdus este egal cu "-" și care să afișeze câte persoane diferite au fost introduse.</li>
 
</ol>
 
  
== Săptămâna 2 ==
+
=== Căutarea unei valori după cheie ===
 +
 
 +
Pentru a căuta o valoare după cheie, se definesc următoarele metode:
  
<ol>
 
<li> Dându-se header-ele <code>treeSet.h</code> și <code>server.h</code> de mai jos, implementați toate funcțiile definite în două fișiere sursă numite <code>server.c</code> și <code>treeSet.c</code>:
 
<ul><li> <code>server.h</code>:
 
 
<syntaxhighlight lang="C">
 
<syntaxhighlight lang="C">
#ifndef SERVER_H
+
/**
#define SERVER_H
+
  * Metoda intoarce a referinta la valoarea asociată cheii key.
 
+
  * @param key cheia cautata.
struct Server {
+
  * @return valoarea asociată cheii, dacă aceasta există,
    /* Numele server-ului in retea - sir de caractere */
+
  *  sau se generează o eroare daca cheia nu exista in map.
    char hostname[30];
+
  */
   
+
V& at(K key);
    /* Adresa IP a server-ului o secvență de 4 valori numerice între 0 și 255 (ex: 192.168.1.10) */
 
    unsigned char ipv4[4];
 
   
 
    /* Adresa hardware pentru adaptorul de retea - o secventa de 6 bytes, in hexa (ex: 60:57:18:6e:a8:e8). */
 
    char hardwareAddress[6];
 
   
 
    /* Tipul procesorului - sir de caractere. */
 
    char cpuType[10];
 
   
 
    /* Frecventa procesorului in Gigahertz. */
 
    float cpuFrequencyGhz;
 
   
 
    /* Cantitatea de memorie RAM, in Gigabytes. */
 
    float ramMemoryGigaBytes;
 
   
 
    /* Capacitatea discului, in Terabytes. */
 
    float diskCapacityTeraBytes;
 
};
 
  
 
/**
 
/**
  * Compara server1 cu server2 si intoarce o valoare corespunzătoare.
+
  * Operatorul intoarce o referinta la valoarea asociată cheii key.
  * @param server1 primul server de comparat.
+
  * @param key cheia cautata.
* @param server2 al doilea server de comparat.
+
  * @return valoarea asociată cheii, dacă aceasta există,  
  * @return o valoare pozitivă dacă primul server are o adresa hardware mai mare, o
+
  *  sau o valoare neutra daca nu există (care se si adauga in map).
  *  valoare negativă dacă al doilea server are o adresa hardware mai mare și 0 dacă
 
*  adresele hardware sunt egale.
 
 
  */
 
  */
int compare(struct Server server1, struct Server server1);
 
  
#endif
+
V& operator[](K key);
 
</syntaxhighlight>
 
</syntaxhighlight>
</li>
 
<li> <code>treeSet.h</code>:
 
<syntaxhighlight lang="C">
 
#ifndef TREE_SET_H
 
#define TREE_SET_H
 
 
#include "server.h"
 
  
struct TreeNode {
+
Exemplu:
    struct TreeNode * parent;
+
<syntaxhighlight lang="cpp" highlight="10,11">
    struct TreeNode * left;
+
#include <map>
    struct TreeNode * right;
+
#include <string>
    struct Server value;
+
#include <cstdio>
};
 
 
struct TreeSet {
 
    struct TreeNode * root;
 
    unsigned size;
 
};
 
 
/**
 
* Functia aloca memorie si intoarce un pointer la un nou TreeSet.
 
* @return adresa noului TreeSet.
 
*/
 
struct TreeSet * createTreeSet();
 
 
/**
 
* Functia intoarce numarul de elemente din multime.
 
* @param set multimea a carei dimensiuni este ceruta.
 
* @return numarul de elemente din multime.
 
*/
 
unsigned treeSetSize(struct TreeSet * set);
 
 
/**
 
* Functia intoarce 1 dacă mulțimea nu conține nici un element.
 
* @param set multimea de interes.
 
* @return 1 dacă mulțimea este goală, 0 în rest.
 
*/
 
char treeSetIsEmpty(struct TreeSet * set);
 
 
/**
 
* Functia adauga elementul dat in multime daca acesta nu exista. Daca
 
*  exista deja, functia nu are nici un efect.
 
* @param set multimea in care trebuie adaugat elementul.
 
* @param server elementul ce trebuie adaugat.
 
*/
 
void treeSetPut(struct TreeSet * set, struct Server server);
 
 
/**
 
* Functia elimina elementul dat din multime daca acesta exista. Daca
 
*  nu exista, functia nu are nici un efect.
 
* @param set multimea din care trebuie eliminat elementul.
 
* @param server elementul ce trebuie eliminat.
 
*/
 
void treeSetRemove(struct TreeSet * set, struct Server server);
 
 
/**
 
* Functia intoarce 1 daca elementul specificat exista in multime.
 
* @param set multimea in care se cauta elementul.
 
* @param server elementul cautat.
 
* @return 1 daca elementul exista in multime, 0 daca nu.
 
*/
 
char treeSetContains(struct TreeSet * set, struct Server server);
 
 
/**
 
* Functia sterge tree set-ul specificat.
 
* @param set tree set-ul ce trebuie sters.
 
*/
 
void deleteTreeSet(struct TreeSet * set);
 
 
   
 
   
#endif
+
int main() {
 +
    std::map<std::string, Person> myMap;
 +
    Person person;
 +
    person.firstName = "Ghita";
 +
    myMap.insert({"191010112345", person});
 +
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap.at("191010112345").fullName.c_str());
 +
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap["191010112345"].fullName.c_str());
 +
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
</li>
 
</ul>
 
</li>
 
<li> Scrieți o altă sursă <code>main.c</code> în care să definiți o funcție <code>struct Server readFromKeyboard()</code> și o altă funcție <code>main</code> care să citească informații legate de servere de la tastatură până când ''hostname''-ul introdus este egal cu "-" și care să afișeze câte servere diferite au fost introduse.</li>
 
</ol>
 

Versiunea curentă din 14 mai 2017 19:58

În acest laborator se vor implementa structuri de date asociative (map) cu arbori binari și funcții hash.

Structura de date asociativă - Map

Structura de date asociativă (map) este o structură de date abstractă care stochează perechi de elemente cheie-valoare, 
într-o ordine arbitrară stabilită de structură, astfel încât nu pot exista două perechi cu aceeași cheie în map.

În esență, un map este o mulțime (set) de chei care sunt întotdeauna stocate împreună cu o valoare.

Map.png

Map-ul are următoarele proprietăți:

  1. Datele sunt plasate într-o ordine oarecare stabilită arbitrar de structură.
  2. Numărul de elemente ce poate fi stocat de structură este nelimitat.
  3. Elementele stocate în map sunt de același fel.
  4. Tipul de date al cheii și tipul de date al valorii pot fi diferite.
  5. Map-ul poate conține doar chei unice, în baza unei funcții definite de egalitate. Altfel spus, dacă două chei sunt egale din punctul de vedere al map-ului, ele nu pot fi ambele prezente în perechi cheie-valoare în map.

Map-ul suportă următoarele operații de bază:

  1. Interogarea numărului de elemente din map.
  2. Verificarea dacă map-ul este gol.
  3. Adăugarea perechi cheie-valoare (insert) - dacă cheia există deja în map, aceasta nu se înlocuiește.
  4. Verificarea dacă o cheie există în map (count).
  5. Căutarea unei valori după o cheie dată (at);
  6. Eliminarea unei chei din map (erase).

Implementarea de structuri asociative cu funcții hash

Definirea funcțiilor hash și a proprietăților lor s-a făcut în laboratorul 5 (SDA Lucrarea 5#Implementarea de mulțimi cu funcții hash).

Tipul de date și funcțiile de bază

În continuare vom prezenta un exemplu de map de la CNP (cheia) la elemente de tip Person (valoarea), implementată cu funcții hash. În primul rând vom defini structura ce memorează datele legate de o persoană:

struct Person {
    std::string firstName;
    std::string lastName;
    std::string idNumber; // seria si numarul de buletin
    std::string address;
    std::string birthday;
    std::string cnp;
};

Funcțiile de hash și de egalitate pentru clasa std::string sunt deja implementate în STL. Dacă totuși aveți nevoie de a folosi niste funcții proprii, acestea se scriu în același fel ca cele pentru mulțimi împlementate cu funcții hash (vezi SDA Lucrarea 5).

Clasa std::unordered_map

Pentru stocarea datelor necesare map-ului, În STL se definește clasa std::unordered_map în header-ul unordered_map. Aceasta este o clasă de tip template care se parametrizează cu două tipuri de date: tipul pentru cheie și tipul pentru valoare:

#include <unordered_map>
#include <string>

int main() {
    std::unorder_map<std::string, Person> myMap; // un map de la string la Person
    return 0;
}

Crearea unui std::unordered_map

Pentru crearea unui std::unordered_map, se definește următorul constructor:

/**
 * Constructorul creeaza un map nou și inițializează structurile de date interne.
 */
unordered_map();

Exemplu:

#include <unordered_map>
#include <string>

int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    myMap["190010112345"] = person;
    return 0;
}

Interogarea numărului de elemente din map

Pentru interogarea numărului de elemente din map se definesc:

/**
 * Metoda intoarce numarul de elemente din map.
 * @return numarul de elemente din map.
 */
uint64_t size();

/**
 * Metoda intoarce true dacă map-ul nu conține nici un element.
 * @return true dacă map-ul este gol, false în rest.
 */
bool empty();

Exemplu:

#include <unordered_map>
#include <string>
#include <cstdio>

int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    myMap["190010112345"] = person;
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    return 0;
}

Adăugarea unei perechi cheie-valoare în map

Pentru operația de adăugare se definesc următoarele metode:

/**
 * Metoda adauga perechea data in map daca cheia nu exista. 
 * @param keyValuePair o pereche cheie-valoare
 * @return o pereche iterator-bool, unde iteratorul identifică perechea 
 *   introdusă în map, sau cea existentă cu aceeași cheie, iar bool spune daca
 *   perechea a fost introdusa sau nu (pentru ca exista deja).
 */
pair<iterator, bool> insert(pair<K, V> keyValuePair);

/**
 * Metoda ia ca argument o cheie și intoarce o referinta (LVALUE) la 
 *  valoarea asociată. Dacă cheia nu există în map, se adaugă cu o valoare
 *  neutră.
 * @param key cheia ce se dorește adăugată (sau interogată).
 * @return o referință la valoarea asociată cheii.
 */
V & operator[](K key);

Exemplu:

#include <unordered_map>
#include <string>
#include <cstdio>
 
int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    return 0;
}

Eliminarea unei chei din map

Pentru operația de eliminare se definește următoarea metodă:

/**
 * Metoda elimina cheia data din map daca acesta exista. Daca
 *  nu exista, functia nu are nici un efect.
 * @param key cheia ce trebuie eliminata.
 */
void erase(K key);

/**
 * Metoda sterge toate elementele din map.
 */
void clear();

Exemplu:

#include <unordered_map>
#include <string>
#include <cstdio>
 
int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    myMap.erase("098764321987");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.erase("191010112345");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.clear();
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");

    return 0;
}

Verificarea dacă o cheie există în map

Pentru a verifica dacă o cheie există în map, se definește următoarea funcție:

/**
 * Functia intoarce 1 daca cheia specificata exista in map, 0 in rest.
 * @param key cheia cautata.
 * @return 1 daca cheia exista in map, 0 daca nu.
 */
uint64_t count(K key);

Exemplu:

#include <unordered_map>
#include <string>
#include <cstdio>
 
int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    myMap.erase("098764321987");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.erase("191010112345");
    printf("Cheia 123 apare in map: %d\n", myMap.count("123"));
    printf("Cheia 190010112345 apare in map: %d\n", myMap.count("190010112345"));
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.clear();
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 
    return 0;
}

Căutarea unei valori după cheie

Pentru a căuta o valoare după cheie, se definește următoarele metode:

/**
 * Metoda intoarce a referinta la valoarea asociată cheii key.
 * @param key cheia cautata.
 * @return valoarea asociată cheii, dacă aceasta există, 
 *  sau se generează o eroare daca cheia nu exista in map.
 */
V& at(K key);

/**
 * Operatorul intoarce o referinta la valoarea asociată cheii key.
 * @param key cheia cautata.
 * @return valoarea asociată cheii, dacă aceasta există, 
 *  sau o valoare neutra daca nu există (care se si adauga in map).
 */

V& operator[](K key);

Exemplu:

#include <unordered_map>
#include <string>
#include <cstdio>
 
int main() {
    std::unorder_map<std::string, Person> myMap;
    Person person;
    person.firstName = "Ghita";
    myMap.insert({"191010112345", person});
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap.at("191010112345").fullName.c_str());
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap["191010112345"].fullName.c_str());
    return 0;
}


Implementarea de structuri asociative cu arbori binari de căutare

Definirea arborilor binari de căutare și a proprietăților lor s-au făcut în laboratorul 5 (SDA Lucrarea 5#Implementarea de mulțimi cu arbori binari de căutare (BST)).

Clasa std::map

Pentru stocarea datelor necesare map-ului, În STL se definește clasa std::map în header-ul map. Aceasta este o clasă de tip template care se parametrizează cu două tipuri de date: tipul pentru cheie și tipul pentru valoare:

#include <map>
#include <string>

int main() {
    std::map<std::string, Person> myMap; // un map de la string la Person
    return 0;
}

Crearea unui std::map

Pentru crearea unui std::map, se definește următorul constructor:

/**
 * Constructorul creeaza un map nou și inițializează structurile de date interne.
 */
map();

Exemplu:

#include <map>
#include <string>

int main() {
    std::map<std::string, Person> myMap;
    Person person;
    myMap["190010112345"] = person;
    return 0;
}

Interogarea numărului de elemente din map

Pentru interogarea numărului de elemente din map se definesc:

/**
 * Metoda intoarce numarul de elemente din map.
 * @return numarul de elemente din map.
 */
uint64_t size();

/**
 * Metoda intoarce true dacă map-ul nu conține nici un element.
 * @return true dacă map-ul este gol, false în rest.
 */
bool empty();

Exemplu:

#include <map>
#include <string>
#include <cstdio>

int main() {
    std::map<std::string, Person> myMap;
    Person person;
    myMap["190010112345"] = person;
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    return 0;
}

Adăugarea unei perechi cheie-valoare în map

Pentru operația de adăugare se definesc următoarele metode:

/**
 * Metoda adauga perechea data in map daca cheia nu exista. 
 * @param keyValuePair o pereche cheie-valoare
 * @return o pereche iterator-bool, unde iteratorul identifică perechea 
 *   introdusă în map, sau cea existentă cu aceeași cheie, iar bool spune daca
 *   perechea a fost introdusa sau nu (pentru ca exista deja).
 */
pair<iterator, bool> insert(pair<K, V> keyValuePair);

/**
 * Metoda ia ca argument o cheie și intoarce o referinta (LVALUE) la 
 *  valoarea asociată. Dacă cheia nu există în map, se adaugă cu o valoare
 *  neutră.
 * @param key cheia ce se dorește adăugată (sau interogată).
 * @return o referință la valoarea asociată cheii.
 */
V & operator[](K key);

Exemplu:

#include <map>
#include <string>
#include <cstdio>
 
int main() {
    std::map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    return 0;
}

Eliminarea unei chei din map

Pentru operația de eliminare se definește următoarea metodă:

/**
 * Metoda elimina cheia data din map daca acesta exista. Daca
 *  nu exista, functia nu are nici un efect.
 * @param key cheia ce trebuie eliminata.
 */
void erase(K key);

/**
 * Metoda sterge toate elementele din map.
 */
void clear();

Exemplu:

#include <map>
#include <string>
#include <cstdio>
 
int main() {
    std::map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    myMap.erase("098764321987");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.erase("191010112345");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.clear();
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");

    return 0;
}

Verificarea dacă o cheie există în map

Pentru a verifica dacă o cheie există în map, se definește următoarea funcție:

/**
 * Functia intoarce 1 daca cheia specificata exista in map, 0 in rest.
 * @param key cheia cautata.
 * @return 1 daca cheia exista in map, 0 daca nu.
 */
uint64_t count(K key);

Exemplu:

#include <map>
#include <string>
#include <cstdio>
 
int main() {
    std::map<std::string, Person> myMap;
    Person person;
    myMap.insert({"191010112345", person});
    myMap["190010112345"] = person;
    myMap.erase("098764321987");
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.erase("191010112345");
    printf("Cheia 123 apare in map: %d\n", myMap.count("123"));
    printf("Cheia 190010112345 apare in map: %d\n", myMap.count("190010112345"));
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
    myMap.clear();
    printf("Map-ul contine %lu elemente.\n", myMap.size());
    printf("Map-ul %s este gol!\n", myMap.empty() ? "" : "NU");
 
    return 0;
}

Căutarea unei valori după cheie

Pentru a căuta o valoare după cheie, se definesc următoarele metode:

/**
 * Metoda intoarce a referinta la valoarea asociată cheii key.
 * @param key cheia cautata.
 * @return valoarea asociată cheii, dacă aceasta există, 
 *  sau se generează o eroare daca cheia nu exista in map.
 */
V& at(K key);

/**
 * Operatorul intoarce o referinta la valoarea asociată cheii key.
 * @param key cheia cautata.
 * @return valoarea asociată cheii, dacă aceasta există, 
 *  sau o valoare neutra daca nu există (care se si adauga in map).
 */

V& operator[](K key);

Exemplu:

#include <map>
#include <string>
#include <cstdio>
 
int main() {
    std::map<std::string, Person> myMap;
    Person person;
    person.firstName = "Ghita";
    myMap.insert({"191010112345", person});
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap.at("191010112345").fullName.c_str());
    printf("Numele persoanei cu cheia 191010112345 este: %s\n", myMap["191010112345"].fullName.c_str());
    return 0;
}