【问题标题】:Hash table - implementing with Binary Search Tree哈希表 - 用二叉搜索树实现
【发布时间】:2015-01-02 08:09:52
【问题描述】:

来自 Cracking the Coding Interview,第 71 页:

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

我知道链表、哈希表和 BST 的基础知识,但我无法理解这些行。它实际上是什么意思?这个最终的数据结构会是一个 Trie 吗?

【问题讨论】:

  • 哈哈同一本书,同样的疑问把我带到了这里……xD

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


【解决方案1】:

该部分的全文指出,最后一段是您询问的内容:

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

不过,通常情况下,这不会完全正常。在上述实现中,哈希 所有可能键的值必须是唯一的,否则我们可能会不小心覆盖数据。这 数组必须非常大——所有可能的键的大小——以防止这样的 “碰撞。”

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

或者,我们可以使用二叉搜索树来实现哈希表。那么我们可以 保证 0(log n) 的查找时间,因为我们可以保持树的平衡。此外, 我们可能会使用更少的空间,因为不再需要在非常 开始。

所以他们在谈论使用 BST(二叉搜索树)来处理冲突。使用 BST 作为唯一数据实际上没有意义结构,因为经过适当调整的哈希的全部意义在于查找的顺序是 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 组合,您仍然需要分配整个哈希表,这样看起来不协调。

然而,从上下文 ("...因为 large 数组不再需要分配..."), 看来他们的意思是他们可以使对偶数据结构的哈希表部分更小,因为冲突处理更有效。换句话说,与包含冲突链接列表的 1000 元素哈希表相比,您可以使用 100 元素哈希表,因为如果使用 BST,冲突不会对搜索时间造成太大破坏。

【讨论】:

  • 我想 BST 将用于碰撞;它有一个类似于链式哈希方法的类比,需要花费 O(N) 来检索;在这种情况下,最好使用 BST。
  • 其实C++ std::map 是使用BST 整体实现的。优点是: 1. 不再需要担心冲突 2. 键是有序的,这样你就可以进行中序遍历。我认为作者实际上是这样想的,而不是只使用 BST 来处理碰撞。
【解决方案2】:

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

  • 这个想法是用数组和 BST 以双层方式实现哈希表。如果没有冲突,仍然可以将值添加到哈希中,但如果有,则可以使用 BST 解决检索冲突元素的性能。

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

【讨论】:

  • 我的想法也是如此,我只是有点担心他们的评论“此外,我们可能会使用更少的空间,因为一开始就不再需要分配一个大数组”。
  • 是的。我不会否认导致该部分的上下文也没有多大意义,但这实际上取决于很多因素。这可能是“不要总是相信你阅读的内容”的情况。
【解决方案3】:

在树的情况下,O(logN) 界限将是最坏的情况。让我们这样看。 我们插入 45、33、55、66、22,然后我们将 45 作为根节点,33 和 55 在级别 1、22 和 66 在级别 2..

所以,如果你要散列值 45,它仍然是一个 O(1) 操作......只有当你在 level2 中查找节点时,它的数量才会接近 O(logN)......树可以是 RB 树/AVL 树,这样它就不会退化为链表......你失去了一些 tiem 效率,但在空间效率上弥补了它..

另一个优势是您无需担心哈希表中的冲突。 http://www.cs.rit.edu/~ib/Classes/CS233_Spring08-09/Slides/Week9_Hashing.pdf

基本上,您将动态分配节点,并且不会在哈希表中未使用的存储桶上浪费任何空间...假设您要使用具有预定大小(busckets)的静态哈希表,那么它会导致空间效率低下的实现。

【讨论】:

    猜你喜欢
    • 2017-11-23
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 2010-12-06
    • 2020-04-20
    • 2015-07-23
    • 1970-01-01
    相关资源
    最近更新 更多