web-dev-qa-db-fra.com

Comment C ++ STL unordered_map résout-il les collisions?

Comment C++ STL unordered_map résout-il les collisions?

En regardant http://www.cplusplus.com/reference/unordered_map/unordered_map/ , il est dit "Clés uniques Deux éléments du conteneur ne peuvent pas avoir de clés équivalentes."

Cela devrait signifier que le conteneur résout effectivement les collisions. Cependant, cette page ne me dit pas comment elle le fait. Je connais certaines façons de résoudre les collisions, comme l'utilisation de listes chaînées et/ou de sondages. Ce que je veux savoir, c'est comment le c ++ STL unordered_map le résout.

51
whiteSkar

La norme en définit un peu plus que la plupart des gens ne semblent le réaliser.

Plus précisément, la norme exige (§23.2.5/9):

Les éléments d'un conteneur associatif non ordonné sont organisés en compartiments. Les clés avec le même code de hachage apparaissent dans le même compartiment.

L'interface comprend un bucket_count Qui s'exécute en temps constant. (tableau 103). Il comprend également un bucket_size Qui doit s'exécuter en temps linéaire sur la taille du compartiment.

Cela décrit essentiellement une implémentation qui utilise le chaînage de collisions. Lorsque vous utilisez le chaînage par collision, répondre à toutes les exigences se situe entre facile et trivial. bucket_count() est le nombre d'éléments dans votre tableau. bucket_size() est le nombre d'éléments dans la chaîne de collision. Les obtenir respectivement en temps constant et linéaire est simple et direct.

En revanche, si vous utilisez quelque chose comme le palpage linéaire ou le double hachage, ces exigences deviennent presque impossibles à satisfaire. Plus précisément, tous les éléments hachés à une valeur spécifique doivent atterrir dans le même compartiment, et vous devez pouvoir compter ces compartiments en temps constant.

Mais, si vous utilisez quelque chose comme le palpage linéaire ou le double hachage, trouver tous les éléments hachés à la même valeur signifie que vous devez hacher la valeur, puis parcourez la "chaîne" d'éléments non vides dans votre tableau pour trouver combien de ceux hachés à la même valeur. Ce n'est pas linéaire sur le nombre d'éléments qui ont haché à la même valeur - c'est linéaire sur le nombre d'éléments qui ont haché à la même chose o une valeur en collision.

Avec suffisamment de travail supplémentaire et une bonne quantité d'étirement de la signification de certaines des exigences presque jusqu'au point de rupture, il pourrait être à peine possible de créer une table de hachage en utilisant autre chose que le chaînage de collision, et toujours au moins en quelque sorte répondre aux exigences - -mais je ne suis pas vraiment certain que c'est possible, et cela impliquerait certainement beaucoup de travail supplémentaire.

Résumé: toutes les implémentations pratiques de std::unordered_set (Ou unordered_map) Utilisent sans aucun doute le chaînage de collisions. Bien qu'il soit (à peine) possible de répondre aux exigences en utilisant un sondage linéaire ou un double hachage, une telle implémentation semble perdre beaucoup et ne gagner presque rien en retour.

63
Jerry Coffin

J'ai trouvé cette réponse en cherchant comment détecter quand mes types entrent en collision, donc je posterai ceci au cas où c'est l'intention de la question .:

Je crois qu'il y a une idée fausse à propos des "clés uniques Deux éléments du conteneur ne peuvent pas avoir de clés équivalentes".

regardez le code ci-dessous

//pseudocode
std::unordered_map<int, char> hashmap;
hashmap[5] = 'a';
hashmap[5] = 'b'; //replace 'a' with 'b', there is no collision being handled.

Je pense que la réponse de Jerry fait référence au système interne qu'il utilise pour réduire les clés aux indices de tableau appropriés.

Si vous souhaitez que les collisions soient gérées pour vos types (avec des compartiments), vous avez besoin de std::unordered_multimap et devra répéter

J'espère que ce code peut être lu sans le contexte avec lequel je l'ai généré. il vérifie essentiellement si un élément du compartiment associé au hachage est l'élément que je recherche.

//sp is std::shared_ptr
//memo is std::unordered_multimap< int, sp<AStarNode> >

//there's probably multiple issues with this code in terms of good design (like using int keys rather than unsigned)

bool AStar_Incremental::hasNodeBeenVisited(sp<AStarNode> node)
{
    using UMIter = std::unordered_multimap<int, sp<AStarNode> >::iterator;

    bool bAlreadyVisited = false;

    //get all values for key in O(1*)
    int hash = WorldGrid::hashGrid(node->location);
    std::pair<UMIter, UMIter> start_end = memo.equal_range(hash); //bucket range
    UMIter start = start_end.first;
    UMIter end = start_end.second;

    //hopefully this is implemented to be O(m) where m is the bucket size.
    for(UMIter bucketIter = start; bucketIter != end; ++bucketIter)
    {
        sp<AStarNode> previousNode = bucketIter->second;
        sf::Vector2i& previousVisit = previousNode->location;
        if (previousVisit == node->location)
        {
            bAlreadyVisited = true;
            break;
        }
    }

    return bAlreadyVisited;
}
0
Enigma22134