deque如何具有摊销的常数时间复杂度

时间:2015-01-20 16:46:08

标签: c++ deque c++03

我从接受的答案中读到here std :: deque具有以下特征

1- Random access - constant O(1)
2- Insertion or removal of elements at the end or beginning - amortized constant O(1)
3- Insertion or removal of elements - linear O(n)

我的问题是关于第2点。双端队列如何在结束或开始时进行摊销不断的插入?

我知道std::vector在最后插入时具有摊销的常数时间复杂度。这是因为矢量是连续的并且是动态阵列。因此,当最后一个push_back的内存不足时,它将分配一个全新的内存块,将现有项目从旧位置复制到新位置,然后从旧位置删除项目。我理解的这个操作是摊销不变的。这如何适用于双端队列?如何在双端队列的顶部和底部插入是否可以摊销。我的印象是它应该是常数O(1)。我知道一个双端队列由内存块组成。

2 个答案:

答案 0 :(得分:9)

deque的通常实现基本上是指向固定大小节点的指针向量。

分配固定大小的节点显然具有恒定的复杂性,因此非常容易处理 - 您只需分摊在该节点中的多个项目之间分配单个节点的成本,以获得恒定的复杂性每个

指针部分的向量是什么(略微)更有趣。当我们分配足够的固定大小节点时,指针向量已满,我们需要增加向量的大小。像std::vector一样,我们需要将其内容复制到新分配的向量中,因此它的增长必须遵循几何(而不是算术)进展。这意味着我们有更多的指针可以复制,我们复制的次数越来越少,因此复制指针的总时间保持不变。

作为旁注:"向量" part通常被视为循环缓冲区,因此如果您将deque用作队列,不断添加到一端并从另一端移除不会导致重新分配向量 - 它只是意味着移动头部和尾指针,跟踪哪些指针是"活跃"在给定的时间。

答案 1 :(得分:1)

(亵渎)答案在于containers.requirements.general,23.2.1 / 2:

  

本条款中的所有复杂性要求仅在   包含对象的操作次数。

因此,重新分配指针数组不会被标准的复杂性保证所涵盖,并且可能需要任意长时间。如前所述,它可能会在任何" sane"中为每个push_front() / push_back()调用(或等效修饰符)添加一个摊销的常量开销。实现。我建议不要在RT-critical代码中使用deque。通常,在RT场景中,您不希望拥有无限制的队列或堆栈(在C ++中默认情况下使用deque作为底层容器),无论如何内存分配都不会失败,因此您将成为最有可能使用预分配的环形缓冲区(例如Boost' s circular_buffer)。