按降序对向量进行排序

Sorting a vector in descending order

提问人:fredoverflow 提问时间:1/27/2012 最后编辑:Abhipso Ghoshfredoverflow 更新时间:11/4/2023 访问量:316324

问:

我应该使用

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

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

按降序对向量进行排序?一种方法或另一种方法有什么优点或缺点吗?

C++ 排序 STL 向量 迭代器

评论

4赞 wilhelmtell 1/28/2012
+1 我认为答案很明显,但这个问题有一点有趣的琐碎。:)
5赞 evandrix 1/30/2013
我会投票给第一个选项,因为那样我就不必处理了。reverse_iterator
4赞 shshnk 6/6/2016
@wilhelmtell 一个菜鸟问题,但为什么要按降序排序第二个问题?我们给出了相同的数组作为排序方法的输入。只是我们以相反的顺序给出它,那么为什么要像 ar.begin() 和 ar.end 那样按降序而不是升序排序。
13赞 fredoverflow 6/7/2016
@shshnk将最小值放在(在我们的例子中,所以最后一个元素)和最大值(在我们的例子中,所以第一个元素)。std::sort(b, e);brbeginerend
0赞 Chnossos 3/11/2021
这回答了你的问题吗?按降序对矢量元素进行排序

答:

80赞 Pubby 1/27/2012 #1

使用第一个:

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

它明确地说明了正在发生的事情 - 即使有评论,误读的可能性也较小。它清晰易读,这正是您想要的。rbeginbegin

此外,考虑到反向迭代器的性质,第二个可能不如第一个效率,尽管您必须对其进行分析才能确定。

19赞 zw324 1/27/2012 #2

根据我的机器,使用第一种方法对 [1..3000000] 的向量进行排序大约需要 4 秒,而使用第二种方法大约需要两倍的时间。显然,这说明了一些事情,但我也不明白为什么。只是认为这会很有帮助。long long

这里报告了同样的事情。

正如 Xeo 所说,他们使用大约相同的时间来完成。-O3

评论

14赞 Xeo 1/27/2012
您是否只是在未启用优化的情况下进行编译?听起来很像这些操作没有内联,并且鉴于它们只是实际迭代器的包装器,难怪它们需要双倍的时间而不内联。reverse_iterator
0赞 Pubby 1/27/2012
@Xeo 即使它们是内联的,某些实现也会使用每个取消引用的加法。
0赞 Xeo 1/27/2012
@ildjarn:因为是这样?例如,成员函数返回包装的迭代器。base()
1赞 zw324 1/27/2012
@Xeo 现在他们俩都在一秒钟内完成。谢谢!
3赞 ildjarn 1/27/2012
@Xeo:我把它收回来;该标准实际上要求以 .奇怪;今天我学到了。9-3std::vector<>::reverse_iteratorstd::reverse_iterator<>
148赞 user541686 4/29/2013 #3

实际上,第一个是个坏主意。使用第二个,或者使用以下方法

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

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

这样,当有人决定应该保留或代替 .numberslonglong longint

评论

2赞 user541686 4/29/2013
@FredOverflow:你在评论中获得了荣誉;)
4赞 RichardHowells 5/15/2013
或者坚持第一个。对 numberContainer 使用 typedef - 一个好主意,这样有人可以换成 long long - 然后写: std::sort(numbers.begin(), numbers.end(), std::greater<numContainer::value_type>());
1赞 Abhishek Divekar 8/15/2016
+1 第一个真的很令人困惑。比另一个是什么? 并且是为特定目的而制作的。greaterrbeginrend
6赞 einpoklum 12/21/2016
为什么不只是什么的?std::greater<typename decltype(numbers)::value_type>()
18赞 Nikolai 12/31/2018
这个答案已经过时了 - 你可以从 C++14 开始使用。std::greater<>()
33赞 shoumikhin 9/10/2015 #4

这又如何呢?

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

评论

