contenitori associativi

55

Upload: hoangtu

Post on 03-Feb-2017

235 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Contenitori associativi
Page 2: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori

● La STL fornisce una serie di classi standard che realizzano dei contenitori.

● Esempi di contenitori sono le liste, i vettori, gli alberi, etc.

● Un contenitore (container) è una collezione di oggetti di tipo omogeneo, organizzati in un certo modo.

Page 3: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Programmazione tradizionale (C)

unsignedcharunsigned

int

double

char

int

float

vector hash

search treelist

algo1()algo2()

algo3()

6 tipi elementari

4 strutture dati

3 algoritmi

24 definizioni

72 definizioni

Fare una libreria di contenitori con la programmazione tradizionale del C porta ad un esplosione di definizioni di strutture dati e di algoritmi. È necessario definire una struttura list per ogni tipo elementare (char, unsigned char, etc.). Inoltre è necessario definire un algoritmo algo1 per ogni struttura list definita. Se E è il numero di tipi elementari, C il numero di contenitori e A il numero di algoritmi, bisogna definire E*C strutture dati ed E*C*A algoritmi.

Page 4: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Programmazione generica

● La STL del C++ utilizza la programmazione generica.

● La classe “list” viene implementata una volta sola. Il tipo degli elementi è un generico tipo T.

● Non importa sapere a priori quale sarà T, basta che soddisfi alcuni requisiti (constraints).

● Per esempio: il T dei search tree dev'essere ordinabile (<)

● Programmazione generica significa programmare utilizzando dei concetti. Un concetto è una famiglia di astrazioni che hanno un insieme comune di operazioni.

Per risolvere questo problema, la libreria STL (Standard Template Library) usa la programmazione generica. La classe list viene implementata una volta sola. Il tipo degli elementi è un generico tipo T. La programmazione generica non va confusa con la programmazione a oggetti, tipica di linguaggi a più alto livello come il Java. In Java il problema si risolve con l'ereditarietà. Ovvero si definisce un tipo object di cui tutti gli altri tipi sono derivati. In questo modo basta definire una lista di object, e automaticamente abbiamo definito tutte le possibili liste di tutti i possibili tipi elementari. L'approccio a oggetti è più semplice e porta a meno errori di programmazione. Tuttavia è poco efficiente, perché bisogna fare continue conversioni di tipo da object a int (nel caso di lista di interi).

Nella programmazione generica invece non si definisce nessuna classe object. Basta che il tipo T fornisca alcuni requisiti (constraints), necessari per stare in una lista. L'approccio generico è più flessibile ed efficiente, ma può portare ad errori di programmazione se usato in modo sbagliato.

Page 5: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Programmazione generica (C++)

algo1()algo2()

algo3()

unsignedcharunsigned

int

double

char

int

float

vector hash

search treelist

6 tipi elementari

4 strutture dati

3 algoritmi

Concetto di tipo elementare “T”

Concetto di “contenitore”

4 definizioni

3 definizioni

Con l'approccio generico si racchiudono tutti i possibili tipi elementari nel concetto di tipo elementare T. Quindi si definisce una sola struttura dati list generica di elementi di tipo T. Poi si racchiudono tutti i possibili contenitori nel concetto di contenitore. Quindi si programmano algoritmi che operano su un generico contenitore.

Per rendere generico il concetto di tipo elementare basta ricorrere al meccanismo dei template. Per rendere generico il concetto di contenitore si ricorre al meccanismo dei template più al concetto di iteratore.

Page 6: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori

● Contenitori si dividono in:● Sequenze● Adattatori● Quasi-contenitori● Contenitori associativi

I contenitori (containers) in C++ si dividono in quattro famiglie: (1) le sequenze sono i contenitori che impongono un ordinamento lineare nelle posizioni degli elementi, ovvero dati due elementi si può sempre definire quale sta prima e quale sta dopo (esempio: list); (2) gli adattatori non sono contenitori indipendenti ma si appoggiano ad un altro container e ne estendono le funzionalità (esempio: priority_queue); (3) i quasi-contenitori non esportano tutte le funzionalità di un contenitore propriamente detto (esempio: string); (4) i contenitori associativi sono contenitori che non impongono un ordinamento lineare nelle posizioni degli elementi (esempio: map).

