C++ 当std::map/std::set应在std::无序\u map/std::无序\u 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::unordered_map/std::unordered_set一样,它使用散列函数,插入/删除/获取元素的平均复杂度恒定,如果我们不需要按特定顺序迭代集合,似乎没有理由使用“旧”std::map/std::set?或者std::map/std::set是更好的选择时还有其他一些情况/原因?例如,它们是否会减少内存消耗,或者它们相对于“无序”版本的唯一优点是排序?

它们是排序的,而编写
它们是排序的,编写
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简单的答案是,在需要性能的时候,每次使用时,都要使用缓存友好的,在这种情况下
    无序的映射