Сортировка вектора в порядке убывания

Должен ли я использовать

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

или

std::sort(numbers.rbegin(), numbers.rend());   // note: reverse iterators

чтобы отсортировать вектор в порядке убывания? Есть ли какие-либо выгоды или недостатки с одним или другим подходом?

Ответ 1

Собственно, первая - плохая идея. Используйте либо второй, либо это:

struct greater
{
    template<class T>
    bool operator()(T const &a, T const &b) const { return a > b; }
};

std::sort(numbers.begin(), numbers.end(), greater());

Таким образом, ваш код не будет внезапно ломаться, если кто-то решит, что numbers должен содержать long или long long вместо int.

Ответ 2

Использовать первую:

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

Это явное из того, что происходит - меньше шансов неверно узнать rbegin как begin, даже с комментарием. Это понятно и понятно, что именно вы хотите.

Кроме того, второй может быть менее эффективным, чем первый, учитывая характер обратных итераторов, хотя вам нужно было бы проконтролировать его, чтобы быть уверенным.

Ответ 3

С помощью С++ 14 вы можете сделать это:

std::sort(numbers.begin(), numbers.end(), std::greater<>());

Ответ 4

Как насчет этого?

std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());

Ответ 5

Вместо функтора, предложенного Мехрдадом, вы можете использовать функцию лямбда.

sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });

Ответ 6

Согласно моей машине, сортировка a long long vector из [1..3000000] с использованием первого метода занимает около 4 секунд, а вторая занимает примерно в два раза больше времени. Очевидно, это что-то говорит, но я тоже не понимаю, почему. Просто подумайте, что это будет полезно.

То же самое сообщило здесь.

Как сказано Xeo, с -O3 они используют примерно то же время, чтобы закончить.

Ответ 7

Первый подход:

    std::sort(numbers.begin(), numbers.end(), std::greater<>());

Вы можете использовать первый подход из-за большей эффективности, чем второй.
Первая временная сложность подхода меньше второй.

Ответ 8

Самый короткий подход:

std::sort(v.rbegin(), v.rend());

Ответ 9

bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);

Ответ 10

Вы можете использовать первый или попробовать приведенный ниже код, который одинаково эффективен

sort(&a[0], &a[n], greater<int>());

Ответ 11

Я не думаю, что вы должны использовать любой из методов в вопросе, поскольку они оба запутывают, а второй - хрупкий, как предлагает Мехрдад.

Я бы отстаивал следующее, поскольку он выглядит как стандартная библиотечная функция и делает свое намерение понятным:

#include <iterator>

template <class RandomIt>
void reverse_sort(RandomIt first, RandomIt last)
{
    std::sort(first, last, 
        std::greater<typename std::iterator_traits<RandomIt>::value_type>());
}

Ответ 12

TL; DR

Используйте любой. Они почти одинаковые.

Скучный ответ

Как обычно, есть плюсы и минусы.

Используйте std::reverse_iterator:

  • Когда вы сортируете пользовательские типы и не хотите реализовывать operator>()
  • Когда вам лень набирать std::greater<int>()

Используйте std::greater, когда:

  • Когда вы хотите иметь более явный код
  • Если вы хотите избежать использования неясных обратных итераторов

Что касается производительности, оба метода одинаково эффективны. Я попробовал следующий тест:

#include <algorithm>
#include <chrono>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std::chrono;

/* 64 Megabytes. */
#define VECTOR_SIZE (((1 << 20) * 64) / sizeof(int))
/* Number of elements to sort. */
#define SORT_SIZE 100000

int main(int argc, char **argv) {
    std::vector<int> vec;
    vec.resize(VECTOR_SIZE);

    /* We generate more data here, so the first SORT_SIZE elements are evicted
       from the cache. */
    std::ifstream urandom("/dev/urandom", std::ios::in | std::ifstream::binary);
    urandom.read((char*)vec.data(), vec.size() * sizeof(int));
    urandom.close();

    auto start = steady_clock::now();
#if USE_REVERSE_ITER
    auto it_rbegin = vec.rend() - SORT_SIZE;
    std::sort(it_rbegin, vec.rend());
#else
    auto it_end = vec.begin() + SORT_SIZE;
    std::sort(vec.begin(), it_end, std::greater<int>());
#endif
    auto stop = steady_clock::now();

    std::cout << "Sorting time: "
          << duration_cast<microseconds>(stop - start).count()
          << "us" << std::endl;
    return 0;
}

С помощью этой командной строки:

g++ -g -DUSE_REVERSE_ITER=0 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out
g++ -g -DUSE_REVERSE_ITER=1 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out

std::greater demo std::reverse_iterator demo

Сроки такие же. Valgrind сообщает о том же количестве пропусков кеша