Page 7: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Sequenze

● vector<T>:

● list<T>:

● deque<T>:

10 1 4 12 4

size

capacitybackfront

110 4 4front back

12

4 12 4 10 1

size size

frontback

Il vector è un semplice array dinamico di grandezza “size”, memorizzato in un buffer di capacità “capacity”. La capacità del buffer è sempre maggiore o uguale alla grandezza del vettore. L'allocazione del buffer è gestita: se il vettore cresce oltre la capacità del buffer, viene automaticamente riallocato in un buffer più capiente, e tutti gli elementi copiati nel nuovo buffer.

La list è una lista doppia. Ogni elemento contiene non solo il puntatore all'elemento successivo ma anche a quello precedente. Sono poi definiti il puntatore alla testa della lista ed il puntatore alla coda della lista.

La deque (pron. “deck”, sta per double-ended queue) è un vettore circolare.Il vector, la list e la deque sono sequenze, in quanto organizzano la

collezione di elementi secondo un ordine lineare. Per “front” si intende l'elemento di testa, cioè il primo della sequenza. Per “back” quello di coda, cioè l'ultimo della sequenza.

Page 8: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

● Un iteratore è un oggetto che punta ad un elemento del contenitore. Sono un'astrazione del concetto di puntatore.

10 1 4 12 4

i

Un iteratore è un oggetto che punta ad un elemento di un contenitore. Può essere pensato come un'astrazione di un puntatore. Il vantaggio degli iteratori è che offrono un set di operazioni comuni con un'interfaccia che non dipende dal tipo di contenitore iterato.

