Come nella Mappa: un Dizionario è un contenitore di elementi del tipo (k,v) dove k è la chiave e v è il suo corrispondente valore. ogni elemento (k,v) viene detto entrata (entry) del Dizionario. chiavi e valori possono essere oggetti di qualsiasi tipo. Diversamente dalla Mappa: sono permesse entrate multiple con la stessa chiave.
Esistono due tipi di dizionari: dizionari non ordinati (non è definita alcuna relazione d'ordine totale sulle chiavi: l'unico test permesso fra chiavi è quello di uguaglianza) dizionari ordinati (è definita una relazione d'ordine totale sulle chiavi)
Metodi fondamentali size(): restituisce il numero di entrate in D. isempty(): verifica se D è vuoto. find(k): se D contiene una entrata con chiave k, restituisce tale entrata, altrimenti restituisce null. findall(k): restituisce una collezione iterabile contenente tutte le entrate con chiave k. insert(k,v): inserisce una entrata con chiave k e valore v in D; restituisce l'entrata creata. remove(e): rimuove da D una entrata e, restituendo l'entrata rimossa; si ha un errore se e non è in D. entries(): restituisce una collezione iterabile delle entrate in D.
Interfaccia public interface Dictionary<K,V> { public int size(); public boolean isempty(); public Entry<K,V> find(k key) throws InvalidKeyException; public Iterable<Entry<K,V>> findall(k key) throws InvalidKeyException; public Entry<K,V> insert(k key, V value) throws InvalidKeyException; public Entry<K,V> remove(entry<k,v> e) throws InvalidEntryException; } public Iterable<Entry<K,V>> entries();
Implementazione con liste non ordinate lista S Le entrate del dizionario D vengono mantenute in una lista non ordinata S non è definita alcuna relazione d'ordine totale sulle chiavi
Implementazione con liste non ordinate Algorithm findall(k): Crea una lista S inizialmente vuota for each entry e in D.entries() do if e.getkey() = k then S.addLast(e) return S Algorithm insert(k,v): Crea una nuova entrata e = (k,v) S.addLast(e) {lecito perché S non è ordinata} return e
Implementazione con liste non ordinate Algorithm remove(e): {assumiamo che e non memorizzi la sua position in S} for each position p in S.positions() do if p.element() = e then S.remove(p) return e lancia una InvalidEntryException {non esiste l'entrata e in D} Algorithm entries(): return S
Implementazione con liste non ordinate Analisi: spazio: O(n) tempo: insert: O(1) find e remove: O(n) (nel caso pessimo si deve scorrere tutta la lista) la performance di remove può essere portata ad O(1) se ogni entrata di D memorizza la sua posizione nella lista S Questa implementazione è opportuna solo per dizionari di piccola taglia o dizionari in cui gli inserimenti sono le operazioni più comuni, mentre le ricerche e le rimozioni sono eseguite di rado (per esempio record di login a una workstation)
ADT Dizionario Implementazione con tabelle hash Possiamo usare una tabella hash con separate chaining per risolvere le collisioni Bucket array A Dizionario basato su lista 0 1 2 3 4 Ogni cella A[i] mantiene un dizionario che contiene tutte le entrate con chiave k tale che h(k) = i
Implementazione con tabelle hash A: bucket array N: dimensione del bucket array n: numero di entrate del dizionario λ: massimo load factor della tabella hash h: funzione hash Algorithm insert(k,v): if (n + 1) / N > λ then raddoppia la taglia del bucket array e fai il rehash delle chiavi e := A[h(k)].insert (k,v) n:= n + 1 return e A[h(k)] è un dizionario quindi possiamo usare il metodo insert per inserire una nuova entrata
Implementazione con tabelle hash Algorithm findall(k): return A[h(k)].findAll (k) Algorithm remove(e): t := A[h(k)].remove (e) n = n 1 return t
Implementazione con tabelle hash Analisi: spazio: O(n) tempo: insert: O(1) remove: atteso: O(1) caso peggiore: O(n) find: atteso: O(1) caso peggiore: O(n) findall: atteso: O(1 + s) dove s è il numero di chiavi restituite. caso peggiore: O(n)