C ++ 0x正在引入unordered_set
boost
以及许多其他地方。我的理解是unordered_set
是具有O(1)
查找复杂性的哈希表。另一方面,set
只不过是具有log(n)
查找复杂性的树。 为什么有人会使用set
代替unordered_set
?即是否需要set
?
答案 0 :(得分:290)
无序集必须以几种方式支付其O(1)平均访问时间:
set
使用较少内存而非unordered_set
来存储相同数量的元素。set
中的查找可能更快,而不是unordered_set
中的查找。unordered_set
的平均情况中的许多操作速度更快,但set
通常会保证更好的最坏情况 (例如insert
)。set
对元素进行排序非常有用。set
与<
,<=
,>
和>=
。 unordered_set
不需要支持这些操作。
答案 1 :(得分:199)
对于想要迭代集合项目的人来说,顺序很重要。
答案 2 :(得分:25)
每当您更喜欢树到哈希表时。
例如,在最坏的情况下,哈希表是“O(n)”。 O(1)是平均情况。最糟糕的是树是“O( log n)”。
答案 3 :(得分:6)
因为std :: set是标准C ++的一部分而unordered_set不是。的C ++ 0x 不是标准,也不是Boost。对于我们许多人来说,便携性至关重要,这意味着坚持标准。
答案 4 :(得分:6)
考虑扫描线算法。这些算法将完全失败并使用哈希表,但与平衡树一起工作得非常漂亮。为了给你一个扫描线算法的具体例子,考虑一下fortune的算法。 http://en.wikipedia.org/wiki/Fortune%27s_algorithm
答案 5 :(得分:6)
在以下情况下使用设置:
在以下情况下使用无序设置:
示例:
设置:
输入:1、8、2、5、3、9
输出:1,2,3,5,8,9
无序设置:
输入:1、8、2、5、3、9
输出:9 3 1 8 2 5(可能是此顺序,受哈希函数的影响)
主要区别在于:
注意:(在某些情况下,set
更方便)例如使用vector
作为键
set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});
for(const auto& vec:s)
cout<<vec<<endl; // I have override << for vector
// 1 2
// 1 3
vector<int>
会成为set
的键,因为vector
会覆盖operator<
的原因。
但是,如果使用unordered_set<vector<int>>
,则必须为vector<int>
创建一个哈希函数,因为vector没有哈希函数,因此必须定义一个像这样的变量:
struct VectorHash {
size_t operator()(const std::vector<int>& v) const {
std::hash<int> hasher;
size_t seed = 0;
for (int i : v) {
seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
return seed;
}
};
vector<vector<int>> two(){
//unordered_set<vector<int>> s; // error vector<int> doesn't have hash function
unordered_set<vector<int>, VectorHash> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});
for(const auto& vec:s)
cout<<vec<<endl;
// 1 2
// 1 3
}
您会发现在某些情况下unordered_set
更为复杂。
主要引自: https://www.geeksforgeeks.org/set-vs-unordered_set-c-stl/ https://stackoverflow.com/a/29855973/6329006
答案 6 :(得分:3)
除了其他人已经提到的内容外,还有一件事。虽然将元素插入到unordered_set的预期摊销复杂度为O(1),但将 采用O(n)因为哈希表需要重组(桶的数量)需要改变) - 即使有'好'的哈希函数。就像在向量中插入元素一样,不时地使用O(n)因为底层数组需要重新分配。
在集合中插入总是最多O(log n)。在某些应用中这可能更为可取。
答案 7 :(得分:3)
请原谅我,还有一件事值得注意的是排序属性:
如果您想在容器中一系列数据,例如:您在设置中存储时间,并且您想要时间从2013-01-01到2014-01 -01
对于 unordered_set ,这是不可能的。
当然,这个例子对于 map 和 unordered_map 之间的用例更有说服力。
答案 8 :(得分:1)
手边,我想说如果你想把它转换成不同的格式,把关系放在一起很方便。
虽然一个访问速度更快,但构建索引或创建和/或访问它时使用的内存的时间也更长。
答案 9 :(得分:1)
如果要对事物进行排序,则可以使用set而不是unordered_set。当存储的顺序无关紧要时,unordered_set用于set。
答案 10 :(得分:1)
虽然这个答案可能要晚10年,但值得指出的是std::unordered_set
也存在安全方面的缺点。
如果哈希函数是可预测的(除非应用了随机盐等对策,否则通常就是这种情况),攻击者可以手工制作会产生哈希冲突并导致所有插入和查找为O( n)时间。
这可以用于非常有效和优雅的拒绝服务攻击。
内部使用哈希映射的语言的许多(大多数?)实现都遇到了这个问题:
答案 11 :(得分:0)
g++
6.4 stdlibc ++有序与无序集合基准测试
我对这个主要的Linux C ++实现进行了基准测试,以了解差异。
完整的基准测试细节和分析已在What is the underlying data structure of a STL set in C++?中给出,在此我不再赘述。
快速摘要:
该图清楚地表明,在这种情况下,当项目超过100k时,哈希图的插入总是快得多,并且随着项目数的增加,差异也随之增加
这种提速的代价是您无法高效地依次移动。
曲线清楚地表明,有序的std::set
是基于BST的,而std::unordered_set
是基于hashmap的。在参考答案中,我进一步确认了通过GDB逐步调试代码。
结果如下所示。 “ BST”表示“已通过std::set
测试,而哈希表”则是“经过std::unordered_set
测试。 “堆”用于std::priority_queue
,我在Heap vs Binary Search Tree (BST)
map
与unordered_map
的相似问题:Is there any advantage of using map over unordered_map in case of trivial keys?
答案 12 :(得分:0)
这是我没有看到列出的一个实际原因……如果在有缺陷的代码中使用不当,无序集会导致代码在不同的机器上表现不同。这是因为值的存储顺序在机器之间不一致。
如果(错误地)编写依赖于存储顺序的代码,结果将是程序在不同机器之间的行为不一致。实际上,如果无序集是返回值列表的函数/方法的实现的一部分,则可能会发生这种情况。该函数的客户端可能没有意识到正在使用一个无序集合,也可能没有意识到返回列表的顺序不能保证一致/可移植。
因此,对于程序员来说,无序集合比有序集合更无情。他们引入了这种用于混淆代码行为的额外机制,这可能会导致耗时/混淆错误,因为它们可能无法在机器之间重现。