La tecnica degli iteratori è usata dalle librerie standard di quasi tutti i linguaggi moderni (Java, C#, perl, etc.) Gli iteratori del C++ sono particolarmente potenti in quanto permetto non solo di accedere agli elementi in lettura e scrittura ma anche di modificare il contenitore stesso (inserire elementi, cancellare elementi, etc.)

Page 9: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

● Su ogni iteratore i sono ridefinite le operazioni di:● Dereferenziazione (accesso all'elemento puntato): *i● Scorrimento all'elemento successivo/precedente nella sequenza: i++, i--

● Test di (dis)uguaglianza: i==j, i!=j

10 1 4 12 4

i

Page 10: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

● Su ogni contenitore c sono definiti due metodi:● c.begin() restituisce un iteratore che punta al primo elemento

di c● c.end() restituisce un iteratore che punta all'elemento

successivo dell'ultimo

10 1 4 12 4

begin end

Page 11: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

10 1 4 12 4

begin end

i ++

void stampa_vector_int(vector<int> vett){vector<int>::iterator i;// scorro gli elementi in ordine:for(i = vett.begin(); i != vett.end(); i++){

cout << * i; // stampo l'elemento}

}

1234567

Questo algoritmo stampa gli elementi di un vettore di interi. La funzione vett.begin() restituisce un iteratore all'elemento di testa (front element). La funzione vett.end() restituisce un iteratore che punta ad un immaginario elemento successivo all'ultimo.

Page 12: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

10 1 4 12 4

begin end

i ++

template<class T>void stampa_vector(vector<T> vett){

typename vector<T>::iterator i;// scorro gli elementi in ordine:for(i = vett.begin(); i != vett.end(); i++){

cout << * i; // stampo l'elemento}

}

12345678

Questo algoritmo stampa gli elementi di un vettore di tipo generico. Quando si usa un'espressione generica a sinistra del “::”, si rende necessaria la parola chiave typename.

Constraints: Il tipo T deve avere il vincolo di essere stampabile (definito l'operatore << su ostream). Quindi la funzione può essere invocata su vettori di interi, caratteri, stringhe, etc. ma non su vettori di classi, a meno che di non ridefinire il <<.

Page 13: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

template<class Cont>void stampa(Cont c){

typename Cont::iterator i;// scorro gli elementi in ordine:for(i = c.begin(); i != c.end(); i++){

cout << * i; // stampo l'elemento}

}

12345678

begin end

i ++

110 4 412

Questo è lo stesso algoritmo ma generico sul tipo di contenitore.Constraints: L'unico vincolo è che il tipo Cont deve supportare gli iteratori.

Quindi funziona con qualsiasi contenitore STL (vector, list, etc.)

Page 14: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di programmazione generica

● Funzione generica che azzera gli elementi precedenti agli elementi dispari di una sequenza

110 4 112

10 4 10

Page 15: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di programmazione generica

template<class Cont>void azzera_predispari(Cont c){

typename Cont::iterator i;for(i = c.begin(); i != c.end(); i++){

if (* i%2 == 1 && i != c.begin())i--;* i = 0; // azzero l'elemento precedentei++;

}}

}

1234567891011

begin end

i

110 4 112

Constraints: Il tipo Cont deve supportare gli iteratori. In più gli elementi devono avere definito l'operatore %. Quindi l'algoritmo è applicabile a vettori, liste, etc. di elementi interi (char, int, unsigned int, etc.)

Page 16: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Validità degli iteratori

● Un iteratore può essere:● Valido: se punta ad un elemento (può essere dereferenziato)● Non valido: se non punta ad un elemento (non può essere

dereferenziato)

begin end

i ++

55 4 55

Un iteratore è valido se punta ad un elemento. L'iteratore restituito da end() è un po' speciale, in quanto non è valido (non punta a nessun elemento) ma su di esso può essere applicato ugualmente l'operatore di spostamento all'indietro (i--).

Page 17: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

template<class Cont> void cancella_pari(Cont& c){typename Cont::iterator i;for(i = c.begin(); i != c.end(); i++)

if (* i % 2 == 0) c.erase(i); // errore!}

12345

begin end

i ++

55 4 55

Validità degli iteratori

● Alcune operazioni che modificano il contenitore invalidano alcuni (o tutti) gli iteratori.

template<class Cont> void cancella_pariOK(Cont& c){typename Cont::iterator i;for(i = c.begin(); i != c.end(); ) {

if (* i % 2 == 0) i = c.erase(i); // OKelse i++; }

}

123456

erroreclassico

La funzione c.erase(iterator i) cancella dalla sequenza l'elemento puntato da i. L'iteratore i viene invalidato (non punta più ad un elemento valido). Un errore comune (esempio 1) consiste nello scorrere una sequenza cancellando o inserendo elementi, senza fare attenzione all'invalidazione degli iteratori. Per mantenere l'iteratore i valido bisogna programmare come nell'esempio 2. Notare che la funzione c.erase() restituisce un iteratore alla posizione successiva a quella cancellata. In questo caso non ho bisogno di incrementare l'iteratore. L'istruzione i++ va messa quindi nella parte else (riga 5) e non nel passo del for (riga 3).

Page 18: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori a costante

● Un iteratore a costante è un iteratore che non può modificare il container

● vector<int>::iterator → iteratore a mutabile● vector<int>::const_iterator → iteratore a costante

● Con contenitori costanti è obbligatorio usare iteratori a costante

template<class Cont> void stampa_pari(const Cont& c){typename Cont::iterator i; // errore! Il contenitore è costante.for(i = c.begin(); i != c.end(); i++)

if (* i % 2 == 0) cout << *c << ' ';}

12345

template<class Cont> void stampa_pariOK(const Cont& c){typename Cont::const_iterator i; // okfor(i = c.begin(); i != c.end(); i++)

if (* i % 2 == 0) cout << *c << ' ';}

12345

Page 19: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori inversi

● Gli iteratori inversi vedono il contenitore come se fosse “rovesciato”. Sono utili per visitare in ordine inverso.

● vector<int>::reverse_iterator → iteratore inverso● vector<int>::const_reverse_iterator → iteratore inverso a costante

● Ogni contenitore esporta le funzioni:● c.rbegin() → iteratore all'ul<mo elemento● c.rend() → iteratore precedente al primo elemento

● i++ sposta l'iteratore i all'indietro.

rend rbegini

55 4 55

++

Page 20: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Iteratori

template<class Cont>void stampa_contrario(Cont c){

typename Cont::reverse_iterator i;// scorro gli elementi in ordine:for(i = c.rbegin(); i != c.rend(); i++){

cout << * i; // stampo l'elemento}

}

12345678

rend rbegini

55 4 55

++

Questo algoritmo stampa dall'ultimo al primo gli elementi di un contenitore.

Page 21: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Mobilità degli iteratori

● Un iteratore può essere:● Forward: permettono di avanzare in una sequenza (i++)● Bidirectional: permettono di scorrere in entrambi i sensi (i++, i--)● Random Access: permettono di saltare da un elemento all'altro

(i++, i--, i+=5, i-=5)

● list<T> esporta iteratori Bidirectional

● vector<T> e deque<T> esportano iteratori Random Access

Page 22: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Mobilità degli iteratori

● L'operatore + è implementato solamente dagli iteratori Random Access.

● Per accedere all'elemento successivo nel caso di iteratori Bidirectional non si può fare *(i+1).

● Bisogna fare:

i++; // sposto l'iteratoreif (i != c.end()) * i = 4;i--; // lo riporto indietro

erroreclassico

Page 23: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Ranges

● Un range è una sotto-sequenza di elementi delimitata da due iteratori a e b:

● [a, a) è un range vuoto

● [begin(), end()) rappresenta tutta la sequenza

[a, b)

a

21 3 54

b

begin end

Un concetto molto usato nella STL è quello di range. Il range è una sottosequenza di elementi delimitata da due iteratori a e b. Si indica con [a, b). Per convenzione la sottosequenza include l'elemento puntato da a ma non quello puntato da b.

Page 24: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

vector<T>

● Array dinamico con riallocazione automatica.

● La grandezza può aumentare e diminuire.

● Quando supera la capacità, il buffer viene automaticamente riallocato.

● Si possono accedere direttamente gli elementi.

● Invalidazione iteratori:● Se si elimina un elemento, si invalidano gli iteratori all'elemento

ed ai successivi.● Se si inserisce un elemento, si invalidano gli iteratori agli elementi

successivi, o tutti in caso di riallocazione.

10 1 4 12 4

size

capacitybackfront

Page 25: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

vector<T> – Funzioni base

● vector<int> vett; → Crea un veEore vuoto di interi.

● vett.push_back(int e); → Inserisce “e” come ul<mo elemento.

● int vett.pop_back(); → Cancella l'ul<mo elemento.

● int vett.size(); → Res<tuisce il numero di elemen<.

● int& vett[5]; → Accede all'elemento di indice 5 (in leEura e/o scrittura). Non esiste per il contenitore “list”.

Page 26: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di vector<T>

● Programma che stampa la somma di N numeri.● Prende il numero N da tastiera.● Prende N numeri da tastiera.● Stampa la somma.

Page 27: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

#include<iostream>#include<vector>using namespace std;int main(){

vector<int> vett; // vettore vuotoint n, c;cin >> n; // attenzione: no controllo errori!vett.reserve(n); // capacità n elementifor(int i = 0; i < n; i++){

cin >> c; // prendo da tastiera n elementi vett.push_back(c);

}int sum = 0;for(vector<int>::iterator j = vett.begin(); j!=vett.end(); j++)

sum += * j; // calcolo la sommacout << sum << endl;

}

1234567891011121314151617

Esempio di vector<T>

15 6 3

size

capacityback

La funzione vett.reserve() server ad allocare un buffer di capacità n. Non cambia il comportamento del programma, ma ne aumenta l'efficienza in quanto evita inutili riallocazioni automatiche. L'uso di vett.reserve() diminuisce la genericità del programma, in quanto è una funzione specifica di vector e non è definita per gli altri contenitori.

Page 28: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Regola aurea per programmare con contenitori

● Quando avete bisogno di un contenitore, ma non sapete quali saranno le operazioni più comuni su di esso, usate vector<T>.

● Scorrete il vector<T> con iteratori e programmate in modo generico.

● Quando avete identificato le operazioni più comuni, sostituite il vector con il contenitore più adatto (vector, list, deque, set, etc.)

Quando si programma, capita molte volte di aver bisogno di una collezione di dati, ma non sappiamo ancora quale è la struttura dati più efficiente per realizzarla (vector, list, etc.). Si consiglia di usare inizialmente la struttura dati vector e programmare in modo generico, usando iteratori ed evitando metodi specifici del vector. Poi quando si sono identificate le operazioni più comuni, cambiare eventualmente la struttura dati. Se sono frequenti le operazioni di accesso diretto, è meglio lasciare vector; se invece sono frequenti gli inserimenti e le cancellazioni a metà, è più efficiente una list; se infine sono frequenti inserimenti e cancellazioni in testa e in coda, è meglio usare la deque. In ogni caso, la programmazione generica renderà minimi i cambiamenti da effettuare nel codice.

Page 29: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

list<T>

● Lista doppia: ogni elemento ha il puntatore al successivo e al precedente.

● C'è una puntatore di testa (front) ed uno di coda (back).

● Non si possono accedere direttamente gli elementi, ma solo attraverso iteratori.

● Invalidazione degli iteratori:● Se si elimina un elemento, si invalidano gli iteratori che puntano

all'elemento.● Se si inserisce un elemento, non si invalida nessun iteratore.

110 4 4front back

12

Page 30: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di list<T>

● Programma che prende N righe da tastiera (fino ad una riga nulla) e le scrive ordinate su un file.

Page 31: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di list<T>

#include<list>#include<string>using namespace std;int main(){

list<string> li ; // lista vuotastring s;do{

getline(cin, s); // input di una riga (no controllo errori!)li.push_back(s);

} while(!s.empty());li.pop_back(); // tolgo la riga vuota finaleli.sort(); // merge sortofstream f("out.txt", ios::out);list<string>::iterator i;for(i = li.begin(); i != li.end(); i++)

f << * i << endl; // stampa su filef.close();

}

