哈希表 - 使用二进制搜索树实现

时间:2015-01-02 08:09:53

标签: data-structures hashtable binary-search-tree

破解编码面试,第71页:

  

或者,我们可以用BST实现哈希表。我们可以   保证O(log n)查找时间,因为我们可以保留树   均衡。另外,我们可以使用更少的空间,因为大阵列没有   需要在一开始就分配更长的时间。

我知道链接列表,哈希表和BST的基础知识,但我无法理解这些行。它究竟意味着什么?这个最终的数据结构会是Trie吗?

3 个答案:

答案 0 :(得分:5)

该部分的完整文字说明,最后一段是您询问的那段:

  

哈希表是一种数据结构,它将键映射到值以进行高效查找。在一个   哈希表的实现非常简单,哈希表有一个底层数组和   哈希函数。当您想要插入对象及其键时,哈希函数会映射   整数的关键字,表示数组中的索引。然后将对象存储在   那个指数。

     

但是,通常情况下,这样做不太合适。在上面的实现中,哈希   所有可能键的值必须是唯一的,否则我们可能会意外覆盖数据。该   数组必须非常大 - 所有可能的键的大小 - 以防止这种情况   “冲突”。

     

我们不是创建一个非常大的数组并将对象存储在索引哈希(键)中   可以使数组更小,并在索引散列(键)%的链表中存储对象   array_length。要获取具有特定键的对象,我们必须搜索链表   这把钥匙。

     

或者,我们可以使用二叉搜索树实现哈希表。我们可以   保证0(log n)查找时间,因为我们可以保持树平衡。另外,   我们可以使用更少的空间,因为不再需要分配大型数组   开始。

所以他们谈论使用BST(二叉搜索树)来处理冲突。使用BST作为 sole 数据实际上没有意义结构,因为正确调整哈希的整个点是查找大约是O(1)的顺序比来自BST的O(log n)更好。最重要的是,使用BST 完全实现哈希表意味着它不是实际上哈希表: - )

但是,请注意,当您在哈希表中发生冲突时,处理它们的常用方法是让每个桶包含其项目的链接列表。在退化的情况下(所有项目散列到同一个存储桶),您最终只得到一个链接列表,O(1)变成O(n)

因此,您可以使用BST,而不是每个存储桶中的链接列表。然后,在单个存储桶包含多个项目(前面提到的冲突)的情况下,您不再具有O(n)搜索复杂度。

您使用哈希函数在O(1)中查找存储桶,然后在O(log n)中搜索BST是否存在冲突。在最好的情况下(每桶一个项目),它仍然是O(1)。最糟糕的情况是O(log n)而不是O(n)

这个理论最初让我关注的唯一问题是他们还讨论了不再需要大量分配的事实。如果它是共享哈希/ BST组合,那么仍然需要分配整个哈希表,以使其看起来不协调。

但是,从上下文(“......因为数组不再需要分配......”),看来它们意味着它们可以使哈希表成为双数据结构的一部分,因为冲突的处理效率更高。换句话说,如果使用BST,则可以使用100个元素的哈希表,而不是具有链接列表的1000个元素哈希表,因为如果使用BST,冲突不会对搜索时间造成太大损害。

答案 1 :(得分:1)

你在这里混淆了几个术语。

  • 我们的想法是以两层方式实现包含数组和BST的哈希表。如果没有碰撞,仍然会在哈希值中添加值,但如果有,则可以解决使用BST检索碰撞元素的性能。

  • trie完全不同;根据您尝试存储的内容,您可能无法将其应用于散列函数。

答案 2 :(得分:1)

在树的情况下,O(logN)绑定将是最坏的情况。让我们以这种方式看待它。 我们插入45,33,53,62,22然后我们将有45作为根节点,33和55在level1,22和66在2级..

所以,如果你要为值45进行散列,那么它仍然是一个O(1)操作......只有当你在level2中查找节点时它才会接近O(logN)....树可能是一个RB树/ AVL树,因此它不会退化成一个链表....你失去了一些效率,但在空间效率方面弥补了它。

另一个优点是你不需要在哈希表中打扰碰撞。 http://www.cs.rit.edu/~ib/Classes/CS233_Spring08-09/Slides/Week9_Hashing.pdf

基本上,你会有一个动态的节点分配,并且在散列表中未使用的桶上没有浪费空间......比如说,你要使用一个预先确定大小的静态哈希表(busckets),那么,它会导致空间效率低下。