web-dev-qa-db-fra.com

Accès à la valeur de la carte par index

Si j'ai une structure comme

std::map<string, int> myMap;
myMap["banana"] = 1;
myMap["Apple"] = 1;
myMap["orange"] = 1;

Comment accéder à myMap [0]?

Je sais que la carte est triée en interne et je suis d'accord avec cela, je veux obtenir une valeur dans la carte par index. J'ai essayé myMap [0] mais j'obtiens l'erreur:

Error   1   error C2679: binary '[' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)   

Je réalise que je pourrais faire quelque chose comme ça:

string getKeyAtIndex (int index){
    map<string, int>::const_iterator end = myMap.end(); 

    int counter = 0;
    for (map<string, int>::const_iterator it = myMap.begin(); it != end; ++it) {
        counter++;

        if (counter == index)
            return it->first;
    }
}

Mais cela est sûrement extrêmement inefficace? Y a-t-il une meilleure façon?

18
Sam

Votre map n'est pas censé être accessible de cette façon, il est indexé par des clés et non par des positions. Un itérateur map est bidirectionnel, tout comme un list, donc la fonction que vous utilisez n'est pas plus inefficace que d'accéder à un list par position. Votre fonction peut être écrite avec l'aide de std::advance( iter, index ) à partir de begin(). Si vous souhaitez un accès aléatoire par position, utilisez un vector ou un deque.

24
K-ballo

Il peut y avoir une méthode spécifique à l'implémentation (non portable) pour atteindre votre objectif, mais pas une méthode portable.

En général, le std::map est implémenté comme un type d'arbre binaire, généralement trié par clé. La définition du premier élément diffère selon l'ordre. De plus, dans votre définition, l'élément [0] est-il le nœud en haut de l'arbre ou le nœud feuille le plus à gauche?

De nombreux arbres binaires sont implémentés sous forme de listes chaînées. La plupart des listes liées ne sont pas accessibles directement comme un tableau, car pour trouver l'élément 5, vous devez suivre les liens. C'est par définition.

Vous pouvez résoudre votre problème en utilisant à la fois un std::vector et un std::map:

  1. Allouez l'objet à partir de la mémoire dynamique.
  2. Stockez le pointeur et la clé dans le std::map.
  3. Stockez le pointeur dans le std::vector à l'endroit où vous le souhaitez.

Le std::map permettra une méthode efficace pour accéder à l'objet par clé.
Le std::vector permettra une méthode efficace pour accéder à l'objet par index. Le stockage des pointeurs ne permet qu'une seule instance de l'objet au lieu d'avoir à conserver plusieurs copies.

5
Thomas Matthews

Réponse précédente (voir commentaire): Que diriez-vous simplement de myMap.begin();

Vous pouvez implémenter une carte à accès aléatoire en utilisant une sauvegarde de vecteur, qui est essentiellement un vecteur de paires. À ce stade, vous perdez bien sûr tous les avantages de la carte de bibliothèque standard.

4
Michael Price

Eh bien, en fait, vous ne pouvez pas. La façon dont vous avez trouvé est très inefficace, elle a une complexité de calcul de O(n) (n opérations dans le pire des cas, où n est le nombre d'éléments dans une carte).

L'accès à un élément dans un vecteur ou dans un tableau a une complexité O(1) par comparaison (complexité de calcul constante, une seule opération).

Considérez que la carte est implémentée en interne comme un arbre noir rouge (ou arbre avl, cela dépend de l'implémentation) et chaque opération d'insertion, de suppression et de recherche est le pire des cas O (log n) (il nécessite un logarithme dans les opérations de base 2 pour trouver un élément dans l'arbre), c'est assez bien.

Vous pouvez utiliser une classe personnalisée qui contient à la fois un vecteur et une carte. L'insertion à la fin de la classe sera moyennée O (1), la recherche par nom sera O (log n), la recherche par index sera O(1) mais dans ce cas, la suppression l'opération sera O (n).

3
Salvatore Previti

vous pouvez utiliser une autre carte comme des conteneurs.
garder une taille Les champs peuvent faciliter l'accès aléatoire à l'arbre de recherche binaire.
voici mon implémentation ...
style std, itérateur d'accès aléatoire ...
arbre équilibré de taille ...
https://github.com/mm304321141/zzz_lib/blob/master/sbtree.h
et arbre B + ...
https://github.com/mm304321141/zzz_lib/blob/master/bpptree.h

1
奏之章

std::map est un conteneur ordonné, mais ses itérateurs ne prennent pas en charge l'accès aléatoire, mais plutôt l'accès bidirectionnel. Par conséquent, vous ne pouvez accéder au nième élément qu'en parcourant tous ses éléments antérieurs. Une alternative plus courte à votre exemple utilise la bibliothèque d'itérateurs standard:

 std::pair<const std::string, int> &nth_element = *std::next(myMap.begin(), N);

Cela a une complexité linéaire, ce qui n'est pas idéal si vous prévoyez d'accéder fréquemment de cette façon dans les grandes cartes.

Une alternative consiste à utiliser un conteneur ordonné qui prend en charge l'accès aléatoire. Par exemple, boost::container::flat_map fournit une fonction membre nth qui vous permet exactement ce que vous recherchez.

0
Jorge Bellon