Existe-t-il un moyen simple ou standard d’avoir un itérateur multi-cartes qui parcourt des clés uniques dans une multi-cartes?
c'est-à-dire que pour un ensemble ressemblant à: {1, "a"}, {1, "lemon"}, {2, "peacock"}, {3, "angel"}
, un itérateur commençant par {1, "a"}
puis incrémenté pointant vers {2, "peacock"}
, puis incrémenté à nouveau pointant vers {3, "angel"}
?
Vous pouvez utiliser upper_bound
pour incrémenter la position de l'itérateur au lieu de ++
:
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
multimap<int,string> mm;
mm.insert(make_pair(1, "a"));
mm.insert(make_pair(1, "lemon"));
mm.insert(make_pair(2, "peacock"));
mm.insert(make_pair(3, "angel"));
for( auto it = mm.begin(), end = mm.end();
it != end;
it = mm.upper_bound(it->first)
)
cout << it->first << ' ' << it->second << endl;
return 0;
}
Ceci résulte en :
1 a
2 peacock
3 angel
Utiliser upper_bound
résulterait en une boucle facile à lire, mais chaque appel effectuera une recherche dans une arborescence binaire, ce qui donnera un O (n log n) au lieu de O(n) traversal. Si la différence d'efficacité compte, vous pouvez structurer votre parcours de la manière suivante:
typedef std::multimap<std::string, int> MapType;
MapType container;
for (MapType::iterator it = container.begin(); it != container.end(); ) {
std::string key = it->first;
doSomething(key);
// Advance to next non-duplicate entry.
do {
++it;
} while (it != container.end() && key == it->first);
}
Comme indiqué dans la réponse sélectionnée, l'utilisation répétée de multimap::upper_bound
conduit à une traversée de la carte par O (n log n). L'utilisation de la fonction externe upper_bound
vous donne O (n). Cependant, vous devez vous assurer de ne comparer que la clé de la carte:
std::multimap<int, std::string> myMap = ... ;
const auto compareFirst = [](const std::pair<const int, std::string>& lhs, const std::pair<const int, std::string>& rhs) {
return lhs.first < rhs.first;
};
for(auto it = myMap.begin(); it != myMap.end(); it = std::upper_bound(it, myMap.end(), *it, compareFirst)) {
// Do stuff...
}
L’approche sous-jacente est essentiellement identique à la solution de user3701170 - c’est-à-dire une recherche linéaire - mais nous avons mis l’incrément d’incrément dans l’instruction for
proprement dite, et non le corps de la boucle. En plus de mettre l’incrément là où il "vit" habituellement, cela signifie également que toute instruction continue
de la boucle se comportera comme prévu.
Exemple exécutable
Ceci est une légère amélioration par rapport à https://stackoverflow.com/a/24212648/895245 avec un test unitaire exécutable:
#include <cassert>
#include <map>
#include <vector>
int main() {
// For testing.
auto m = std::multimap<int, int>{
{1, 2},
{1, 3},
{2, 4}
};
std::vector<int> out;
// The algorithm.
auto it = m.begin();
auto end = m.end();
while (it != end) {
auto key = it->first;
// Do what you want to do with the keys.
out.Push_back(key);
do {
if (++it == end)
break;
} while (it->first == key);
}
// Assert it worked.
assert(out == std::vector<int>({1, 2}));
}
si vous devez passer rapidement toutes les clés uniques, vous pouvez utiliser std :: map à la place;
typedef std::map< KeyType, std::list< ValueType > > MapKeyToMultiValue;
L'insertion serait plus difficile. Cependant, vous pouvez parcourir toutes les clés sans avoir à vous soucier des entrées en double. L'insertion ressemblerait à ceci:
void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< MapKeyToMultiValue::iterator, bool > ret =
map.insert( MapKeyToMultiValue::value_type( key, empty ) );
it = ret.first;
}
it->second.Push_back( value );
}
ou vous pouvez faire cela très basé sur un modèle:
template<typename KeyType, typename ValueType,
typename MapType = std::map< KeyType, std::list< ValueType > > >
void insert_multi( MapType &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< typename MapType::iterator, bool > ret =
map.insert( typename MapType::value_type( key, empty ) );
it = ret.first;
}
it->second.Push_back( value );
}
Le programme de test complet se présente comme suit:
#include <map>
#include <list>
#include <string>
#include <stdio.h>
typedef std::string KeyType;
typedef int ValueType;
typedef std::map< KeyType, std::list< ValueType > > MapKeyToMultiValue;
void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< MapKeyToMultiValue::iterator, bool > ret =
map.insert( MapKeyToMultiValue::value_type( key, empty ) );
it = ret.first;
}
it->second.Push_back( value );
}
template<typename KeyType, typename ValueType,
typename MapType = std::map< KeyType, std::list< ValueType > > >
void insert_multi( MapType &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< typename MapType::iterator, bool > ret =
map.insert( typename MapType::value_type( key, empty ) );
it = ret.first;
}
it->second.Push_back( value );
}
int main()
{
MapKeyToMultiValue map;
insert_m(map, std::string("aaa"), 1 );
insert_m(map, std::string("aaa"), 2 );
insert_m(map, std::string("bb"), 3 );
insert_m(map, std::string("cc"), 4 );
insert_multi(map, std::string("ddd"), 1 );
insert_multi(map, std::string("ddd"), 2 );
insert_multi(map, std::string("ee"), 3 );
insert_multi(map, std::string("ff"), 4 );
for(auto i = map.begin(); i != map.end(); ++i)
{
printf("%s\n", i->first.c_str() );
}
return 0;
}
Essayez equal_range
:
http://fr.cppreference.com/w/cpp/container/multimap/equal_range
Cela doit être une correspondance exacte.