如何在C++中高效使用unordered_map
C++提供了std::unordered_set和std::unordered_map,分别用作哈希集和哈希映射。它们在常数平均时间内执行插入/删除/访问操作。
- 然而,最坏情况的复杂度为O(n 2 )。
- 原因是unordered_map通过输入值取模质数来存储键值对,然后将其存储在哈希表中。
- 当输入的数据很大且输入值是这个质数的倍数时,会发生很多冲突,可能导致O(n 2 )的复杂度。
- 根据编译器不同,质数可能是107897或126271。
示例1: 如果我们插入上述两个质数的倍数并计算执行时间。其中一个质数的执行时间比另一个长得多。
输出:
显然,其中一个质数的时间复杂度是O(n 2 )。
标准内置的哈希函数与此类似,应用于unordered_map:
上述函数可能会产生许多冲突。插入在HashMap中的键不均匀分布,并且在插入大量质数倍数后,进一步插入会导致哈希函数将所有先前的键重新分配到新的槽中,从而使其变慢。因此,我们的想法是要随机化哈希函数。
我们的想法是使用一种方法,使我们哈希映射中的键均匀分布。这将防止发生冲突。为此,我们使用斐波那契数列。与斐波那契数列相关的黄金比例(Phi = 1.618)具有一个性质,即它可以将任何范围平均分成若干段,而不需要回到起始位置。
我们可以创建自己的简单哈希函数。下面是哈希函数:
基本上,上面的哈希函数生成随机哈希值以存储键。欲了解更多信息,请参考这篇文章Fibonacci哈希。
示例2: 使用上面的哈希函数,程序运行得非常快。
输出:
预先分配空间
默认情况下,unordered_map的容量是16,并为此创建了哈希表。 但是,每次达到阈值时,unordered_map的容量会增加一倍,并根据新的哈希表重新为所有值进行哈希处理。
因此,我们可以使用.reserve()方法预先根据我们的输入大小保留容量。
1024可以根据输入大小替换为任何int值。 这可以防止重新哈希和动态分配,从而使程序更有效。
设置max_load_factor
unordered_map 的 max_load_factor 确定了碰撞的概率。默认值为1。
将其设置为较低的值,如0.25,可以大大减少碰撞的概率。
示例: 使用上述两种方法可以使 umap 更快:
输出: