如果我使用合并 2 个已排序链表的算法,我想知道合并 ? 已排序列表的真实时间复杂度。
有多种算法可供选择。
我们将?定义为一个排序列表中值的(最大)个数,因此值的总数以??为界。 ? 的明确定义很重要,因为有些人会将其定义为所有列表中值的 total 数量 - 这显然会导致不同的公式。我将值的总数定义为?。所以?=??
朴素算法
此算法合并前两个列表,然后将结果与第三个列表合并,然后将结果与第四个列表合并,......等等。
该算法需要执行 ?−1 次合并,每次合并将比前一次合并 ? 更多的项目。第一次合并涉及两个?元素列表,所以它是 O(2?)
因此我们有以下关系?:
?(2) = O(2?)
?(?) = ?(?−1) + O(??)
所以:
?(?) = O(?∑?=2..??) = O(??²) = O(??)
最后一步使用triangular numbers的公式
分而治之算法
(二进制)分治算法定义了当您只有 2 个列表时的基本情况,并将它们与已知算法合并。在所有其他情况下,它会合并来自递归的两个列表。这确实是merge sort 使用的原理。
现在递归关系如下:
?(2) = O(2?)
?(?) = 2?(?/2) + O(??)
如果我们将这个关系展开一次,我们得到:
?(?) = 2(2?(?/4) + O(??/2)) + O(??) = 4?(?/4) + O(??) + O(??)
如果再次展开:
?(?) = 8T(?/8) + O(3??)
log2? - 1 次扩展后:
?(?) = (?/2)?(2) + O(??log?)
代入?(2),我们得到:
?(?) = O(??) + O(??log?) = O(??log?) = O(?log?)
基于堆的算法
当您创建一个二进制最小堆时,其中每个条目都是一个链表,由其第一个节点的值作为键,然后您可以重复从堆中提取具有最小元素的列表,从该列表中提取该元素,然后将该列表再次插入堆中,它将根据其新键找到它的位置(因为它现在少了一个节点)。如果列表变为空,则不再将其插入堆中。
构建堆的成本为 O(?)。一个堆提取和插入的成本为 O(log?)。有 ?? 提取要做,所以复杂度是 O(?) + O(??log?) = O(??log?) = O(?log?)