连续(不固定)输入随机数的最佳排序算法是什么?
What is the best sort algorithm for continuously (NOT FIXED) input of random numbers?
假设我有一个系统一直接收一个接一个的连续随机数作为输入 (0,5,2,10,6,20......)
,
我的目的是将它们以最佳性能排序。
因此每次迭代后输出大小都会增加并且输入是顺序的。
我想使用插入排序或 BST,但我不确定对于这个问题哪个更好,因为我知道插入排序是 O(n-n^2)
而 BST 插入是 O(log(n))
拜托,有什么建议吗?
插入排序对于小输入(小于 1000)是有效的,否则因为它需要 O(n^2) 的 运行 时间,如果你不确定有多大,它的复杂性会增长得非常快您的输入会增长到,然后使用具有 运行 时间 O(nlogn) 的快速排序或堆排序,这比 O(n^2).
快得多
这取决于你想如何使用结果。
例如:如果你输入了很多数字,并且你使用BST存储它们,你需要1000多步才能找到索引1000。如果你将排序后的结果存储在一个数组中,你只需要需要 1 步 (return index[1000]).
如果您只需要最高或最低的数字,然后将其从列表中删除。之后,您需要下一个最高或最低的数字,使用堆会更快。
另外请记住,如果随机数以某种方式排序,则 BST 看起来像一个列表,然后您没有 O(log n) 但 O(n) 用于插入。
你要考虑的事情还有很多。所以请告诉我们您需要什么
我认为使用一些承诺 O(log n)
performacne(如 AVL、black-red 等)的 BST 是您的最佳选择。
打印出当前数据是使用树的中序遍历完成的。
如果每次添加元素都需要排序,这不是排序问题而是插入问题。任何排序算法都会矫枉过正。
如果你的数据必须存储在一个数组中,你不能不移动元素,解决方案是Ω(N)。这可以通过直接插入 (O(N)) 有效地实现。 (先进行二分搜索再插入会进行较少的比较,但不确定您是否会注意到差异。)
如果你有更多的自由,BST 确实是一个更有效的解决方案。如果您需要对最坏情况成本 (O(Log N)) 的绝对保证,则 BST 需要平衡(因此 AVL、红黑……根据您的喜好)。如果您的数据足够随机,则可能没有必要。
如果您的数据具有特殊属性(例如小的离散范围),可以使用临时解决方案。在给定的示例中,一个简单的计数直方图将实现 O(1) 更新时间。
有几个因素会影响您的解决方案的效率:
- 找到插入点的 READS 数
- 插入的 WRITES(例如轮班、重新平衡)数量
- 总计 memory/locality(影响缓存未命中)常量 (K) 的大小是相关的,因为它会影响每个缓存级别中适合的元素数量。
- 分支预测未命中
请注意,与算法相比,这些基于数据结构的变化更大,这似乎总是插入排序的变体,因为您需要对每个元素进行添加
Data Structure | READS | WRITES | Memory | locality | Branches
---------------|--------|--------|------------|----------|---------
Sorted Vector |O(logN) | O(N) | O(N) | high | high (FFTFFT)
Linked List |O(N) | O(1) | O(K*N) | low | low (FFFFFFFFFFT)
Red Black Tree |O(logN) | O(K) | O(K*NlogN) | low | high (FFTFFT)
Btree 16 node |O(logN) | O(16) | O(NlogN) | medium | medium (FFTF)
* K 表示常数明显高于具有相同 O()
的其他解
最佳解决方案可能因当前的体系结构限制而异。如果 memory/cache 大小很小,排序的向量可能是最优的。
如果分支未命中代价高昂,则链表可能是最优的,因为除了最后一个分支之外的分支都是假的
但似乎如果您使用具有大量节点的 Btree P
您将获得向量的局部性和内存效率,索引 O (logN) 读取速度,并将写入次数限制为 O(P
) 而不是 O(N)。我会从 16 的 P
开始,然后使用二进制搜索来优化 P
、
不幸的是,真正的答案是尝试所有这些并根据您的用例进行基准测试
原问题没有说清楚数据在接收数字时必须多久检索一次,或者如何检索数字(按索引,仅最小的,仅最大的,或全部) , ... ).
一种选择是使用链表的自下而上合并排序逻辑,它使用一个小的(26 到 32 个元素)引用或指针数组,每个引用或指针指向一个列表。 Array[i] 是指向包含(2 的 i 次方)节点的列表的引用或指针,array[0] 指向大小为 1 的列表,array[1] -> 大小为 2 的列表,array[2] -> 大小为 4 的列表,数组的最后一个成员指向一个大小不受限制的列表。节点一个一个地插入到数组中,对应一个接一个数。
问题是数据存储在列表数组中,因此只进行了部分排序。要获得完全排序的列表,列表数组将合并为一个列表。通常只有在所有数据都存储到数组中后才会这样做。
关于链表自下而上归并排序的 Wiki 文章:
http://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists
此方法提供快速的平均插入时间,偶尔会出现较长时间。每隔一个数字就存储在数组 [0] 中。 2 次方边界的输入涉及多个合并步骤,第 16 个输入最终合并了两个包含 8 个数字的列表,第 1024 个输入最终合并了两个包含 512 个数字的列表。
如前所述,二叉搜索树(偶尔重新平衡)可能是更好的解决方案。
假设我有一个系统一直接收一个接一个的连续随机数作为输入 (0,5,2,10,6,20......)
,
我的目的是将它们以最佳性能排序。
因此每次迭代后输出大小都会增加并且输入是顺序的。
我想使用插入排序或 BST,但我不确定对于这个问题哪个更好,因为我知道插入排序是 O(n-n^2)
而 BST 插入是 O(log(n))
拜托,有什么建议吗?
插入排序对于小输入(小于 1000)是有效的,否则因为它需要 O(n^2) 的 运行 时间,如果你不确定有多大,它的复杂性会增长得非常快您的输入会增长到,然后使用具有 运行 时间 O(nlogn) 的快速排序或堆排序,这比 O(n^2).
快得多这取决于你想如何使用结果。
例如:如果你输入了很多数字,并且你使用BST存储它们,你需要1000多步才能找到索引1000。如果你将排序后的结果存储在一个数组中,你只需要需要 1 步 (return index[1000]).
如果您只需要最高或最低的数字,然后将其从列表中删除。之后,您需要下一个最高或最低的数字,使用堆会更快。
另外请记住,如果随机数以某种方式排序,则 BST 看起来像一个列表,然后您没有 O(log n) 但 O(n) 用于插入。
你要考虑的事情还有很多。所以请告诉我们您需要什么
我认为使用一些承诺 O(log n)
performacne(如 AVL、black-red 等)的 BST 是您的最佳选择。
打印出当前数据是使用树的中序遍历完成的。
如果每次添加元素都需要排序,这不是排序问题而是插入问题。任何排序算法都会矫枉过正。
如果你的数据必须存储在一个数组中,你不能不移动元素,解决方案是Ω(N)。这可以通过直接插入 (O(N)) 有效地实现。 (先进行二分搜索再插入会进行较少的比较,但不确定您是否会注意到差异。)
如果你有更多的自由,BST 确实是一个更有效的解决方案。如果您需要对最坏情况成本 (O(Log N)) 的绝对保证,则 BST 需要平衡(因此 AVL、红黑……根据您的喜好)。如果您的数据足够随机,则可能没有必要。
如果您的数据具有特殊属性(例如小的离散范围),可以使用临时解决方案。在给定的示例中,一个简单的计数直方图将实现 O(1) 更新时间。
有几个因素会影响您的解决方案的效率:
- 找到插入点的 READS 数
- 插入的 WRITES(例如轮班、重新平衡)数量
- 总计 memory/locality(影响缓存未命中)常量 (K) 的大小是相关的,因为它会影响每个缓存级别中适合的元素数量。
- 分支预测未命中
请注意,与算法相比,这些基于数据结构的变化更大,这似乎总是插入排序的变体,因为您需要对每个元素进行添加
Data Structure | READS | WRITES | Memory | locality | Branches
---------------|--------|--------|------------|----------|---------
Sorted Vector |O(logN) | O(N) | O(N) | high | high (FFTFFT)
Linked List |O(N) | O(1) | O(K*N) | low | low (FFFFFFFFFFT)
Red Black Tree |O(logN) | O(K) | O(K*NlogN) | low | high (FFTFFT)
Btree 16 node |O(logN) | O(16) | O(NlogN) | medium | medium (FFTF)
* K 表示常数明显高于具有相同 O()
的其他解最佳解决方案可能因当前的体系结构限制而异。如果 memory/cache 大小很小,排序的向量可能是最优的。 如果分支未命中代价高昂,则链表可能是最优的,因为除了最后一个分支之外的分支都是假的
但似乎如果您使用具有大量节点的 Btree P
您将获得向量的局部性和内存效率,索引 O (logN) 读取速度,并将写入次数限制为 O(P
) 而不是 O(N)。我会从 16 的 P
开始,然后使用二进制搜索来优化 P
、
不幸的是,真正的答案是尝试所有这些并根据您的用例进行基准测试
原问题没有说清楚数据在接收数字时必须多久检索一次,或者如何检索数字(按索引,仅最小的,仅最大的,或全部) , ... ).
一种选择是使用链表的自下而上合并排序逻辑,它使用一个小的(26 到 32 个元素)引用或指针数组,每个引用或指针指向一个列表。 Array[i] 是指向包含(2 的 i 次方)节点的列表的引用或指针,array[0] 指向大小为 1 的列表,array[1] -> 大小为 2 的列表,array[2] -> 大小为 4 的列表,数组的最后一个成员指向一个大小不受限制的列表。节点一个一个地插入到数组中,对应一个接一个数。
问题是数据存储在列表数组中,因此只进行了部分排序。要获得完全排序的列表,列表数组将合并为一个列表。通常只有在所有数据都存储到数组中后才会这样做。
关于链表自下而上归并排序的 Wiki 文章:
http://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists
此方法提供快速的平均插入时间,偶尔会出现较长时间。每隔一个数字就存储在数组 [0] 中。 2 次方边界的输入涉及多个合并步骤,第 16 个输入最终合并了两个包含 8 个数字的列表,第 1024 个输入最终合并了两个包含 512 个数字的列表。
如前所述,二叉搜索树(偶尔重新平衡)可能是更好的解决方案。