c++ 什么时候应该使用std::map / std::set而不是std::unordered_map / std::unordered_set?

guz6ccqo  于 2023-11-19  发布在  其他
关注(0)|答案(3)|浏览(119)

C++11标准引入了std::unordered_mapstd::unordered_set,它们使用hash函数,并且具有(平均)恒定的插入/删除/获取元素的复杂度。
如果我们不需要以特定的顺序遍历集合,似乎没有理由使用“旧”std::mapstd::set
是否有其他的情况/原因,当std::mapstd::set将是一个更好的选择?他们会例如更少的内存消耗,或者是排序是他们唯一的优势比“无序”版本?

xtupzzrd

xtupzzrd1#

它们是有序的,写<比写hash和equality更容易。
永远不要低估易用性,因为90%的代码对代码性能的影响微不足道。提高10%的速度可能会占用您为另一种类型编写哈希的时间。
OTOH,一个很好的哈希组合器是一次写入,get-state-as-tie使<==hash几乎免费。
基于节点操作的容器之间的拼接保证可能会更好,因为拼接成一个散列Map并不像一个好的有序容器拼接那样是免费的。
最后,迭代器的失效保证是不同的。盲目地用一个无序的meow替换一个成熟的测试过的moew可能会产生bug。也许map的失效特性对你来说是值得的。

eufgjt7s

eufgjt7s2#

std::set/std::mapstd::unordered_set/std::unordered_map用于非常不同的问题领域,并且不能相互替换。

  1. std::set/std::map用于问题是在元素顺序之间移动,并且元素访问在平均情况下是O(log n)时间是可以接受的。通过使用std::set/std::map,还可以检索其他信息,例如查找大于给定元素的元素数量。
  2. std::unordered_set/std::unordered_map用于元素访问必须在O(1)的时间复杂度在平均情况下,顺序并不重要,例如,如果你想保持整数键的元素在std::vector,这意味着vec[10] = 10,但这是不实际的方法,因为如果键非常大,例如,一个键是20,另一个键是50000,那么只保留两个值,一个std::vector的大小为50001,如果你使用std::set/std::map,那么元素访问复杂度是O(log n)而不是O(1)。在这个问题中,使用std::unordered_set/std::unordered_map,并且通过使用hashing而不分配大空间,在平均情况下提供O(1)常数时间复杂度。
y0u0uwnf

y0u0uwnf3#

| map                 | unordered_map
---------------------------------------------------------
Ordering        | increasing order    | no ordering
                | of keys(by default) |

Implementation  | Self balancing BST  | Hash Table
                | like Red-Black Tree | 

search time     | log(n)              | O(1) -> Average 
                |                     | O(n) -> Worst Case

Insertion time  | log(n) + Rebalance  | Same as search
                      
Deletion time   | log(n) + Rebalance  | Same as search

字符串
在某些情况下,BST具有明显的优势:

  • BST本质上提供了通过一个简单的按序遍历以排序的顺序检索所有键的能力,而哈希表需要额外的努力来实现这一功能。
  • 使用BST可以很容易地进行顺序统计、查找最接近的较低和较高的元素、进行范围查询。与排序一样,这些操作不是哈希表的自然操作。
  • 可预测性能:自平衡BST确保所有操作的一致O(log n)性能,而哈希提供了平均情况下的O(1)时间复杂度,但对于特定操作可能会恶化到O(n),特别是在表遍历期间。
  • 使用BST可以更有效地进行范围搜索。
  • BST允许多个键共享相同的值,而哈希表依赖于唯一的键来识别元素,并且不能容纳具有相同值的多个键。
  • BST在内存和计算复杂性方面的开销较低,而哈希表需要额外的内存来存储哈希值和处理冲突。

--
关于这个主题的进一步讨论:here

相关问题