La Standard Template Library Heap, algoritmi e funtori Pericle Perazzo 27 maggio 2011
Riassunto contenitori e iteratori Un contenitore è un oggetto che contiene un insieme di altri oggetti di tipo omogeneo (=gli elementi del contenitore). Un iteratore è un oggetto che punta ad un elemento di un contenitore. 1
vector<int> Riassunto contenitori e iteratori set<int> deque<int> map<string, int> list<int> 2
Riassunto contenitori e iteratori Gli iteratori possono essere usati per visitare o fare operazioni più complesse sui contenitori. list <int > l; /... / list <int >:: iterator i; for (i = l. begin (); i!= l. end (); i ++) cout << *i << ; 3
Riassunto contenitori e iteratori Mobilità degli iteratori: Forward iterators: possono muoversi in avanti (++) Bidirectional iterators: possono muoversi avanti e indietro (++, --) list<>, set<>, map<> Random access iterators: possono fare salti e calcolare distanze (++, --, +=, -=, a-b) vector<>, deque<>, 4
Vincoli sulla genericità Un set<t> è un albero binario di ricerca di elementi di tipo T. Posso definire un set<t> per qualsiasi tipo T? No. Il tipo T deve essere ordinabile. Deve essere definito l operatore < tra due operandi di tipo T (vincolo sulla genericità di T). Per esempio, non può essere definito un set<> di numeri complessi. 5
Heap in C++ 6
priority queue<t> priority queue<t> realizza uno heap di oggetti di tipo T. Vincolo: T deve essere ordinabile ( operatore <). #include<queue> Usa la rappresentazione linearizzata e si appoggia ad un vector<t> per la memorizzazione. 7
priority queue<t> priority_queue::priority_queue() costruttore di default O(1) priority_queue::priority_queue(iter first, Iter last) costruttore che copia dalla sequenza [first, last) e costruisce lo heap O(n) int priority_queue::size() const O(1) bool priority_queue::empty() const O(1) const T& priority_queue::top() const O(1) void priority_queue::push(const T&) O(log n) void priority_queue::pop() elimina l elemento di cima O(log n) Non ci sono iteratori. 8
priority queue<t>: int main (){ priority_queue < string > q; q. push (" pippo "); q. push (" topolino "); q. push (" pluto "); q. push (" zapotec "); q. push (" minnie "); while (!q. empty ()) { cout << q. top () << " "; q. pop (); } cout << endl ; } esempio output: zapotec topolino pluto pippo minnie 9
Algoritmi in C++ 10
Algoritmi La Standard Template Library fornisce un insieme di algoritmi più comuni (quicksort, mergesort, ricerca binaria, etc.) #include<algorithm> Ogni algoritmo può essere applicato a molte strutture dati diverse (programmazione generica). 11
Sorting void sort(iter first, Iter last); Esegue quicksort sugli elementi della sequenza [first, last) ( O(n log n)). Vincoli sulla genericità: Gli elementi devono essere ordinabili (<). Gli iteratori devono essere random access (scelta del perno, riconoscimento caso base). vector<>, deque<> Ordinamento di un vettore: vector <int > v; /... / sort (v. begin (), v. end ()); 12
Sorting Per le liste si usa una speciale funzione membro: list <int > l; /... / l. sort (); Esegue mergesort su tutta la lista ( O(n log n)). Vincolo: Gli elementi devono essere ordinabili (<). 13
Ricerca lineare Iter find(iter first, Iter last, const T& val); Esegue una ricerca lineare sulla sequenza [first, last). Restituisce un iteratore al primo elemento trovato uguale a val, o last se l elemento non esiste. ( O(n)). Vincolo: Gli elementi devono essere confrontabili ( operatore ==). vector <int > v; /... / if( find (v. begin (), v. end (), 10)!= v. end ()) cout << "Ho trovato un 10! "; else cout << " Non ho trovato nessun 10. "; 14
Ricerca binaria bool binary search(iter first, Iter last, const T& val); Esegue una ricerca binaria sulla sequenza ordinata [first, last). Restituisce true se l elemento val è stato trovato, false altrimenti ( O(log n)). Vincoli sulla genericità: Elementi ordinabili (<). Iteratori random access. vector <int > v; /... / sort (v. begin (), v. end ()); if( binary_search (v. begin (), v. end (), 10)) cout << "Ho trovato un 10! "; else cout << " Non ho trovato nessun 10. "; 15
Ricerca binaria Se voglio ottenere un iteratore che punta all elemento trovato: Iter lower bound(iter first, Iter last, const T& val); Esegue una ricerca binaria sulla sequenza ordinata [first, last) ( O(log n)). 1. Se val esiste ed è unico: restituisce un iteratore a val. 2. Se esiste più di un val: restituisce un iteratore al primo val. 3. Se val non esiste: restituisce un iteratore all elemento immediatamente maggiore (o last se non esiste). 16
Ricerca binaria Ignora tutti gli elementi < val e prende l estremo inferiore dei rimanenti. val = 10 1. Se 10 esiste ed è unico: 2. Se esiste più di un 10: 3. Se val non esiste: 17
Ricerca binaria Esempio: raddoppio tutti gli elementi che valgono 7: vector <int > v; /... / sort (v. begin (), v. end ()); vector <int >:: iterator i; i = lower_bound (v. begin (), v. end (), 7); // i 7 p o t r e b b e r o e s s e r e a l l a f i n e, // q u i n d i c o n t r o l l o anche che i non vada f u o r i d a l v e t t o r e : while (i!= v. end () && *i == 7) { *i *= 2; i ++; } 18
Algoritmi vari const T& max(const T& a, const T& b) const T& min(const T& a, const T& b) Restituiscono rispettivamente l elemento maggiore e minore. Vincolo: definito l operatore <. void swap(t& a, T& b) Scambia a con b. Ha complessità costante per i containers (string, vector<>, list<>). Iter max element(iter first, Iter last) Iter min element(iter first, Iter last) Restituiscono rispettivamente l iteratore all elemento massimo e all elemento minimo dell intervallo [first, last). Vincolo: definito l operatore <. Iter copy(iter first, Iter last, Iter to) Copia gli elementi dall intervallo sorgente [first, last) all intervallo destinazione che inizia da to. Restituisce un iteratore alla fine dell intervallo destinazione. 19
Algoritmi vari vector <int > a; vector <int > b; /... / copy (a. begin (), a. end (), b. begin ()); 20
Funtori 21
Funtori L invocazione di funzione è un operatore come tutti gli altri (operatore ()). nome f unzione(lista parametri) Posso implementare l operatore () in una classe: class A{ /... / public : int operator () ( int arg1, int arg2 ){ /... / } }; La classe A è un funtore (oggetto che rappresenta una funzione) ed è invocabile: int i; A a; i = a(2, 3); 22
Funtori void for each(iter first, Iter last, Funct f) Per ogni elemento nell intervallo [first, last) esegue f(elemento). Vincolo: f deve essere invocabile con un argomento di tipo T. void stampa ( int a){ if (a >= 0 && a <= 5) cout << a << endl ; } int main (){ vector <int > v; /... / for_each (v. begin (), v. end (), stampa ); } Se voglio stampare gli interi tra 6 e 10 devo dichiarare una nuova funzione. 23
Funtori Definisco un funtore: class StampaTra { int a, b; public : StampaTra ( int _a, int _b ){a = _a; b = _b ;} void operator () ( int num ){ if( num >= a && num <= b) cout << num << endl ; } }; int main (){ StampaTra f(0, 5); StampaTra f2 (6, 10); vector <int > v; /... / for_each (v. begin (), v. end (), f); for_each (v. begin (), v. end (), f2 ); } 24
Funtori void sort(iter first, Iter last, Funct less) Ordina [first, last) usando less come funzione di confronto. Vincolo: less deve essere invocabile con il seguente formato: bool less(const T& a, const T& b) Iter find if(iter first, Iter last, Funct test) Restituisce un iteratore al primo elemento di [first, last) su cui test ritorna true. Vincolo: test deve essere invocabile con il seguente formato: bool test(const T& a) 25
Funtori Esempio: Ordinare un vettore dal primo elemento che vale tra 1 e 5 in poi. vector <int > v (10); for ( int i = 0; i < 10; i ++) cin >> v[i]; Tra f(1, 5); // f u n t o r e che, i n v o c a t o, r e s t i t u i s c e // t r u e s e i l numero s t a t r a 1 e 5 vector <int >:: iterator j = find_if (v. begin (), v. end (), f); sort (j, v. end ()); 26
Funtori I funtori possono essere usati anche per modificare la struttura di alcuni containers. Esempio: set<string> con ordinamento case insensitive ( Pippo = pippo ). set<string, NoCaseLess> s; map<string, int, NoCaseLess> m; Vincolo: NoCaseLess invocabile con il formato: bool NoCaseLess(string, string). 27
Esercizio Scrivere un programma che fa le seguenti operazioni: 1. Prende da tastiera 10 interi e li memorizza su un vector<int> (usare un for o il for each). 2. Prende da tastiera un numero N > 0. 3. Ordina il vector con ordinamento modulo N: a < N b a N < b N (usare il sort a 3 argomenti). 4. Stampare su schermo il vettore ottenuto (usare un for o il for each). N=10 28
Esercizio - Soluzione / u t i l i p e r i l f o r e a c h / void prendi ( int & i){ cin >> i;} void stampa ( int i){ cout << i << ;} / F u n t o r e che, s e i n v o c a t o, t e s t a s e a è minore modulo N d i b / class LessModN { int N; public : LessModN ( int _N ){N = _N ;} bool operator ()( int a, int b){ return (a % N < b % N );} }; 29
int main (){ vector <int > v; int N; v. resize (10); // v e t t o r e d i 10 e l e m e n t i a 0 cout << " Vettore : "; for_each (v. begin (), v. end (), prendi ); cout << " Modulo di ordinamento : "; cin >> N; if(n <= 0) { cerr << " Errore : N deve essere > 0" << endl ; exit ( -1); } sort (v. begin (), v. end (), LessModN (N )); cout << " Vettore risultante : "; for_each (v. begin (), v. end (), stampa ); } 30
Esercizio 2 Scrivere un programma che fa le seguenti operazioni: 1. Prende da tastiera 10 interi e li memorizza su un vector<int> (usare un for o il for each). 2. Ordina i primi 5 elementi (usare il sort). 3. Ordina gli ultimi 5 elementi (usare il sort). 4. Stampa su schermo il vettore ottenuto (usare un for o il for each). 5. Copia gli 8 elementi centrali su una list<int> (usare il copy). 6. Stampa su schermo la lista ottenuta (usare un for o il for each). 31
Esercizio 2 - Soluzione int main ( int argc, char * argv []) { vector <int > v (10); vector <int >:: iterator i; for (i = v. begin (); i!= v. end (); i ++) cin >> *i; // punto 1 sort (v. begin (), v. begin () + 5); // punto 2 sort (v. begin () + 5, v. end ()); // punto 3 for (i = v. begin (); i!= v. end (); i ++) cout << *i << ; // punto 4 cout << endl ; list <int > l (8); copy (v. begin () + 1, v. end () - 1, l. begin ()); // punto 5 list <int >:: iterator j; for (j = l. begin (); j!= l. end (); j ++) cout << *j << ; // punto 6 cout << endl ; } 32
Esercizio 2 - Soluzione con for each void prendi ( int & num ){ cin >> num ;} void stampa ( int num ){ cout << num << ;} int main ( int argc, char * argv []) { vector <int > v (10); for_each (v. begin (), v. end (), prendi ); // punto 1 sort (v. begin (), v. begin () + 5); // punto 2 sort (v. begin () + 5, v. end ()); // punto 3 for_each (v. begin (), v. end (), stampa ); // punto 4 cout << endl ; list <int > l (8); copy (v. begin () + 1, v. end () - 1, l. begin ()); // punto 5 for_each (l. begin (), l. end (), stampa ); // punto 6 cout << endl ; } 33