(我把这个作为面试问题,并希望提供一些帮助。)
您的k
排序列表中包含n
总数不同的数字。
显示如何创建一个单个排序列表,其中包含O(n * log(k))
这个想法是使用大小 k 。
的最小堆按堆上的所有 k 列表(每个列表一个堆输入),由它们的最小值(即第一个)值
键入然后反复这样做:
- 从堆中提取顶部列表(具有最小键)
- 从该列表中提取最小值并将其推在结果列表上
- 将缩短列表向后推回(如果不是空),现在由其新的最小值 kemper
重复直到将所有值推到结果列表上。
初始步骤的时间复杂性为 o(klogk)。
上面的3个步骤将重复 n 次。在每次迭代中,每个迭代的成本是:
- o(1)
- o(1)如果使用指针/索引实现提取(不移动列表中的所有值)
- o(log k),因为堆的大小永远不大于
因此,所产生的复杂性为 o(nlogk)(如 k < em n ,初始步骤不重要)。
正如所述问题时,不需要K-way合并(或堆)。以任何顺序合并列表对,直到产生单个排序列表也将具有时间复杂性o(n log(k))。如果问题询问了如何在单个通过中合并k列表,则需要K-Way合并。
考虑k == 32的情况,为了简化数学,假设所有列表都按顺序合并,以便每个合并通过合并所有n个元素。在第一次通过之后,有k/2列表,在第二次通过之后,k/4列表之后,log2(k)= 5通过后,将所有k(32)列表合并为单个排序列表。除了简化数学,合并列表的顺序无关紧要,时间复杂性在O(n log2(k))时保持不变。
使用k-way合并通常仅在使用外部设备合并数据时,例如一个或多个磁盘驱动器(或经典用法磁带驱动器),其中i/o时间足够好,足够好,可以堆在开销上。被忽略。对于基于RAM的合并/合并排序,对于2条合并/合并排序或K-Way Merge/Merge排序,操作总数大致相同。在带有16个寄存器的处理器上,其中大多数用作索引或指针,优化(无堆)4向合并(使用8个寄存器作为索引或指针到每个运行的当前和结束位置)可能会更快一些)比由于更友好的高速缓存而不是2路合并。
当N=2
时,您通过迭代弹出两个列表的列表合并,最小。在某种程度上,您创建了一个支持pop_front
操作的虚拟列表,该列表为:
pop_front(a, b): return if front(a) <= front(b) then pop_front(a) else pop_front(b)
您可以很好地安排像树一样的合并方案,其中这些虚拟列表成对合并:
pop_front(a, b, c, d): return if front(a, b) <= front(c, d) then pop_front(a, b) else pop_front(c, d)
每个流行音乐都将涉及到树中的每个级别,从而导致每个pop的成本O(Log k)
。
上述推理是错误的,因为它不考虑front
操作,涉及两个元素之间的比较,这将级联级联,最后需要每个输出元素的k-1
比较。
可以通过"记忆"前元素来规避这一点,即在进行比较后将其保留在两个列表旁边。然后,当弹出一个元素时,此前元素会更新。
正如@trincot所建议的。
5 7 32 21
5
6 4 8 23 40
2
7 7 20 53
2
2 4 6 8 10