Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ deque如何具有摊销的恒定时间复杂性_C++_Deque_C++03 - Fatal编程技术网

C++ deque如何具有摊销的恒定时间复杂性

C++ deque如何具有摊销的恒定时间复杂性,c++,deque,c++03,C++,Deque,C++03,我从公认的答案中了解到,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) 我的问题是关于第二点。deque如何在末尾或开头插入摊销常量 我知道std::vector对于最后的插

我从公认的答案中了解到,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)
我的问题是关于第二点。deque如何在末尾或开头插入摊销常量


我知道
std::vector
对于最后的插入有一个摊销的恒定时间复杂度。这是因为向量是连续的,并且是动态数组。因此,当最后一次
推回操作的内存不足时,它将分配一个全新的内存块,将现有项目从旧位置复制到新位置,然后从旧位置删除项目。据我所知,这一操作是摊销常数。这一点如何适用于德克?deque顶部和底部的插入如何摊销为常数。我的印象是它应该是常数O(1)。我知道deque是由内存块组成的。

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

分配固定大小的节点显然具有恒定的复杂性,因此这很容易处理——您只需将分配单个节点的成本分摊到该节点中的项目数量上,即可获得每个节点的恒定复杂性

指针向量部分(稍微)更有趣。当我们分配足够多的固定大小的节点以使指针向量满时,我们需要增加向量的大小。与std::vector类似,我们需要将其内容复制到新分配的向量,因此它的增长必须遵循几何(而不是算术)级数。这意味着我们有更多的指针要复制,我们复制的频率越来越低,因此用于复制指针的总时间保持不变

作为旁注:“向量”部分通常被视为循环缓冲区,因此,如果您将deque用作队列,不断地向一端添加和从另一端删除不会导致重新分配向量——这只意味着移动头指针和尾指针,跟踪哪些指针处于“活动”状态在给定时间。

答案在于容器。要求。概述,23.2.1/2:

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


因此,重新分配指针数组不属于标准的复杂性保证范围,可能需要任意长的时间。如前所述,在任何“正常”实现中,它很可能会为每个
push\u front()
/
push\u back()
调用(或等效的修饰符)添加摊销的固定开销。不过,我不建议在RT关键代码中使用
deque
。一般情况下,在RT场景中,您不希望有无绳队列或堆栈(默认情况下,C++中使用<代码> DEQu< /COD>作为基础容器),无论如何,内存分配都可能失败,因此您将最有可能使用预分配的环缓冲区(例如Boost的代码>循环环缓冲器)。相反。

适用与
std::vector
相同的原则。操作是
O(1)
,当先前分配的插槽已满时,需要重新分配。@njzk2您不需要复制旧值。您只需分配一个新的固定大小的块并在开始/结束时链接它。即O(1);未摊销O(1)[假设固定内存量的分配为O(1)]。也许它使用摊销来允许使用单个大的连续内存块实现,但是在实践中它不是很有用,除非
deque
没有太大变化,并且您确实需要连续内存来非常有效地执行某些特定的操作。@Bakuriu:ok。那就新的分配吧。感谢您的澄清。根据这句话:“在末尾或开头插入或删除元素-常数O(1)”,但如果我正确阅读了标准,23.3.3.4/3规定,
…在数据的开头或结尾插入单个元素总是需要恒定的时间…
其中“总是”一词似乎甚至排除了几何再分配。@MarkB:我认为你的读数是合理的,但我知道满足这一(以及所有其他)要求的唯一方法是将向量部分预分配为你所支持的最大部分,因此你根本不会增加它。这显然是可能的,但我认为大多数人宁愿它增长,即使是以摊销的恒定复杂性为代价。我碰巧同意你的解释,但我想确保我没有遗漏完全明显的东西。@MarkB:不,我不这么认为。然而,可能值得一提的是,您通常希望单个节点中的项数足够大,指针的数量总是相当小,因此1)即使时间增长,也不太可能很大,2)无论如何也很少发生。@MarkB相关gcc讨论:基本上
“本条款中的所有复杂性要求均仅根据所包含对象上的操作数量来说明。“
。因此,它不包括在helper结构上操作的复杂性。
std::vector
通过包装使其看起来像持有
t
,因此几乎所有操作都具有恒定成本操作?该特定示例无效,因为在23.3.7.1/1中,标准保证向量是连续的:“向量的元素是连续存储的,这意味着如果v是一个向量,其中T是bool以外的某种类型,那么它服从恒等式&v[n]==&v[0]+n,对于所有的0?我刚才指出的是,正如我提到的,如果你写了一个容器,它(根据标准规则)会将许多操作计算为非摊销O(1),包括像“擦除一半容器”这样的操作,而不是“对容器排序”。是的,它不是一个
向量,而是一个容器。看起来很有趣