我有以下情况:
N
的数量最多可以是100,但是要通过平均值。案例分析的目的,我会说N
可以是100,10k或100k 鉴于,我的要求std :: set不是一个好选择。我可以使用push_back N*O(1)
和std :: sort O(NlogN)
在向量中进行所有元素插入,并进行相等比较(N);总计2N+NlogN
可以轻松击败std :: set memory / speed。
此主题已在此处进行了详细审核: http://lafstern.org/matt/col1.pdf 和这里: What is the difference between std::set and std::vector?
让我们继续讨论如果我使用新的unordered_set。 N*O(1))
元素的插入(N
+相等查找(N
平均案例)总计为2N
。
现在,对于unordered_set,我需要创建一个哈希,这对我的情况来说并不容易。我猜测只有哈希部分会导致我的复杂数据结构超过2N
。
但是,为什么对于简单的unique_ptr值插入,有人会得到以下性能结果: http://kohei.us/2010/03/31/stl-container-performance-on-data-insertion/
似乎矢量排序+相等仍然比unordered_set更好,直到大量元素(100k)。 unordered_set不使用红黑树吗?那么这个性能影响来自哪里?
这里有一个稍微相关的帖子: Performance of vector sort/unique/erase vs. copy to unordered_set
答案 0 :(得分:3)
如果你的元素有一个简单的排序函数,并且你知道它们是不同的,那么你最好将它们放在一个向量中并对它们进行排序。理论上,具有良好散列函数的基于散列表的解决方案可以进行比较O(n)而不是O(n log n),但是有许多减轻事实:
日志 n 是一个小数字。例如,如果n是二十亿,那么 log n 是31(使用二进制日志,这通常是隐含的)。
标准库无序集合需要为每个元素分配。这是规范有效要求的,因为向无序集合添加元素不会使对现有元素的引用无效,与标准库向量的情况不同。
每个桶完成对无序集合的迭代(同样,这在规范中),结果是迭代涉及随机存储器访问。迭代向量是顺序的,这对缓存更友好。
简而言之,即使排序为O(n log n),基于O(n)散列的解决方案很可能具有较大的每元素常量,并且由于log n是一个较小的数字,基于矢量的解决方案将更快。通常要快得多。
基于散列的解决方案的速度会慢多少取决于分配器的速度,并且不同的标准库实现之间存在相当大的差异。但即使是超快速的分配器也不太可能为您提供有竞争力的性能,并且当您的表变得足够大时,哈希表的缓存不友好性将变得非常重要。
即使你有一些重复的元素,你可能会更好地使用向量,但这取决于你有多少重复。由于哈希表可能占用的内存至少是具有相同元素数量的向量的两倍,因此只要你不希望元素的数量是多少,一个简单的经验法则就是使用向量。超过两倍的独特元素。 (排序后很容易消除重复。有一个标准的库函数可以做到这一点。)