Structuri de date pentru partiţii de mulţimi O partiţie finită a unei mulţimi nevide S este o mulţime finită de submulţimi ale lui S: {S 1, S 2,..., S n P(S) care satisface condiţiile următoare: S i 0 pentru toţi 1 i n, S 1 S 2... S n = S, S i S j = dacă 1 i j n. Rezultă că, pentru fiecare x S există o singură submulţime S i astfel încât x S i. Vom desemna această submulţime cu S x. O mulţime de reprezentanţi a unei partiţii {S 1, S 2,..., S n este o submulţime {x 1, x 2,..., x n a lui S, astfel încât x i S i pentru toţi 1 i n. Presupunem că S este o mulţime de valori de tip E (adică A E), şi că putem testa egalitatea a două valori a, b E cu a == b. Vom prezenta două implementări ale unei structuri de date abstracte DSet<E> ale cărei instanţe reprezentă o partiţie S = {S 1,..., S n împreună cu o mulţime de reprezentanţi ai ei, şi permite efectuarea operaţiilor următoare E findset(e x) returnează reprezentantul submulţimii S i pentru care x S i. void makeset(e x) pentru x S, modifică instanţa curentă într-o reprezentare a partiţiei S = {S 1, S 2,..., S m, {x a mulţimii S {x. void s_union(e x,e y) pentru x, y S, modifică instanţa curentă încât să reprezinte partiţia (S {S x, S y ) {S x S y a mulţimii S. O metodă utilă care se poate adăuga la DSet<E> este void showsubsets(); care afişează elementele fiecărei submulţimi pe câte un rând, separate de spaţiu. Implementarea cu liste simplu înlănţuite Fiecare submulţime S i a partiţiei este reprezentată ca o listă de noduri simplu înlănţuite, unde nodul fiecărui element x S are structura (ca template C++) 1
struct ListNode<E> { E e; // continutul nodulul; are valoarea x ListNode<E>* next; // link spre nodul urmator din S i ListNode<E>* repr; // link spre primul nod din lista Reprezentantul unei submulţimi S i = {x 1, x 2,..., x p reprezentată ca o listă înlănţuită de astfel de noduri se consideră a fi elementul corespunzător primului nod din listă: e:x 1 next: e:x 2 next: e:x p next:/ Cu această implementare, o partiţiie este reprezentată de o instanţă a structurii (ca template C++) struct LinkedListSet<E> :public DSet<E> { map<e,listnode<e> > nodemap; using LNtype = ListNode<E>*;... unde nodemap reţine maparea elementelor mulţimii S la noduri din liste. O implementare simplă a operaţiilor descrise mai devreme este: void makeset(e x) { nodemap[x]=listnode<e>(x,nullptr,nullptr); nodemap[x].repr = &(nodemap[x]); E findset(e x) { return nodemap[x].repr->e; void s_union(e x,e y) { LNtype ptr1 = &nodemap[x]; LNtype newrepr=ptr1->repr; while (ptr1->next!= nullptr) ptr1 = ptr1->next; ListNode<E>* ptr2 = nodemap[y].repr; ptr1->next=ptr2; while(ptr2!=nullptr) { ptr2->repr=newrepr; ptr2=ptr2->next; 2
void showsubsets() { for(auto& p: nodemap) { LNtype ptr = &(p.second); if (p.second.repr == ptr) { for(;ptr!=nullptr;ptr = ptr->next) cout << ptr->e<< ; cout << endl; Implementarea cu o pădure de arbori Fiecare submulţime S i a partiţiei este reprezentată ca un arbore în care nodul fiecărui element x S are structura (ca template C++) struct TreeNode<E> { E e; // continutul nodulul; are valoarea x int rank; // estimare a adancimii arborelui TreeNode<E>* p; // link spre nodul parinte Rădăcina arborelui este singurul nod care se referă la el însuşi prin câmpul p. Reprezentantul unei submulţimi S i = {x 1, x 2,..., x p reprezentată cu un astfel de arbore este elementul corespunzător nodului rădăcină. Cu aceasta implementare, o partiţie este reprezentată de o instanţă a structurii (ca template C++) struct TreeSet<E> :public DSet<E> { map<e,treenode<e> > nodemap;... unde nodemap reţine maparea elementelor mulţimii S la noduri din arbori. O implementare simplă a operaţiilor descrise mai devreme este: void makeset(e x) { nodemap[x]=treenode<e>(x,0); nodemap[x].p = &(nodemap[x]); E findset(e x) { TreeNode<E>* xnod= &nodemap[x]; if(xnod!=xnod->p) xnod->p = &nodemap[findset(xnod->p->e)]; return xnod->p->e; 3
void s_union(e x,e y) { TreeNode<E>* xnod = &nodemap[findset(x)]; TreeNode<E>* ynod = &nodemap[findset(y)]; if (xnod->rank>ynod->rank) ynod->p=xnod; else { xnod->p=ynod; if(xnod->rank==ynod->rank) ynod->rank++; void showsubsets() { set<string> e_set; for(auto& p: nodemap) e_set.insert(p.first); for(string e1 : e_set) { bool b = false; for(string e2 : e_set) if(findset(e2)==e1) { cout << e2 << ; b=true; if(b) cout << endl; 1 Teme de laborator Se consideră dat un fişier text graf.txt în care este memorat un graf ponderat cu mulţimea de noduri {1, 2,..., N şi M muchii. Fişierul graf.txt are structura următoare: prima linie conţine numerele întregi N şi M, separate cu spaţiu; celelalte M linii sunt de forma a b w şi reprezintă muchia de la nodul a la b cu ponderea w, un număr întreg pozitiv. Liniile pentru muchii apar în ordine crescătoare a valorilor ponderilor. De exemplu, un fişier cu conţinutul 5 7 1 5 8 1 4 9 5 4 9 2 5 10 4 2 12 3 1 14 2 3 17 4
reprezintă graful ponderat Folosiţi/implementaţi structura de date descrisă mai devreme pentru a scrie programe C++ care rezolvă problemele următoare: 1. Se citeşte un graf ponderat simplu din fişierul graf.txt şi i se determină componentele conexe. Programul trebuie să afişeze pe rânduri separate nodurile componentelor conexe ale grafului. 2. Se citeşte un graf ponderat simplu şi conex şi se determină un arbore minim de acoperire. Programul trebuie să afişeze muchiile arborelui de acoperire găsit, precum şi suma ponderilor muchiilor sale. 5