Как я могу найти минимальное значение на карте?

У меня есть map и я хочу найти минимальное значение (с правой стороны) на карте. Вот как я это сделал:

bool compare(std::pair<std::string ,int> i, pair<std::string, int> j) {
  return i.second < j.second;
}
////////////////////////////////////////////////////
std::map<std::string, int> mymap;

mymap["key1"] = 50;
mymap["key2"] = 20;
mymap["key3"] = 100;

std::pair<char, int> min = *min_element(mymap.begin(), mymap.end(), compare); 
std::cout << "min " << min.second<< " " << std::endl;

Код выше работает нормально, и я могу получить минимальное значение. Однако, когда я помещаю этот код в свой класс следующим образом, он, похоже, не работает:

int MyClass::getMin(std::map<std::string, int> mymap) {
  std::pair<std::string, int> min = *min_element(mymap.begin(), mymap.end(), 
                                                 (*this).compare);
                                                 // Error probably due to "this".
  return min.second; 
}

bool MyClass::compare(
    std::pair<std::string, int> i, std::pair<std::string, int> j) { 
  return i.second < j.second; 
}

Как я могу заставить код работать с моим классом? Кроме того, есть ли лучшее решение, которое не требует написания дополнительной функции compare?

Ответы

Ответ 1

У вас есть несколько вариантов. "Лучший" способ сделать это - с функтором, это гарантированно будет самым быстрым:

typedef std::pair<std::string, int> MyPairType;
struct CompareSecond
{
    bool operator()(const MyPairType& left, const MyPairType& right) const
    {
        return left.second < right.second;
    }
};



int MyClass::getMin(std::map<std::string, int> mymap) 
{
  std::pair<std::string, int> min 
      = *min_element(mymap.begin(), mymap.end(), CompareSecond());
  return min.second; 
}

(Вы также можете вставить класс CompareSecond внутри MyClass.

С кодом, который у вас есть сейчас, вы можете легко изменить его для работы. Просто выполните функцию static и используйте правильный синтаксис:

static bool 
MyClass::compare(std::pair<std::string, int> i, std::pair<std::string, int> j) 
{ 
  return i.second < j.second; 
}

int MyClass::getMin(std::map<std::string, int> mymap) 
{
  std::pair<std::string, int> min = *min_element(mymap.begin(), mymap.end(), 
                                                 &MyClass::compare);
  return min.second; 
}

Ответ 2

В С++ 11 вы можете сделать это:

auto it = min_element(pairs.begin(), pairs.end(),
                      [](decltype(pairs)::value_type& l, decltype(pairs)::value_type& r) -> bool { return l.second < r.second; });

Или поместите его в хорошую функцию, подобную этой (обратите внимание, я не гуру шаблонов; это, вероятно, неправильно во многих отношениях):

template<typename T>
typename T::iterator min_map_element(T& m)
{
    return min_element(m.begin(), m.end(), [](typename T::value_type& l, typename T::value_type& r) -> bool { return l.second < r.second; });
}

С С++ 14 это еще больше упрощает:

min_element(pairs.begin(), pairs.end(),
            [](const auto& l, const auto& r) { return l.second < r.second; });

Ответ 3

Проблема в том, что это:

bool MyClass::compare

Требуется экземпляр класса для вызова. То есть вы не можете просто вызвать MyClass::compare, но вам нужно someInstance.compare. Тем не менее, min_element нуждается в первом.

Простое решение состоит в том, чтобы сделать его static:

static bool MyClass::compare

// ...

min_element(mymap.begin(), mymap.end(), &MyClass::compare);

Это больше не требует вызова экземпляра, и ваш код будет в порядке. Вы можете сделать его более общим с функтором, хотя:

struct compare2nd
{
    template <typename T>
    bool operator()(const T& pLhs, const T& pRhs)
    {
        return pLhs.second < pRhs.second;
    }
};

min_element(mymap.begin(), mymap.end(), compare2nd());

Все это делает захват второй из каждой пары и захватывает их, работает с любой парой. Это может быть сделано для общего, но это слишком много.

Если вам нужно посмотреть по значению достаточно, я рекомендую вам использовать Boost Bimap. Это двунаправленная карта, поэтому и ключ, и значение могут использоваться для поиска. Вы просто получили бы карту карты значений.

Наконец, вы всегда можете просто отслеживать минимальный элемент, входящий в вашу карту. Каждый раз, когда вы вставляете новое значение, проверяйте, меньше ли оно вашего текущего значения (и это должно быть, вероятно, указателем на пару карт, запустите его как null), а если оно ниже, укажите на новый минимум. Запрос минимального значения становится таким же простым, как разыменование указателя.

Ответ 4

У меня действительно есть другой вопрос: если вам нужно регулярно получать минимальные значения правой стороны, вы уверены, что map - лучшая структура?

Я бы предложил использовать Boost.MultiIndex вообще для этих проблем с несколькими способами индексирования одного и того же набора объектов... но если вам понадобится только этот бит "обратного отображения" Boost.Bimap, может оказаться проще.

Таким образом, вы не будете искать линейный поиск при поиске минимума:)

Ответ 5

С++ 14

Как упоминалось в комментарии Джонатана Гейслера к ответу Timmmm, С++ 14 позволяет объявлять параметры лямбда-функции с помощью спецификатора auto type. В результате вы можете сократить строку min_element на основе min_element (и улучшить ее читаемость) следующим образом:

auto it = std::min_element(std::begin(mymap), std::end(mymap),
                           [](const auto& l, const auto& r) { return l.second < r.second; });

Примечание 1: если вы поместите эту строку в функцию MyClass::getMin(), вы должны вернуть it->second. Однако, чтобы учесть пустую карту, вы должны адаптировать return строку следующим образом (или аналогично):

return it == mymap.end() ? -1 : it->second;

Примечание 2: Как также упомянуто Lance Diduck, вы должны передать карту с помощью const ссылки на ваш getMin() функция. Таким образом, вы создаете ненужную копию всей карты.

Код на Ideone