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