C++ 当std::map/std::set应在std::无序\u map/std::无序\u set上使用时?
正如在新标准中引入的std::unordered_map/std::unordered_set一样,它使用散列函数,插入/删除/获取元素的平均复杂度恒定,如果我们不需要按特定顺序迭代集合,似乎没有理由使用“旧”std::map/std::set?或者std::map/std::set是更好的选择时还有其他一些情况/原因?例如,它们是否会减少内存消耗,或者它们相对于“无序”版本的唯一优点是排序?它们是排序的,而编写C++ 当std::map/std::set应在std::无序\u map/std::无序\u set上使用时?,c++,algorithm,c++11,hash,std,C++,Algorithm,C++11,Hash,Std,正如在新标准中引入的std::unordered_map/std::unordered_set一样,它使用散列函数,插入/删除/获取元素的平均复杂度恒定,如果我们不需要按特定顺序迭代集合,似乎没有理由使用“旧”std::map/std::set?或者std::map/std::set是更好的选择时还有其他一些情况/原因?例如,它们是否会减少内存消耗,或者它们相对于“无序”版本的唯一优点是排序?它们是排序的,而编写它们是排序的,编写std::set/std::map和std::unordered_
它们是排序的,编写std::set/std::map
和std::unordered_set/std::unordered_map
用于非常不同的问题领域,彼此无法替换。
std::set/std::map
用于问题围绕元素顺序移动且元素访问为O(logn)的情况,在平均情况下,时间是可以接受的。通过使用std::set/std::map
还可以检索其他信息,例如查找大于给定元素的元素数
std::unordered_set/std::unordered_map
用于元素访问在一般情况下必须是O(1)时间复杂度且顺序不重要的情况,例如,如果要将整数键的元素保留在std::vector
中,则表示vec[10]=10
但这是不实用的方法,因为如果键非常大,例如一个键是20
,另一个键是50000
,则只保留两个值,大小为50001
的std::vector
必须分配,如果使用std::set/std::map
则元素访问复杂度为O(log n)不是O(1)。在这个问题中,使用了std::unordered_集/std::unordered_映射
,它通过使用散列
而不分配大空间,在平均情况下提供了O(1)恒定的时间复杂度
std::set/std::map
和std::unordered_set/std::unordered_map
用于非常不同的问题领域,不能相互替换。
std::set/std::map
用于问题围绕元素顺序移动且元素访问为O(logn)的情况,在平均情况下,时间是可以接受的。通过使用std::set/std::map
还可以检索其他信息,例如查找大于给定元素的元素数
std::unordered_set/std::unordered_map
用于元素访问在一般情况下必须是O(1)时间复杂度且顺序不重要的情况,例如,如果要将整数键的元素保留在std::vector
中,则表示vec[10]=10
但这是不实用的方法,因为如果键非常大,例如一个键是20
,另一个键是50000
,则只保留两个值,大小为50001
的std::vector
必须分配,如果使用std::set/std::map
则元素访问复杂度为O(log n)不是O(1)。在这个问题中,使用了std::unordered_集/std::unordered_映射
,它通过使用散列
而不分配大空间,在平均情况下提供了O(1)恒定的时间复杂度
FWIW,不要使用标准提供的无序容器。它们基本上需要是链表的向量。还有更好的基于散列的开源容器。我的建议是,如果您需要维护排序顺序,请仅使用std::map
/std::set
。否则使用基于散列的容器。有序映射/集的优点是它们是有序的。这真的是我能想到使用它们的唯一原因。@NathanOliver那里的“更好”哈希映射(比如absl)在很多方面都更好,但不是所有方面都更好。我试着用我们库中的那些替代品替换无序的集合/映射,在某些地方这是一个不错的收获,在一半的地方这是一个灾难性的损失。@Johy简单的答案是,在需要性能的时候,每次使用都要使用缓存友好的东西,在这种情况下是无序的集合
和无序的集合
。但请记住,如果您将这些结构用于缓冲区,并尝试从多线程上下文访问它们,您可能会遇到问题,因为有时底层数组将不得不增长(对于列表vs向量,情况也是如此)。本质上,在做出最终决定之前,先进行基准测试和分析。@NathanOliver根据我在robin hood
和hopscotch
中最流行的tessil
、abseil
和skarupke
实现中的经验,它们只能处理简单类型。如果您甚至需要存储智能指针
,性能就会下降,而使用糟糕的散列函数
时,其中一些指针就会放弃并崩溃或耗尽内存。因此,我建议在决定使用std
以外的任何东西之前进行基准测试。FWIW,不要使用标准提供的无序容器。它们基本上需要是链表的向量。还有更好的基于散列的开源容器。我的建议是,如果您需要维护排序顺序,请仅使用std::map
/std::set
。否则使用基于散列的容器。有序映射/集的优点是它们是有序的。这真的是我能想到使用它们的唯一原因。@NathanOliver那里的“更好”哈希映射(比如absl)在很多方面都更好,但不是所有方面都更好。我试着用我们库中的那些替代品替换无序的集合/映射,在某些地方这是一个不错的收获,在一半的地方这是一个灾难性的损失。@Johy简单的答案是,在需要性能的时候,每次使用时,都要使用缓存友好的,在这种情况下无序的映射