我正在尝试了解哈希表-有人可以向我解释清楚吗?


25

我想了解php中哈希表的正确用法和实现(对不起)。

我读过某个地方,没有经验的程序员创建了一个哈希表,然后对其进行遍历。现在,我明白了为什么这是错误的,但是我还没有完全了解我的理解是否正确(如果您知道我的意思)。

因此,有人可以向我解释如何在php中实现哈希表(大概是一个关联数组),也许更重要的是,如何“使用哈希”访问值以及这实际上意味着什么?

Answers:


37

简单哈希表概述

作为更新,哈希表是一种在数据结构中的特定键下存储值的方法。例如,我可以将值存储"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占用。有多种方法可以解决此问题,最简单的方法是一次又一次地调用哈希函数,直到找到一个空白空间。

因此逻辑如下:

  1. (2 + 1)%8 = 3
  2. 索引3已满
  3. 3重新插入我们的哈希函数。(3 +1)%8 = 4,为空。
  4. 将我们的值放入索引4中

现在,哈希表看起来像这样,值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,则哈希表将必须使用与存储数据和检索数据相同的冲突逻辑。根据哈希表的工作方式,您可以一遍又一遍地对键进行哈希处理,直到找到所需的条目(或找到一个空格),或者遍历链接列表,直到找到该项目为止(或到达列表的末尾)

摘要

因此,哈希表是快速存储和访问键值对的一种好方法。在此示例中,我们使用了与值相同的键,但是在现实世界的哈希表中,键并不局限于此。哈希函数将对键起作用以生成索引,然后可以将键/值存储在该索引处。哈希表并不是真正要遍历,尽管可以这样做。如您所见,哈希表可以有很多空格,并且对其进行迭代将浪费时间。即使哈希表具有在其迭代器中跳过空格查找的逻辑,您也更适合使用为迭代器设计的数据结构,例如链表。


2
ASCII艺术FTW!
安托

2
好答案。值得一提的是,每个索引都是链表的方法称为链接。
alexn

+1出色的答案,几乎消除了我的所有疑问。需要再问一个问题。是否每个实现都使用哈希存储整数?还是用于特定情况?如果是的话,那是什么情况?
0decimal0

@PHIfounder我不确定我是否能完全理解您的问题,但是对键执行的哈希函数被设计为通用的,而不仅仅是适用于诸如整数之类的特定数据类型。如果我们在谈论C代码,则可以将哈希表设计为接受键(void *)作为键和值,并对键的指针值进行哈希计算。
杰夫,

@Jeff实际上我可能会问这个傻瓜,但是我在说的是计算机的内部结构; 是否每台计算机在内部都使用像哈希表这样的数据结构来存储存储引用整数?
0decimal0

7

想象一个拥有数千本书的图书馆。您需要对书籍进行整理,以便能够尽快按标题查找每本书籍。

一种(常见的)方法是按字母顺序对书籍进行排序。如果标题以“ G”开头,则找到“ G”区域,然后寻找第二个字母,说“ö”,然后是“ d”,“ e”,“ l”,并缩小搜索范围,依此类推。 ,直到找到这本书。但是,这可能会花费很长时间,而且,当新书到货时,您有时需要重新组织布局以为新书腾出空间。

那是二进制搜索。很好。

但是,有一种更快的方法。假设您列举了所有书架和书架,然后为每本书计算了一个特殊的,希望是唯一的编号,该编号映射到应该在其中找到书的书架/架子。计算“密钥”的方式并不重要,只要它给出一个看起来随机的数字即可。例如,您可以在标题中添加所有字母的字符代码,然后将其除以某个质数(可能不是最好的方法,但是仍然可以使用)。

那是哈希。这样会更快,因为您无需浏览整个书架和书架即可查找标题中的下一个字母。散列通常是一次性操作,除非当两本或多本书籍解析为相同的键时发生“冲突”。但这很好,您知道它们彼此相邻,并且根据哈希函数的质量,在同一个键下不应有太多。

哈希表具有一些局限性和异想天开(重新哈希/调整大小),从而使二进制搜索始终是可行的竞争者。关于哪种方法更好,并非全是黑白。但这是一个不同的故事。

PS很抱歉没有直接回答您的问题(用PHP写一个哈希表),但这很详细,它被称为“编程”;)


2
我喜欢与计算机无关的问题的非计算机相关解释。+1
加布林2011年

1

就我所知,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个字符,有时是完整的散列)将用于识别所谓的“存储桶”,即值的存储区域(右侧)。

然后,每当您访问哈希表时,都可以使用键来获取该值。密钥再次被计算为哈希值,并且哈希值用于快速查找关联的值。因此,如果只是存储所有内容,则哈希表可以比仅线性搜索更快地进行查找。唯一的缺点是某些哈希实现遭受冲突,这是两个不同键的相同计算哈希值。通常,您不必担心太多。

我希望这提供了一些背景知识,但是如果您对此主题感兴趣,请尝试阅读有关该主题的更多信息。我的解释很基本,我敢肯定那里有足够的空洞,但足以满足快速解释的需要。

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.