【问题标题】:Why prefer std::vector over std::deque? [duplicate]为什么更喜欢 std::vector 而不是 std::deque? [复制]
【发布时间】:2013-09-26 14:48:56
【问题描述】:

它们都具有 O(1) 的访问复杂度和 O(n) 的随机插入/删除复杂度。但是vector由于重新分配和复制而在扩展时成本更高,而deque则没有这个问题。

似乎deque性能更好,但为什么大多数人使用vector而不是deque?

【问题讨论】:

  • vector 在访问项目时效率更高。两者的 O(1) 复杂度并不意味着两者都使用相同数量的周期。此外,如果可能,重新分配 vector 将移动其项目,而不一定要复制它们。
  • @syam 两者的访问复杂度都不是 O(1) 吗?
  • @texasbruce:是的;但是deque 通常有一个额外的间接级别,使访问速度变慢。 O(1) 只是意味着它不会随着容器变大而变得更糟。两者都已摊销了 O(1) 时间以将元素添加到末尾;但是您认为向量(有时)较慢的观点仍然成立。
  • @texasbruce 一个特定的操作可以有 O(1) 复杂度,需要 1000 个周期来执行,O(1) 复杂度但只需要 10 个周期。
  • 值得一提的是,MSVC 的双端队列实现很容易有效地退化为一个指针向量(如果 sizeof(T) > 16),它就像一个向量,但具有额外的间接和分配。

标签: c++ stl


【解决方案1】:
why most people use vector instead of deque?

因为这是他们被教导的。

vectordeque 的用途略有不同。如果您需要的话,它们都可以用作简单的对象容器。在学习 C++ 编程时,大多数人所需要的——一个可以放入、取出和走过的桶。

当 StackOverflow 被问到“我应该默认使用哪个容器”之类的问题时,答案几乎总是vector。这个问题通常是从学习 C++ 编程的上下文中提出的,并且在程序员提出这样的问题时,他们还不知道他们不知道什么。还有很多他们还不知道。因此,我们 (StackOverflow) 需要一个容器,它几乎可以满足所有需求,无论好坏,几乎可以在任何情况下使用,并且不需要程序员在找到近似正确答案之前提出所有正确的问题。此外,该标准特别推荐使用vectorvector 并不是最佳 适合所有用途,事实上deque 在许多常见用途方面都比vector 好——但对于学习程序员来说,我们应该改变它并没有那么好来自标准对新手 C++ 程序员的建议,因此 StackOverflow 登陆 vector

在学习了语法的基础知识以及(容我们说)C++ 编程背后的策略之后,程序员分成了两个分支:一个喜欢学习更多并编写更好的程序,另一个不喜欢。那些不这样做的人将永远坚持vector。我想很多程序员都属于这个阵营。

试图超越这个阶段的少数程序员开始提出其他问题——就像你在这里提出的问题。他们知道有很多他们还不知道,他们想开始发现这些东西是什么。他们会很快(或较慢)发现,在vectordeque 之间进行选择时,一些他们以前没想过要问的问题是:

  1. 我需要内存是连续的吗?
  2. 我是否需要避免大量重新分配?
  3. 插入后是否需要保留有效的迭代器?
  4. 我的集合是否需要与某些古老的类 C 函数兼容?

然后他们真正开始思考他们正在编写的代码,发现更多他们不知道的东西,然后节拍继续......

【讨论】:

  • +1 仅用于打孔列表。 (我能想到过去与我共事过的许多“专业人士”在选择他们的容器之前没有考虑这些问题,这很可悲)。剩下的就是结冰了。很好的答案。
【解决方案2】:

来自 C++ 标准第 23.1.1 节:

vector is the type of sequence that should be used by default... deque is 
the data structure of choice when most insertions and deletions take place
at the beginning or at the end of the sequence.

但是opposite direction 中有一些参数。

理论上vector 至少与deque 一样高效,因为它提供了一部分功能。如果您的任务只需要 vector 的接口提供的内容,请首选 vector - 它不会比双端队列更差。

【讨论】:

  • @syam 但也不禁止双端队列也有连续存储。效率低下,但仍有可能。
  • @syam 实际上,如果您像在后面一样将内存留在前面(即将数据放在内存块的中间),那么push_front 将与@987654327 一样工作@
  • 我正在删除导致进一步争论的段落部分:)
  • @cluracan, IvayloStrandjev:然后删除我现在没用的 cmets。 ;)
  • 值得注意的是,Herb Sutter(链接文章的作者)很久以前就改用vectorcoding.derkeiler.com/Archive/C_CPP/comp.lang.cpp/2004-10/…
【解决方案3】:

但由于重新分配和复制,向量在扩展时成本更高

虽然vector 确实有时必须随着它的增长重新分配它的数组,但它会呈指数增长,因此摊销复杂度仍然是 O(1)。通常,您可以通过明智地使用 reserve() 来避免重新分配。

似乎双端队列的性能更好

性能有很多方面; push_back 花费的时间只是一。在某些应用程序中,容器可能很少被修改,或者在启动时填充然后从不修改。在这种情况下,迭代和访问速度可能更重要。

vector 是最简单的容器:一个连续的数组。这意味着可以通过简单的指针算法来实现迭代和随机访问,并且访问元素可以像解引用指针一样快。

deque 有进一步的要求:它不能移动元素。这意味着典型的实现需要额外的间接级别——它通常被实现为类似于数组指针的数组。这意味着元素访问需要解引用两个指针,这将比一个慢。

当然,速度通常不是关键问题,您根据容器的行为属性而不是性能来选择容器。如果您需要元素是连续的,您可能会选择vector,也许是为了使用基于指针和数组的 API。如果你想保证元素不会移动,你可以选择dequelist,这样你就可以存储指向它们的指针。

【讨论】:

  • +1 这个和另一个可能是直接回答 OP 的问题和观察的最佳措辞答案。不过,我有一个问题要问你。确实,向量具有指数增长模式,但就这一事实的本质而言,摊销插入复杂性不是几何衰减(即倒数)吗?小是的,但是1,我不明白。
  • @WhozCraig:n 大小的重新分配最多会在每次 n 插入时发生一次。所以摊销成本是O(n/n) == O(1)
  • 嘿。天哪,我需要在做数学之前喝杯咖啡。谢谢,迈克。非常赞赏。
【解决方案4】:

对于 cplusplus :

因此,它们提供了与向量类似的功能,但具有 元素的有效插入和删除也在开头 序列,而不仅仅是在它的结尾。但是,与向量不同,双端队列是 不保证将其所有元素存储在连续存储中 位置,因此不允许通过偏移指针直接访问 元素。

【讨论】:

    【解决方案5】:

    就我个人而言,我更喜欢使用deque(我最终总是会宠坏自己,并且出于某种原因不得不使用push_front),但vector确实有它的用途/区别,主要的是:

    vector 具有连续内存,而deque 通过页/块分配。 请注意,页面/块相当有用:在容器前面的恒定时间插入/擦除。同样典型的是,将一大块内存分成一系列较小的块比单个内存块更有效

    您也可以争辩说,因为deque 是“缺少”大小预留方法(capacity/reserve),所以您不必担心。

    我强烈建议您阅读萨特斯关于该主题的GotW

    【讨论】:

      猜你喜欢
      • 2012-11-25
      • 2013-02-05
      • 1970-01-01
      • 2011-06-30
      • 2012-03-07
      • 2019-02-25
      • 2019-04-20
      • 2015-01-15
      • 2017-10-27
      相关资源
      最近更新 更多