В связанном вопросе я спросил, как получить значение максимального элемента в векторе объектов в c++ на основе некоторого поля указанных объектов. Я расширил алгоритм, чтобы получить индекс этого элемента max, чтобы впоследствии я мог просто поместить его из вектора объекта.
Вот код, который я использую прямо сейчас, и он отлично работает:
vector<MyOwnClass> scoretracker // MyOwnClass has a ".score" field
// Some code filling scoretracker
auto max = std::max_element(scoretracker.begin(), scoretracker.end(),
[] (const MyOwnClass &a, const MyOwnClass &b )
{
return a.score < b.score; // I am not sure how this line works
});
int index = distance(scoretracker.begin(), max);
Итак, я попытался изменить это, чтобы получить второе наивысшее (или n-е значение) вместо max, но до сих пор мои попытки не удались.
Это заставляет меня понять, что я действительно не понимаю, почему "return a.score <b.score" возвращает самое высокое значение.
Посмотрев, как работает max_element, я не уверен, можно ли его использовать, чтобы найти второй по величине.
О, наконец, я предпочел бы не pop_back наивысшее значение из вектора, найти новый max (второе наибольшее значение в исходном векторе) и добавить некоторую логику для восстановления исходной версии. Хорошо, если я должен, я сделаю это, но может быть какое-то свойство итератора или что-то еще, чего я не знаю...
попробуй это:
auto max = std::sort(scoretracker.begin(), scoretracker.end(),
[] (const MyOwnClass &a, const MyOwnClass &b )
{
return a.score < b.score;
});
тогда
scoretracker.back().score;
даст вам последний элемент
scoretracker.at(position).score;
будет возвращать элемент позиции, где позиция может быть любым числом
std::nth_element
Если вам нужен индекс только один раз (и не для всех значений), вы можете использовать:
std::size_t get_index_of_nth_greatest(const std::vector<MyOwnClass>& v, std::size_t k)
{
std::vector<std::size_t> indexes(v.size());
std::iota(indexes.begin(), indexes.end(), 0);
std::nth_element(indexes.begin(), indexes.begin() + k, indexes.end(),
[&](int lhs, int rhs)
{
return v[lhs].score > v[rhs].score;
}
);
return indexes[k];
}
Примечание. Как отмечает Влад из Москвы, с дублирующимися вводами, нет гарантии порядка дубликатов, и поэтому у вас могут быть одинаковые индексы для разных k
.
partial_sort
, поэтому решил, что должно существовать что-то похожее на nth_element
.
Если вам нужен более сложный подход, вы можете посмотреть на функцию std::partition
.
std :: partition принимает контейнер и делит его на две части.
std::vector<MyOwnClass> v(100);
// fill vector with stuff. Find n-th element.
auto mid = v[somwhere_in_the_middle];
auto p = std::partition(v.begin(), v.end(),
[mid](MyOwnClass v){ return v.score < mid.score; } );
Каждый элемент больше p
находится справа от p
. Меньшие слева. Если вы ищете второй по величине, вы идете вправо, пока расстояние v.end() - p
достаточно велико.
Этот метод называется quick-select, основанный на идеях quicksort, и подробно описан здесь. Как найти k-й наибольший элемент в несортированном массиве длины n в O (n)? ,
И это, конечно, уже реализовано как std::nth_element
и может использоваться как
std::nth_element(v.begin(), v.begin()+5, v.end(), std::greater<int>());
Чтобы получить 6-й наибольший элемент на 6-й позиции.
+5
обозначает позицию (считая от 0) искомого предмета. auto p = std::nth_element(scoretracker.begin(), scoretracker.begin()+1, scoretracker.end(), std::greater<double>() );
Странно, но есть ошибка, говорящая, что в std нет члена «больше», что, по-видимому, неверно (VS2012Pro, C ++)
Вы можете сделать это, взяв max
элемент, который у вас уже есть, и ищет std::max_element()
каждой стороны и получая std::max()
этих двух значений:
struct MyOwnClass
{
int score = 0;
MyOwnClass(int s): score(s) {}
};
vector<MyOwnClass> scores = {{4}, {2}, {7}, {9}, {3}};
int main()
{
// we need this more than once
auto compare = [] (const MyOwnClass &a, const MyOwnClass &b)
{
return a.score < b.score; // I am not sure how this line works
};
auto max = std::max_element(scores.begin(), scores.end(), compare);
std::cout << "max: " << max->score << '\n';
// call std::max_element() twice with ranges either side of the max element
// and get the std::max() of those values
auto next_max = std::max(std::max_element(scores.begin(), max, compare)->score
, std::max_element(max + 1, scores.end(), compare)->score);
std::cout << "next max: " << next_max << '\n';
}
ПРИМЕЧАНИЕ. Если вы хотите получить третий или четвертый элементы, этот метод является методом уменьшения отдачи, возможно, лучше сделать std::sort()
.
max_element
вы можете просто использоватьstd::sort
. Затем вы можете просто найти 1-й, 2-й или n-й самый высокий элемент, просто проиндексировав отсортированный массив (или отсортированную копию, если хотите).