【问题标题】:std::list sort algorithm runtimestd::list 排序算法运行时
【发布时间】:2017-09-04 14:40:42
【问题描述】:

我有一个元素列表,其顺序几乎是正确的,并且与它们的正确位置相比,这些元素的位置相对较少(例如,没有应该在列表前面的元素是最后)。

实际来源:我有一个传入的 UDP 包流,其中包含所有标记有特定时间戳的信号。评估数据表明,包没有以正确的顺序发送(或接收),因此时间戳不是不断增加,而是有点抖动。要导出数据,我需要提前对其进行排序。

;DR >

我想使用std::list.sort() 对这个列表进行排序。

std::list.sort() 使用的排序算法是什么,列表几乎已排序这一事实对它有何影响。我有一种“感觉”,基于分而治之的算法可能会从中受益。

对于我非常具体的问题,是否有更有效的算法?

【问题讨论】:

  • many sorting algorithms out there with different performance characteristics for different situations。您将不得不对各种实现进行基准测试,看看哪一个是最好的。在你确定它真的很重要之后。
  • shreevatsa.wordpress.com/2008/05/16/… - 做基准测试,如果它是瓶颈,开始担心算法
  • 冒泡排序在这里实际上可能很有用。来自维基百科:“在计算机图形学中,冒泡排序很受欢迎,因为它能够检测几乎排序的数组中的一个非常小的错误(比如两个元素的交换),并以线性复杂度 (2n) 来修复它。”
  • 为什么要使用列表?
  • @manni66 看到“Fire Lancer”的回答后,我切换到一个向量并为每个项目从后面插入

标签: c++ algorithm sorting stl


【解决方案1】:

如果每个元素都在其正确位置的 k 个位置内,则插入排序将进行少于 kN 个比较和交换/移动。它也很容易实现。

将此与合并排序或快速排序所需的 N*log(N) 操作进行比较,看看这是否更适合您。

【讨论】:

    【解决方案2】:

    没有定义使用哪种算法,尽管平均应该在N log N 左右,例如快速排序。

    如果您在消费数据包时将数据包附加到“队列”的末尾,因此您希望队列始终排序,那么您可以预期任何新数据包的正确位置几乎总是在“末尾”附近。

    因此,无需对整个队列进行排序,只需将数据包插入正确的位置即可。从队列的后面开始,将现有的数据包时间戳与已经存在的数据包进行比较,将其插入到具有较小时间戳的第一个数据包之后(可能总是结束),如果没有这样的数据包,则将其插入前面非常混乱。

    或者,如果您想按顺序添加所有数据包然后对其进行排序,则冒泡排序应该是相当理想的,因为列表应该仍然几乎已经排序。

    【讨论】:

    • OP 没有询问std::sort(可能是快速排序),而是std::list 成员函数sort(几乎可以肯定不是快速排序) .确实,平均比较次数必须为N log N
    • 是的,快速排序对我来说是一个糟糕的例子,但我从来没有提到std::sort。 (事实上​​,您不能将std::sort 与列表一起使用,因为它不是随机访问,同样的原因一些快速排序实现可能不适合)
    • 我从快速排序跳到std::sort,因为我以为你做了相反的事情。我的错。
    【解决方案3】:

    在大多数排序的数据上,Insertion SortBubble Sort 是表现最好的最常见数据。

    Live demo

    还要注意,具有 list 结构会对索引访问施加额外的限制,因此需要索引访问的算法将执行得更差。因此,插入排序更适合,因为它只需要顺序访问。

    【讨论】:

      【解决方案4】:

      对于 2015 年之前的 Visual Studio,使用 26 个内部列表的自下而上合并排序,遵循此 wiki 文章中显示的算法:

      https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists

      Visual Studio 2015 增加了对没有默认分配器的支持。 26 个内部列表初始值设定项可以扩展为 26 个具有用户指定分配器的初始值设定项实例,具体而言:_Myt _Binlist[_MAXBINS+1] = { _Myt(get_allocator()), ... , _Myt(get_allocator())};,但微软的某个人改用了基于迭代器的自顶向下合并排序。这比较慢,但是如果比较抛出异常,它不需要特殊的恢复。该更改的作者指出,如果以性能为目标,那么将列表复制到数组或向量,对数组或向量进行排序,然后从数组或向量创建一个新列表会更快。

      有一个关于这个的先前线程。

      `std::list<>::sort()` - why the sudden switch to top-down strategy?

      在您的情况下,像双向链表上的插入排序应该更快,如果找到了顺序节点,则从列表中删除,向后或向前扫描到正确的位置并将节点重新插入列表中.如果您想使用 std::list,您可以使用迭代器、擦除和安放来“移动”节点,但这涉及为每次移动释放和重新分配一个节点。使用您自己的双向链表实现这一点会更快,在这种情况下,您可以只操作链接,避免使用 std::list 时释放和重新分配内存。

      【讨论】:

        猜你喜欢
        • 2012-01-21
        • 2021-10-28
        • 2011-01-08
        • 1970-01-01
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        • 1970-01-01
        • 2018-12-16
        相关资源
        最近更新 更多