Diferență între revizuiri ale paginii „SDA Lucrarea 5”
Linia 67: | Linia 67: | ||
== Clasa <code>std::unordered_set</code> == | == Clasa <code>std::unordered_set</code> == | ||
− | Clasa din STL care implementează mulțimi folosind funcții hash poartă numele de [http://en.cppreference.com/w/cpp/container/unordered_set std::unordered_set] și este definită în header-ul <code>unordered_set</code>. | + | Clasa din STL care implementează mulțimi folosind funcții hash poartă numele de [http://en.cppreference.com/w/cpp/container/unordered_set std::unordered_set] și este definită în header-ul <code>unordered_set</code>. Mai departe vom exemplifica definirea funcțiilor de egalitate și hash pentru utilizarea unui <code>std::unordered_set</code> în scopul stocării unor elemente de tip <code>struct Person</code>: |
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
struct Person { | struct Person { | ||
char firstName[30]; | char firstName[30]; | ||
Linia 80: | Linia 80: | ||
</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> | + | 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>equal</code> să întoarcă 1 dacă cnp-urile celor două argumente de tip <code>struct Person</code> sunt identice: |
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
− | /** | + | namespace std { |
− | + | /** | |
− | + | * Functia intoarce true daca CNP-urile celor doua persoane sunt egale. | |
− | + | * @param person1 prima persoana de comparat. | |
− | + | * @param person2 a doua persoana de comparat. | |
− | + | * @return true daca CNP-urile celor doua persoane sunt identice. | |
− | + | */ | |
+ | template<> | ||
+ | bool equal<Person>(Person person1, Person person2) { | ||
+ | return strcmp(person1.cnp, person2.cnp) == 0; | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Linia 98: | Linia 102: | ||
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]): | 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]): | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
− | /** | + | namespace std { |
− | + | template<> | |
− | + | struct hash<Person> { | |
− | + | /** | |
− | + | * Functie hash pentru structurile de tip Person. | |
− | + | * @param person peroana pentru care se doreste codul hash. | |
+ | * @return codul hash al persoanei person | ||
+ | */ | ||
+ | size_t operator()(Person const & person) const { | ||
+ | return (size_t) atoi(person.cnp + 7); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | == Crearea unui <code>std::unordered_set</code> == | |
− | |||
− | |||
− | + | Pentru instanțierea unui obiect de tip <code>std::unordered_set</code>, se folosește constructorul fără argumente: | |
− | + | <syntaxhighlight lang="cpp"> | |
+ | #include<unordered_set> | ||
+ | #include<cstdio> | ||
− | < | + | int main() { |
+ | std::unorder_set<int> mySet; | ||
+ | printf("My set is empty: %d\n", mySet.empty()); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
− | + | == Interogarea numărului de elemente din mulțime == | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Pentru interogarea numărului de elemente din mulțime aunt definite: | |
− | + | <syntaxhighlight lang="cpp"> | |
− | <syntaxhighlight lang=" | ||
/** | /** | ||
− | * Functia | + | * Functia intoarce numarul de elemente din multime. |
− | * @return | + | * @return numarul de elemente din multime. |
*/ | */ | ||
− | + | size_t size(); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
/** | /** | ||
− | * Functia intoarce | + | * Functia intoarce true daca multimea e goala. |
− | + | * @return true daca multimea e goala | |
− | * @return | ||
*/ | */ | ||
− | + | bool empty(); | |
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Exemplu: | |
− | + | <syntaxhighlight lang="cpp"> | |
− | + | #include<unordered_set> | |
+ | #include<cstdio> | ||
− | + | int main() { | |
+ | std::unorder_set<float> mySet; | ||
+ | mySet.insert(1); | ||
+ | mySet.insert(1.1); | ||
+ | mySet.insert(1); | ||
− | + | printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty()); | |
− | + | return 0; | |
− | + | } | |
− | |||
− | |||
− | |||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | == Adăugarea unui element în mulțime == | |
− | |||
− | |||
− | |||
− | |||
− | |||
Pentru operația de adăugare se definește următoarea funcție: | Pentru operația de adăugare se definește următoarea funcție: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
/** | /** | ||
* Functia adauga elementul dat in multime daca acesta nu exista. Daca | * Functia adauga elementul dat in multime daca acesta nu exista. Daca | ||
* exista deja, functia nu are nici un efect. | * exista deja, functia nu are nici un efect. | ||
− | * @param | + | * @param element elementul ce trebuie adaugat. |
− | |||
*/ | */ | ||
− | void | + | void insert(T element); |
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Exemplu: | |
− | + | ||
− | + | <syntaxhighlight lang="cpp"> | |
− | # | + | #include<unordered_set> |
− | # | + | #include<cstdio> |
− | + | int main() { | |
− | + | std::unorder_set<float> mySet; | |
+ | mySet.insert(1); | ||
+ | mySet.insert(1.1); | ||
+ | mySet.insert(1); | ||
− | + | printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty()); | |
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
=== Eliminarea unui element din mulțime === | === Eliminarea unui element din mulțime === | ||
Linia 206: | Linia 203: | ||
Pentru operația de eliminare se definește următoarea funcție: | Pentru operația de eliminare se definește următoarea funcție: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
/** | /** | ||
* Functia elimina elementul dat din multime daca acesta exista. Daca | * Functia elimina elementul dat din multime daca acesta exista. Daca | ||
* nu exista, functia nu are nici un efect. | * nu exista, functia nu are nici un efect. | ||
− | * @param | + | * @param element elementul ce trebuie eliminat. |
− | |||
*/ | */ | ||
− | void | + | void erase(T element); |
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Exemplu: | |
− | + | ||
− | # | + | <syntaxhighlight lang="cpp"> |
− | # | + | #include<unordered_set> |
− | + | #include<cstdio> | |
+ | |||
+ | int main() { | ||
+ | std::unorder_set<float> mySet; | ||
+ | mySet.insert(1); | ||
+ | mySet.insert(1.1); | ||
+ | mySet.insert(1); | ||
+ | |||
+ | printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty()); | ||
+ | |||
+ | mySet.erase(1); | ||
+ | |||
+ | printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty()); | ||
− | + | return 0; | |
− | + | } | |
+ | </syntaxhighlight> | ||
=== Verificarea dacă un element există în mulțime === | === Verificarea dacă un element există în mulțime === | ||
− | Pentru a verifica dacă un element există în mulțime, se definește următoarea | + | Pentru a verifica dacă un element există în mulțime, se definește următoarea metodă: |
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="cpp"> |
/** | /** | ||
− | * | + | * Metoda intoarce 1 daca elementul specificat exista in multime, 0 dacă nu. |
− | * @param | + | * @param element elementul cautat. |
− | |||
* @return 1 daca elementul exista in multime, 0 daca nu. | * @return 1 daca elementul exista in multime, 0 daca nu. | ||
*/ | */ | ||
− | + | size_t count(T element); | |
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Exemplu: | |
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang="cpp"> | |
− | + | #include<unordered_set> | |
+ | #include<cstdio> | ||
− | + | int main() { | |
+ | std::unorder_set<float> mySet; | ||
+ | mySet.insert(1); | ||
+ | mySet.insert(1.1); | ||
+ | mySet.insert(1); | ||
− | + | printf("Element %f is in set (0 or 1): %d\n", 1, mySet.count(1)); | |
− | + | printf("Element %f is in set (0 or 1): %d\n", 2, mySet.count(2)); | |
− | + | return 0; | |
− | + | } | |
− | |||
− | |||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Implementarea mulțimilor cu arbori binari de căutare (Binary Search Trees - BST) == | == Implementarea mulțimilor cu arbori binari de căutare (Binary Search Trees - BST) == |
Versiunea de la data 19 aprilie 2017 23:16
În acest laborator se vor implementa mulțimi cu funcții hash și arbori binari.
Mulțimea
Mulțimea este o structură de date abstractă care stochează o colecție de elemente unice în care ordinea nu contează.
Mulțimea are următoarele proprietăți:
- Datele sunt plasate într-o ordine oarecare stabilită arbitrar de structură.
- Numărul de elemente ce poate fi stocat de structură este nelimitat.
- Elementele stocate în mulțime sunt de același fel.
- Mulțimea poate conține doar elemente unice, în baza unei funcții definite de egalitate. Altfel spus, dacă două elemente sunt egale din punctul de vedere al mulțimii, ele nu pot fi ambele conținute de mulțime.
Mulțimea suportă următoarele operații de bază:
- Interogarea numărului de elemente din mulțime.
- Verificarea dacă mulțimea este goală.
- Adăugarea unui element în mulțime (insert).
- Verificarea dacă un element există în mulțime (count).
- Eliminarea unui element din mulțime (erase).
Pentru a verifica egalitatea a două elemente vom defini o funcție equals
care va întoarce true
dacă cele două argumente sunt egale și false
dacă nu. Această funcție trebuie să existe pentru orice implementare de mulțime, atunci când elementele nu sunt valori ce pot fi comparate cu operatorul implicit ==:
namespace std {
/**
* Functia verifica daca valorile value1 si value2 sunt egale.
* @param value1 prima valoare de comparat.
* @param value2 a doua valoare de comparat.
* @return true daca cele doua valori sunt egale si false in rest.
*/
bool equal(T value1, T value2);
}
Atenție, pentru a putea fi folosită de clasele de tip mulțime din STL, această funcție trebuie declarată în interiorul namespace-ului std
.
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 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 equal
astfel încât dacă două elemente e1 și e2 sunt considerate egale (equal(e1, e1) == true
), atunci obligatoriu hash(e1) == hash(e2)
:
namespace std {
template<class T>
struct hash {
/**
* Metoda calculeaza un hashcode pentru elementul element.
* @param element referinta la un element de tip T pentru care se calculeaza hash-ul
* @return hashcode-ul calculat.
*/
std::size_t operator()(T const& element) const;
};
}
Să definim noile elemente de sintaxă:
std::size_t
este un tip de dată ce reprezintă o dimensiune (deci este un număr întreg fără semn), iar dimensiunea lui în biți depinde de compilator (poate fi sinonim cuuint32_t
sau cuuint64_t
); un cod hash este reprezentat ca o valoare de tipstd::size_t
operator()
este numele unui operator (mai precis operatorul 'paranteză rotundă') care poate fi supraîncărcat astfel încât să facă altceva decât comportamentul obișnuit (adică apel de funcție); în acest caz, se poate folosi sintaxahash<SomeType>(someValueOfSomeType)
pentru a apela metodaoperator()
din structurahash<T>
T const &element
conține două elemente de noutate:- cuvântul cheie
const
face ca elementul să nu poată fi modificat de metodă (ci doar citit) - operatorul
&
se numește referință, similară cu un pointer, iar scopul lui în acest context este să permită accesarea datelor din variabilaelement
fără a copia tot conținutul structurii pe stivă, lucru care se întâmplă cu variabilele non-referință trimise ca argument funcțiilor.
- cuvântul cheie
- cuvântul cheie
const
de la sfârșitul metodei specifică faptul că această metodă nu poate modifica membrii clasei (în acest caz clasa nu are alți membri, deci prezența acestui cuvânt cheie este redundantă).
Clasa std::unordered_set
Clasa din STL care implementează mulțimi folosind funcții hash poartă numele de std::unordered_set și este definită în header-ul unordered_set
. Mai departe vom exemplifica definirea funcțiilor de egalitate și hash pentru utilizarea unui std::unordered_set
în scopul stocării unor elemente de tip struct Person
:
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];
};
Pentru a implementa o funcție hash pentru variabilele de tip struct Person
, 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 equal
să întoarcă 1 dacă cnp-urile celor două argumente de tip struct Person
sunt identice:
namespace std {
/**
* Functia intoarce true daca CNP-urile celor doua persoane sunt egale.
* @param person1 prima persoana de comparat.
* @param person2 a doua persoana de comparat.
* @return true daca CNP-urile celor doua persoane sunt identice.
*/
template<>
bool equal<Person>(Person person1, Person person2) {
return strcmp(person1.cnp, person2.cnp) == 0;
}
Mai departe definim funcția hash pentru o Persoană.
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.
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 atoi):
namespace std {
template<>
struct hash<Person> {
/**
* Functie hash pentru structurile de tip Person.
* @param person peroana pentru care se doreste codul hash.
* @return codul hash al persoanei person
*/
size_t operator()(Person const & person) const {
return (size_t) atoi(person.cnp + 7);
}
};
}
Crearea unui std::unordered_set
Pentru instanțierea unui obiect de tip std::unordered_set
, se folosește constructorul fără argumente:
#include<unordered_set>
#include<cstdio>
int main() {
std::unorder_set<int> mySet;
printf("My set is empty: %d\n", mySet.empty());
return 0;
}
Interogarea numărului de elemente din mulțime
Pentru interogarea numărului de elemente din mulțime aunt definite:
/**
* Functia intoarce numarul de elemente din multime.
* @return numarul de elemente din multime.
*/
size_t size();
/**
* Functia intoarce true daca multimea e goala.
* @return true daca multimea e goala
*/
bool empty();
Exemplu:
#include<unordered_set>
#include<cstdio>
int main() {
std::unorder_set<float> mySet;
mySet.insert(1);
mySet.insert(1.1);
mySet.insert(1);
printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty());
return 0;
}
Adăugarea unui element în mulțime
Pentru operația de adăugare se definește următoarea funcție:
/**
* Functia adauga elementul dat in multime daca acesta nu exista. Daca
* exista deja, functia nu are nici un efect.
* @param element elementul ce trebuie adaugat.
*/
void insert(T element);
Exemplu:
#include<unordered_set>
#include<cstdio>
int main() {
std::unorder_set<float> mySet;
mySet.insert(1);
mySet.insert(1.1);
mySet.insert(1);
printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty());
return 0;
}
Eliminarea unui element din mulțime
Pentru operația de eliminare se definește următoarea funcție:
/**
* Functia elimina elementul dat din multime daca acesta exista. Daca
* nu exista, functia nu are nici un efect.
* @param element elementul ce trebuie eliminat.
*/
void erase(T element);
Exemplu:
#include<unordered_set>
#include<cstdio>
int main() {
std::unorder_set<float> mySet;
mySet.insert(1);
mySet.insert(1.1);
mySet.insert(1);
printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty());
mySet.erase(1);
printf("My set has %u elements. My set is empty: %d\n", mySet.size(), mySet.empty());
return 0;
}
Verificarea dacă un element există în mulțime
Pentru a verifica dacă un element există în mulțime, se definește următoarea metodă:
/**
* Metoda intoarce 1 daca elementul specificat exista in multime, 0 dacă nu.
* @param element elementul cautat.
* @return 1 daca elementul exista in multime, 0 daca nu.
*/
size_t count(T element);
Exemplu:
#include<unordered_set>
#include<cstdio>
int main() {
std::unorder_set<float> mySet;
mySet.insert(1);
mySet.insert(1.1);
mySet.insert(1);
printf("Element %f is in set (0 or 1): %d\n", 1, mySet.count(1));
printf("Element %f is in set (0 or 1): %d\n", 2, mySet.count(2));
return 0;
}
Implementarea mulțimilor cu arbori binari de căutare (Binary Search Trees - BST)
Pentru a putea plasa elemente într-o mulțime implementată cu arbori binari de căutare, pe mulțimea acestor elemente trebuie să existe definită o relație de ordine.
Mulțimile implementate cu arbori binari de căutare, în plus față proprietățile mulțimilor definite mai sus, garantează faptul că elementele sunt plasate în ordine în mulțime (Ordered Set).
Din acest motiv trebuie definită o funcție care să compare două elemente. Vom numi această funcție compare
și se ca comporta similar cu funcția strcmp
: 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:
/**
* Compara value1 cu value2 si intoarce o valoare corespunzătoare.
* @param value1 prima valoare de comparat.
* @param value2 a doua valoare de comparat.
* @return 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.
*/
int compare(T value1, T value2);
Tipul de date și funcțiile de bază
Î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:
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;
};
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ă hardwareAddress
:
/**
* Compara server1 cu server2 si intoarce o valoare corespunzătoare.
* @param server1 primul server de comparat.
* @param server2 al doilea server de comparat.
* @return o valoare pozitivă dacă primul server are o adresa hardware mai mare, o
* 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);
Structurile TreeSet
și TreeNode
Pentru stocarea datelor necesare mulțimii, definim următoarele structuri:
struct TreeNode {
struct TreeNode * parent;
struct TreeNode * left;
struct TreeNode * right;
struct Server value;
};
struct TreeSet {
struct TreeNode * root;
unsigned size;
};
Crearea unui TreeSet
Pentru a crea un TreeSet
, definim următoarea funcție:
/**
* Functia aloca memorie si intoarce un pointer la un nou TreeSet.
* @return adresa noului TreeSet.
*/
struct TreeSet * createTreeSet();
Funcția trebuie să realizeze următorii pași:
- Se alocă memorie pentru un nou
struct TreeSet
ce trebuie inițializată automat cu 0. - Se întoarce adresa alocată.
- Complexitate în timp: O(1)
- Complexitate în spațiu: O(1)
Interogarea numărului de elemente din mulțime
Pentru interogarea numărului de elemente din mulțime definim:
/**
* 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);
Funcția va întoarce valoarea din câmpul size
din structură.
- Complexitate în timp: O(1)
- Complexitate în spațiu: O(1)
Pentru a afla dacă mulțimea este goală definim:
/**
* 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);
Funcția va întoarce 1 dacă valoarea din câmpul size
este 0.
- 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:
/**
* 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);
Adăugarea se realizează în felul următor:
- Dacă dimensiunea mulțimii este 0, se alocă memorie pentru un nod nou
newNode
în care:value
va lua valoarea luiserver
parent
,left
șiright
vor lua valoareaNULL
.
- Câmpul
root
ia valoareanewNode
,size
se incrementează și funcția se încheie. - Altfel, se definește o variabilă de tip nod numită
tmpNode
care se inițializează cu valoarea câmpuluiroot
. - Într-o buclă infinită se realizează următorii pași:
- Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este 0 (elementele sunt egale), funcția se încheie. - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este mai mare ca 0 șitmpNode->left
este diferit deNULL
, atuncitmpNode
ia valoareatmpNode->left
. - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este mai mare ca 0 șitmpNode->left
este egal cuNULL
, atunci se alocă memorie pentru un nodnewNode
nou după regula de la 1,newNode->parent
ia valoarea luitmpNode
,tmpNode->left
ia valoarea luinewNode
,size
se incrementează cu 1 și funcția se încheie. - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este mai mică ca 0 șitmpNode->right
este diferit deNULL
, atuncitmpNode
ia valoareatmpNode->right
. - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este mai mică ca 0 șitmpNode->right
este egal cuNULL
, atunci se alocă memorie pentru un nodnewNode
nou după regula de la 1,newNode->parent
ia valoarea luitmpNode
,tmpNode->right
ia valoarea luinewNode
,size
se incrementează cu 1 și funcția se încheie.
- Dacă rezultatul funcției
- Complexitate în timp: O(1) best case (element adăugat imediat sub rădăcină), O(log2n) 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:
/**
* 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);
Eliminarea se realizează în felul următor (o descriere în imagini poate fi găsită 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):
- Folosind un pointer la nod
tmpNode
, se folosește tehnica de la căutarea unui element în mulțime pentru a identifica nodul care memorează serverulserver
. 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,tmpNode
va fi pointer la nodul ce trebuie eliminat. În plus, se va defini o variabilă de tip chardirection
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ândtmpNode
este pointer la nodul ce trebuie șters,direction
va fi 1 sau -1 în funcție de poziția noduluitmpNode
față de nodul părinte. - Dacă
tmpNode->left
șitmpNode->right
sunt ambeleNULL
(nodul ce trebuie șters este frunză), atunci, dacădirection
este -1,tmpNode->parent->left
ia valoareaNULL
, altfeltmpNode->parent->right
ia valoareaNULL
, nodultmpNode
este dezalocat,size
se decrementează și funcția se încheie. - Dacă
tmpNode->left
esteNULL
șitmpNode->right
este diferit deNULL
(nodul are un singur copil), atunci, dacădirection
este -1,tmpNode->parent->left
ia valoareatmpNode->right
, altfeltmpNode->parent->right
ia valoareatmpNode->right
, nodultmpNode
este dezalocat,size
se decrementează și funcția se încheie. - Dacă
tmpNode->left
este diferit deNULL
șitmpNode->right
esteNULL
(nodul are un singur copil), atunci, dacădirection
este -1,tmpNode->parent->left
ia valoareatmpNode->left
, altfeltmpNode->parent->right
ia valoareatmpNode->left
, nodultmpNode
este dezalocat,size
se decrementează și funcția se încheie. - Dacă
tmpNode->left
șitmpNode->right
sunt ambele diferite deNULL
(nodul ce trebuie șters are doi copii), atunci:- Se foloște un al doilea pointer la nod numit
minSubtreeNode
cu care se caută cea mai mică valoare din subarborele din dreapta noduluitmpNode
. tmpNode->value
ia valoareaminSubtreeNode->value
apoi se ștergeminSubtreeNode
după aceleași reguli de mai sus.
- Se foloște un al doilea pointer la nod numit
- Complexitate în timp: O(1) best case (element găsit imediat sub rădăcină), O(log2n) average case (arbore echilibrat), O(n) worst case (arbore dezechilibrat).
- Complexitate în spațiu: O(1)
Verificarea dacă un element există în mulțime
Pentru a verifica dacă un element există în mulțime, se definește următoarea funcție:
/**
* 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);
Verificarea unui element dacă este sau nu în mulțime se realizează în felul următor:
- Se definește un pointer la nod numit
tmpNode
care se inițializează cu valoarea luiroot
. - Cât timp
tmpNode
este diferit deNULL
:- Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este 0, se întoarce valoarea 1 (elementul a fost găsit). - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este pozitiv,tmpNode
ia valoarea luitmpNode->left
. - Dacă rezultatul funcției
compare
cu argumenteletmpNode->value
șiserver
este negativ,tmpNode
ia valoarea luitmpNode->right
.
- Dacă rezultatul funcției
- 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(log2n) average case (arbore echilibrat), O(n) worst case (arbore dezechilibrat).
- Complexitate în spațiu: O(1)
Ștergerea unui TreeSet
Pentru dezalocarea unui TreeSet
, se definește următoarea funcție:
/**
* Functia sterge tree set-ul specificat.
* @param set tree set-ul ce trebuie sters.
*/
void deleteTreeSet(struct TreeSet * set);
Pentru ștergerea unui TreeSet
se urmează următorii pași:
- Se realizează o parcurgere în post-ordine (stânga-dreapta-rădăcină) și se șterg toate nodurile din arbore.
- Se șterge memoria alocată pentru structura de tip
TreeSet
.
- Complexitate în timp: O(n)
- Complexitate în spațiu: O(1)
Exerciții
Săptămâna 1
- Dându-se fișierele Fișier:LinkedList.h, Fișier:LinkedList.c și header-ele
hashSet.h
șiperson.h
de mai jos, implementați toate funcțiile definite în două fișiere sursă numiteperson.c
șihashSet.c
:-
person.h
:#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]; }; /** * 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); /** * Functie hash pentru structurile de tip Person. * @param person peroana pentru care se doreste codul hash. * @return codul hash al persoanei person */ unsigned hash(struct Person person); #define T struct Person #endif
-
hashset.h
:#ifndef HASH_SET_H #define HASH_SET_H #include "linkedList.h" #include "person.h" #define MAX_HASH 1000000 struct HashSet { 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. * @return un pointer la o structura de tip HashSet. */ struct HashSet * createHashSet(); /** * Functia intoarce numarul de elemente din multime. * @param set multimea a carei dimensiuni este ceruta. * @return numarul de elemente din multime. */ unsigned hashSetSize(struct HashSet * 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 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); /** * Functia sterge hash set-ul specificat. * @param set hash set-ul ce trebuie sters. */ void deleteHashSet(struct HashSet * set); #endif
-
- Scrieți o altă sursă
main.c
în care să definiți o funcțiestruct Person readFromKeyboard()
și o altă funcțiemain
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.
Săptămâna 2
- Dându-se header-ele
treeSet.h
șiserver.h
de mai jos, implementați toate funcțiile definite în două fișiere sursă numiteserver.c
șitreeSet.c
:-
server.h
:#ifndef SERVER_H #define SERVER_H 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; }; /** * Compara server1 cu server2 si intoarce o valoare corespunzătoare. * @param server1 primul server de comparat. * @param server2 al doilea server de comparat. * @return o valoare pozitivă dacă primul server are o adresa hardware mai mare, o * 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 server2); #endif
-
treeSet.h
:#ifndef TREE_SET_H #define TREE_SET_H #include "server.h" struct TreeNode { struct TreeNode * parent; struct TreeNode * left; struct TreeNode * right; struct Server value; }; 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
-
- Scrieți o altă sursă
main.c
în care să definiți o funcțiestruct Server readFromKeyboard()
și o altă funcțiemain
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.