如何提高哈希图迭代的复杂性

How to improve the complexity of HashMap iteration?

本文关键字:复杂性 迭代 哈希图 何提高      更新时间:2023-10-16

我实现了一个自定义的HashMap类(C++,但应该无关紧要(。实现很简单——

  • 一个大数组包含指向项的指针。
  • 每个项目都包含键值对和一个指向 Item 的指针(在发生键冲突时形成链表(。
  • 我还为它实现了一个迭代器。

我递增/递减迭代器的实现效率不高。从当前位置,迭代器扫描哈希数组以查找下一个非空条目。当地图人口稀少时,这是非常低效的(对于我的用例来说就是这样(。

任何人都可以建议更快的实现,而不会影响插入和查找等其他操作的复杂性吗?我的主要用例是查找,次要用例是插入。甚至不需要迭代,我只是为了学习而想知道这一点。

PS:为什么我实现了自定义类?因为我需要找到具有一定容错能力的字符串,而我见过的现成哈希图仅提供完全匹配。

编辑:为了澄清,我说的是增加/减少已经获得的迭代器。是的,这样做主要是为了遍历整个地图。

在我的情况下,字符串(键(中的错误是由 OCR 错误引起的。所以我不能使用用于检测打字错误的错误处理技术。拳头字符出错的几率几乎与上一个相同。

另外,我的键总是字符串,确切地说是一个词。条目数将少于 5000。所以 2^16 的哈希表大小对我来说就足够了。即使它仍然人口稀少,但这没关系。

我的哈希函数:

哈希代码大小为 16 位。

字长的前 5 位。 ==> 最大可能的密钥长度 = 32。合理,因为键是一个单词。

最后 11 位用于字符代码的总和。我只存储英文字母字符,不需要区分大小写。所以 26 个代码就足够了,0 到 25。所以一个键 32 'z' = 25 * 32 = 800。这完全在 2^11 之内。如果将来需要,我什至可以添加区分大小写的余地。

现在,当您将包含错误的键与正确的键进行比较时,用"你好"说"地狱"1. 按键长度大致相同2. 它们的字符总和将因丢弃/添加/扭曲的字符的总和而异。

在哈希代码中,由于前 5 位用于长度,因此整个表为每个可能的键长度都有固定的部分。所有部分的大小相同。第一部分存储长度为 1 的键,第二部分存储长度为 2 的键,依此类推。

现在'hello'存储在第 5 部分中,因为长度为 5。当我们试图找到"你好"时,'hello' 的哈希码 = (长度 - 1( (字符总和( = (4( (7 + 4 + 11 + 11 + 14( = (4( (47(= (00100((00000101111(

类似地,'Helo' 的哈希码 = (3((36(= (00011((00000100100(

  1. 我们跳到它的桶里,在那里找不到它。
  2. 所以我们尝试检查一个扭曲的字符。这不会更改长度,但会将字符的总和最多更改为 -25 到 +25。所以我们从25个地方往后搜索到25个地方。即,我们在同一部分中检查从 (36-25( 到 (36+25( 的总和部分。我们找不到它。
  3. 我们检查是否有其他字符错误。这意味着正确的字符串将仅包含 3 个字符。所以我们转到第三部分。现在由于额外的字符而导致的字符总和最多会增加 25,必须对其进行补偿。因此,在第三部分中搜索适当的 25 个位置 (36 - 0( 到 (36 - 25(。我们再次没有找到。
  4. 现在我们考虑缺少字符的情况。因此,原始字符串将包含 5 个字符。哈希代码的第二部分,原始字符串中的字符总和,将增加 0 到 25 的倍数。所以我们在第 25 部分中搜索相应的 5 个存储桶,(36 + 0( 到 (36 + 25(。现在,由于 47('hello' 的总和部分(位于此范围内,我们将找到哈希码的匹配项。我们也知道这场比赛是由于缺少一个角色。因此,我们比较允许容差 1 个缺失字符的键。我们得到了一场比赛!

实际上,这已经实现为允许密钥中的多个错误。也可以对其进行优化,使其在第一部分仅使用 25 个位置(因为它只有一个字符(依此类推。此外,检查 25 个位置似乎矫枉过正,因为我们已经知道密钥的最大和最小字符。但是在出现多个错误的情况下,它会变得复杂。

你提到了字符串的"容错能力"。 为什么不在哈希函数本身中内置"容差",从而消除迭代的需要。

你可以

走Javas LinkedHashMap类的方式。它还通过使其成为双向链表来为哈希图添加有效的迭代。

这些条目是键值对,具有指向上一个和下一个条目的指针。哈希图本身具有大数组以及链表的头部。

插入/删除是两种数据结构的恒定时间,搜索通过哈希图完成,迭代通过链表完成。