选择哪种容器来快速搜索/插入大量数据?

时间:2014-12-04 08:19:47

标签: c++ algorithm vector deque

所以这是一个思想实验。我希望拥有大量的结构集合,例如:

struct
{
    KeyType key;
    ValueType value;
}

我需要通过密钥快速访问并快速插入新值。

我不会使用std :: map因为它对一个结构来说有太大的内存开销,而对于大量的数据来说它可能是巨大的。正确?

接下来我会考虑使用排序的std :: vector和binary_search。搜索很好,但向矢量添加新值会太慢。想象一下,你需要在排序数组的开头添加一个新值,你必须正确地移动数据aaaaaAAAALOT!

如果我使用双端队列怎么办?据我所知,它对于push_back / push_front有O(1),但仍然是O(n)用于插入(因为它无论如何都要移动数据,但是数据更少)。

问题是:

1)在实际情况下,在向量中插入数据的O(n)是否比向量中的O(n)快得多?

2)当你向Deque插入一个值并且它应该进入的存储桶已满时会发生什么?

3)如果您需要存储大量数据并需要两个快速操作:搜索和插入,是否有另一种更好的容器类型?

谢谢!

4 个答案:

答案 0 :(得分:4)

  

我不会使用std :: map因为它对一个结构来说有太大的内存开销,而对于大量的数据来说它可能是巨大的。正确?

这取决于结构的大小......它们越大,开销占总内存使用量的比例越小。例如,一个std::map实现可能平均说每个元素的20个字节的内务处理数据(我只是在你自己的系统上进行了测量),所以如果你的结构大小是几百个字节 - 谁在乎... 。?但是,如果结构保持2 ints,那么它就是一个很大的比例......

  

接下来我会考虑使用排序的std :: vector和binary_search。搜索很好,但向矢量添加新值会太慢。想象一下,你需要在排序数组的开头添加一个新值,你必须正确地移动数据aaaaaAAAALOT!

完全不合适......

  

1)在实际情况下,在向量中插入数据的O(n)是否比向量中的O(n)快得多?

由于deque可能是作为固定大小数组的向量实现的,因此插入意味着将所有元素移动到容器的最近端。洗牌的效率可能稍微低一点,但如果插入容器的前端,它可能会更快地结束。

2)当你向Deque插入一个值并且它应该进入的存储桶已满时会发生什么?

如上所述,它需要随机播放,溢出:

  • 成为下一个“桶”的第一个元素的最后一个元素,移动所有这些元素并溢出到下一个桶中等。

  • 成为前一个存储桶的最后一个元素的第一个元素,移动所有这些元素并溢出到下一个存储桶中等。

  

3)如果您需要存储大量数据并需要两个快速操作:搜索和插入,是否有另一种更好的容器类型?

unordered_map,实现为哈希映射。如果您有小对象(例如少于20或30个字节)或元素数量的固定上限,您通常可以轻松地使用自定义代码优于unordered_map,但除非表格访问占据主导地位,否则很少值得努力应用程序的性能,以及性能至关重要。

答案 1 :(得分:3)

  

3)如果您需要存储大量数据并需要两个快速操作:搜索和插入,是否有另一种更好的容器类型?

考虑使用std::unordered_map,它是哈希映射的实现。插入,查找和删除在平均情况下都是O(1)。这假定您只会根据其确切的密钥查找项目;如果您的搜索可能有不同的约束,那么您需要一个不同的结构,或者您需要多个地图来将您要搜索的各种键映射到相应的对象。

这要求KeyType有一个可用的哈希函数,作为标准库的一部分或由您提供。

答案 2 :(得分:2)

没有容器可以为您提供最好的世界。就像你说的那样,你需要最好的查找/插入,只需要最少的空间来存储元素。

下面是您可以考虑实施的容器列表: -

矢量: -

优势: -

1) Space is allocated only for holding data. 
2) Good for random access.
3) Container of choice if insertions/deletions are not in the middle of the container.

弱点: -

1) poor performance if insertions/deletions are at the middle.
2) rellocations happen if reserve is not used properly.

双端队列: -

如果插入/删除位于容器的开头和末尾,则选择deque over vector。

MAP: -

对矢量的缺点: -

1) more space is allocated for holding pointers.

优于矢量: -

1) better insertions/deletions/lookup as compared to vector.

如果使用std::unordered_map,那么这些字典操作将分摊为O(1)。

答案 3 :(得分:1)

首先,为了直接回答你的问题:

  

1)在真实情况下,在双端队列中插入数据的O(n)是否更快   比载体中的O(n)?

与向量相比,必须移动的元素数量(平均)仅为一半。然而,由于数据存储在非连续存储器中,它实际上可能表现更差,因此复制/移动相同数量的元素的效率要低得多(例如,不能根据单个memcopy操作实现)。

  

2)向Deque及其中插入值时会发生什么   应该进入是否充分?

至少对于gnu gcc Libstdc ++实现,除了第一个和最后一个桶之外的每个桶都是满的。我相信,插入中间意味着所有元素都被移动/复制到一个槽到更近端(前面或后面),效果在所有桶中涟漪直到达到第一个或最后一个。

总之,std :: deque始终优于vector的唯一场景是,如果你将它用作(惊讶)队列(只插入和删除前面或末尾的元素),那就是什么该实现已针对。它没有针对中间的插入进行优化。

  

3)如果需要,是否有另一种更好的容器类型   存储大量数据并需要两个快速操作:搜索和插入?

正如其他人所说:像std :: unordered_map这样的哈希表是您正在寻找的数据结构。

从我所听到的情况来看,std :: unordered_map是一个稍微不太理想的实现,因为它使用存储桶来解决散列冲突,而这些存储桶是作为链表实现的(here 是Chandler Carruth关于不同数据结构性能的一般主题的一个非常有趣的演讲。对于大数据结构的随机访问,缓存局部性应该少得多,所以在你的情况下这可能不是一个大问题。

最后,我想提一下,如果您的价值和关键类型是小POD,并且取决于您的大集合有多大(我们谈论的是数百万或者数十亿元素)以及您实际拥有的频率要插入/删除元素,可能仍然存在一种情况,其中简单的std :: vector优于任何其他STL容器。一如既往:如果您的思想实验成为现实,请尝试并衡量。