Übersicht über einfache Hash-Tabellen
Als Auffrischung ist eine Hash-Tabelle eine Möglichkeit, einen Wert unter einem bestimmten Schlüssel in einer Datenstruktur zu speichern. Zum Beispiel könnte ich den Wert "a"
unter dem Schlüssel speichern 1
und ihn später abrufen, indem ich den Schlüssel 1
in der Hash-Tabelle nachschaue.
Das einfachste Beispiel für eine Hash-Tabelle, das ich mir auf Anhieb vorstellen kann, ist eine Hash-Tabelle, in der nur Ganzzahlen gespeichert werden können, wobei der Schlüssel für den Hash-Tabelleneintrag auch der gespeicherte Wert ist. Angenommen, Ihre Tabelle hat die Größe 8 und ist im Prinzip ein Array im Speicher:
---------------------------------
| | | | | | | | |
---------------------------------
0 1 2 3 4 5 6 7
Hash-Funktion
Hash-Funktionen geben Ihnen einen Index, wo Sie Ihren Wert speichern können. Eine recht einfache Hash - Funktion für diese Tabelle wäre 1 zu dem Wert hinzufügen , die Sie speichern möchten, und dann mod es um 8 (Tabellengröße). Mit anderen Worten, Ihre Hash-Funktion ist (n+1)%8
, wo n
ist die ganze Zahl, die Sie speichern möchten.
Einsätze
Wenn Sie einen Wert in diese Hash-Tabelle einfügen möchten, rufen Sie (in diesem Fall (n+1)%8
) Ihre Hash-Funktion für den Wert auf, den Sie einfügen möchten, um einen Index zu erhalten. Wenn wir beispielsweise 14 einfügen möchten, würden wir (14 + 1) % 8
index aufrufen und abrufen 7
, sodass wir den Wert in index einfügen würden 7
.
---------------------------------
| | | | | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Ebenso können wir 33, 82 und 191 wie folgt einfügen:
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Kollisionen
Aber was passiert, wenn wir versuchen, etwas einzufügen, das mit einem Eintrag kollidieren würde? 2 in Index gehen sollte 3
, aber es wird aufgenommen von 82. Es mehrere Möglichkeiten, dieses Problem zu lösen, ist die einfachste unsere Hash - Funktion erneut aufrufen und wieder wiederholt , bis wir einen leeren Raum finden.
Die Logik ist also wie folgt:
- (2 + 1)% 8 = 3
- Index 3 ist voll
- Stecken Sie 3 wieder in unsere Hash-Funktion. ( 3 + 1)% 8 = 4 , was leer ist.
- Platzieren Sie unseren Wert in Index 4 .
Nun sieht die Hash-Tabelle so aus, wobei der Wert 2 im Index gespeichert ist 4
.
---------------------------------
|191| |33 |82 |2 | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Der Nachteil dieser Lösung ist, dass unser Tisch bald voll sein wird! Wenn Sie wissen, dass Ihre Datenmenge begrenzt ist, sollte dies kein Problem sein, solange Ihre Tabelle groß genug ist, um alle möglichen Werte aufzunehmen. Wenn Sie in der Lage sein möchten, mehr zu halten, können Sie Kollisionen unterschiedlich behandeln. Gehen wir zurück zu dem Punkt, an dem wir vor dem Einfügen von 2 waren.
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Wenn Sie sich erinnern, (2+1)%8
gibt uns Index 3
, der genommen wird. Wenn Sie nicht möchten, dass sich Ihre Hash-Tabelle füllt, können Sie jeden Tabellenindex als verknüpfte Liste verwenden und an die Liste an diesem Index anhängen. Anstatt die Hash-Funktion erneut aufzurufen, hängen wir sie einfach an die Liste im Index an 3
:
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Diese Liste kann so groß werden, wie es der Speicher zulässt. Ich kann 18 einfügen und es wird nur an 2 angehängt:
-----
|18 |
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Lookups
Die Suche nach Werten in Ihrer Hash-Tabelle ist schnell, da Ihre Hash-Tabelle ziemlich groß ist. Sie rufen einfach Ihre Hash-Funktion auf und erhalten den Index. Angenommen, Sie möchten sehen, ob 82 in Ihrer Tabelle enthalten ist. Die Suchfunktion würde (82+1)%8
= aufrufen 3
und das Element im Index betrachten 3
und es für Sie zurückgeben. Wenn Sie 16 nachschlagen, wird die Suchfunktion im Index nachgeschlagen 1
und es wird festgestellt, dass sie nicht vorhanden ist.
Lookups müssen auch mit Kollisionen umgehen!
Wenn Sie versuchen, den Wert 2 nachzuschlagen, müsste Ihre Hash-Tabelle dieselbe Kollisionslogik verwenden, die zum Speichern der Daten verwendet wurde, wie zum Abrufen der Daten. Abhängig von der Funktionsweise Ihrer Hash-Tabelle werden Sie entweder den Schlüssel immer wieder hashen, bis Sie den gesuchten Eintrag finden (oder eine leere Stelle finden), oder Ihre verknüpfte Liste durchlaufen, bis Sie das Element gefunden haben (oder an das Ende der Liste)
Zusammenfassung
Hash-Tabellen sind daher eine gute Möglichkeit, Schlüssel-Wert-Paare schnell zu speichern und darauf zuzugreifen. In diesem Beispiel wurde derselbe Schlüssel wie der Wert verwendet, aber in Hash-Tabellen der realen Welt sind die Schlüssel nicht so eingeschränkt. Hash-Funktionen arbeiten mit den Schlüsseln, um einen Index zu generieren, und dann kann der Schlüssel / Wert an diesem Index gespeichert werden. Hash-Tabellen sind eigentlich nicht dazu gedacht, durchlaufen zu werden, obwohl dies möglich ist. Wie Sie sehen, können Hash-Tabellen viele Leerzeichen enthalten, und es wäre Zeitverschwendung, sie zu durchlaufen. Auch wenn die Hash-Tabelle über eine Logik zum Überspringen von Leerraumsuchen im Iterator verfügt, ist es besser, eine für Iteratoren konzipierte Datenstruktur wie verknüpfte Listen zu verwenden.