我尝试使用外部排序来回答这个问题,但是面试官回答说复杂性是高n.n(log(n)),即n square * logn。 有没有更好的选择。
简化问题: 让我们假设我们有1000个元素进行排序,空间仅分配给100个元素。什么是比外部排序花费更少时间的最佳算法。
答案 0 :(得分:5)
我不知道你(或采访者)的意思,但
我的建议是10路(在你的情况下)合并:
O(1)
O((n/max_mem) * (max_mem) log(max_mem)))
= O(n log(max_mem))
O(n log(n/max_mem))
使用minHeap或O(n^2/max_mem)
琐碎(实际上可能更快)关于计算,这是O(n (log(max_mem)+log(n/max_mem)))
= O(n log(n))
关于磁盘I / O,如果所有合并都在一次通过中完成,则2*n
读取2*n
只写 。
更一般地说,它是(1+[depth of the merge tree])*n
所有写入都是顺序的。 第一个读取是顺序读取,第二个读取是连续的,从10个文件中交错。
如果有更多数据,则需要重复或递归合并(每个块100个,然后重复选择N个块)。在这一点上,值得用@ amit的答案中的替换/选择替换split + sort步骤,特别是当数据已经几乎排序时(你可以完全避开合并步骤)。
请注意,较高的N可能会增加计算(如果您使用正确的结构,则会非常轻微),但会显着减少磁盘I / O的数量(达到一定数量;如果您一次合并太多的块,则可能读取缓冲区的内存不足,导致不必要的读取)。磁盘I / O很昂贵,CPU周期不是。
答案 1 :(得分:3)
标准的做法是External Sort。
在外部排序中 - 使O(nlogn)
复杂性变得非常重要 - 尽可能减少磁盘读/写次数,并使顺序最多的读取和写入(而非随机)也很重要 - 因为顺序完成后磁盘访问效率更高。
这样做的标准方式确实是一种k-way合并排序,正如@JanDvorak所建议的那样,但是我想要纠正的建议有一些缺点和补充:
k
的标准公式 - 合并的“顺序”是M/(2b)
(其中M是内存的大小,b
是每个“缓冲区的大小” “(通常是磁盘块)。b
个条目来完成的 - 在内存中填充M/2
。内存的其余部分用于“预测”(允许连续工作,最少等待IO) - 从运行请求更多元素,以及输出缓冲区 - 以保证块中的顺序正确。log_k(N/(2M))
,其中k
是运行次数(先前已计算),M
是内存的大小,N
是文件的大小。每次迭代都需要1次顺序读取和1次顺序写入整个文件。那就是说 - file_size / memory_size的比例通常比10更多。如果你只对10的比率感兴趣,可能会进行局部优化,但不适用于{{1 }}
答案 2 :(得分:3)
也许面试官希望你问:这些数字是J. Bentley(Cracking the Oyster)提到的独特的七位数电话号码吗?