当与列表链接的单独链接时,为什么我们在哈希表中使用线性探测?

时间:2014-05-23 05:44:46

标签: performance algorithm hash hashtable time-complexity

我最近了解了处理哈希表中冲突的不同方法。并且看到链接列表的单独链接总是更加节省时间,为了节省空间,我们为线性探测分配了预定义的内存,以后我们可能不会使用,对于单独的链接,我们动态地利用内存,因此是与链表单独链接不是比线性探测更有效吗?如果是,为什么我们根本使用线性探测呢?

2 个答案:

答案 0 :(得分:39)

我很惊讶您发现链式散列比线性探测更快 - 实际上,线性探测通常比链接快得多。这主要归功于locality of reference,因为在线性探测中执行的访问往往比在链式散列中执行的访问更接近。

线性探测还有其他成功。例如,插入线性探测哈希表不需要任何新的分配(除非你正在重新划分表),所以在内存稀缺的网络路由器等应用程序中,很高兴知道一旦表设置完成,可以将元素放入其中,而不存在malloc失败的风险。

线性探测的一个缺点是,如果哈希函数选择不当,primary clustering会导致表的性能显着下降。虽然链式散列仍然可能受到错误散列函数的影响,但它对具有附近散列码的元素不太敏感,这些散列码不会对运行时产生负面影响。理论上,线性探测仅在散列函数为5-independentthere's sufficient entropy in the keys时才提供预期的O(1)查找。有很多种方法可以解决这个问题,因为使用Robin Hood hashing技术或hopscotch hashing,两者都有比香草线性探测更好的最坏情况。

线性探测的另一个缺点是,当负载系数接近1时,其性能会显着下降。您可以通过定期重新定位或使用上述Robin Hood散列技术来解决此问题。

希望这有帮助!

答案 1 :(得分:9)

当哈希表接近满时,线性探测实际上更有效。

从历史上看,一个人的内存非常非常少,所以每个字节都很重要(而且在某些情况下,内存非常有限)。

为什么使用更少的内存?

考虑表格的样子:(根据Wikipedia单独的链接变体 - 还有其他变体,但它们通常使用更多内存)

Linear             Separate chaining #1    Separate chaining #2
probing            List head in table      Pointer in table
|------|           |------|---|            |---|    |------|---|
|Object|           |Object|Ptr|            |Ptr| -> |Object|Ptr|
|------|           |------|---|            |---|    |------|---|
|Object|           |Object|Ptr|            |Ptr| -> |Object|Ptr|
|------|           |------|---|            |---|    |------|---|
| NULL |           | NULL |Ptr|            |Ptr|
|------|           |------|---|            |---|
 .                  .                       .
 .                  .                       .
 .                  .                       .

Ptr代表"指针" - 任何不指向某事物的指针都可以被视为NULL

单独链接#1明显比线性探测(总是)使用更多内存,因为表中的每个元素都大于指针的大小。

当表格中没有多少时,单独的链接#2可能会有优势,但是当它变满时,它会为每个元素大约增加2个指针。


templatetypedef可能正确的线性探测通常更快(他很少出错),但它通常教会说单独的链接更快,你可以在主要的API&#39中看到它; s(例如,像Java implementations),可能是因为相信,为了避免线性探测慢的情况(有一些精心选择的值,你可以快速到达线性探测的O(n)表现,而单独的链接仍然是O(1)),或者可能是出于其他原因。