Найти второе (или n) наибольшее значение поля в векторе объекта c ++

0

В связанном вопросе я спросил, как получить значение максимального элемента в векторе объектов в 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 (второе наибольшее значение в исходном векторе) и добавить некоторую логику для восстановления исходной версии. Хорошо, если я должен, я сделаю это, но может быть какое-то свойство итератора или что-то еще, чего я не знаю...

  • 4
    Вместо max_element вы можете просто использовать std::sort . Затем вы можете просто найти 1-й, 2-й или n-й самый высокий элемент, просто проиндексировав отсортированный массив (или отсортированную копию, если хотите).
  • 0
    Хорошо, я пытаюсь реализовать это прямо сейчас!
Показать ещё 6 комментариев
Теги:
object
iterator
vector
field

4 ответа

2
Лучший ответ

попробуй это:

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;

будет возвращать элемент позиции, где позиция может быть любым числом

  • 0
    Ему не нужно сортировать всю последовательность, чтобы получить n-й самый высокий элемент, достаточно std::nth_element
  • 0
    OMG это доступно ?? Вау, я не знаю, когда все увидят, теперь C ++ обеспечивает. Кстати, спасибо @Slava
Показать ещё 1 комментарий
3

Если вам нужен индекс только один раз (и не для всех значений), вы можете использовать:

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.

  • 0
    Я знал о partial_sort , поэтому решил, что должно существовать что-то похожее на nth_element .
  • 0
    @ Jarod42 Это неправильное решение, потому что в общем случае элементы cntainer могут совпадать.
Показать ещё 4 комментария
1

Если вам нужен более сложный подход, вы можете посмотреть на функцию 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-й позиции.

  • 0
    Хорошо читая на nth_element, это звучит как то, что я хочу. Из того, что я понимаю, +5 обозначает позицию (считая от 0) искомого предмета. auto p = std::nth_element(scoretracker.begin(), scoretracker.begin()+1, scoretracker.end(), std::greater<double>() ); Странно, но есть ошибка, говорящая, что в std нет члена «больше», что, по-видимому, неверно (VS2012Pro, C ++)
  • 0
    Хорошо, я должен добавить #include <функционал> stackoverflow.com/questions/16567699/…
Показать ещё 5 комментариев
0

Вы можете сделать это, взяв 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().

Ещё вопросы

Сообщество Overcoder
Наверх
Меню