Comment puis-je implémenter le tri de cartes STL par valeur?
Par exemple, j'ai une carte m
:
map<int, int> m;
m[1] = 10;
m[2] = 5;
m[4] = 6;
m[6] = 1;
Je voudrais trier cette carte en fonction de la valeur de m
. Donc, si j'imprime la carte, j'aimerais obtenir le résultat comme suit:
m[6] = 1
m[2] = 5
m[4] = 6
m[1] = 10
Comment puis-je trier la carte de cette façon? Existe-t-il un moyen de gérer la clé et la valeur avec des valeurs triées?
Vous pouvez créer une deuxième carte, avec les valeurs de la première carte comme clés et les clés de la première carte comme valeurs.
Cela ne fonctionne que si toutes les valeurs sont distinctes. Si vous ne pouvez pas supposer cela, vous devez créer une carte à plusieurs au lieu d'une carte.
Videz toutes les paires clé-valeur dans un set<pair<K, V> >
first, où set
est construit avec un foncteur inférieur à qui compare uniquement la deuxième valeur de la paire. De cette façon, votre code fonctionne toujours même si vos valeurs ne sont pas toutes distinctes.
Ou vider les paires clé-valeur dans un vector<pair<K, V> >
, puis triez ce vecteur avec le même foncteur inférieur à.
Je me demande comment puis-je implémenter le tri de la carte STL par valeur.
Vous ne pouvez pas, par définition. Une carte est une structure de données qui trie son élément par clé.
Vous devriez utiliser Boost.Bimap pour ce genre de chose.
Basé sur l'idée de @ swegi, j'ai implémenté une solution dans c ++ 11 en utilisant un multimap
:
map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
multimap<int, int> mm;
for(auto const &kv : m)
mm.insert(make_pair(kv.second, kv.first)); // Flip the pairs.
for(auto const &kv : mm)
cout << "m[" << kv.second << "] = " << kv.first << endl; // Flip the pairs again.
J'ai également implémenté une solution C++ 11 basée sur l'idée de @Chris en utilisant un vecteur de paires. Pour un tri correct, je fournis un expression lambda comme foncteur de comparaison:
map<int, int> m = {{1, 10}, {2, 5}, {4, 6}, {6, 1}};
using mypair = pair<int, int>;
vector<mypair> v(begin(m), end(m));
sort(begin(v), end(v), [](const mypair& a, const mypair& b) { return a.second < b.second; });
for(auto const &p : v)
cout << "m[" << p.first << "] = " << p.second << endl;
La première solution est plus compacte, mais les deux solutions devraient avoir à peu près les mêmes performances. L'insertion dans un multimap
est de O (log n) , mais cela doit être fait pour n entrées, résultant en O (n log n) . Le tri du vecteur dans la deuxième solution entraîne également O (n log n) .
J'ai également essayé l'idée de @Chris d'utiliser un ensemble de paires. Cependant, cela ne fonctionnera pas si les valeurs ne sont pas toutes distinctes. L'utilisation d'un foncteur qui compare uniquement le deuxième élément de la paire n'aide pas. Si vous insérez d'abord make_pair(1, 1)
dans l'ensemble, puis essayez d'insérer make_pair(2, 1)
, la deuxième paire ne sera pas insérée, car les deux paires sont considérées comme identiques par cet ensemble. Vous pouvez voir cet effet ici sur Ideone .
Je viens de faire une question similaire dans mon livre c ++. La réponse que j'ai trouvée n'est peut-être pas très efficace:
int main()
{
string s;
map<string, int> counters;
while(cin >> s)
++counters[s];
//Get the largest and smallest values from map
int beginPos = smallest_map_value(counters);
int endPos = largest_map_value(counters);
//Increment through smallest value to largest values found
for(int i = beginPos; i <= endPos; ++i)
{
//For each increment, go through the map...
for(map<string, int>::const_iterator it = counters.begin(); it != counters.end(); ++it)
{
//...and print out any pairs with matching values
if(it->second == i)
{
cout << it->first << "\t" << it->second << endl;
}
}
}
return 0;
}
//Find the smallest value for a map<string, int>
int smallest_map_value(const map<string, int>& m)
{
map<string, int>::const_iterator it = m.begin();
int lowest = it->second;
for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
{
if(it->second < lowest)
lowest = it->second;
}
return lowest;
}
//Find the largest value for a map<string, int>
int largest_map_value(const map<string, int>& m)
{
map<string, int>::const_iterator it = m.begin();
int highest = it->second;
for(map<string, int>::const_iterator it = m.begin(); it != m.end(); ++it)
{
if(it->second > highest)
highest = it->second;
}
return highest;
}
J'ai trouvé cela dans thispointer . L'exemple trie un std :: map <std :: string, int> par toutes les valeurs int.
#include <map>
#include <set>
#include <algorithm>
#include <functional>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 13 } };
// Declaring the type of Predicate that accepts 2 pairs and return a bool
typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
// Defining a lambda function to compare two pairs. It will compare two pairs using second field
Comparator compFunctor =
[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
{
return elem1.second < elem2.second;
};
// Declaring a set that will store the pairs using above comparision logic
std::set<std::pair<std::string, int>, Comparator> setOfWords(
mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
// Iterate over a set using range base for loop
// It will display the items in sorted order of values
for (std::pair<std::string, int> element : setOfWords)
std::cout << element.first << " :: " << element.second << std::endl;
return 0;
}
Créez une autre carte, fournissez une fonction less () basée sur la valeur not key, ET la fonction doit retourner true si la valeur1 <= value2 (pas strictement <). Dans ce cas, les éléments avec des valeurs non distinctes peuvent également être triés.