【问题标题】:What is the difference between std::sort and std::stable_sort?std::sort 和 std::stable_sort 有什么区别?
【发布时间】:2014-07-22 01:27:32
【问题描述】:

我想知道 std::sort 和 std::stable_sort 在功能、内存和硬件方面有何不同? documentation 提到“将 [first,last) 范围内的元素按升序排序,如 sort,但 stable_sort 保留具有等效值的元素的相对顺序。”,但这对我来说没有意义。什么是“相对顺序”和“等效值”?

【问题讨论】:

  • std::sort通常使用introsort,而std::stable_sortmerge sort
  • 这个问题似乎离题了,因为标准文档已经清楚地回答了这个问题,而且这些参考资料很容易研究。
  • 问题不是题外话,因为答案可以在互联网上的其他地方找到……SO 的全部意义在于为用户提供编程问题的快速咨询参考。因此,虽然您可以否决“不清楚或无用”,但它似乎并不是“离题”。恕我直言。
  • @πάνταῥεῖ:尽管询问文档或询问文档中不清楚的地方 离题。 SO 不是在这里告诉人们在哪里可以找到文档,也不是为人们澄清所说的文档。它在这里提供答案,几乎所有答案都可以在 some 文档的某个地方找到。
  • 这是一个非常有效的问题。即使我不理解文档并到达这里以找到答案。当人们想知道 STL 数据结构等之间的区别时,类似的问题已经问过很多次了。OP 为这个问题获得了金牌,这表明它对于更多不了解文档的程序员来说是多么有价值。

标签: c++ algorithm


【解决方案1】:

是的,正如你所说,这不是 C++ 独有的概念。

稳定的排序保留语义等价值的物理顺序。


std::sort:

不保证保留相等元素的顺序。
复杂性: O(N·log(N)),其中N = std::distance(first, last) 比较

std::stable_sort:

保证相等元素的顺序被保留。
复杂性: O(N·log^2(N)),其中N = std::distance(first, last) cmp 的应用程序。如果有额外的内存可用,那么复杂度是O(N·log(N))

这意味着std::stable_sort 在执行时间方面的执行效率不高,除非“有额外的内存可用”,在这种情况下,就内存消耗而言,它的执行效率不高。

【讨论】:

  • 根据经验,对于大小为 65536 的数组,我发现 stable_sort 比 sort 更快。我得出的结论是,“不能非常有效地执行”并不是全部。
  • @JoachimW 与任何算法一样,我们只谈论增长率,并且完全可以预期一种算法可能在输入较小的情况下比另一种算法更快,但性能随着输入数量的增加,前者的降级速度比后者快。因此,我很想知道当您将 65536 换成 [许多] 其他数字时会得到什么结果。 (我承认我的措辞掩盖了这种可能性。)
  • 结果也可能以微妙的方式取决于输入数据中存在的任何偏序,对吧?
  • @JoachimW stable_sort 对于某些输入集可能会更快,尤其是具有大量等效元素的输入集,仅仅是因为它直接采用“绝对 O(n log n)”方法,而不是通过先介绍几级。不过,对于 2^16 个唯一整数的混洗数组,我不希望它比 stable_sort 更快。
【解决方案2】:

如前所述,标准仅指出 std::stable_sort 保留相等元素的原始顺序,而 std::sort 没有。

在 HP / Microsoft STL 的情况下,std::sort 通常是快速排序,除非嵌套太深,在这种情况下它切换到堆排序。快速排序时间复杂度通常是 O(n log(n)),但最坏的情况是 O(n^2),这可以通过切换到堆排序来避免,因为堆排序总是 O(n log(n)) (但比快速排序慢,因此仅用于避免 O(n^2))。

在 HP / Microsoft STL 的情况下,std::stable_sort 是一种混合自下而上归并排序,使用插入排序创建 32 个元素的排序组,然后对这些组进行自下而上归并排序。数组(或向量)被一分为二,分配一个临时数组(或向量)大小为待排序数组的 1/2,用于对数组的两半进行归并排序。然后将其中一个半数组移动到临时数组以进行最后的合并传递。合并排序也是 O(n log n),对对象数组进行排序需要更长的时间,但如果对指向对象的指针数组进行排序,其中调用中包含比较函数,则合并排序通常更快。这是因为归并排序比快速排序涉及更多的移动,但比较的次数更少。

对于整数数组的排序,基数排序更快。如果按字节排序,则对 32 位整数数组排序需要 4 遍,对 64 位整数数组排序需要 8 遍。

【讨论】:

    【解决方案3】:

    正如您正确意识到的那样,std::stable_sort() 保留了被视为等效的对象的相对顺序。 std::sort() 没有这个要求。因此,std::stable_sort() 可能更需要资源:它可能会更慢,并且可能会使用更多临时内存,因为它必须遵守更多约束。我不知道有什么算法可以像排序一样高效地进行就地稳定排序。

    【讨论】:

    • 我认为举个例子会很有帮助。我理解它好像我有[s1, s2, s3] 作为[3, 2, 2] 并在数组上执行stable_sort 以获得[2, 2, 3],然后stable_sort 确保数组顺序保持为[s2, s3, s1] 而不是那里有可能s2s3 的位置会混淆。对吗?
    【解决方案4】:

    我认为一个排序结构而不是整数列表的示例有助于阐明两者之间的区别。

    想象一下建筑物中的邻居列表,您按照他们居住的楼层排序。

    struct Neighbour
    {
       int floor;
       string name;
       Neighbour(int f, string n) : floor(f), name(n) {}
    };
    
    std::vector<Neighbour> vec = {Neighbour(1,Bob), Neighbour(2,Anna), ... };
    
    • 1 鲍勃
    • 2 安娜
    • 3 彼得
    • 4 鲍勃
    • 5 劳拉

    如果您现在想按字母顺序对列表进行排序,并且您使用

    std::sort(vec.begin(), vec.end(), [](Neighbour a, Neighbour b){ return a.name < b.name; }
    

    结果可能是:

    • 2 安娜
    • 1 鲍勃
    • 4 鲍勃
    • 5 劳拉
    • 3 彼得

    或:

    • 2 安娜
    • 4 鲍勃
    • 1 鲍勃
    • 5 劳拉
    • 3 彼得

    使用 stable_sort,可以确保您始终获得第一个结果。重复项的顺序与它们在初始列表中的顺序相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-14
      • 2012-05-08
      • 2013-01-10
      • 2023-04-09
      • 2020-10-16
      • 2020-09-30
      • 2021-10-16
      • 2015-07-18
      相关资源
      最近更新 更多