Dizionari in c
- La struttura dizionario in C non esiste, per queste adesso la ricreeremo basandoci sulle caratteristiche dei dizionari Python
- Questo ci permetterà di capire come funziona un dizionario e il motivo pre cui la maggior parte delle operazioni su un dizionario richiedono tempo medio costante
Caratteristiche dei dizionari (python):
Funzioni:
- Creazione dizionario vuoto
- Inserimento di una coppia chiave valore (item)
- Modifica di un valore associato ad una chiave
- Cancellazione di una coppia (item)
- Lettura di un valore associato ad una chiave
- Ricerca, controllare se una chiave è presente o no
oss: in python tutte queste operazioni hanno costo costante nel caso medio tranne operatore di iterazione ha costo lineare, leggi Introduzione semplicistica alla complessità algoritmica in python
Struttura Dato:
oss: possiamo costruire un dizionario utilizzando sia un array “normale” sia utilizzando le liste concatenate Linked List (Struttura Dati),
- in entrambi i casi non sarebbe possibile avere un costo medio costante per la maggior parte delle operazioni)
Esempio utilizzando lista concatenata
Nota
Costo medio è uguale a costo peggiore diviso numero di elementi
Costi:
-
search(k):
- input chiave, output se è presente o no
- Costo: peggiore: O(n^2), medio: O(n)
-
set(k ,v):
- input: chiave e valore, output: associa chiave a valore se chiave non presente nel dizionario, se presente cambia il valore associato alla chiave con quello nuovo
- Costo dipende dalla search
-
get(k):
- input: chiave, output valore associato alla chiave
- Costo: dipende dalla search
-
del(k):
- input: chiave, output cancella coppia associata alla chiave
- costo: dipende dalla search
Chiaramente in python i dizionari non sono implementati in questo modo
Implementazione Stile Python
Strutture:
Funzioni
Funzione Hash
Caratteristiche di una funzione hash:
- deterministica: per uno stesso input restituisce lo stesso output
- veloce
- distribuisce bene le chiavi nell’array
oss: esistono diverse funzioni hash più o meno complesse, in questo caso utilizziamo una funzione hash molto basilare ma funzionale.
Descrizione: funzione che prendendo in input una chiave restituisce l’indice dell’array contenente il puntatore alla lista di trabocco associata a quella chiave
- Input: dizionario (d), stringa (chiave)
- Output: numero intero (ovvero l’indice della array )
- Costo:
Funzionamento:
- prendiamo la chiave e la dividiamo in caratteri
- facciamo l’ XOR bit a bit dei caratteri
- Calcoliamo il modulo del risultato per capacità dell’array in modo che non sia mai superiore all’indice massimo delle array (M)
Funzione ridimensionamento del dizionario:
-
Descrizione:
- funzione che prende in input un dizionario e la nuova dimensione che gli vogliamo dare
- deve creare un nuovo dizionario, e spostare tutti gli elementi del vecchio nel nuovo (calcolando i nuovi indici hash)
-
Input: dizionario (d), int (nuova dimensione del dizionario)
-
Output: puntatore alla nuovo dizionario
-
Costo:
- h costo costante
- il resto della funzione ha comunque costo costante se la funzione hash distribuisce uniformemente le coppie di valori nelle liste di trabocco
-
*Funzionamento:
- Calcoliamo l’indice del primo nodo della lista di trabocco con la funzione hash
- Troviamo il puntatore al primo nodo della lista di trabocco (attraverso l’indice precedentemente calcolato)
- Scorriamo tutta la lista di trabocco finche non troviamo la chiave
- return = puntatore al nodo in cui è contenuta la chiave interessata
dict_search:
-
Descrizione: funzione che restituisca un puntatore al nodo in cui è contenuta la chiave che stiamo cercando o NULL se non presente
-
Input: dizionario (d), stringa (chiave)
-
Output: puntatore al nodo in cui è contenuta la chiave o NULL se non presente
-
Costo:
- h costo costante
- il resto della funzione ha comunque costo costante se la funzione hash distribuisce uniformemente le coppie di valori nelle liste di trabocco
-
*Funzionamento:
- Calcoliamo l’indice del primo nodo della lista di trabocco con la funzione hash
- Troviamo il puntatore al primo nodo della lista di trabocco (attraverso l’indice precedentemente calcolato)
- Scorriamo tutta la lista di trabocco finche non troviamo la chiave
- return = puntatore al nodo in cui è contenuta la chiave interessata
dict_init:
- Descrizione: funzione che inizializza un dizionario vuoto di capacità dichiarata (M)
- Input: capacità dell’array
- Output: puntatore al dizionario
- Costo:
oss: ho deciso che sia obbligatorio inserire una capacità maggiore di zero
in0:
- Descrizione: funzione che inserisce un nuovo nodo all’inizio di una lista a trabocco, nodo che contiene una coppia di chiave valore (e)
- Input: primo nodo di una lista trabocco (nd), dict_item (e)
- Output: nuovo primo nodo della lista a trabocco
- Costo: costante
dict_set:
- Descrizione: funzione che inserisce una coppia chiave valore nel dizionario se non presente, se la chiave è già presente aggiorna il valore associato alla chiave
- Input: dizionario (d), coppia chiave valore (item)
- Output: ---
- Costo:
- h costo costante
- dict_search costo costante (se liste del dizionario sono di dimensione costante)
- in0 costo costante
- resize lineare con il numero di elementi dell’ dizionario(n)
dict_del:
- Descrizione: funzione elimina una coppia chiave valore associata a una specifica chiave
- Input: dizionario (d), chiave (k)
- Output: ---
- Costo:
- h costo costante
- ricerca posizione chiave nella lista costante se elementi ben suddivisi nelle liste di trabocco
- cancellazioni costanti