【问题标题】:What is the best data structure to implement a queue? [closed]实现队列的最佳数据结构是什么? [关闭]
【发布时间】:2015-05-28 20:53:07
【问题描述】:

我只需要入队和出队操作。

【问题讨论】:

  • 嗯,这在很大程度上取决于语言。并非所有语言都具有相同的功能。您使用什么语言?

标签: data-structures queue


【解决方案1】:

从理论上讲,一个有头有尾的单链表。从前面移除,附加到尾部。在那里,您有理论上的 O(1) 恒定时间复杂度(即使在最坏的情况下),而且存储空间比双向链表要少。

从实际的角度来看,可增长的基于数组的连续结构可以使用循环索引更好地执行。由于空间局部性(例如适合多个相邻元素的缓存行),硬件擅长处理连续内存。那些算法复杂度更差,只有摊销的常数时间和最坏情况下的线性时间复杂度(尽管它很少见,通常并不重要)。

此外,从实际的角度来看,展开列表也可以很好地工作(基本上存储在链接在一起的节点中的多个元素的数组,为您提供引用的局部性 + 保证恒定时间的入队和出队)。

“最好”在这里真的很难说,因为它取决于您的需求。

例如,带有尾部的单链表具有每个节点的分配/解除分配开销和丢失引用位置的弱点,除非您使用有助于缓解这些弱点的高效、连续分配器来支持这一点。它还为每个元素支付列表指针/引用的内存开销(由于每个节点的单独分配,可能还会增加一些)。正如 cmets 中所指出的那样,链表通常远不如听起来那么好,因为它们与硬件的实际性质并不一致(至少没有分配器的大量帮助)。

循环数组有一个弱点,它需要过多的容量来减少重新分配的数量(否则最坏情况下的线性时间复杂度会更频繁地出现)和副本(尽管在某些情况下它们可能很浅) .此外,由于它只是一个大的连续内存块,如果您正在处理大量数据集,即使在具有虚拟寻址的机器上也可能出现内存不足错误(内存不足并不一定意味着所有内存都已用完)在这种情况下,这意味着未找到与请求大小匹配的连续未使用页面集)。

展开列表减轻了列表指针和节点分配开销,但在节点中存储了一些多余的容量,这可能会非常浪费,例如,如果您使用能够存储每个节点 64 个元素的展开列表并且您正在只需在队列中存储 3 个元素。

【讨论】:

  • 关于链表结构非常好。我觉得很多初学者过度使用它(如果它有任何实际用途的话)。
  • 我认为链表其实很有用,但一般不是开箱即用的。每个节点的分配/解除分配开销和局部性的损失在实践中往往比理论上更糟糕。当它与高效的 O(1) 固定分配器结合使用时,只需在恒定时间内修改指针即可传输整个元素列表的好处在需要进行大量传输的数据结构中非常有用。跨度>
【解决方案2】:

您可以使用数组(内存中的连续空格) 你也可以使用链表(不一定是连续的)

Array 及其更高级的衍生物(ArrayList、vector 等)可能更复杂。它们效率不高,因为如果开始添加太多元素,可能会耗尽连续内存空间,并且必须将队列中的所有内容复制到新的内存块中。

对我来说,链表似乎很有效,只要你跟踪前后(头和尾,随便你怎么称呼它)。

这可能会有所帮助:https://www.cs.bu.edu/teaching/c/queue/linked-list/types.html

【讨论】:

    【解决方案3】:

    我不推荐array(或在array 之上实现的任何数据结构),因为dequeue 操作会导致所有元素的移动。在这种情况下,我会选择single ended linkedList,其中您在末尾插入并从开头删除,但如果您想从最后一个删除,则需要doubly linkedlist,因为您需要倒数第二个节点上的句柄来删除最后一个节点(dequeue),这将导致扫描单指针linkedlist的完整列表。

    【讨论】:

    • 在数组版本中,我们只需移动 HEAD 指针。我们实际上并没有删除和移动元素。
    猜你喜欢
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    • 2019-08-28
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    相关资源
    最近更新 更多