123456789101112131415161718

La funzione li.sort() ordina la lista per mezzo dell'algoritmo merge sort. È specifica per il tipo list<T>. Per usarla, T deve essere ordinabile.

Page 32: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Operazioni su sequenze

Operazione: Funzione: vector: list: deque:vector<T> c; Default constructor.

Crea un contenitore vuoto.O(1) O(1) O(1)

vector<T> c(int n, T elem);

Fill constructor.Crea un contenitore di n elementi uguali a elem

O(n) O(n) O(n)

vector<T> c(iterator a, iterator b);

Range contructor.Crea un contenitore copiando gli elementi da [a,b)

O(n) O(n) O(n)

operator==(vector A, vector B);

Restituisce vero sei i contenitori sono uguali (stessi elementi nello stesso ordine).

O(n) O(n) O(n)

~vector<T>; Distruttore.Invoca delete su ogni elemento.

O(n) O(n) O(n)!

Il distruttore di un contenitore invoca automaticamente la delete su ogni elemento. Questo comportamento è essenziale per definire liste di oggetti di tipo class.

Page 33: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Operazioni su sequenze

Operazione: Funzione: vector: list: deque:iterator c.begin(); iterator c.end();

Iteratori di inizio e fine sequenzaO(1) O(1) O(1)

int c.size()const; Numero di elementi O(1) O(n) O(1)void c.resize(int m, T elem);

