对于非重复项目,最有效的标准容器是什么

What is the most efficient std container for non-duplicated items?

本文关键字:标准 是什么 有效 于非重 项目      更新时间:2023-10-16

非重复元素添加到 STL 容器中的最有效方法是什么,哪种容器最快?我有大量的数据,恐怕每次我尝试检查它是否是新元素时,都需要花费很多时间。我希望地图非常快。

// 1- Map
map<int, int> Map;
...
if(Map.find(Element)!=Map.end()) Map[Element]=ID;
// 2-Vector
vector<int> Vec;
...
if(find(Vec.begin(), Vec.end(), Element)!=Vec.end()) Vec.push_back(Element);
// 3-Set
// Edit: I made a mistake: set::find is O(LogN) not O(N)

setmap 在查找键时都具有O(log(N))性能。 vectorO(N).

setmap 之间的区别 ,就您应该关注的那样,您是需要将键与值相关联,还是直接存储值。如果需要前者,请使用map,如果需要后者,请使用set

在这两种情况下,您都应该只使用insert()而不是执行find()

原因是insert()当且仅当容器尚未包含该值时(在 map 的情况下,如果容器不包含该键),才会将值插入容器中。这可能看起来像

Map.insert(std::make_pair(Element, ID));

对于地图或

Set.insert(Element);

为一组。

您可以查阅返回值以确定是否实际执行了插入。


如果您使用的是 C++11,则还有两个选择,即 std::unordered_mapstd::unordered_set 。这两者都摊销了插入和查找的O(1)性能。但是,它们还要求键(或值,在 set 的情况下)是可哈希的,这意味着您需要专门针对您的密钥进行std::hash<>。相反,std::mapstd::set 要求键(或值,在 set 的情况下)响应operator<()

如果您使用的是 C++11,则可以使用 std::unordered_set .这将允许你O(1)存在检查(技术上摊销O(1) - O(n)在最坏的情况下)。

std::set可能是您O(lg n)的第二选择。

基本上,std::unordered_set是一个哈希表,std::set是一个树结构(在我见过的每个实现中都是一棵红色的黑色树)1

根据哈希分布的程度和项目数量,std::set 实际上可能更快。如果它确实对性能至关重要,那么与往常一样,您需要进行基准测试。

1)从技术上讲,我认为两者都不需要作为哈希表或平衡BST实现。如果我没记错的话,标准只是规定了运行时限制,而不是实现 - 事实证明,这些是唯一符合边界的可行实现。

你应该使用std::set;它是一个容器,旨在保存对象的单个(等效)副本,并实现为二叉搜索树。因此,它在容器的大小上是O(log N)的,而不是O(N)的。

std::setstd::map通常共享其底层实现的很大一部分;您应该查看本地 STL 实现。

话虽如此,复杂性只是衡量性能的一种标准。使用排序向量可能会有更好的性能,因为它使数据彼此保持本地,因此更有可能命中缓存。如今,缓存一致性是数据结构设计的重要组成部分。

听起来你想使用std::set。它的元素是唯一的,因此在添加元素时无需关心唯一性,并且a.find(k)(其中astd::setk是值)被定义为复杂度对数。

如果你的元素可以为O(1)进行哈希处理,那么最好在unordered_mapunordered_set中使用索引(而不是在map/set中,因为它们在实现中使用RB树,这是O(logN)发现复杂性)

您的示例显示了明确的模式:

check if the value is already in container
  if not, add the value to the container.

这两个操作都可能需要一些时间。首先,如果元素没有以任何特定方式排列(例如,只是一个普通std::vector),则可以在O(N)时间内(线性搜索)完成查找元素,如果元素被排序(例如,std::mapstd::set),则可以在O(logN)时间内完成(二叉搜索),如果元素被散列(例如, std::unordered_mapstd::unordered_set)。

对于普通向量或无序容器(哈希容器),插入将是 O(1)(摊销),尽管哈希容器会慢一点。对于排序的容器(如 set 或 map),您将具有日志时插入,因为它需要在插入之前查找插入它的位置。

所以,结论,使用std::unordered_setstd::unordered_map(如果你需要键值功能)。而且在插入之前您无需检查,这些是唯一密钥容器,它们不允许重复。

如果您(或任何同等产品)无法使用std::unordered_set/std::unordered_map(从 C++11 开始)或 std::tr1::unordered_set/std::tr1::unordered_map(自 2007 年起),那么下一个最佳选择是 std::set/std::map .