Menu
Coddy logo textTech

Hashtabelle

Zuletzt aktualisiert

Eine Hashtabelle speichert Elemente in einem Array von Buckets. Um einen Schlüssel abzulegen oder zu finden, führt sie den Schlüssel durch eine Hashfunktion und nimmt das Ergebnis modulo der Anzahl der Buckets - das ergibt den Bucket-Index in O(1). Drücke oben auf Abspielen, um zu sehen, wie jeder Schlüssel zu einem Bucket gehasht und gespeichert wird und wie eine Suche dann direkt zum richtigen Bucket springt.

Wenn zwei Schlüssel im selben Bucket landen - eine Kollision - verwendet diese Visualisierung separate Verkettung: Jeder Bucket enthält eine kleine Liste, und kollidierende Schlüssel werden ihr angehängt. Solange die Tabelle nicht zu voll ist (ein niedriger Füllfaktor) und der Hash die Schlüssel gut verteilt, bleiben die Ketten kurz und Einfügen, Suchen und Löschen sind im Durchschnitt O(1).

Zeitkomplexität

OperationDurchschnittSchlechtester Fall
EinfügenO(1)O(n) (alle Schlüssel kollidieren)
SuchenO(1)O(n)
LöschenO(1)O(n)
SpeicherO(n)O(n)

Schlüsselkonzepte

BegriffBedeutung
HashfunktionBildet einen Schlüssel auf einen Bucket-Index ab
KollisionZwei Schlüssel landen im selben Bucket
Separate VerkettungJeder Bucket enthält eine Liste kollidierender Einträge
Offene AdressierungAlternative: den nächsten freien Slot sondieren
FüllfaktorEinträge / Buckets - steuert die Größenänderung

Durchgerechnetes Beispiel

Einfügen der Schlüssel 20, 34, 9, 13 in eine Tabelle mit 7 Buckets, mit hash(k) = k % 7:

SchrittStrukturAktion
20 einfügenBucket 6: [20]20 % 7 = 6, Bucket 6 leer, 20 speichern
34 einfügenBucket 6: [20, 34]34 % 7 = 6, Kollision mit 20, 34 an die Kette anhängen
9 einfügenBucket 2: [9]9 % 7 = 2, Bucket 2 leer, 9 speichern
13 einfügenBucket 6: [20, 34, 13]13 % 7 = 6, Kollision, 13 an die Kette von Bucket 6 anhängen
34 suchenBucket 6: [20, 34, 13]34 % 7 = 6, Kette durchlaufen: 20 nein, 34 passt - gefunden

Wann eine Hashtabelle verwenden

Verwende sie, wennVermeide sie, wenn
Du im Durchschnitt O(1) beim Einfügen, Suchen und Löschen nach Schlüssel brauchstDu die Schlüssel sortiert halten musst - verwende einen ausgeglichenen BST
Die Schlüssel keine nützliche Ordnung haben und du nur exakte Zugehörigkeit prüfstDu Bereichsabfragen oder Suchen nach dem nächstgelegenen Schlüssel brauchst
Du dir etwas zusätzlichen Speicher für Buckets und einen niedrigen Füllfaktor leisten kannstDer Speicher extrem knapp ist und jedes Byte zählt
Eine gute Hashfunktion für deinen Schlüsseltyp existiertDie Worst-Case-Latenz beschränkt sein muss - Kollisionen machen sie O(n)

Hash Table-Code

Eine saubere, lauffähige Hash Table-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.

Hash Table-Code in Python

Python
1class HashTable:2    def __init__(self, size=8):3        self.size = size4        self.buckets = [[] for _ in range(size)]5
6    def _index(self, key):7        # Hash the key to a bucket; different keys can collide8        return sum(ord(ch) for ch in key) % self.size9
10    def set(self, key, value):11        bucket = self.buckets[self._index(key)]12        for i, (k, _) in enumerate(bucket):13            if k == key:14                bucket[i] = (key, value)  # update existing key15                return16        bucket.append((key, value))  # chain on collision17
18    def get(self, key):19        for k, v in self.buckets[self._index(key)]:20            if k == key:21                return v22        raise KeyError(key)23
24    def delete(self, key):25        bucket = self.buckets[self._index(key)]26        for i, (k, _) in enumerate(bucket):27            if k == key:28                del bucket[i]29                return30        raise KeyError(key)31
32
33table = HashTable()34table.set("apple", 3)35table.set("banana", 7)36table.set("cherry", 5)37
38print("apple  ->", table.get("apple"))39print("banana ->", table.get("banana"))40table.set("apple", 10)41print("apple  ->", table.get("apple"))42table.delete("banana")43print("bucket sizes:", [len(b) for b in table.buckets])
Führe diesen Code im Python-Playground aus

