我应该使用
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
或者
std::sort(numbers.rbegin(), numbers.rend()); // note: reverse iterators
以降序对向量进行排序?一种方法或另一种方法有什么好处或缺点吗?
reverse_iterator
的问题了。
std::sort(b, e);
将最小值放在 b
(在我们的例子中是 rbegin
,所以是 last 元素),最大值放在 e
(在我们的例子中是 rend
,所以 < i>第一个元素)。
实际上,第一个是一个坏主意。使用第二个,或者这个:
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
时,您的代码不会静默中断。
使用 c++14,您可以这样做:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
std::sort(numbers.begin(), numbers.end(), std::greater{});
C++20 std::ranges::sort(numbers, std::ranges::greater());
使用第一个:
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
它清楚地说明了正在发生的事情 - 将 rbegin
误读为 begin
的可能性较小,即使有评论也是如此。它清晰易读,这正是您想要的。
此外,考虑到反向迭代器的性质,第二个可能比第一个效率低,尽管您必须对其进行分析才能确定。
那这个呢?
std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());
=
和+
的符号只是方便表示∈
和∪
。在这种情况下,O(n*log(n)) + O(n)
是 O(n*log(n)) ∪ O(n)
的方便表示法,与 O(n*log(n))
相同。 “计算”这个词是一个很好的建议,你的语气是对的。
您可以使用 Lambda 函数,而不是 Mehrdad 建议的仿函数。
sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
根据我的机器,使用第一种方法对 [1..3000000] 的 long long
向量进行排序大约需要 4 秒,而使用第二种方法大约需要两倍的时间。显然,这说明了一些事情,但我也不明白为什么。只是认为这会有所帮助。
同样的事情报告了 here。
正如 Xeo 所说,使用 -O3
他们使用大约相同的时间来完成。
reverse_iterator
操作没有内联,并且考虑到它们只是实际迭代器的包装器,难怪它们在没有内联的情况下花费双倍的时间。
base()
成员函数返回包装的迭代器。
std::vector<>::reverse_iterator
是根据 std::reverse_iterator<>
实现的。奇异的;今天我学会了。 :-P
第一种方法是指:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
您可能会使用第一种方法,因为它比第二种方法效率更高。第一种方法的时间复杂度小于第二种方法。
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);
TL;博士
使用任何。它们几乎相同。
无聊的回答
像往常一样,有利也有弊。
使用 std::reverse_iterator
:
当您对自定义类型进行排序并且您不想实现 operator>()
当你懒得输入 std::greater
在以下情况下使用 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 报告相同数量的缓存未命中。
您可以使用第一个或尝试下面同样有效的代码
sort(&a[0], &a[n], greater<int>());
我认为您不应该使用问题中的任何一种方法,因为它们都令人困惑,而第二种方法正如 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>());
}
std::greater
比较器要混乱一千倍......
不定期副业成功案例分享
greater
比另一个是什么?rbegin
和rend
是为特定目的而制作的。std::greater<typename decltype(numbers)::value_type>()
之类的?std::greater<>()
自 C++14 起。