web-dev-qa-db-fra.com

std :: map, comment trier par valeur, puis par clé

Je dois trier une carte par valeur, puis par clé. J'ai une carte avec un contenu comme celui-ci ...

  1  realistically
  8         really
  4         reason
  3     reasonable
  1     reasonably
  1     reassemble
  1    reassembled
  2      recognize
 92         record
 48        records
  7           recs

Il me faut mettre les valeurs dans l’ordre, mais le facteur clé est que les clés doivent être dans l’ordre alphabétique après que les valeurs sont dans l’ordre. Quelle est la meilleure manière de s'occuper de ça?

35
Trevor Hutto

std::map va trier ses éléments par keys. Peu importe la values lors du tri.

Vous pouvez utiliser std::vector<std::pair<K,V>> puis le trier en utilisant std::sort suivi de std::stable_sort:

std::vector<std::pair<K,V>> items;

//fill items

//sort by value using std::sort
std::sort(items.begin(), items.end(), value_comparer);

//sort by key using std::stable_sort
std::stable_sort(items.begin(), items.end(), key_comparer);

Le premier tri doit utiliser std::sort puisqu'il s'agit de nlog(n), puis utiliser std::stable_sort qui est n(log(n))^2 dans le pire des cas.

Notez que bien que std::sort soit choisi pour des raisons de performances, il est nécessaire que std::stable_sort soit utilisé pour un ordre correct, car vous souhaitez que la valeur par ordre soit préservée.


@gsf a noté dans le commentaire, vous pouvez utiliser only std::sort si vous choisissez un comparateur qui compare values en premier, et s’ils sont égaux, triez la keys.

auto cmp = [](std::pair<K,V> const & a, std::pair<K,V> const & b) 
{ 
     return a.second != b.second?  a.second < b.second : a.first < b.first;
};
std::sort(items.begin(), items.end(), cmp);

Cela devrait être efficace. 

Mais attendez, il existe une meilleure approche: store std::pair<V,K> au lieu de std::pair<K,V> et vous n’avez donc pas besoin de comparateur - le comparateur standard pour std::pair serait suffisant, car il compare first (qui est V) en premier puis second qui est K :

std::vector<std::pair<V,K>> items;
//...
std::sort(items.begin(), items.end());

Cela devrait bien fonctionner.

59
Nawaz

Vous pouvez utiliser std::set au lieu de std::map.

Vous pouvez stocker la clé et la valeur dans std::pair et le type de conteneur ressemblera à ceci:

std::set< std::pair<int, std::string> > items;

std::set triera ses valeurs en fonction des clés d'origine et des valeurs stockées dans std::map.

14
ks1322

std::map trie déjà les valeurs à l'aide d'un prédicat que vous définissez ou std::less si vous n'en fournissez pas. std::set stockera également les éléments dans l'ordre du comparateur défini. Cependant, ni set ni map ne vous permettent d'avoir plusieurs clés. Je suggérerais de définir un std::map<int,std::set<string> si vous voulez accomplir cela en utilisant uniquement votre structure de données. Vous devez également vous rendre compte que std::less pour string va trier lexicographiquement et non par ordre alphabétique.

1
rerun

EDIT: Les deux autres réponses font un bon point. Je suppose que vous voulez les commander dans une autre structure ou pour les imprimer.

"Meilleur" peut signifier différentes choses. Voulez-vous dire "plus facile", "le plus rapide", "le plus efficace", "le moins de code", "le plus lisible?"

L'approche la plus évidente consiste à effectuer une boucle double. Au premier passage, ordonnez les valeurs:

if(current_value > examined_value)
{
    current_value = examined_value
    (and then swap them, however you like)
}

Ensuite, lors de la deuxième passe, alphabétisez les mots, mais seulement si leurs valeurs correspondent.

if(current_value == examined_value)
{
    (alphabetize the two)
}

À proprement parler, il s’agit d’une "sorte de bulle" lente, car chaque fois que vous effectuez un échange, vous devez tout recommencer. Une "passe" est terminée lorsque vous parcourez toute la liste sans effectuer aucun échange.

Il existe d'autres algorithmes de tri, mais le principe serait le même: ordre par valeur, puis par ordre alphabétique.

0
Matt