为什么(无冲突)哈希表查找真的是O(1)?


10

免责声明:我知道这里和Stackoverflow上已经存在类似的听起来的问题。但是它们都是关于碰撞的,这不是我要的。

我的问题是:为什么在碰撞较少查找O(1)摆在首位?

假设我有此哈希表:

Hash  Content
-------------
ghdjg Data1
hgdzs Data2
eruit Data3
xcnvb Data4
mkwer Data5
rtzww Data6

现在,我正在寻找k哈希函数h(k)给出的键h(k) = mkwer。但是查找如何“知道”哈希mkwer在位置5?为什么不必在所有按键中滚动O(n)才能找到它?哈希不能是某种真正的硬件地址,因为我失去了移动数据的能力。据我所知,哈希表未按哈希排序(即使是哈希,搜索也将采用O(log n))?

知道哈希如何帮助您找到表格中的正确位置?

Answers:


24

哈希函数不会返回诸如的字符串mkwer。它直接返回项目在数组中的位置。例如,如果您的哈希表有十个条目,则哈希函数将返回0–9范围内的整数。


1
谢谢。:)我的错误是在考虑像MD5或SHA这样的哈希表哈希函数。但是,哈希当然可以是整数位置,这是我没有想到的。现在,我知道要查找的内容了,我什至很快找到了一个很好的例子:PHP的哈希函数:github.com/php/php-src/blob/PHP-5.6.10/Zend/zend_hash.h#L237
Foo

13
@FooBar:MD5和SHA也会从输入中计算出单个数字,以十六进制形式谈论哈希值是如此普遍。就像内存地址很少被认为是十进制一样。
nperson325681 '16

4
另外,MD5等太长,无法直接用作数组索引。可能会使用哈希的某些部分,例如低n位。
chirlu '16

6

哈希函数根据给定的字符串计算数组位置。如果这是完美的哈希,则意味着肯定没有冲突,最有可能的数组至少是元素数量的两倍。

例如,我将给出非常差的字母散列,仅用于说明机制:
0) 1)对于字符串中的每个字符均采用ascii值,如果小写则减去'a',如果大写则减去'A',然后将值加到x。x = x m o d 52 2)结果数,例如15是数组的索引。 X=0;
X=XØd52

这种非常简单的哈希(有限且容易发生冲突)在哈希机制上不同于其他哈希,不考虑给定的输入。在更高级的方案中,散列数更大,已调整为元素数量。为所有输入生成完美的哈希,以确保不发生冲突。

这是因为根据字符串计算哈希值取决于函数计算的复杂程度,而不取决于元素的数量。Ø1个

在完美哈希的情况下,重新计算元素时,在数组负载较大时发生冲突的最简单情况是数组大小增加,函数采用更大的输出模,并且元素移到新位置。Hķ

阵列是连续存储器片段,以获得元件你采取的第一个元素(数组开始)的地址,然后添加到这个地址Ñ * 小号Ž Ë ö ˚F é é ë Ñ 所以你必须显式存储单元。ñ-ŤHñs一世žËØFËËËñŤ


1
查找如何知道哈希在表中的何处?它既不是有序的,也不是硬件地址。
Foo Bar

HXCñvb=8

但是并不是每个索引都会被填充。如果我的散列1、4、8、90和223充满数据,查找如何找到正确的位置?在这种情况下,索引“ 90”位于位置4,因为大多数其他索引都不存在。一个空的哈希表不是具有所有可能位置的无限大小!
Foo Bar

H一个H一个HXCñvb=H一个[90]

哈希函数不会将索引返回到数组中。相反,它返回可以映射到数组中的可预测数字。通常使用模数运算符,将哈希表存储桶的数量作为其他操作数来完成。
Christopher Schultz

3

为了扩展David Richerby的答案,术语“ 哈希函数 ”有点重载。通常,当我们谈论哈希函数时,我们会想到MD5,SHA-1或类似Java的.hashCode()方法,它们会将一些输入转换为一个数字。但是,此数字的域(即最大值)不太可能与您要存储数据的哈希表的大小相同。(MD5是16个字节,SHA-1是20个字节,并且.hashCode()int-4个字节)。

因此,您的问题是有关下一步的问题-一旦我们有了一个可以将任意输入映射到数字的哈希函数,如何将它们放入特定大小的数据结构中?具有另一个功能,也称为“哈希函数”!

这种函数的一个简单例子是 ; 您可以轻松地将多个任意大小映射到模中数组中的特定索引。这在CLRS中被称为“除法”:

ķķ

Hķ=ķ

...

=2pHķpķ

〜算法简介,第11.3.1节-CLRS

Java HashMap使用除法的修改版本,该方法执行预处理步骤以解决较弱的.hashCode()实现,因此它可以使用二乘幂数组。您可以确切地看到该.getEntry()方法中发生了什么(评论是我的):

 // hash() transforms key.hashCode() to protect against bad hash functions
 int hash = (key == null) ? 0 : hash(key.hashCode());
 // indexOf() converts the resulting hash to a value between 0 and table.length-1
 for (Entry<K,V> e = table[indexFor(hash, table.length)];
     ...

Java 8带来了HashMap更快的重写速度,但更难读取。但是,它使用相同的一般原则进行索引查找。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.