Esempio di applicazione
TDA Map
[GT3 8.1-8.2]
8 1 8 2]
Motivazione: mappe e grafi
‡
‡
‡
Ci sono algoritmi su grafi che hanno bisogno di
associare delle informazioni ai vertici e/o agli archi
Esempio
‡
Sia u un nodo in un grafo.
‡
Posso considerare una mappa (per ogni nodo u) con
chiavi (Object)
COLORE, PREDECESSORE, DISTANZA
‡
Ad esempio, i campi predecessore/colore/distanza
(BFS) oppure il peso degli archi e campi
predecessore/chiave (MST).
(PREDECESSORE, v), (COLORE, grigio), (DISTANZA, 0)
[key1, value1], [key2, value2], [key3, value3]
Per il nodo u, so che:
il predecessore è v, il colore è grigio, la distanza è 0.
Invece di aggiungere campi nell’implementazione di un
vertice o di un arco, aggiungiamo solo un’istanza della
classe che implementa MAP
L’uso della mappa, garantisce che, dato un nodo, non
posso avere due o più predecessori, due o più colori,
due o più distanze… perché k deve essere unica
Operazioni sul TDA Map
Definizione
‡
Infatti non consentiamo a due persone (valori) di
avere la stessa matricola (key)
‡
‡
‡
Mappa che conserva le informazioni sugli studenti (ad
esempio, nome, cognome, indirizzo, esami, numero di
matricola…): la chiave potrebbe essere il numero di
matricola
‡
Il TDA Map è un contenitore di oggetti (Entry) che
sono coppie chiave-valore (k,v)
Sia la chiave k sia il valore v possono essere oggetti
qualsiasi.
qualsiasi
Differenza con PQ: non sono consentite più
entry con la stessa chiave k.
‡
Le operazioni principali da fare su un TDA Map
sono di ricerca, inserimento e cancellazione di
elementi.
„
„
„
size(), isEmpty()
keys(): restituisce un iteratore delle chiavi di M
values(): restituisce un iteratore dei valori di M
Si lavora sulle chiavi…
Potete pensarle con “chiavi di
ricerca” abbinate agli elementi
(valori), che uso per reperirli.
1
Operazioni sul TDA Map
„
„
„
get(k): Se la mappa M ha l’entry con chiave k, restituisce il
valore associato, altrimenti null.
put(k v): Inserisce ll’entry
put(k,
entry (k,
(k v) nella mappa M
M. Se la chiave
k non è già presente in M, restituisce null;
altrimenti restituisce il vecchio valore associato a k.
remove(k): Se nella mappa M c’è un entry con chiave k, si
rimuove l’entry con chiave k e si restituisce il
valore associato. Altrimenti si restituisce null.
Interfaccia per il TDA Map
public interface Map {
public int size();
//restituisce il numero di entry della Map
public boolean isEmpty();
//verifica se la Map è vuota
//inserisce una coppia key-value e rimpiazza il value precedente, se k
//c’è già, altrimenti null.
public Object
p
j
put(Object
p
( j
key,
y, Object
j
value)) throws InvalidKeyException;
y
p
;
//Restituisce il value associato a k
public Object get(Object key) throws InvalidKeyException;
//Cancella la coppia (chiave-valore) specificata da key
public Object remove(Object key) throws InvalidKeyException;
public Iterator keys();
//Restituisce un iteratore delle chiavi della mappa
public Iterator values();
//Restituisce un iteratore dei valori della mappa
}
Osservazione su “null”
„
„
„
„
Abbiamo visto che le operazioni get(k), put(k,v) e remove(k)
se eseguite su una mappa M che non contiene un’entry con
chiave k, restituisce null
COME IMPLEMENTIAMO???
“Cose abbastanza naturali…”
1.
Si tratta di un value speciale,
p
, detto sentinella.
public interface Entry {
public Object key();
public Object element();
}
Questo significa che potrebbe esserci ambiguità con l’entry
(k,null), ossia con value null [poco appropriato]
Se si vuole mantenere questa possibilità, si può pensare ad
un’eccezione.
‡
Esempio
Operation
1.
2.
3.
4.
5
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Una entry della mappa viene rappresentata dalla
classe MyEntry, come fatto per le code a priorità.
isEmpty()
put(5,A)
put(7,B)
put(2,C)
put(8 D)
put(8,D)
put(2,E)
get(7)
get(4)
get(2)
size()
remove(5)
remove(2)
get(2)
isEmpty()
L’implementazione sarà una classe interna, come per le
code a priorità.
COME IMPLEMENTIAMO???
Output
true
null
null
null
null
C
B
null
E
4
A
E
null
false
Map
Ø
(5,A)
(5,A),(7,B)
(5,A),(7,B),(2,C)
(5 A) (7 B) (2 C) (8 D)
(5,A),(7,B),(2,C),(8,D)
(5,A),(7,B),(2,E),(8,D)
(5,A),(7,B),(2,E),(8,D)
(5,A),(7,B),(2,E),(8,D)
(5,A),(7,B),(2,E),(8,D)
(5,A),(7,B),(2,E),(8,D)
(7,B),(2,E),(8,D)
(7,B),(8,D)
(7,B),(8,D)
(7,B),(8,D)
“Cose abbastanza naturali…”
2.
I metodi presenti nell’interfaccia Map devono lanciare
l’eccezione InvalidKeyException se la chiave specificata non è
per il contenitore in esame. Ma qual
q
è qui
q il criterio di
valida p
validità????
3.
Il metodo checkKey dovrà solo controllare che k non è null, non
c’è bisogno di una relazione d’ordine totale tra le chiavi…
2
Abbiamo bisogno di confrontare le chiavi….
¾
¾
¾
¾
Un’ implementazione basata su liste
‡
Dobbiamo decidere se due chiavi sono uguali.
Se le chiavi hanno una relazione d’ordine totale determinata da
qualche comparatore C, possiamo usare C.compare(k1,k2) e
vedere quando ritorna 0… ma non conviene.
Per una mappa generica, possiamo usare un oggetto EQUALITY
TESTER che supporta l’operazione isEqualTo(k1,k2) sulle chiavi.
Potremmo anche usare il metodo equals di Java, ma non è
generale.
Possiamo implementare efficientemente il TDA
Map usando una lista non ordinata (implementata
come lista doppiamente-linkata)
„
Memorizziamo gli elementi della mappa in una lista senza
un ordinamento
nodes/positions
header
9 a
6 c
5 f
trailer
8 d
entries
Esistono implementazioni migliori (basate su tabelle hash)
Abbiamo bisogno di confrontare le chiavi….
¾
¾
¾
Quando creiamo una mappa M, abbiniamo un EQUALITY
TESTER basato su un comparatore.
Nelle slides successive, usiamo “=“ per testare l’uguaglianza.
Nell’implementazione Java possiamo fare come per la coda a
Priorità, ossia
Interfaccia da
scrivere
Algoritmo get(k) [compl. O(size)]
Algorithm get(k):
B = M.positions() //B è un iteratore delle posizioni in M
while B.hasNext() do
p = B.next()
//la prossima posizione in B
if (p.element().key() == k)
then
return (p.element()).value()
return null //non ci sono entry con chiave uguale a k
protected static class DefaultEqualityTester implements EqualityTester{
public DefaultEqualityTester() { /* default constructor */}
}
public boolean isEqualTo(Object a, Object b) throws ClassCastException {
return (a.equals(b));
}
Confronto con il package java.util.Map
Metodi del TDA Map
size()
isEmpty()
get(k)
put(k,v)
remove(k)
keys()
values()
Metodi java.util.Map
size()
isEmpty()
get(k)
put(k,v)
remove(k)
keySet().iterator()
values().iterator()
Inoltre, non si usa un Equality Tester esterno, ma solo il metodo
equals (se non è appropriato per il nostri oggetti – vedi esempio
dei punti nel piano – occorre sovrascriverlo…)
Algoritmo remove(k) [compl. O(size)]
Algorithm remove(k):
B =M.positions()
while B.hasNext() do
p = B.next()
if (p.element().key() == k) then
t = p.element().value()
M.remove(p)
size = size – 1
return t
return null //non c’erano entry con chiave k
3
Algoritmo put(k,v) [compl. O(size)]
Algorithm put(k,v):
B = M.positions()
while M.hasNext() do
p = B.next()
if (p.element().key() == k) then
t = p.element().value()
M.replace(p,(k,v))
//come implemento??? Esercizio.
return t
M.insertLast((k,v))
size = size + 1
return null
//non c’erano già value con chiave k
Esercizi proposti
‡
Implementare il TDA Map con liste non ordinate
‡
Scrivere un programma di test per la classe
‡
Scrivere il metodo toStringKey() che restituisca una
stringa contenente le chiavi della mappa; scrivere un
metodo toStringValue() che restituisca una stringa
contenente i values della mappa. Usare i metodi del TDA
Map.
4