Diminuisce o aumenta il numero di elementi

O(|m-n|)* O(|m-n|) O(|m-n|)

bool c.empty()const; size()==0 O(1) O(1) O(1)int c.capacity()const; Capacità del buffer O(1) no novoid c.reserve()const; Riserva capacità del buffer O(n) no noT& c[int i]; Accede all'elemento i-esimo O(1) no O(1)**

!

* O(n) se provoca riallocazione** Meno efficiente del vector<T>

La funzione c.empty() equivale a fare c.size()==0 ma ha complessità O(1).

Page 34: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Operazioni su sequenze

Operazione: Funzione: vector: list: deque:iterator c.insert(iterator it, T elem);

Inserisce elem prima dell'elemento puntato da it.

O(n) O(1) O(n)

vector::insert(iterator it, int n, T elem);

Fill insert.Inserisce n copie di elem prima dell'elemento puntato da it.

O(n) O(n) O(n)

vector::insert(iterator it, iterator a, iterator b);

Range insert.Inserisce gli elementi in [a,b) prima dell'elemento puntato da it.

O(n) O(n) O(n)

iterator c.erase(iterator it);

Cancella l'elemento puntato da it.Restituisce un iteratore valido all'elemento successivo.

O(n) O(1) O(n)

void c.erase(iterator a, iterator b);

