有人可以解释这两种实现之间的主要区别(优点/缺点)吗?
对于库,建议采用哪种实现方式?
答案 0 :(得分:56)
Wikipedia's article on hash tables提供了一个明显更好的解释和概述人们使用的不同哈希表方案,而不是我能够摆脱困境。事实上,阅读那篇文章可能比在这里提出问题更好。 :)
那说......
链式哈希表索引到指向链表头部的指针数组。每个链接列表单元格都具有为其分配的密钥以及为该密钥插入的值。当您想要从其键中查找特定元素时,键的散列用于计算要遵循的链接列表,然后遍历该特定列表以查找您所追求的元素。如果哈希表中的多个键具有相同的哈希值,那么您将拥有包含多个元素的链接列表。
链式散列的缺点是必须遵循指针才能搜索链表。好处是,链式哈希表只会随着负载因子(哈希表中的元素与桶阵列长度的比率)的增加而线性变慢,即使它超过1也是如此。
开放寻址哈希表索引到指向(键,值)对的指针数组。您可以使用密钥的哈希值来确定数组中的哪个插槽首先查看。如果哈希表中的多个键具有相同的哈希值,那么您可以使用某种方案来决定另一个要查找的槽。例如,线性探测是指您选择一个之后的下一个插槽,然后是之后的下一个插槽,依此类推,直到找到与您正在寻找的键匹配的插槽,或者您打空插槽(在这种情况下,密钥不得在那里)。
当加载因子较低时,开放寻址通常比链式散列更快,因为您不必遵循列表节点之间的指针。如果负载因子接近1,它会变得非常非常慢,因为在找到您要查找的密钥或空插槽之前,您最终通常必须搜索存储区阵列中的许多插槽。此外,哈希表中的元素永远不会超过桶数组中的条目。
为了处理当加载因子接近1时所有哈希表至少变得更慢(并且在某些情况下实际上完全中断)的事实,实际的哈希表实现使桶阵列更大(通过分配新的桶阵列,以及当加载因子超过某个值(通常约为0.7)时,将元素从旧元素复制到新元素中,然后释放旧元素。
上述所有内容都有很多变化。再次,请参阅维基百科文章,它确实非常好。
对于一个供其他人使用的图书馆,我会强烈推荐实验。由于它们通常对性能至关重要,因此通常最好使用其他人已经仔细调整的哈希表的实现。有很多开源的BSD,LGPL和GPL许可的哈希表实现。
例如,如果您正在使用GTK,那么您会发现有一个很好的hash table in GLib。
答案 1 :(得分:2)
答案 2 :(得分:1)
我的理解(简单来说)是这两种方法都有利有弊,尽管大多数图书馆使用链接策略。
链接方法:
此处哈希表数组映射到项目的链接列表。如果碰撞次数相当小,这是有效的。最糟糕的情况是O(n)
,其中n是表中元素的数量。
使用线性探针打开寻址:
此处发生碰撞时,请转到下一个索引,直到我们找到一个开放点。因此,如果碰撞次数较少,则速度非常快且节省空间。这里的限制是表中的条目总数受到数组大小的限制。链接不是这种情况。
另一种方法是使用二叉搜索树链接。在这种方法中,当发生冲突时,它们存储在二叉搜索树而不是链表中。因此,这里最糟糕的情况是O(log n)
。在实践中,这种方法最适合于非常不均匀的分布。
答案 3 :(得分:0)
开放式寻址与单独链接
如果将键保留为哈希表本身中的条目,则适合进行线性探测,双重和随机哈希处理... 这样做称为“开放式寻址” 也称为“封闭式哈希”
另一个想法:哈希表中的条目只是指向链表(“链”)头部的指针;链接列表的元素包含键... 这称为“单独链接” 也称为“开放式哈希”
通过单独的链接,冲突解决变得很容易:只要在其链表中插入一个键,就可以将其插入 (为此,可以使用比链表更高级的数据结构;但正如我们将看到的,链表在一般情况下效果很好) 让我们看看分析这些策略的时间成本
来源:http://cseweb.ucsd.edu/~kube/cls/100/Lectures/lec16/lec16-25.html
答案 4 :(得分:-1)
如果在创建表时不知道将插入哈希表中的项目数,则链接哈希表优于打开寻址。
增加加载因子(项目数/表大小)会导致开放地址哈希表中的主要性能损失,但性能仅在链式哈希表中线性降低。
如果您正在处理内存不足并希望减少内存使用量,请转到开放寻址。如果您不担心内存并且想要速度,请选择链式哈希表。
如有疑问,请使用链式哈希表。添加比预期更多的数据不会导致性能降低到爬行速度。