由于几天前的this问题,有一些事情让我对std::deque::push_back/push_front
与实际std::deque
实施的复杂性要求有所不满。野。
上一个问题的结果是这些操作需要O(1)
最坏情况的复杂性。我确认c++11
确实存在这种情况:
来自23.3.3.4 deque修饰符,参考insert,push / emplace front / back
复杂性:插入的元素数量加上复杂度是线性的 到双端队列开始和结束的距离较小。插入单个 在deque的开头或结尾处的元素总是需要恒定的时间和 导致对T的构造函数的单个调用。
这与索引的O(1)
复杂性要求相结合,通过operator[]
等。
问题是实现并不严格满足这些要求。
就msvc
和gcc
而言,std::deque
实现是一个被阻塞的数据结构,由指向(固定大小)块的动态指针组成,每个块存储一个数据元素的数量。
在最坏的情况下,push_back/front etc
可能需要分配一个额外的块(这是很好的 - 固定大小分配是O(1)
),但它也可能要求块指针的动态数组是调整大小 - 这不是很好,因为这是O(m)
,其中m
是块的数量,在一天结束时是O(n)
。
显然,这仍然是摊销O(1)
的复杂性,而且一般来说m << n
在实践中它会非常快。但似乎存在一致性问题?
另外一点,我不知道如何设计一个严格满足O(1)
和push_back/front etc
operator[]
复杂度的数据结构。您可以拥有块指针的链接列表,但这不会为您提供所需的operator[]
行为。真的可以吗?
答案 0 :(得分:3)
在C ++ 11 FDIS中,我们可以阅读:
23.2.3序列容器[sequence.reqmts]
16 / 表101列出了为某些类型的序列容器而非其他序列容器提供的操作。实现应为“容器”列中显示的所有容器类型提供这些操作,并应实施它们以便摊销的恒定时间。
其中表101 被命名为可选序列容器操作,并列出deque
和push_back
操作的push_front
。
因此,在您引用的段落中,它似乎更像是一个轻微的遗漏。也许值得一个缺陷报告?
请注意,对构造函数的单调用仍然存在。
答案 1 :(得分:0)
我怀疑块指针的重新分配是以几何增加的大小完成的 - 这是std :: vector的常见技巧。我认为这在技术上是O(log m),但是当你指出m&lt;&lt; n,实际上它不会影响现实世界的结果。