Range erase.Cancella gli elementi nel range [a,b).

O(n) O(n) O(n)

!

!

Le funzioni insert ed erase hanno complessità O(1) per la lista, e complessità O(n) per gli altri tipi di sequenze. Questo perché nel vector e nella deque, ogni volta che si inserisce o cancella un elemento nel mezzo, bisogna spostare gli altri elementi per ricompattare la sequenza.

Page 35: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Operazioni su sequenze

Operazione: Funzione: vector: list: deque:

T front(); Accede al primo elemento. O(1) O(1) O(1)

push_front(T e); Aggiunge e come primo elemento.O(n) O(1) O(1)

T pop_front(); Restituisce e cancella il primo elemento.

O(n) O(1) O(1)

T back(); Accede all'ultimo elemento. O(1) O(1) O(1)push_back(T elem); Aggiunge elem come ultimo

elemento.O(1)* O(1) O(1)

T pop_back(); Restituisce e cancella l'ultimo elemento.

O(1) O(1) O(1)

!

!

* O(n) se provoca riallocazione

La push_front() e la pop_front() hanno complessità O(n) per il vector, ma solo O(1) per la deque. Questo è dovuto alla sua natura di array circolare.

Page 36: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Operazioni su sequenze

Operazione: Funzione: vector: list: deque:void swap(container c1, container c2);

Scambia due contenitori in modo efficiente.

O(1) O(1) O(1)

iterator copy(iterator a, iterator b, iterator to);

Copia gli elementida [a,b)a [to,to+(b-a)).

O(n) O(n) O(n)

void sort(iterator a, iterator b);

Esegue quick sort sul range [a,b).O(nlogn) no O(nlogn)

void c.sort(); Esegue merge sort su una lista. no O(nlogn) nobool is_sorted(iterator a, iterator b);

Restituisce true se la sequenza [a,b) è ordinata.

O(n) O(n) O(n)

void make_heap(iterator a, iterator b);

Trasforma [a,b) in uno heap. O(n) no O(n)

iterator max_element(iterator a, iterator b);

Restituisce l'elemento massimo in [a,b).

O(n) O(n) O(n)

iterator min_element(iterator a, iterator b);

Restituisce l'elemento minimo in [a,b).

O(n) O(n) O(n)

!

!

La funzione globale sort(iterator a, iterator b) esegue quick sort sul range [a,b). Può essere applicata soltanto a contenitori che esportano iteratori random access. Quindi non su “list”, ma su “vector” e “deque”.

Per ordinare una “list” si ricorre alla funzione c.sort() (dove c è una list), che esegue merge sort.

Page 37: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Adattatori

● Estendono le funzionalità di una sequenza (vector, list, deque)

● queue<T> → coda Last In – First Out (estende deque<T>)

● stack<T> → pila First In – First Out (estende deque<T>)

● priority_queue<T> → struEura heap (estende vector<T>)

Gli adattatori si appoggiano ad una sequenza, estendendone le funzionalità.

Page 38: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Quasi-contenitori

● Non hanno tutte le funzionalità di un contenitore ordinario. Sono specializzati per usi specifici.

● string → stringa di caraEeri. Specializzata per operazioni su sottostringhe.

