数组vs矢量vs列表

时间:2009-12-15 05:51:11

标签: c++ arrays list stl vector

我正在维护一个包含10个条目的固定长度表。每个项目都是4个字段的结构。将有数字位置指定的插入,更新和删除操作。我想知道哪个是用于维护此信息表的最佳数据结构:

  1. 数组 - 插入/删除由于移位而占用线性时间;更新需要恒定的时间;没有空间用于指针;使用[]访问项目的速度更快。

  2. stl vector - 插入/删除由于移位而需要线性时间;更新需要恒定的时间;没有空间用于指针;访问项目比数组慢,因为它是对operator []和链表的调用。

  3. stl list - 插入和删除需要线性时间,因为在应用插入/删除之前需要迭代到特定位置;指针需要额外的空间;访问项目比数组慢,因为它是链接列表线性遍历。

  4. 现在,我的选择是使用数组。这是否合理?或者我错过了什么?

    哪个更快:遍历列表,然后插入节点或在数组中移动项目以产生空位置然后将项目插入该位置?

    衡量这种表现的最佳方法是什么?我可以在操作之前和之后显示时间戳吗?

8 个答案:

答案 0 :(得分:49)

使用STL vector 。它提供了与list同样丰富的接口,消除了管理数组所需内存的痛苦。

您必须非常努力地公开operator[]的性能成本 - 它通常会被内联。

我没有任何号码可以给你,但我记得阅读性能分析,描述vector<int>如何比list<int>更快,即使对于插入和删除(当然在一定规模下)。问题的真相是我们使用的这些处理器非常快 - 如果你的矢量适合L2缓存,那么它真的会非常快。另一方面,列表必须管理会破坏L2的堆对象。

答案 1 :(得分:25)

过早优化是万恶之源。

根据您的帖子,我认为没有理由在这里选择基于性能的数据结构。选择最方便的东西,当且仅当性能测试表明它是一个问题时,才返回进行更改。

答案 2 :(得分:12)

真正值得花一些时间来理解列表和向量之间的根本区别。 两者之间最显着的区别是它们存储元素并跟踪它们的方式。

- 列表 -

List包含具有存储在其中的previous和next元素的地址的元素。这意味着无论列表大小如何,您都可以使用恒定速度O(1)在列表中的任何位置插入或删除元素。您还可以在任何地方以恒定速度拼接(插入另一个列表)到现有列表中。原因是list只需要为我们插入列表的元素更改两个指针(前一个和下一个)。

如果您需要随机访问,列表并不好。因此,如果计划访问列表中的第n个元素 - 必须逐个遍历列表 - O(n)速度

- 向量 -

Vector包含序列中的元素,就像数组一样。这对于随机访问非常方便。访问向量中的“第n”个元素是一个简单的指针计算(O(1)速度)。然而,向矢量添加元素是不同的。如果想在向量中间添加一个元素 - 必须重新分配该元素之后的所有元素,以便为新条目腾出空间。速度取决于矢量大小和新元素的位置。最糟糕的情况是在向量中插入位置2处的元素,最好的是附加新元素。因此 - 插入与速度O(n)一起工作,其中“n”是需要移动的元素的数量 - 不一定是矢量的大小。

还有其他差异涉及内存要求等,但理解列表和向量实际工作方式的这些基本原则确实值得花一些时间。

一如既往......“过早优化是所有邪恶的根源”所以首先要考虑什么更方便,让事情按照你想要的方式工作,然后进行优化。对于您提到的10个条目 - 使用什么并不重要 - 无论您使用何种方法,都无法看到任何性能差异。

答案 3 :(得分:6)

首选std :: vector over和array。矢量的一些优点是:

  • 当增加大小时,它们会从可用空间分配内存。
  • 他们不是伪装的指针。
  • 他们可以增加/减少运行时间。
  • 他们可以使用 at()进行范围检查。
  • 向量知道它的大小,因此您不必计算元素。

使用向量的最令人信服的理由是它使您免于显式内存管理,并且它不会泄漏内存。向量跟踪它用于存储其元素的内存。当向量需要更多元素内存时,它会分配更多内存;当向量超出范围时,它释放内存。因此,用户无需关心向量元素的内存分配和释放。

答案 4 :(得分:3)

您正在做出您不应该做的假设,例如“访问项目比数组慢,因为它是对运算符[]的调用。”我可以理解它背后的逻辑,但在我们描述它之前你和我都可以知道。

如果这样做,您会发现在启用优化时根本没有开销。编译器内联函数调用。 内存性能的差异。静态分配数组,而向量动态分配。列表为每个节点分配,如果您不小心,可以限制缓存。

有些解决方案是从堆栈中分配vector,并为list设置池分配器,以便节点可以适应缓存。

因此,您不必担心不受支持的声明,而应该担心让您的设计尽可能干净。那么哪个更有意义呢?数组,向量还是列表?我不知道你要做什么,所以我不能回答你。

“默认”容器往往是vector。有时阵列也是完全可以接受的。

答案 5 :(得分:1)

首先注意几点:

关于选择数据结构的一个好的经验法则:通常,如果您检查了所有可能性并确定数组是您的最佳选择,请重新开始。你做错了。

STL列表不支持operator[],如果它们的原因是它比索引数组慢,则与函数调用的开销无关。

正如所说的那样,矢量在这里是明显的赢家。对operator[]的调用基本上可以忽略不计,因为向量的内容保证在内存中是连续的。它支持insert()erase()操作,如果使用数组,您将自己编写这些操作。基本上它归结为这样一个事实:向量本质上是一个升级的数组,它已经支持你需要的所有操作。

答案 6 :(得分:0)

  

我正在维护一个包含10个条目的固定长度表。每个项目都是   4个领域的结构。将有插入,更新和删除   操作,由数字位置指定。我想知道哪个是   用于维护此信息表的最佳数据结构:

基于此描述,似乎列表可能是更好的选择,因为在数据结构的中间插入和删除时它是O(1)。不幸的是,当使用列表进行插入和删除时,你不能使用数字位置,就像你可以使用数组/向量一样。这种困境导致了一系列问题,这些问题可用于初步决定哪种结构最适合使用。如果测试清楚地显示出错误的选择,以后可以更改此结构。

您需要提出的问题有三个问题。首先是您计划在相对于随机读取的中间进行删除/插入的频率。第二个是与迭代器相比使用数字位置的重要性。最后,你的结构中的顺序很重要。

如果第一个问题的答案是随机读取将比矢量/数组更可能正常工作。注意,即使使用operator []表示法,迭代数据结构也不被视为随机读取。对于第二个问题,如果你绝对需要数字位置而不是矢量/数组,即使这可能会导致性能下降。后来的测试可能表明,相对于使用数字位置更容易编码,这种性能损失可以忽略不计。最后,如果顺序不重要,您可以使用O(1)算法在矢量/数组中插入和删除。示例算法如下所示。

template <class T>
void erase(vector<T> & vect, int index) //note: vector cannot be const since you are changing vector
{
  vect[index]= vect.back();//move the item in the back to the index
  vect.pop_back(); //delete the item in the back
}

template <class T>
void insert(vector<T> & vect, int index, T value) //note: vector cannot be const since you are changing vector
{
  vect.push_back(vect[index]);//insert the item at index to the back of the vector
  vect[index] = value; //replace the item at index with value
}

答案 7 :(得分:0)

我相信,如果您需要在起始或中间用途列表中进行更多插入/删除操作(内部双向链接),并且您需要随机访问数据并添加到最后一个元素使用数组(向量具有动态分配,如果您需要更多操作来进行排序,调整大小等,请使用向量)