C++ 为什么std::sort()比std::make_heap()快?

C++ 为什么std::sort()比std::make_heap()快?,c++,algorithm,sorting,c++11,vector,C++,Algorithm,Sorting,C++11,Vector,我的std::vector中有13721057元素。我需要对这个向量进行排序并获取前25个元素。我想,既然可以在O(N)中构建堆,那么弹出25个元素(每个元素都是O(logN))肯定比在O(NlogN)中排序整个向量要快 但是,当我计时代码时: clock_t tStart = clock(); sort(mostFrequent.begin(), mostFrequent.end(), greater<Sequence>()); printf("Time taken: %.2fs\

我的
std::vector
中有
13721057
元素。我需要对这个向量进行排序并获取前25个元素。我想,既然可以在
O(N)
中构建堆,那么弹出25个元素(每个元素都是
O(logN)
)肯定比在
O(NlogN)
中排序整个向量要快

但是,当我计时代码时:

clock_t tStart = clock();
sort(mostFrequent.begin(), mostFrequent.end(), greater<Sequence>());
printf("Time taken: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);

对整个向量进行排序似乎要快得多。这是为什么?

这不是一个完整的答案,但要从13721057中获得前25个元素,您最好使用它

如果您只需要第25个元素,那么


作为旁注。为了获得排序顺序小于X的第一个元素,我将使用lambda执行
auto mid=std::partition
,然后执行
std::sort(begin,mid)
。可能有更好的方法。

编辑:正如一篇评论中所建议的那样,我也尝试使用预排序输入,在这种情况下,我确实设法以比生成堆更快的速度对我的“复制成本高”类型进行排序,但仅以大约5-10%的小幅度进行

无论我尝试什么,我都无法在Solaris或Linux(GCC4.4)上复制您的结果
make_heap
总是以花费时间的三分之一的数量出现

  • 没有优化vs-O3只改变总时间,而不是相对顺序
  • 我用了你的确切数量的物品
  • 首先尝试排序
    int
    ,然后是一个更大的“昂贵的复制”类
  • 猜猜你在用什么
  • 将计时调用移到printf之外,以确保始终正确订购

我假设造成这种差异的实际原因是,要么是您的
运算符的复杂性不同,要么是复制您的对象相对于以我的测试无法复制的方式对其进行比较而言代价高昂。

可能是隐藏常量。还要尝试测试std::nth_element()。是否多次运行此操作?你有没有改变哪一个是第一个?你是在启用优化的情况下编译的吗?另外,找到第25个元素可能会更快。
std::partition
,13M个元素的0.5s听起来快得令人怀疑。你为什么不为
std::sort
std::make_heap
提供相同的比较函子呢?啊,这就是我要找的!非
std::partition
:)第n个元素():“新的第n个元素之前的所有元素都小于或等于新的第n个元素之后的元素。”我认为这将是隐藏常数最少的最快方法。然后你可以对前25个元素进行排序。谢谢大家,
partial\u sort
当然更快。但是我仍然不明白为什么
排序
要快得多,因为
部分排序
是我最终尝试用堆实现的。@JohanLundberg nth\u元素像快速排序(super-fast)一样实现,并且花费了O(N)个时间。排序25个元素:O(25*log25)(只需要做一次,不需要太多)。抱歉,但我会打赌O(13721057)+O(25*log25)而不是O(13721057*log25)@SashaMN一个填充了常量的大O是毫无意义的。你会尝试使用预排序向量的“昂贵复制”类吗?(假设您使用OP的技术对排序使用
std::less
,对堆使用
std::greater
,这将产生相同的结果,那么对排序和堆使用pessimal应该接近最优。)@rici预排序向量在概念上是快速排序的最坏情况,所以我没有尝试这种变化。如果我有时间,我也会尝试测试这个案例。这取决于识别轴心的策略。如果使用中点元素作为轴心,则它是最佳的。如果您使用随机枢轴,它仍然非常好,并且仍然具有在分区操作期间不进行任何交换的优势。(我没有使用昂贵的副本进行测试,但使用长-长,它将std::sort从1.22秒减少到了0.30秒。它还极大地增加了部分_排序的成本。)@Mark B:“预排序向量在概念上是快速排序的最坏情况”-仅当范围的第一个或最后一个元素被选为枢轴时,没有人会愚蠢到这样做。[好吧,假设几乎没有人。]@ArneVogel老实说,这在很大程度上取决于使用的分区算法类型。中位数为3或随机划分算法往往能很好地处理大部分排序的输入。
clock_t tStart = clock();
make_heap(mostFrequent.begin(), mostFrequent.end());
printf("Time taken: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);