Menu
Coddy logo textTech

Table de Hachage

Dernière mise à jour

Une table de hachage stocke les éléments dans un tableau de seaux. Pour placer ou trouver une clé, elle passe la clé dans une fonction de hachage et prend le résultat modulo le nombre de seaux - cela donne l'indice du seau en O(1). Appuyez sur lecture ci-dessus pour voir chaque clé être hachée vers un seau et stockée, puis une recherche sauter directement vers le bon seau.

Lorsque deux clés tombent dans le même seau - une collision - cette visualisation utilise le chaînage séparé : chaque seau contient une petite liste, et les clés en collision y sont ajoutées. Tant que la table n'est pas trop pleine (un faible facteur de charge) et que le hachage répartit bien les clés, les chaînes restent courtes et l'insertion, la recherche et la suppression sont en O(1) en moyenne.

Complexité temporelle

OpérationMoyennePire cas
InsertionO(1)O(n) (toutes les clés entrent en collision)
RechercheO(1)O(n)
SuppressionO(1)O(n)
EspaceO(n)O(n)

Concepts clés

TermeSignification
Fonction de hachageAssocie une clé à un indice de seau
CollisionDeux clés tombent dans le même seau
Chaînage séparéChaque seau contient une liste des entrées en collision
Adressage ouvertAlternative : sonder le prochain emplacement libre
Facteur de chargeentrées / seaux - déclenche le redimensionnement

Exemple résolu

Insertion des clés 20, 34, 9, 13 dans une table de 7 seaux, avec hash(k) = k % 7 :

ÉtapeStructureAction
Insérer 20seau 6 : [20]20 % 7 = 6, seau 6 vide, stocker 20
Insérer 34seau 6 : [20, 34]34 % 7 = 6, collision avec 20, ajouter 34 à la chaîne
Insérer 9seau 2 : [9]9 % 7 = 2, seau 2 vide, stocker 9
Insérer 13seau 6 : [20, 34, 13]13 % 7 = 6, collision, ajouter 13 à la chaîne du seau 6
Rechercher 34seau 6 : [20, 34, 13]34 % 7 = 6, parcourir la chaîne : 20 non, 34 correspond - trouvé

Quand utiliser une table de hachage

À utiliser quandÀ éviter quand
Vous avez besoin d'insertion, recherche et suppression par clé en O(1) moyenVous devez garder les clés triées - utilisez un ABR équilibré
Les clés n'ont pas d'ordre utile et vous ne testez que l'appartenance par correspondance exacteVous avez besoin de requêtes par intervalle ou de recherches de la clé la plus proche
Vous pouvez vous permettre un peu de mémoire supplémentaire pour les seaux et un faible facteur de chargeLa mémoire est extrêmement limitée et chaque octet compte
Une bonne fonction de hachage existe pour votre type de cléLa latence du pire cas doit être bornée - les collisions la rendent O(n)

Code de Hash Table

Une implémentation propre et exécutable de Hash Table en Python, JavaScript, Java, C++, C. Choisissez un langage, copiez le code ou ouvrez-le préchargé dans le Playground Coddy.

Code de Hash Table en 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])
Exécutez ce code dans le Playground Python

FAQ sur la table de hachage

Qu'est-ce qu'une collision de hachage et comment est-elle résolue ?
Une collision se produit lorsque deux clés différentes tombent dans le même seau. Les deux solutions courantes sont le chaînage séparé (chaque seau contient une liste et les clés en collision y sont ajoutées - montré ici) et l'adressage ouvert (sonder le prochain emplacement vide du tableau). Les deux gardent les recherches correctes ; ils diffèrent par l'agencement de la mémoire et les performances sous forte charge.
Quelle est la complexité temporelle d'une table de hachage ?
L'insertion, la recherche et la suppression sont en O(1) en moyenne lorsque la fonction de hachage répartit les clés uniformément et que le facteur de charge reste faible. Dans le pire cas - toutes les clés entrant en collision dans un seul seau - elles se dégradent en O(n), d'où l'importance d'une bonne fonction de hachage et du redimensionnement.
Qu'est-ce qu'un facteur de charge ?
Le facteur de charge est le nombre d'entrées stockées divisé par le nombre de seaux. À mesure qu'il augmente, les chaînes s'allongent et les opérations ralentissent, c'est pourquoi la plupart des tables de hachage se redimensionnent (re-hachage vers un tableau plus grand) une fois qu'il dépasse un seuil comme 0.75.
Quand dois-je utiliser une table de hachage plutôt qu'un arbre binaire de recherche ?
Utilisez une table de hachage lorsque vous n'avez besoin que de recherches par correspondance exacte et que vous voulez une vitesse O(1) moyenne sans exigence d'ordre. Utilisez un arbre binaire de recherche équilibré lorsque vous avez besoin de clés triées, de requêtes par intervalle ou de recherches de prédécesseur/successeur, ce qu'une table de hachage ne peut pas faire efficacement. Un ABR offre des opérations O(log n) garanties, tandis qu'une table de hachage échange cette garantie contre des performances moyennes plus rapides.
Quelle est la différence entre le chaînage séparé et l'adressage ouvert ?
Le chaînage séparé stocke les clés en collision dans une liste par seau, de sorte qu'un seau peut contenir de nombreuses entrées et que la table ne se remplit jamais vraiment. L'adressage ouvert garde tout dans le tableau lui-même et sonde le prochain emplacement libre lors d'une collision, ce qui est favorable au cache mais se dégrade fortement lorsque le facteur de charge approche 1 et nécessite une gestion soignée de la suppression. Le chaînage tolère des facteurs de charge plus élevés ; l'adressage ouvert utilise la mémoire de manière plus compacte.
Pourquoi ne puis-je pas compter sur une table de hachage pour garder mes clés dans l'ordre d'insertion ou de tri ?
Une fonction de hachage disperse délibérément les clés entre les seaux pour éviter les regroupements, de sorte que l'ordre d'itération reflète l'agencement des seaux, et non l'ordre d'insertion ou de tri. Si vous avez besoin d'un ordre, utilisez une carte/un arbre ordonné ou une structure comme une carte à ordre d'insertion (par exemple, le dict de Python préserve l'ordre d'insertion, mais c'est une garantie du langage, pas une propriété inhérente de la table de hachage). Ne supposez jamais que l'ordre d'itération correspond à la façon dont vous avez inséré les clés, sauf si le langage le promet explicitement.
Coddy programming languages illustration

Maîtrisez les algorithmes avec Coddy

COMMENCER