这是一个非常天真的问题,但我找不到对它的明确讨论。 每个人都同意使用哈希来处理只有10个元素的地图容器,这样就太过分了。有序的地图会快得多。一百个;一千个,等等,地图应按logN进行缩放,其中N =地图中的#对。所以对于一千,它需要三倍的时间;百万,六倍; 100亿,是时间的九倍。
当然,我们可以相信,对于已排序的容器,可以在O(1)(常数)时间与O(logN)中搜索设计良好的散列容器。但隐含的常数是什么?哈希地图在什么时候丢失地图?特别是,如果键是整数,则键搜索的开销很小,因此映射中的常量会很小。
尽管如此,关于每个人都认为散列容器更快。已经完成了大量的实时测试。
发生了什么事?
答案 0 :(得分:3)
正如您所说,基于哈希的地图确实有可能比二叉搜索树渐近更快,查询时间为O(1)
vs O(log(N))
- 但这完全取决于在允许的输入数据分布上使用的散列函数的性能。
使用哈希表可以考虑两个重要的最坏情况:
O(N)
。O(1)
查询时间,但空间复杂度在极限情况下基本上可以无限制。另一方面,二进制搜索树(至少在大多数标准库实现中使用的红黑树)享有最坏情况O(log(N))
时间和O(N)
空间复杂度。
所有这些(在我看来)的最新结果是,如果你对输入数据有足够的了解来设计一个“好”的哈希函数(没有太多的冲突,就不会产生过多的稀疏散列桶的分配)使用散列映射通常是更好的选择。
如果无法确保哈希函数的性能超过预期输入,请使用BST。
一个人变得比另一个更好的确切点完全取决于问题/机器。
希望这有帮助。
答案 1 :(得分:1)
正如你所正确指出的那样 - 魔鬼在细节中(在这种情况下是常数)。您必须对代码进行基准测试,以确定哪种代码对您更有效,因为 O-Notation 是针对无穷小的值,而您正在处理现实世界的约束。
如果它确实是O(1)(即:has函数确实非常好)并且散列函数计算相对较快(开始时) - 不依赖于输入的大小,散列会更快)。
地图上的开销是遍历树,而键比较可能或多或少快(整数更快,字符串更慢),遍历树总是依赖于输入(树深度)。对于较大的树,请考虑使用B-Trees而不是标准映射(在C ++中通常使用红黑树实现)。
同样,神奇的词是基准。
答案 2 :(得分:1)
哈希映射更快的确切点将取决于机器。
确实只需要O(log n)“步”来遍历地图。但是暂时看一下常数因素,请注意该日志的基数是2,而不是10;并且二叉树可能实现为红黑树,通常不是完美平衡的。 (如果内存服务,它可能比log2(n)深2倍。)
然而,真正推动差异的是有序地图的差的地点。这些O(log n)步骤中的每一步涉及不可预测的分支,这对于指令流水线是不利的。更糟糕的是,它涉及追逐一个随机指向内存的指针。现代CPU的经验法则是:“数学很快;记忆很慢。”这是一个值得记住的好规则,因为每一代人都会变得更加真实。 CPU核心速度通常比内存速度提高得快。
因此,除非您的地图足够小以适应缓存,否则这些随机指针解除引用非常对整体性能不利。计算哈希只是数学(因此很快),追逐O(1)指针比追逐O(log n)更好;对于大n来说通常要好得多。
但同样,哈希表优势的确切点将取决于具体的系统。