Answers:
作为更新,哈希表是一种在数据结构中的特定键下存储值的方法。例如,我可以将值存储"a"
在key下1
,然后通过1
在哈希表中查找key 来检索它。
我可以想到的最简单的哈希表示例是只能存储整数的哈希表,其中哈希表条目的键也是要存储的值。假设您的表大小为8,基本上是内存中的数组:
---------------------------------
| | | | | | | | |
---------------------------------
0 1 2 3 4 5 6 7
哈希函数为您提供了存储值的索引。此表的一个非常简单的哈希函数将1添加到你要存储的值,然后国防部 8(表的大小)它。换句话说,您的哈希函数为(n+1)%8
,其中n
您要存储的整数。
如果您想在此哈希表中插入值,则可以对要插入的值调用哈希函数(在本例中为(n+1)%8
),以为您提供索引。例如,如果要插入14,则将调用(14 + 1) % 8
并获取index 7
,因此将其值插入index中7
。
---------------------------------
| | | | | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
同样,我们可以像这样插入33、82和191:
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
但是,如果我们尝试插入会与条目冲突的内容,会发生什么?2应该进入index 3
,但是它被82占用。有多种方法可以解决此问题,最简单的方法是一次又一次地调用哈希函数,直到找到一个空白空间。
因此逻辑如下:
现在,哈希表看起来像这样,值2存储在index处4
。
---------------------------------
|191| |33 |82 |2 | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
该解决方案的缺点是很快,我们的桌子就满了!如果您知道数据量有限,那么只要表足够大以容纳所有可能的值,就不成问题。如果您希望能够容纳更多的东西,可以以不同的方式处理碰撞。让我们回到插入2之前的位置。
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
如果您还记得,请(2+1)%8
给我们提供index 3
。如果您不希望哈希表填满,则可以将每个表索引用作链接列表,然后将其追加到该索引的列表中。因此,我们无需再次调用哈希函数,而只需将其追加到列表的index处即可3
:
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
然后,此列表可以增长到内存允许的最大范围。我可以插入18,并将其附加到2:
-----
|18 |
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
假设哈希表的大小非常大,则在哈希表中查找值很快。您只需调用您的哈希函数,并获取索引。假设您要查看表中是否有82。查找函数将调用(82+1)%8
= 3
,并查看index中的项目3
,然后为您返回。如果您查找16,则查找函数将在index中查找1
,并发现它不存在。
如果尝试查找值2,则哈希表将必须使用与存储数据和检索数据相同的冲突逻辑。根据哈希表的工作方式,您可以一遍又一遍地对键进行哈希处理,直到找到所需的条目(或找到一个空格),或者遍历链接列表,直到找到该项目为止(或到达列表的末尾)
因此,哈希表是快速存储和访问键值对的一种好方法。在此示例中,我们使用了与值相同的键,但是在现实世界的哈希表中,键并不局限于此。哈希函数将对键起作用以生成索引,然后可以将键/值存储在该索引处。哈希表并不是真正要遍历,尽管可以这样做。如您所见,哈希表可以有很多空格,并且对其进行迭代将浪费时间。即使哈希表具有在其迭代器中跳过空格查找的逻辑,您也更适合使用为迭代器设计的数据结构,例如链表。
想象一个拥有数千本书的图书馆。您需要对书籍进行整理,以便能够尽快按标题查找每本书籍。
一种(常见的)方法是按字母顺序对书籍进行排序。如果标题以“ G”开头,则找到“ G”区域,然后寻找第二个字母,说“ö”,然后是“ d”,“ e”,“ l”,并缩小搜索范围,依此类推。 ,直到找到这本书。但是,这可能会花费很长时间,而且,当新书到货时,您有时需要重新组织布局以为新书腾出空间。
那是二进制搜索。很好。
但是,有一种更快的方法。假设您列举了所有书架和书架,然后为每本书计算了一个特殊的,希望是唯一的编号,该编号映射到应该在其中找到书的书架/架子。计算“密钥”的方式并不重要,只要它给出一个看起来随机的数字即可。例如,您可以在标题中添加所有字母的字符代码,然后将其除以某个质数(可能不是最好的方法,但是仍然可以使用)。
那是哈希。这样会更快,因为您无需浏览整个书架和书架即可查找标题中的下一个字母。散列通常是一次性操作,除非当两本或多本书籍解析为相同的键时发生“冲突”。但这很好,您知道它们彼此相邻,并且根据哈希函数的质量,在同一个键下不应有太多。
哈希表具有一些局限性和异想天开(重新哈希/调整大小),从而使二进制搜索始终是可行的竞争者。关于哪种方法更好,并非全是黑白。但这是一个不同的故事。
PS很抱歉没有直接回答您的问题(用PHP写一个哈希表),但这很详细,它被称为“编程”;)
就我所知,PHP中的哈希表仅通过以下方式实现:
$my_hash = array(
1 => "Bob",
2 => "Alice",
3 => "Jack"
);
然后,您可以通过以下调用来访问数据:
echo $my_hash[2]; // Will echo "Alice"
您可以使用foreach()函数迭代数组的内容。
理解哈希表的最佳方法是读取类似http://en.wikipedia.org/wiki/Hash_table之类的内容,但大致可以归结为这一点:array()调用内的每一行的左侧都是键。这些密钥将进行哈希计算,结果是哈希。您以前可能已经看过MD5或SHA哈希,它看起来与此非常相似。该散列的特定部分(通常是前X个字符,有时是完整的散列)将用于识别所谓的“存储桶”,即值的存储区域(右侧)。
然后,每当您访问哈希表时,都可以使用键来获取该值。密钥再次被计算为哈希值,并且哈希值用于快速查找关联的值。因此,如果只是存储所有内容,则哈希表可以比仅线性搜索更快地进行查找。唯一的缺点是某些哈希实现遭受冲突,这是两个不同键的相同计算哈希值。通常,您不必担心太多。
我希望这提供了一些背景知识,但是如果您对此主题感兴趣,请尝试阅读有关该主题的更多信息。我的解释很基本,我敢肯定那里有足够的空洞,但足以满足快速解释的需要。