Data structures 实现队列的最佳数据结构是什么?
我只需要操作enque和dequeue 可以使用数组(内存中的连续空格) 您也可以使用链表(不一定是连续的) 数组及其更奇特的派生(ArrayList、vector等)可能更复杂。它们的效率不高,因为如果您开始添加太多的元素,可能会耗尽连续的内存空间,您将不得不将队列中的所有内容复制到新的内存块中 在我看来,一个链表似乎相当有效,只要你能跟踪前面和后面(头和尾,随便你怎么称呼它)Data structures 实现队列的最佳数据结构是什么?,data-structures,queue,Data Structures,Queue,我只需要操作enque和dequeue 可以使用数组(内存中的连续空格) 您也可以使用链表(不一定是连续的) 数组及其更奇特的派生(ArrayList、vector等)可能更复杂。它们的效率不高,因为如果您开始添加太多的元素,可能会耗尽连续的内存空间,您将不得不将队列中的所有内容复制到新的内存块中 在我看来,一个链表似乎相当有效,只要你能跟踪前面和后面(头和尾,随便你怎么称呼它) 这可能会有所帮助:从理论上讲,一个既有头又有尾的单链表。从前面移除,附加到尾部。这就是理论上的O(1)常数时间复杂度
这可能会有所帮助:从理论上讲,一个既有头又有尾的单链表。从前面移除,附加到尾部。这就是理论上的O(1)常数时间复杂度(即使是最坏的情况),它的存储量比双链表少 从实用的角度来看,使用循环索引,基于可扩展数组的连续结构可以执行得更好。由于空间位置(例如,适合多个相邻元素的缓存线),硬件擅长处理连续内存。它们的算法复杂度更差,只有摊销的常数时间和最坏情况下的线性时间复杂度(虽然这种情况很少发生,所以通常不太重要) 同样从实用的角度来看,展开列表也可以很好地工作(基本上是存储在链接在一起的节点中的多个元素的数组,为您提供了引用的位置+保证的恒定时间排队和退队) 这里很难说“最好”,因为这取决于你的需要 例如,带有尾部的单链表具有每个节点的分配/解除分配开销和丢失引用位置的弱点,除非您使用一个高效、连续的分配器来支持这一弱点,以帮助缓解这些弱点。它还为每个元素支付了一个列表指针/ref的内存开销(由于每个节点的单独分配,还可能增加一些内存开销)。正如评论中指出的那样,链表通常远不如它们听起来那么好,因为它们与硬件的实际性质不太吻合(至少在没有分配器的大量帮助的情况下) 圆形阵列有一个弱点,即它需要过剩的容量来减少重新分配(或者最坏情况下的线性时间复杂性将更频繁地出现)和拷贝(尽管在某些情况下它们可能很浅)。此外,由于它只是一个大的连续内存块,如果您使用的是大量数据集,即使在使用虚拟寻址的机器上,也可能会出现内存不足错误(内存不足并不一定意味着在这种情况下所有内存都已用完,这意味着找不到与请求大小匹配的连续未使用页面集)
展开列表减轻了列表指针和节点分配开销,但在节点中存储了一些多余的容量,如果您使用的展开列表的容量为每个节点存储64个元素,而您只在队列中存储了3个元素,那么这可能会造成相当大的浪费。我不建议使用
数组(或在数组
之上实现的任何数据结构)因为dequeue
操作将导致所有元素的移动。在这种情况下,我会选择单端linkedList
,在此处插入末尾并从开头删除,但如果要从最后一个节点删除,则需要双端linkedList
,因为需要倒数第二个节点上的句柄才能删除最后一个节点(dequeue
),如果是单指针linkedlist
,它将导致扫描整个列表。这在很大程度上取决于语言。不是所有语言都有相同的功能。您使用的是什么语言?关于链表结构,这一点非常好。我觉得很多初学者过度使用它(如果它有任何实际用途的话)。我认为链表实际上非常有用,但通常不是现成的。每个节点的分配/取消分配开销和局部性损失往往使它在实践中比理论上更糟糕。当它与一个有效的O(1)相结合时固定了分配器,那么通过在固定时间内修改指针来传输整个元素列表的好处在需要进行大量传输的数据结构中非常有用。