есть ли итератор для уникальных ключей в std :: multimap? - PullRequest
37 голосов
/ 21 февраля 2012

Есть ли простой или стандартный способ иметь многокарточный итератор, который перебирает уникальные ключи в мультикарте?

т.е. для набора, который выглядит как: {1, "a"}, {1, "lemon"}, {2, "peacock"}, {3, "angel"} итератор, который будет начинаться с {1, "a"}, затем приращение будет указывать на {2, "peacock"}, а затем снова приращение будет указывать на {3, "angel"}?

Ответы [ 6 ]

44 голосов
/ 21 февраля 2012

Вы можете использовать upper_bound для увеличения позиции итератора вместо ++:

#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;
}

Это приводит к :

1 a
2 peacock
3 angel
26 голосов
/ 13 июня 2014

Использование upper_bound приведет к простоте чтения цикла, но каждый вызов будет выполнять поиск в двоичном дереве, что приведет к O (n log n) вместо O (n) обход.Если разница в эффективности имеет значение, вы можете структурировать свой обход следующим образом:

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);
}
3 голосов
/ 24 мая 2017

Как отмечено в выбранном ответе, повторное использование multimap::upper_bound приводит к обходу карты O (n log n).Использование внешней функции upper_bound дает вам O (n).Однако вам нужно убедиться, что вы сравниваете только ключ карты:

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...

}

Базовый подход, по сути, такой же, как и у решения user3701170 - т.е. линейный поиск - но мы добавили шаг приращения в forсобственно оператор, а не тело цикла.Помимо размещения приращения там, где оно «обычно» живет, это также означает, что все операторы continue в цикле будут вести себя как ожидалось.

2 голосов

Пример выполнения

Это небольшое улучшение по сравнению с https://stackoverflow.com/a/24212648/895245 с работающим модульным тестом:

#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}));
}
1 голос
/ 26 мая 2015

если вам нужно быстро передать все уникальные ключи, тогда вы можете использовать вместо него std :: map;

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 );
}

Полная программа тестирования выглядит следующим образом:

#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;
}
0 голосов
/ 30 мая 2018

Попробуйте equal_range:

http://en.cppreference.com/w/cpp/container/multimap/equal_range

Это должно быть точное совпадение.

...