我应该使用什么?
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
或者std::sort(numbers.rbegin(), numbers.rend()); // note: reverse iterators
如何将向量按降序排序?其中一种方法是否比另一种方法更有优势或劣势?
实际上,第一个不是好主意。使用第二个或者这个:
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
时,您的代码就不会无声地出现问题。
rbegin
和rend
是为特定目的而设计的。 - Abhishek Divekarstd::greater<typename decltype(numbers)::value_type>()
或类似的东西? - einpoklumstd::greater<>()
。 - Nikolai使用C++14,您可以这样做:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
std::sort(numbers.begin(), numbers.end(), std::greater{});
对一组数字进行排序,使用的是C++17标准,排序方式为从大到小。C++20:
std::ranges::sort(numbers, std::ranges::greater());
对一组数字进行排序,使用的是C++20标准,排序方式为从大到小。在C++20中,可以使用ranges库来进行排序操作。 - Simon Eatwellstd::sort(numbers.end(), numbers.begin(), std::greater{})
或std::sort(numbers.begin(), something_else.end(), std::greater{})
。始终优先选择std::ranges
。 - undefined使用第一个:
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))
。关于“计算”,这是一个好建议,你的语气也是正确的。 - pjvandehaar你可以使用Lambda函数,而不是像Mehrdad建议的那样使用一个函数对象。
sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
reverse_iterator
操作没有进行内联,考虑到它们只是实际迭代器的包装器,所以没有内联就需要花费双倍的时间是很正常的。 - Xeobase()
成员函数返回封装的迭代器。 - Xeostd::vector<>::reverse_iterator
必须是以 std::reverse_iterator<>
为基础实现的。有点奇怪,今天我学到了。 :-P - ildjarn第一种方法如下:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
您可以使用第一种方法,因为它比第二种方法更有效率。
第一种方法的时间复杂度小于第二种方法。
任选一种,它们几乎相同。
像往常一样,各有优缺点。
使用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
演示
std::reverse_iterator
演示
时间相同。Valgrind报告缓存未命中数量相同。
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);
sort(&a[0], &a[n], greater<int>());
std::sort(b, e);
将最小值放在b
(在我们的情况下是rbegin
,即 最后 一个元素),将最大值放在e
(在我们的情况下是rend
,即 第一个 元素)。 - fredoverflow