所以这是一个思想实验。我希望拥有大量的结构集合,例如:
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)如果您需要存储大量数据并需要两个快速操作:搜索和插入,是否有另一种更好的容器类型?
谢谢!
答案 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容器。一如既往:如果您的思想实验成为现实,请尝试并衡量。