【问题标题】:Merging k sorted linked lists - analysis合并k个排序的链表——分析
【发布时间】:2011-02-11 22:02:54
【问题描述】:

我正在为一个问题考虑不同的解决方案。假设我们有 K 个已排序的链表,并且我们将它们合并为一个。所有这些列表共有 N 个元素。

众所周知的解决方案是使用优先级队列并从每个列表中弹出/推送第一个元素,我可以理解为什么需要O(N log K) 时间。

但让我们看看另一种方法。假设我们有一些 MERGE_LISTS(LIST1, LIST2) 过程,它合并两个排序列表,这将花费 O(T1 + T2) 时间,其中 T1T2 代表 LIST1LIST2 大小。

我们现在所做的通常意味着将这些列表配对并逐对合并它们(如果数字是奇数,例如,最后一个列表可以在第一步被忽略)。这通常意味着我们必须创建以下合并操作的“树”:

N1, N2, N3... 代表LIST1, LIST2, LIST3 尺寸

  • O(N1 + N2) + O(N3 + N4) + O(N5 + N6) + ...
  • O(N1 + N2 + N3 + N4) + O(N5 + N6 + N7 + N8) + ...
  • O(N1 + N2 + N3 + N4 + .... + NK)

很明显,这些行中会有log(K),每个行都执行O(N) 操作,所以MERGE(LIST1, LIST2, ... , LISTK) 操作的时间实际上等于O(N log K)

我的朋友告诉我(两天前)这需要O(K N) 时间。所以,问题是——我是不是在某个地方搞错了,还是他真的错了?如果我是对的,为什么不能使用这种“分而治之”的方法来代替优先队列方法?

【问题讨论】:

  • 嗯,我想它比 priority_queue 算法更糟糕,因为它使用了很多额外的内存。尽管如此,它更容易实现......嗯:)
  • N 在这个问题中定义了所有列表中的元素总数,而不是其中一个列表的平均元素数。
  • 我可以理解会有logK行数,但是你能解释一下为什么每一行都是O(n)吗?比如第一行,不会有k*O(n)吧?
  • 无论你使用什么策略,都会有 NK 个元素被触及。因此,复杂度不能从 O(NK) 降低。

标签: algorithm merge list


【解决方案1】:

如果您有少量列表要合并,这种成对方案可能比优先队列方法更快,因为每次合并的操作极少:基本上每个项目只有一个比较和两个指针重新分配(转移到一个新的单链表)。正如您所展示的,它是O(N log K)log K 处理每个N 项目的步骤)。

但我相信最好的优先级队列算法是 O(sqrt(log K))O(log log U) 用于插入和删除(其中 U 是可能的不同优先级的数量)——如果你可以用一个值而不是有优先级使用比较——所以如果你正在合并可以给出的项目,例如整数优先级,而K 很大,那么最好使用优先级队列。

【讨论】:

  • 我实际上认为值得分析。这种理论分析有时可能会受益,但通常我现在将尝试确定与优先队列方法相比,我的方法开始缺乏什么 K 之后。谢谢你的回答。
【解决方案2】:

它确实在O(N*log K) 中运行,但不要忘记O(N*log K)O(N*K) 的子集。 IE。你的朋友也没有错。

【讨论】:

  • 看起来是挽救我们友谊的绝妙方法 :))
【解决方案3】:

这是O(2*log(K)*N) 这是O(N*log(K)),你不可能有最糟糕的复杂性,因为你只有2N 次添加到O(log(K)) 中的优先级队列。

或者您可以将所有元素推入O(2N) 中的向量中。并在O(2n*log(2n)) 中排序。然后你有O(2N+2N*Log(2N)),这是O(N*LOG(N)),正是你的K = N

【讨论】:

  • 一般来说,我只想知道我的分析是否正确,而不考虑N log K之前的常数。可能,如果这将成为我的代码中的瓶颈位置,我会使用优先队列算法。
【解决方案4】:

根据您的描述,听起来您的过程确实是 O(N log K)。它也可以工作,所以你可以使用它。

我个人会使用带有优先级队列的第一个版本,因为我怀疑它会更快。在粗略的 big-O 意义上它并没有更快,但我认为如果你真的计算出两者所进行的比较和存储的数量,第二个版本将需要多几倍的工作。

【讨论】:

    猜你喜欢
    • 2019-07-22
    • 2019-03-20
    • 2018-04-19
    • 1970-01-01
    • 2011-01-21
    相关资源
    最近更新 更多