hash table c programs implement hash table
Questo tutorial spiega le tabelle hash C ++ e le mappe hash. Imparerai anche le applicazioni e l'implementazione della tabella hash in C ++:
come usare il comando find in unix
L'hashing è una tecnica che consente di mappare una grande quantità di dati su una tabella più piccola utilizzando una 'funzione hash'.
Utilizzando la tecnica di hashing, possiamo cercare i dati in modo più rapido ed efficiente rispetto ad altre tecniche di ricerca come la ricerca lineare e binaria.
Cerchiamo di capire la tecnica di hashing con un esempio in questo tutorial.
=> Leggere la serie di formazione Easy C ++.
Cosa imparerai:
- Hashing in C ++
- Implementazione della tabella hash C ++
- Applicazioni dell'hashing
- Conclusione
Hashing in C ++
Prendiamo un esempio di una biblioteca universitaria che ospita migliaia di libri. I libri sono ordinati in base a soggetti, dipartimenti, ecc. Tuttavia, ogni sezione avrà numerosi libri che rendono quindi molto difficile la ricerca di libri.
Pertanto, per superare questa difficoltà, assegniamo un numero o una chiave univoca a ciascun libro in modo da conoscere immediatamente la posizione del libro. Ciò si ottiene effettivamente tramite l'hashing.
Continuando con l'esempio della nostra biblioteca, invece di identificare ogni libro in base al suo dipartimento, argomento, sezione, ecc. Che può risultare in una stringa molto lunga, calcoliamo un valore intero univoco o una chiave per ogni libro nella biblioteca usando una funzione unica memorizzare queste chiavi in una tabella separata.
La funzione unica di cui sopra è chiamata 'funzione hash' e la tabella separata è chiamata 'tabella hash'. Una funzione hash viene utilizzata per mappare il valore dato a una particolare chiave univoca nella tabella hash. Ciò si traduce in un accesso più rapido agli elementi. Più efficiente è la funzione di hashing, più efficiente sarà la mappatura di ogni elemento sulla chiave univoca.
Consideriamo una funzione hash h (x) che mappa il valore ' X ' a ' x% 10 'Nella matrice. Per i dati forniti, possiamo costruire una tabella hash contenente chiavi o codici hash o hash come mostrato nel diagramma sottostante.
Nel diagramma sopra, possiamo vedere che le voci nell'array sono mappate alle loro posizioni nella tabella hash usando una funzione hash.
Quindi possiamo dire che l'hashing viene implementato utilizzando due passaggi come indicato di seguito:
# 1) Il valore viene convertito in una chiave intera o hash univoca utilizzando una funzione hash. Viene utilizzato come indice per memorizzare l'elemento originale, che rientra nella tabella hash.
Nel diagramma sopra, il valore 1 nella tabella hash è la chiave univoca per memorizzare l'elemento 1 dalla matrice di dati fornita sul LHS del diagramma.
#Due) L'elemento dall'array di dati viene archiviato nella tabella hash dove può essere recuperato rapidamente utilizzando la chiave con hash. Nel diagramma sopra, abbiamo visto che abbiamo memorizzato tutti gli elementi nella tabella hash dopo aver calcolato le rispettive posizioni utilizzando una funzione hash. Possiamo usare le seguenti espressioni per recuperare valori hash e indice.
hash = hash_func(key) index = hash % array_size
Funzione hash
Abbiamo già detto che l'efficienza della mappatura dipende dall'efficienza della funzione hash che utilizziamo.
Una funzione hash fondamentalmente dovrebbe soddisfare i seguenti requisiti:
- Facile da calcolare: Una funzione hash, dovrebbe essere facile da calcolare le chiavi univoche.
- Meno collisioni: Quando gli elementi corrispondono agli stessi valori chiave, si verifica una collisione. Dovrebbero esserci collisioni minime il più possibile nella funzione hash utilizzata. Poiché le collisioni sono destinate a verificarsi, dobbiamo utilizzare tecniche di risoluzione delle collisioni appropriate per prenderci cura delle collisioni.
- Distribuzione uniforme: La funzione hash dovrebbe comportare una distribuzione uniforme dei dati nella tabella hash e quindi impedire il clustering.
Tabella hash C ++
La tabella hash o una mappa hash è una struttura dati che memorizza i puntatori agli elementi della matrice di dati originale.
Nel nostro esempio di libreria, la tabella hash per la libreria conterrà i puntatori a ciascuno dei libri nella libreria.
La presenza di voci nella tabella hash semplifica la ricerca di un particolare elemento nell'array.
Come già visto, la tabella hash utilizza una funzione hash per calcolare l'indice nell'array di bucket o slot utilizzando il quale è possibile trovare il valore desiderato.
Considera un altro esempio con il seguente array di dati:
Supponiamo di avere una tabella hash di dimensione 10 come mostrato di seguito:
Ora usiamo la funzione hash indicata di seguito.
Hash_code = Key_value % size_of_hash_table
Ciò corrisponderà a Hash_code = Key_value% 10
Utilizzando la funzione sopra, mappiamo i valori delle chiavi alle posizioni della tabella hash come mostrato di seguito.
Elemento dati | Funzione hash | Codice hash |
---|---|---|
22 | 22% 10 = 2 | Due |
25 | 25% 10 = 5 | 5 |
27 | 27% 10 = 7 | 7 |
46 | 46% 10 = 6 | 6 |
70 | 70% 10 = 0 | 0 |
89 | 89% 10 = 9 | 9 |
31 | 31% 10 = 1 | 1 |
Usando la tabella sopra, possiamo rappresentare la tabella hash come segue.
Pertanto, quando dobbiamo accedere a un elemento dalla tabella hash, ci vorrà solo O (1) tempo per eseguire la ricerca.
Collisione
Di solito calcoliamo il codice hash utilizzando la funzione hash in modo da poter mappare il valore della chiave al codice hash nella tabella hash. Nell'esempio precedente dell'array di dati, inseriamo un valore 12. In tal caso, il codice hash per il valore della chiave 12 sarà 2. (12% 10 = 2).
Ma nella tabella hash, abbiamo già una mappatura al valore-chiave 22 per hash_code 2 come mostrato di seguito:
Come mostrato sopra, abbiamo lo stesso codice hash per due valori, 12 e 22, ovvero 2. Quando uno o più valori chiave corrispondono alla stessa posizione, si verifica una collisione. Pertanto, la posizione del codice hash è già occupata da un valore chiave e c'è un altro valore chiave che deve essere posizionato nella stessa posizione.
Nel caso dell'hashing, anche se abbiamo una tabella hash di dimensioni molto grandi, è inevitabile che ci sia una collisione. Questo perché troviamo un piccolo valore univoco per una grande chiave in generale, quindi è completamente possibile che uno o più valori abbiano lo stesso codice hash in un dato momento.
Dato che una collisione è inevitabile nell'hashing, dovremmo sempre cercare modi per prevenire o risolvere la collisione. Esistono varie tecniche di risoluzione delle collisioni che possiamo utilizzare per risolvere la collisione che si verifica durante l'hashing.
Tecniche di risoluzione delle collisioni
Le seguenti sono le tecniche che possiamo impiegare per risolvere la collisione nella tabella hash.
Concatenamento separato (hash aperto)
Questa è la tecnica di risoluzione delle collisioni più comune. Questo è anche noto come hashing aperto e viene implementato utilizzando un elenco collegato.
Nella tecnica del concatenamento separato, ogni voce nella tabella hash è un elenco collegato. Quando la chiave corrisponde al codice hash, viene inserita in un elenco corrispondente a quel particolare codice hash. Pertanto, quando due chiavi hanno lo stesso codice hash, entrambe le voci vengono inserite nell'elenco collegato.
Per l'esempio precedente, il concatenamento separato è rappresentato di seguito.
Il diagramma sopra rappresenta il concatenamento. Qui usiamo la funzione mod (%). Vediamo che quando due valori chiave equivalgono allo stesso codice hash, colleghiamo questi elementi a quel codice hash utilizzando un elenco collegato.
Se le chiavi sono distribuite uniformemente nella tabella hash, il costo medio per la ricerca della chiave particolare dipende dal numero medio di chiavi nell'elenco collegato. Pertanto il concatenamento separato rimane efficace anche quando si verifica un aumento del numero di voci rispetto agli slot.
Il caso peggiore per il concatenamento separato è quando tutte le chiavi corrispondono allo stesso codice hash e quindi vengono inserite in un solo elenco collegato. Quindi, dobbiamo cercare tutte le voci nella tabella hash e il costo che sono proporzionali al numero di chiavi nella tabella.
Sondaggio lineare (indirizzamento aperto / hash chiuso)
Nella tecnica di indirizzamento aperto o di sondaggio lineare, tutti i record di ingresso vengono memorizzati nella tabella hash stessa. Quando il valore-chiave viene mappato a un codice hash e la posizione a cui punta il codice hash non è occupata, il valore della chiave viene inserito in quella posizione.
Se la posizione è già occupata, utilizzando una sequenza di sondaggio il valore della chiave viene inserito nella posizione successiva che non è occupata nella tabella hash.
Per il rilevamento lineare, la funzione hash può cambiare come mostrato di seguito:
hash = hash% hashTableSize
hash = (hash + 1)% hashTableSize
hash = (hash + 2)% hashTableSize
hash = (hash + 3)% hashTableSize
Vediamo che in caso di sondaggio lineare l'intervallo tra gli slot o le sonde successive è costante, cioè 1.
Nel diagramma sopra, vediamo che nello 0thlocation inseriamo 10 usando la funzione hash 'hash = hash% hash_tableSize'.
Ora l'elemento 70 corrisponde anche alla posizione 0 nella tabella hash. Ma quella posizione è già occupata. Quindi, usando il rilevamento lineare troveremo la posizione successiva che è 1. Poiché questa posizione non è occupata, posizioniamo la chiave 70 in questa posizione come mostrato usando una freccia.
La tabella hash risultante è mostrata di seguito.
Il sondaggio lineare può soffrire del problema del “Clustering primario” in cui c'è la possibilità che le celle continue si occupino e la probabilità di inserire un nuovo elemento si riduce.
Inoltre, se due elementi ottengono lo stesso valore alla prima funzione hash, entrambi questi elementi seguiranno la stessa sequenza di probe.
Sondaggio quadratico
Il sondaggio quadratico è uguale al sondaggio lineare con l'unica differenza che è l'intervallo utilizzato per il sondaggio. Come suggerisce il nome, questa tecnica utilizza una distanza non lineare o quadratica per occupare gli slot quando si verifica una collisione invece della distanza lineare.
Nel sondaggio quadratico, l'intervallo tra gli slot viene calcolato aggiungendo un valore polinomiale arbitrario all'indice già sottoposto a hash. Questa tecnica riduce il clustering primario in misura significativa ma non migliora il clustering secondario.
Doppio hash
La tecnica del doppio hashing è simile al sondaggio lineare. L'unica differenza tra il doppio hashing e il sondaggio lineare è che nella tecnica del doppio hashing l'intervallo utilizzato per il sondaggio viene calcolato utilizzando due funzioni hash. Poiché applichiamo la funzione hash alla chiave una dopo l'altra, elimina il clustering primario e quello secondario.
Differenza tra concatenamento (hash aperto) e rilevamento lineare (indirizzamento aperto)
Concatenamento (Hashing aperto) | Sonda lineare (indirizzamento aperto) |
---|---|
I valori chiave possono essere archiviati all'esterno della tabella utilizzando un elenco collegato separato. | I valori chiave devono essere memorizzati solo all'interno della tabella. |
Il numero di elementi nella tabella hash può superare la dimensione della tabella hash. | Il numero di elementi presenti nella tabella hash non supererà il numero di indici nella tabella hash. |
La cancellazione è efficiente nella tecnica di concatenamento. | La cancellazione può essere scomoda. Può essere evitato se non richiesto. |
Poiché per ogni posizione viene mantenuto un elenco collegato separato, lo spazio occupato è ampio. | Poiché tutte le voci sono contenute nella stessa tabella, lo spazio occupato è minore. |
Implementazione della tabella hash C ++
Possiamo implementare l'hashing utilizzando array o elenchi collegati per programmare le tabelle hash. In C ++ abbiamo anche una funzione chiamata 'hash map' che è una struttura simile a una tabella hash ma ogni voce è una coppia chiave-valore. In C ++ si chiama hash map o semplicemente map. La mappa hash in C ++ è solitamente non ordinata.
C'è un'intestazione definita nella Standard Template Library (STL) di C ++ che implementa la funzionalità delle mappe. Abbiamo coperto Mappe STL in dettaglio nel nostro tutorial su STL.
La seguente implementazione è per l'hashing utilizzando gli elenchi collegati come struttura dati per la tabella hash. Usiamo anche 'Chaining' come tecnica di risoluzione delle collisioni in questa implementazione.
#include #include using namespace std; class Hashing { int hash_bucket; // No. of buckets // Pointer to an array containing buckets list *hashtable; public: Hashing(int V); // Constructor // inserts a key into hash table void insert_key(int val); // deletes a key from hash table void delete_key(int key); // hash function to map values to key int hashFunction(int x) { return (x % hash_bucket); } void displayHash(); }; Hashing::Hashing(int b) { this->hash_bucket = b; hashtable = new list (hash_bucket); } //insert to hash table void Hashing::insert_key(int key) { int index = hashFunction(key); hashtable(index).push_back(key); } void Hashing::delete_key(int key) { // get the hash index for key int index = hashFunction(key); // find the key in (inex)th list list :: iterator i; for (i = hashtable(index).begin(); i != hashtable(index).end(); i++) { if (*i == key) break; } // if key is found in hash table, remove it if (i != hashtable(index).end()) hashtable(index).erase(i); } // display the hash table void Hashing::displayHash() { for (int i = 0; i ' << x; cout << endl; } } // main program int main() { // array that contains keys to be mapped int hash_array() = {11,12,21, 14, 15}; int n = sizeof(hash_array)/sizeof(hash_array(0)); Hashing h(7); // Number of buckets = 7 //insert the keys into the hash table for (int i = 0; i < n; i++) h.insert_key(hash_array(i)); // display the Hash table cout<<'Hash table created:'< Produzione:
Tabella hash creata:
0 -> 21 -> 14
1 -> 15
Due
3
4 -> 11
5 -> 12
6
Tabella hash dopo l'eliminazione della chiave 12:
0 -> 21 -> 14
1 -> 15
Due
3
4 -> 11
5
6
L'output mostra una tabella hash creata di dimensione 7. Usiamo il concatenamento per risolvere la collisione. Visualizziamo la tabella hash dopo aver eliminato una delle chiavi.
Applicazioni dell'hashing
# 1) Verifica delle password: La verifica delle password viene solitamente eseguita utilizzando funzioni hash crittografiche. Quando la password viene inserita, il sistema calcola l'hash della password e viene quindi inviata al server per la verifica. Sul server vengono memorizzati i valori hash delle password originali.
# 2) Strutture dati: Diverse strutture di dati come unordered_set e unordered_map in C ++, dizionari in Python o C #, HashSet e hash map in Java utilizzano tutte coppie chiave-valore in cui le chiavi sono valori univoci. I valori possono essere gli stessi per chiavi diverse. L'hashing viene utilizzato per implementare queste strutture di dati.
# 3) Message Digest: Questa è ancora un'altra applicazione che utilizza un hash crittografico. Nei digest di messaggi, calcoliamo un hash per i dati inviati e ricevuti o anche per i file e li confrontiamo con i valori memorizzati per garantire che i file di dati non vengano manomessi. L'algoritmo più comune qui è 'SHA 256'.
# 4) Operazione del compilatore: Quando il compilatore compila un programma, le parole chiave per il linguaggio di programmazione vengono memorizzate in modo diverso dagli altri identificati. Il compilatore utilizza una tabella hash per memorizzare queste parole chiave.
# 5) Indicizzazione del database: Le tabelle hash vengono utilizzate per l'indicizzazione del database e le strutture dati basate su disco.
# 6) Array associativi: Gli array associativi sono array i cui indici sono di tipo di dati diverso da stringhe di tipo intero o altri tipi di oggetto. Le tabelle hash possono essere utilizzate per implementare array associativi.
Conclusione
L'hashing è la struttura dati più utilizzata poiché richiede tempo costante O (1) per le operazioni di inserimento, eliminazione e ricerca. L'hashing viene implementato principalmente utilizzando una funzione hash che calcola un valore chiave più piccolo univoco per voci di dati di grandi dimensioni. Possiamo implementare l'hashing usando array ed elenchi collegati.
Ogni volta che una o più voci di dati corrispondono agli stessi valori di chiavi, si verifica una collisione. Abbiamo visto varie tecniche di risoluzione delle collisioni tra cui sondaggio lineare, concatenamento, ecc. Abbiamo anche visto l'implementazione dell'hashing in C ++.
Per concludere, possiamo dire che l'hashing è di gran lunga la struttura dati più efficiente nel mondo della programmazione.
=> Cerca qui l'intera serie di formazione C ++.
Lettura consigliata
- Come scrivere scenari di test complessi di logica aziendale utilizzando la tecnica della tabella decisionale
- Tabella di convalida sul campo (FVT): una tecnica di progettazione di test per la convalida sul campo
- Tutorial QTP # 15 - Utilizzo di aree di testo, tabelle e punti di controllo della pagina in QTP
- MAPPE In STL
- Tutto sui router: tipi di router, tabella di instradamento e instradamento IP
- Le 40 migliori domande e risposte per i colloqui di MySQL (domande 2021)
- Le 90 principali domande e risposte dell'intervista SQL (ULTIME)
- Comandi dei programmi di utilità Unix: Which, Man, Find Su, Sudo (Part D)