● valarray<T> → veEore matema<co. Specializzato per operazioni di calcolo su molti dati. T dev'essere un tipo matematico.

● bitset<N> → insieme di N flag (N è un intero). Viene memorizzato in modo compatto (1 bit per flag). Specializzato per operazioni bit a bit.

Page 39: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori associativi

● Contenitori che offrono una ricerca efficiente basata sul valore di una chiave.

● La chiave può essere di un qualsiasi tipo T (ordinabile).

● set<T> → albero binario di ricerca di chiavi T.

● map<T, ValueT> → albero binario di ricerca di coppie chiave-valore (pair<T, ValueT>).

● Le visita tramite iteratore esegue una visita simmetrica dell'albero, quindi gli elementi vengono visitati in modo ordinato (crescente)

Page 40: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

set<T>

● Realizza un albero binario di ricerca di elementi di tipo T. Gli elementi sono ordinati e non ci sono ripetizioni.

● Una volta inseriti, non è possibile modificare gli elementi.

6

3 10

9 1241

0 2 14

Page 41: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

set<T> – Funzioni base

● set<int> s; → Crea un set vuoto di interi.

● s.insert(int e); → Inserisce “e” nel set.

● iterator s.find(int e); → Cerca “e” nel set. Restituisce un iteratore che punta ad “e”, o s.end() se “e” non esiste.

● int s.size(); → Res<tuisce il numero di elemen<.

Page 42: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di set<T>

● Programma che prende da tastiera delle parole (fino alla parola “fine”) e stampa un messaggio se la parola è già stata inserita. Dopodiché stampa le parole in modo ordinato.

Page 43: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di set<T>

#include<stream>#include<string>#include<set>using namespace std;

int main(){set<string> parole; // albero vuotostring p;do{

cin >> p;if (parole.find(p) != parole.end())

cout << "Hai gia' inserito: " << p << endl;else

parole.insert(p);} while(p != "fine");

}

12345678910111213141516

Page 44: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

map<T, ValueT>

● Realizza un dizionario di coppie chiave-valore, ottimizzato per la ricerca sulla chiave.

● È implementato con un albero binario di ricerca ordinato sulle chiavi. Ogni nodo contiene una coppia chiave-valore. Non ci sono ripetizioni sulle chiavi.

● Una volta inserite, le chiavi sono costanti, il relativo valore può essere modificato

Pluto 7

Minnie 2 Topolino 2

Zapotec 13Pippo 4Eta Beta 9

Page 45: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

map<T, ValueT>

● È possibile accedere ad un elemento tramite indicizzazione diretta (operator[]), con indice di tipo T (non necessariamente intero). Per esempio:

● L'operatore m["pippo"] cerca un elemento con chiave “pippo”.● Se tale elemento esiste, accede al campo valore.● Altrimenti lo crea (con chiave “pippo” e valore 0), e accede al

campo valore.

map<string, int> m;

m["pippo"] = 4;

L'operatore di indicizzazione diretta (parentesi quadre) serve sia ad accedere (in lettura o in scrittura) il campo valore associato ad una chiave, sia ad inserire un nuovo elemento nell'albero.

Page 46: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

map<T, ValueT>

● Con map<T, ValueT> è possibile costruire dei semplici database in memoria.

Mario Rossi

Gianni Verdi

Paolo Bianchi

Franco Rosi

123 45678925

26

30

27

345 678901

456 789012

234 567890

Nome Età Num. telefono

T

ValueT

struct info{int eta;long numTelefono;

} ;map<string, info> db;...db["Mario Rossi"].eta = 26;

Page 47: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di map<T, ValueT>

● Programma che legge una lista di contatti da file, ci aggiunge un contatto preso da tastiera, e poi li stampa.

● Ogni contatto è su una riga ed ha il formato:email numero-di-telefono

Page 48: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esempio di map<T, ValueT>

#include<map>#include<string>using namespace std;int main(){

