哈希表如何在O(1)中返回值?

时间:2013-10-06 12:20:05

标签: hashtable

让我们说有一个Hash函数。它存储'n'键值对。如果我需要特定键的值,则散列函数遍历所有键以找到我们正在寻找其值的键。如果是,那么复杂性如何变为O(1)? hash如何查找键?

3 个答案:

答案 0 :(得分:3)

不,这不是哈希表的工作原理。散列表本质上是一个数组,它将值存储在与其键的散列相对应的索引处。所以,假设我想将字符串"abc"映射到另一个字符串"xyz",并假设"abc"哈希到42。我要做的是转到我的表的索引42并放置字符串"xyz"。现在,如果稍后我想找到与字符串"abc"关联的值,我会再次哈希它并转到相应的索引(42),在那里找到"xyz"。这总体上是O(1)操作。总结:

Mapping "abc" to "xyz"...

1. hash("abc") = 42

2. Place in "xyz" table:

 ---+-----------------------------------+---
... |      |      | "xyz" |      |      | ...
 ---+-----------------------------------+---
       40     41     42     43     44

Later...

1. Query "abc"

2. hash("abc") = 42

3. Look at index 42, find the value "xyz"

为了描述哈希表的工作原理,我略微过度简化了,我恳请您浏览一下hash table维基百科的文章,以获得更深入的描述。另请注意,很多时候您会看到哈希表实现为链接列表数组,以便考虑两个键散列到相同数字的情况(所谓的hash collisions)。使用普通数组将无法处理此类情况,因为我们无法在同一位置存储多个值。例如,这是Java如何实现HashMap

例如,请参考上面的示例并假设我们想要将"123"映射到"pqr",并假设"123" 哈希到42.最终的结果看起来像这样:

       40     41     42     43     44
 ---+-----------------------------------+---
... |      |      |   +   |      |      | ...
 ---+-----------------|-----------------+---
                      |
              +---------------+
              | "abc" | "xyz" |
              +---------------+
                      |
              +---------------+
              | "123" | "pqr" |
              +---------------+

请注意,我们知道必须将键与值一起显式存储。现在,如果我们想要使用键"123"进行查询,我们将转到其哈希位置(42)并遍历在那里找到的链表,直到找到具有键"123"的链表。然后我们将返回相应的值"pqr"

此时您可能有两个问题:

  • hash()函数如何在O(1)中运行?
  • 如果我们需要遍历一个链表,那么整个操作怎么能是O(1)?

关于第一个问题,在讨论哈希表的复杂性时,通常不考虑哈希过程(可能不是实际一个恒定时间操作),仅仅因为假设与其他后续流程相比,不会非常耗时。事实上,在许多情况下,实际上 的散列是不变的。例如,由于字符串在许多语言中是不可变的,因此它们的哈希值通常只计算一次然后缓存,从而导致在第一次哈希操作之后进行恒定时间哈希。

至于第二个问题,当我们有一个好的哈希函数和一个合理大小的表时,形成的链表应该非常短(大概不超过3个)。因此,遍历过程被认为是恒定时间。

答案 1 :(得分:1)

名称中的“哈希”是一个函数,它基本上将密钥转换为该密钥的(理想情况下)唯一索引。实际上,每个哈希都是一个“桶”,可能包含多个值,以允许冲突。

另见http://en.wikipedia.org/wiki/Hash_function

答案 2 :(得分:0)

尽管前面的两个答案都是正确的(并且我同时投了两票),但我觉得要加入一些见解。

首先,哈希表是一种抽象数据类型,这意味着可以有许多数据结构和检索算法可以在特定实现中使用。数组,二进制搜索树,字典等只是一些可能的实现的例子。

其次,重要的一点是,用于访问密钥值的平均用例检索是固定时间,即O(1),而不是最差情况。< / p>

因此密钥映射到理想的唯一存储位置。但是,在实际场景中总是存在冲突,并且通过存储那些多个值(例如维护链接列表,树或另一个第二级哈希)来处理冲突。

关键是,对于良好的散列函数,与正常的常量时间索引访问相比,冲突 quire rare 。因此,短语AVERAGE CASE。

在散列函数中最坏情况下针对键检索值永远不会是O(1)。它可以是log(n)甚至是最差的。但是,对于每个良好的散列函数,它的出现频率是如此之小,以至于平均情况复杂度仍然保持在O(1)即恒定时间。