在我的申请中,我有以下要求 -
数据结构只会填充一些值(不是键/值对)。 这些值可能会重复,但我希望数据结构只存储一次。
我将通过上面创建的数据结构的所有元素迭代100次。元素在迭代中出现的顺序并不重要。
约束1表明我必须使用set或unordered_set,因为数据不是键值对的形式。
现在设置插入比unordered_set插入更昂贵,但数据结构只在我的程序开头填充一次。
我认为决定因素是我可以多快地遍历数据结构的所有元素。我不确定set或unordered_set对于此目的是否会更快。我相信标准没有提到这个事实,因为这个操作对于任一数据结构都是O(n)。但我想知道iterator.next()的数据结构会更快。
答案 0 :(得分:12)
有几种方法。
std::unordered_set
具有最快O(1)
查找/插入和O(N)
次迭代的O(N)
(与每个容器一样)。如果您的数据变化很大,或需要大量随机查找,这可能是最快的。但测试。std::vector
执行单个std::unordered_set
复制,并从连续的内存布局中获取100次。 测试这是否比常规boost::flat_set
更快。 std::set
,它提供std::vector
接口和flat_set
存储后端(即连续的内存布局,非常缓存和预取友好)。同样,测试这是否能加速其他两个解决方案。对于最后一个解决方案,请参阅Boost文档以了解一些权衡(了解所有其他问题,如迭代器失效,移动语义和异常安全性):
Boost.Container flat_ [multi] map / set容器是ordered-vector 基于Austern's和Alexandrescu的基于关联的容器 准则。这些有序的载体容器也受益 最近,为C ++添加了移动语义,加速了 插入和擦除时间相当大。扁平关联容器 具有以下属性:
- 比标准关联容器更快的查找
- 比标准关联容器
更快的迭代次数- 减少小对象(以及使用shrink_to_fit时的大对象)的内存消耗
- 改进了缓存性能(数据存储在连续内存中)
- 非稳定迭代器(插入和擦除元素时迭代器无效)
- 无法存储不可复制和不可移动的值类型
- 比标准关联容器更弱的异常安全性(复制/移动构造函数在删除擦除值时可以抛出 和插入)
- 比标准关联容器更慢插入和删除(特别适用于不可移动类型)
注意:查询速度越快,意味着O(log N)
对连续内存执行O(log N)
而不是std::set
指针追逐常规std::unordered_set
}}。当然,O(1)
会N
查找,对于大型{{1}}来说会更快。
答案 1 :(得分:5)
我建议您使用set或unordered_set进行“过滤”,完成后,将数据移动到固定大小的矢量
答案 2 :(得分:4)
如果构建数据结构不考虑性能问题(或者至少只是略微考虑),请考虑将数据保存到std::vector
:没有任何事情可以打败它。
为了加快数据结构的初始构建,您可能首先插入std::unordered_set
或至少使用一个用于在插入之前检查存在。
在第二种情况下,它不需要包含元素,但可以包含例如指数。
std::vector<T> v;
auto h = [&v](size_t i){return std::hash<T>()(v[i]);};
auto c = [&v](size_t a, size_t b){return v[a] == v[b];};
std::unordered_set<size_t, decltype(h), decltype(c)> tester(0, h, c);
答案 3 :(得分:3)
我强烈建议您不在这种情况下使用。 set
是二叉树,unordered_set
是哈希表 - 因此它们使用大量内存,并且迭代速度慢且引用的局部性差。如果您必须经常插入/删除/查找数据,set
或unordered_set
这是一个不错的选择,但现在您需要只读取,存储,排序数据一次,仅使用数据很多次。
在这种情况下,排序的矢量可以是一个很好的选择。 vector
是动态数组,因此开销很低。
直接看看代码。
std::vector<int> data;
int input;
for (int i = 0; i < 10; i++)
{
std::cin >> input;
data.push_back(input); // store data
}
std::sort(data.begin(), data.end()); // sort data
这就是全部。您的所有数据都准备好了。
如果您需要删除重复项set
,请在排序后使用unique
- erase
。
data.erase(
std::unique(data.begin(), data.end()),
data.end()
);
请注意,您应该使用lower_bound
,upper_bound
和equal_range
而不是find
或find_if
来使用排序数据。
答案 4 :(得分:2)
无序集使用散列表提供近O(1)时间搜索。这是通过使用键的散列来计算从数据集的开头搜索的元素(键)的偏移量来完成的。除非您的数据集很小(如char
s),否则不同的键可能具有相同的哈希值(冲突)。
为了最大限度地减少冲突,无序集必须保持数据存储相当稀疏。这意味着找到一个键最多是O(1)时间(除非发生碰撞)。
然而,当迭代遍历散列表时,我们的迭代器将在我们的数据存储区中遇到大量未使用的空间,这将减慢迭代器对下一个元素的查找速度。我们可以使用额外的指针链接散列表中的相邻元素,但我不认为无序集合会这样做。
鉴于上述情况,我建议您使用排序向量作为“集合”。使用二分法,您可以在O(log n)时间搜索商店,并且遍历列表是微不足道的。向量具有额外的优势,即内存是连续的,因此您不太可能遇到缓存未命中。