我只需要"enque"和"dequeue"这两个操作。
从理论上讲,一个带有头和尾的单向链表。从前面删除,添加到尾部。这样你就可以拥有理论上的O(1)常数时间复杂度(即使是最坏情况下),并且比双向链表占用更少的存储空间。
从实际角度来看,基于可增长数组的连续结构可以使用循环索引实现更好的性能。硬件在处理连续内存时表现出色,因为它具有空间局部性(适合多个相邻元素的缓存行等)。这些结构的算法复杂度较差,只有摊销常数时间和最坏情况下的线性时间复杂度(虽然这种情况非常罕见,通常不会对性能产生太大影响)。
同样从实际角度来看,展开列表可能效果很好(基本上是存储在节点中的多个元素的数组链接在一起,给你参考的局部性+保证的常数时间入队和出队)。
“最好”的选择真的很难说,因为它取决于您的需求。
例如,带有尾部的单向链表存在节点分配/释放开销和引用局部性丢失的弱点,除非您支持一个高效的连续分配器来帮助缓解这些弱点。它还需要为每个元素支付列表指针/引用的内存开销(加上可能由于每个节点的单独分配而产生的一些额外开销)。正如评论中指出的那样,链表通常并不像听起来那么好,因为它们与硬件实际性质的匹配程度不够高(至少需要通过分配器的大量帮助才能达到理想状态)。array
(或任何基于 array
实现的数据结构),因为 dequeue
操作会导致所有元素的移动。在这种情况下,我会选择 单向链表
,在其中你可以在末尾插入并从开头删除,但是如果你想从末尾删除,则需要使用 双向链表
,因为你需要一个指向倒数第二个节点的指针来删除最后一个节点 (dequeue
),而在单指针 链表
的情况下,这将导致扫描整个列表。