Hashtabelle FAQ

Was ist eine Hash-Kollision und wie wird sie aufgelöst?
Eine Kollision tritt auf, wenn zwei verschiedene Schlüssel im selben Bucket landen. Die beiden gängigen Lösungen sind separate Verkettung (jeder Bucket enthält eine Liste, und kollidierende Schlüssel werden angehängt - hier gezeigt) und offene Adressierung (den nächsten leeren Slot im Array sondieren). Beide halten die Suchen korrekt; sie unterscheiden sich im Speicher-Layout und in der Leistung bei hoher Last.
Was ist die Zeitkomplexität einer Hashtabelle?
Einfügen, Suchen und Löschen sind im Durchschnitt O(1), wenn die Hashfunktion die Schlüssel gleichmäßig verteilt und der Füllfaktor niedrig gehalten wird. Im schlechtesten Fall - alle Schlüssel kollidieren in einem einzigen Bucket - verschlechtern sie sich zu O(n), weshalb eine gute Hashfunktion und Größenänderung wichtig sind.
Was ist ein Füllfaktor?
Der Füllfaktor ist die Anzahl der gespeicherten Einträge geteilt durch die Anzahl der Buckets. Mit seinem Wachstum werden die Ketten länger und die Operationen langsamer, weshalb die meisten Hashtabellen die Größe ändern (in ein größeres Array umhashen), sobald er einen Schwellenwert wie 0.75 überschreitet.
Wann sollte ich eine Hashtabelle statt eines binären Suchbaums verwenden?
Verwende eine Hashtabelle, wenn du nur exakte Suchen brauchst und durchschnittliche O(1)-Geschwindigkeit ohne Ordnungsanforderung willst. Verwende einen ausgeglichenen binären Suchbaum, wenn du sortierte Schlüssel, Bereichsabfragen oder Vorgänger-/Nachfolger-Suchen brauchst, was eine Hashtabelle nicht effizient kann. Ein BST bietet garantierte O(log n)-Operationen, während eine Hashtabelle diese Garantie gegen schnellere Durchschnittsleistung eintauscht.
Was ist der Unterschied zwischen separater Verkettung und offener Adressierung?
Separate Verkettung speichert kollidierende Schlüssel in einer Liste pro Bucket, sodass ein Bucket viele Einträge enthalten kann und die Tabelle nie wirklich voll wird. Offene Adressierung hält alles im Array selbst und sondiert bei einer Kollision den nächsten freien Slot, was cache-freundlich ist, sich aber stark verschlechtert, wenn der Füllfaktor sich 1 nähert, und eine sorgfältige Behandlung des Löschens erfordert. Verkettung toleriert höhere Füllfaktoren; offene Adressierung nutzt den Speicher kompakter.
Warum kann ich mich nicht darauf verlassen, dass eine Hashtabelle meine Schlüssel in Einfüge- oder Sortierreihenfolge hält?
Eine Hashfunktion streut die Schlüssel absichtlich über die Buckets, um Clusterbildung zu vermeiden, sodass die Iterationsreihenfolge das Bucket-Layout widerspiegelt, nicht die Einfüge- oder Sortierreihenfolge. Wenn du eine Ordnung brauchst, verwende eine geordnete Map/einen Baum oder eine Struktur wie eine einfügereihenfolge-erhaltende Map (z. B. bewahrt Pythons dict die Einfügereihenfolge, aber das ist eine Sprachgarantie, keine inhärente Eigenschaft der Hashtabelle). Nimm nie an, dass die Iterationsreihenfolge der Reihenfolge deiner Einfügungen entspricht, es sei denn, die Sprache verspricht es ausdrücklich.
Coddy programming languages illustration

Meistere Algorithmen mit Coddy

LOS GEHT'S