【问题标题】:Is std::deque (double ended queue) really random access and constant time insertions?std::deque (双端队列)真的是随机访问和恒定时间插入吗?
【发布时间】:2017-12-26 03:42:59
【问题描述】:

我不断从人们和文档中听到 std::deque 是像 std::vector 一样的随机访问,并且是像链表一样的恒定时间插入和删除。此外,与向量不同,std::deque 可以在恒定时间内在开头和结尾插入元素。我不确定标准是否指定了 std::deque 的特定实现,但它应该代表双端队列,并且很可能该实现将类似向量的分配缓冲区与指针链接在一起。一个关键的区别是元素不像 std::vector 那样连续。

鉴于它们不是连续的,当人们说它们是像向量一样的随机访问时,我是否认为他们的意思是“摊销”而不是实际上作为向量?这是因为向量只涉及一个偏移地址,保证每次真正的恒定时间访问,而双端队列必须计算所请求元素在序列中的距离,并计算沿它必须有多少链接缓冲区go 以找到该特定元素。

同样,在链表中的插入只涉及断开一个链接并设置另一个链接,而在双端队列中的插入(不是最后一个或第一个成员,必须至少在对于该特定链接缓冲区,插入点至少指向一个位置。因此,例如,如果一个链接缓冲区有 10 个成员,并且您碰巧在该特定缓冲区的开头插入,那么它必须至少移动 10 个成员(如果它更糟超出缓冲区容量,不得不重新分配)。

我有兴趣知道这一点,因为我想实现一个队列,而在标准库中,std::queue 就是他们所说的容器适配器,这意味着我们不会确切地知道它是什么容器类型,只要因为它以队列的方式运行。我认为它通常是 std::deque 或 std::list 的一种包装器。鉴于我只对先进先出的简单功能感兴趣,我几乎可以肯定链表在插入队列和从末尾弹出时会更快。所以我很想明确选择它而不是 std::queue。

另外,我知道链表在迭代时对缓存不太友好,但我们将链表与向量而不是双端队列进行比较,据我所知,这必须做相当大的工作记录保存的数量,以便在迭代时知道它在多远以及在哪个特定缓冲区中。

编辑:我刚刚发现对另一个感兴趣的问题的评论:

“我见过的 C++ 实现使用了一个指针数组 固定大小的数组。这实际上意味着 push_front 和 push_back 不是真正的恒定时间,而是智能增长 因素,你仍然得到摊销常数时间,所以 O(1) 不是 如此错误,实际上它比矢量更快,因为你是 交换单个指针而不是整个对象(以及更少的指针 比对象)。 – Matthieu M. 2011 年 6 月 9 日"

发件人:What really is a deque in STL?

【问题讨论】:

  • "std::queue 是他们所说的容器适配器,这意味着我们不知道它是什么容器类型" -- @987654322 使用的底层容器@ 可通过模板参数进行配置。默认为deque<T>
  • deque 在中间没有固定时间插入,只有在末端。
  • @T.C 谢谢,我刚刚看到:“对于涉及频繁插入或删除除开头或结尾以外的位置的元素的操作,双端队列的性能更差,迭代器和引用的一致性也较差而不是列表和转发列表。”
  • 事实证明只在结尾和开头插入分期常数,如果我链接的那个图是真的,那么在开头插入平均比在结尾插入要慢。

标签: c++ vector linked-list std deque


【解决方案1】:

是随机访问,但恒定时间插入和删除只针对序列的开头和结尾,中间是O(n)。

访问一个项目是 O(1),但它的效率不如向量 O(1)。无论它的位置如何,即使无论双端队列中有多少项目,它都将进行相同数量的计算来确定一个项目在双端队列中的位置。将此与一个列表进行比较,在哪里可以找到您必须遍历 n-1 个前面的项目的第 n 个项目。

【讨论】:

  • 我发现一条评论(我把它放在我的问题中)说开头和结尾的插入只是摊销常数(当它不必增长时)。我想这是相同的向量,但不适用于始终保证为常数时间的链表。
  • 在 std::deque 的开头和结尾插入和擦除总是摊销常数,大多数时候它确实是常数,但有时它必须增长(并被复制),这就是'摊销'部分进来了。列表总是 O(1) 插入到特定位置,但作为回报,遍历列表的效率较低。
  • 我在问题中链接的图表很有趣,因为如果很多人说它只是一个向量的向量是真的,那么在开头插入平均比在最后,因为每次创建新缓冲区时,保存向量的向量元素总是必须向前移动。
  • 好吧,想想向量的情况,它有大小和容量,任何未使用的容量总是在最后,双端队列也允许在开头使用未使用的空间,它并不是真正的向量向量(至少不是 std::vector)和 vector 一样多,其中 Block 是正在存储的项目的固定大小单元。块类似于: struct Block { T items[8]; };双端队列知道“第一个”项目的偏移量,并知道它的大小和已分配的块数。所以不仅deque不连续,增长因子也比vector小。
  • deque 不必在扩展时复制。一个合理的实现可以分配并开始填充一个新块。
猜你喜欢
  • 2019-10-07
  • 2020-08-10
  • 2017-01-24
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 2014-04-23
  • 1970-01-01
  • 2014-05-23
相关资源
最近更新 更多