15赞 greg 2/23/2016
原因可能是为了避免额外的复杂性:O(n * log(n)) + O(n) vs O(n * log(n))
40赞 pjvandehaar 3/2/2016
@greg O(n * log(n)) = O(n * log(n) + n)。它们是定义同一集合的两种方法。你的意思是说“这可能更慢”。
6赞 Ofek Gila 9/30/2018
@pjvandehaar格雷格很好。他明确没有说 O(n * log(n) + n),他说 O(n * log(n)) + O(n)。你说得对,他的措辞不清楚(尤其是他对复杂性这个词的误用),但你本可以用更友善的方式回答。例如:也许你的意思是使用“计算”这个词而不是“复杂性”这个词。将数字反转是一个不必要的 O(n) 步骤,而其他方面相同的 O(n * log(n)) 步骤。
5赞 pjvandehaar 10/3/2018
@OfekGila 我的理解是,big-O 表示法是关于函数集的,而涉及 和 的符号只是方便的意思和。在这种情况下,是一个方便的符号,其与 相同。“计算”这个词是一个很好的建议,你的语气是对的。=+O(n*log(n)) + O(n)O(n*log(n)) ∪ O(n)O(n*log(n))
141赞 mrexciting 6/11/2016 #5

使用 c++14,您可以这样做:

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

评论

18赞 Simon Eatwell 2/16/2022
C++17 C++20std::sort(numbers.begin(), numbers.end(), std::greater{});std::ranges::sort(numbers, std::ranges::greater());
0赞 roulette01 11/4/2023
如果C++20可用,后者@SimonEatwell优于前者?
1赞 Simon Eatwell 11/4/2023
@roulette01 是的,后者更胜一筹,因为 C++20 是可用的,因为在前一种情况下,人们可能会意外地传递一对错误的迭代器。一个人可能会不小心做或.总是喜欢.std::sort(numbers.end(), numbers.begin(), std::greater{})std::sort(numbers.begin(), something_else.end(), std::greater{})std::ranges
32赞 Julian Declercq 7/31/2016 #6

您可以使用 Lambda 函数,而不是 Mehrdad 建议的函子。

sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
6赞 Krish Munot 8/13/2016 #7

您可以使用第一个代码,也可以尝试下面的代码,这同样有效

sort(&a[0], &a[n], greater<int>());
9赞 user7069426 3/22/2017 #8
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);

评论

6赞 Stefan Hegny 3/23/2017
为了成为一个有效的答案,你应该考虑写一些关于你的与OP的提及方法的优点/缺点
2赞 user325117 4/29/2017 #9

我认为您不应该使用问题中的任何一种方法,因为它们都令人困惑,而且第二种方法正如 Mehrdad 所建议的那样脆弱。

我主张以下几点,因为它看起来像一个标准的库函数,并且明确了它的意图:

#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>());
}

评论

6赞 Apollys supports Monica 12/15/2017
这就像仅仅使用比较器要混乱一千倍......std::greater
0赞 Philipp Claßen 4/10/2018
@Apollys我同意从 C++14 开始,std::greater<> 看起来像是首选解决方案。如果您没有 C++14,如果您想排除 std::greater<int 的任何意外>它仍然很有用(例如,当类型在某个时候从 int 更改为 long 时)。
15赞 rashedcs 6/6/2017 #10

第一种方法是指:

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

您可以使用第一种方法,因为比第二种方法效率更高。
第一种方法的时间复杂度小于第二种方法。

评论

3赞 Philipp Claßen 4/10/2018
这与mrexciting的答案相同。关于复杂性的评论对我来说也不清楚。
11赞 ivaigult 7/30/2019 #11

TL;博士

使用任何。它们几乎是一样的。

无聊的答案

像往常一样,有利有弊。

用: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 报告的缓存未命中次数相同。

0赞 Simon Eatwell 11/4/2023 #12

对于 C++20:

std::ranges::sort(numbers, std::ranges::greater());

这排除了传入一对无效迭代器的任何可能性,例如:

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