map<string, long int> contatti;string email;long int telefono;ifstream f("contatti.txt", ios::in);while(!f.eof()){

f >> email >> telefono;contatti[email] = telefono; // inserimento di un elemento

}f.close();cin >> email >> telefono;contatti[email] = telefono;map<string, long int>::iterator i;for(i = contatti.begin(); i != contatti.end(); i++) // visita ordinata

cout << i->first << ' ' << i->second << endl;}

12345678910111213141516171819

Page 49: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori associativi

Operazione: Funzione: set: map:set<T> c; Default constructor.

Crea un contenitore vuoto.O(1) O(1)

set<T> c(iterator a, iterator b);

Range constructor.Crea un contenitore copiando dal range [a,b).

O(nlogn) O(nlogn)

~set<T>; Destructor.Invoca delete su tutti gli elementi.

O(n) O(n)

operator==(set A, set B) Restituisce vero se i set sono uguali (contengono gli stessi elementi).

O(n) O(n)!

Page 50: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori associativi

Operazione: Funzione: set: map:iterator c.begin();iterator c.end();

Iteratori di inizio e fine sequenza.

O(1) O(1)

int c.size(); Numero di elementi. O(n) O(n)bool c.empty(); c.size()==0 O(1) O(1)ValueT& c[T k]; Restituisce l'elemento con

chiave k.Lo crea se non esiste.

no O(logn)

iterator c.find(T k); Trova l'elemento k.* O(logn) O(logn)iterator c.lower_bound(T k);

Trova il più piccolo elemento > k.*

O(logn) O(logn)

iterator c.upper_bound(T k);

Trova il più grande elemento <= k.*

O(logn) O(logn)

* Restituisce c.end() se tale elemento non esiste.

!

L'accesso tramite parentesi quadra ad un elemento di una map, crea l'elemento se non esiste. L'elemento creato assume un valore iniziale di default. Se si vuole testare la presenza di un elemento senza crearlo, si deve utilizzare la funzione find().

Page 51: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Contenitori associativi

Operazione: Funzione: set: map:c.insert(T elem); Inserisce elem. O(logn) noc.insert(iterator a, iterator b);

Range insert.Inserisce gli elementi in [a,b).

O(logn) per ogni elemento

O(logn) per ogni elemento

void c.erase(T elem); Cancella l'elemento elem. O(logn) O(logn)void c.erase(iterator i); Cancella l'elemento

puntato da i.O(logn) O(logn)

void c.erase(iterator a, iterator b);

Range erase.Cancella gli elementi nel range [a,b).

O(logn) per ogni elemento

O(logn) per ogni elemento

* La funzione make_pair(T k, ValueT elem) crea una struttura dati pair<T,ValueT>.

Page 52: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Altri contenitori associativi

● multiset<T>: come set, ma sono ammessi valori ripetuti.

● multimap<T, ValueT>: come map, ma sono ammessi valori ripetuti delle chiavi.

● hash_set<T>: come set, ma gli elementi sono memorizzati con struttura ad hash.

● Più efficiente per la ricerca● Non garantisce ordinamento

● hash_map<T, ValueT>: come map, ma gli elementi sono memorizzati con struttura ad hash.

● hash_multiset<T>

● hash_multimap<T, ValueT>

Page 53: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esercizio riassuntivo

● Scrivere una funzione generica:

che unisce a due a due gli elementi adiacenti, sommandoli fra loro.

template<class Cont> void f(Cont& c);

110 4 412

1611 4

Page 54: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Esercizio riassuntivo – Soluzione

template<class Cont>void fondi(Cont& c){

typename Cont::iterator i;for(i = c.begin(); i != c.end(); i++){

int a = * i;i++;if (i != c.end()){

* i += a;i--;i = c.erase(i);

}else

i--;}

}

12345678910111213141516

10 1 4 12 4

begin end

i ++

Page 55: Contenitori associativi

Algoritmi e strutture dati Contenitori e Iteratori STL

Riferimenti

● Matthew H. Austern, “Generic programming and the STL,” Addison-Wesley

● Bjarne Stroustrup, “The C++ Programming Language,” Addison Wesley

● Guida di riferimento sul C/C++: http://en.cppreference.com/w/