Как я могу найти минимальное значение на карте?
У меня есть 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