【发布时间】:2015-05-28 20:53:07
【问题描述】:
我只需要入队和出队操作。
【问题讨论】:
-
嗯,这在很大程度上取决于语言。并非所有语言都具有相同的功能。您使用什么语言?
标签: data-structures queue
我只需要入队和出队操作。
【问题讨论】:
标签: data-structures queue
从理论上讲,一个有头有尾的单链表。从前面移除,附加到尾部。在那里,您有理论上的 O(1) 恒定时间复杂度(即使在最坏的情况下),而且存储空间比双向链表要少。
从实际的角度来看,可增长的基于数组的连续结构可以使用循环索引更好地执行。由于空间局部性(例如适合多个相邻元素的缓存行),硬件擅长处理连续内存。那些算法复杂度更差,只有摊销的常数时间和最坏情况下的线性时间复杂度(尽管它很少见,通常并不重要)。
此外,从实际的角度来看,展开列表也可以很好地工作(基本上存储在链接在一起的节点中的多个元素的数组,为您提供引用的局部性 + 保证恒定时间的入队和出队)。
“最好”在这里真的很难说,因为它取决于您的需求。
例如,带有尾部的单链表具有每个节点的分配/解除分配开销和丢失引用位置的弱点,除非您使用有助于缓解这些弱点的高效、连续分配器来支持这一点。它还为每个元素支付列表指针/引用的内存开销(由于每个节点的单独分配,可能还会增加一些)。正如 cmets 中所指出的那样,链表通常远不如听起来那么好,因为它们与硬件的实际性质并不一致(至少没有分配器的大量帮助)。
循环数组有一个弱点,它需要过多的容量来减少重新分配的数量(否则最坏情况下的线性时间复杂度会更频繁地出现)和副本(尽管在某些情况下它们可能很浅) .此外,由于它只是一个大的连续内存块,如果您正在处理大量数据集,即使在具有虚拟寻址的机器上也可能出现内存不足错误(内存不足并不一定意味着所有内存都已用完)在这种情况下,这意味着未找到与请求大小匹配的连续未使用页面集)。
展开列表减轻了列表指针和节点分配开销,但在节点中存储了一些多余的容量,这可能会非常浪费,例如,如果您使用能够存储每个节点 64 个元素的展开列表并且您正在只需在队列中存储 3 个元素。
【讨论】:
您可以使用数组(内存中的连续空格) 你也可以使用链表(不一定是连续的)
Array 及其更高级的衍生物(ArrayList、vector 等)可能更复杂。它们效率不高,因为如果开始添加太多元素,可能会耗尽连续内存空间,并且必须将队列中的所有内容复制到新的内存块中。
对我来说,链表似乎很有效,只要你跟踪前后(头和尾,随便你怎么称呼它)。
这可能会有所帮助:https://www.cs.bu.edu/teaching/c/queue/linked-list/types.html
【讨论】:
我不推荐array(或在array 之上实现的任何数据结构),因为dequeue 操作会导致所有元素的移动。在这种情况下,我会选择single ended linkedList,其中您在末尾插入并从开头删除,但如果您想从最后一个删除,则需要doubly linkedlist,因为您需要倒数第二个节点上的句柄来删除最后一个节点(dequeue),这将导致扫描单指针linkedlist的完整列表。
【讨论】: