2014-12-13 72 views
0

我實現了一個自定義的HashMap類(在C++中,但不應該)。實現很簡單 -如何提高HashMap迭代的複雜度?

  • 一個大數組包含指向Items的指針。
  • 每個項目包含鍵值對和一個指向項目的指針(以在發生鍵衝突時形成鏈接列表)。
  • 我也爲它實現了一個迭代器。

我的遞增/遞減迭代器的實現不是非常有效。從當前位置,迭代器掃描下一個非空條目的散列數組。這是非常低效的,當地圖稀疏填充(這將是我的用例)。

任何人都可以建議更快的實現,而不會影響插入和查找等其他操作的複雜性嗎?我的主要用例是find,secondary是插入。迭代甚至不需要,我只是想知道這是爲了學習。

PS:爲什麼我實現了一個自定義類?因爲我需要查找具有一定容錯性的字符串,而準備好的哈希映射只能提供完全匹配。

編輯:爲了澄清,我正在談論遞增/遞減已獲得的迭代器。是的,這主要是爲了遍歷整個地圖。

在我的情況下字符串(鍵)中的錯誤發生在OCR錯誤。所以我不能使用錯誤處理技術來檢測輸入錯誤。拳頭性格錯誤的機會幾乎與最後一個相同。

此外,我的鍵總是字符串,一個字是確切的。條目數將少於5000.所以2^16的哈希表大小對我來說就足夠了。即使它仍然是人口稀少,但沒關係。

我的散列函數:

哈希碼大小是16位。

字長的前5位。 ==>最大可能的密鑰長度= 32。合理的,因爲密鑰是一個單詞。

最後11位的char代碼總和。我只存儲英文字母字符,並且不需要區分大小寫。所以26個代碼就足夠了,0到25.所以一個32'z'= 25 * 32 = 800的密鑰,這個在2^11之內。如果將來需要,我甚至可以添加區分大小寫。

現在,當你比較包含錯誤與正確的一個關鍵, 說「地獄」與「你好」的關鍵 1.長度大約是相同 2和他們的字符會由不同的總和丟失/添加/扭曲的字符。

在散列碼中,前5位用於長度,整個表具有用於每個可能長度的鍵的固定部分。所有部分的大小相同。第一部分存儲長度爲1,長度爲2的第二部分等等。

現在'你好'存儲在第5部分,長度爲5。'當我們試圖找到'你好'時, 'hello'的哈希碼=(長度-1)(字符之和)=(4)(7 + 4 + 11 + 11 + 14)=(4)(47) =(00100)(00000101111)

同樣, '直升機' 的哈希碼=(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的因子。因此我們在第5部分(36 + 0)到(36 + 25)中搜索相應的25個桶。現在,47(「hello」的總和部分)位於此範圍內,我們將找到哈希碼的匹配項。答:我們也知道這場比賽是由於缺少一個人物而造成的。所以我們比較一下允許容差爲1的字符。我們得到一場比賽!

實際上,這已被實現以允許在關鍵字中出現多個錯誤。 它也可以優化爲只使用25個位置(因爲它只有一個字符)等等。 此外,檢查25個地方似乎過分,因爲我們已經知道密鑰的最大和最小字符。但是如果出現多個錯誤,它會變得複雜。

+0

我不明白,當你試圖找到一個值時,你不應該立即用給定的鍵轉到數組中的第一個「Item」,然後遍歷一個鏈表?如果你不得不遍歷整個數組,那麼你的數組太小或者你的散列函數不是很好(這種情況發生的唯一方法就是碰撞是否存在_tons_)。 – Jared 2014-12-13 06:42:43

+2

@Jared I *認爲*(很可能不正確)他說的是迭代整個哈希映射*,而不僅僅是一個碰撞鏈。我很高興知道更清晰,我同意。 – WhozCraig 2014-12-13 07:01:46

+0

你是說你想把''hello''和''hwllo'''映射到同一個散列值(因爲它們是相似的 - 注意'e'和'w'很容易被意外地鍵入另一個基於他們在QWERTY鍵盤上的位置)。因此,你想快速查找本質上相似的字符串組?因爲如果你說要查看每個字符串並進行比較(並找到最接近的字符串),那麼你沒有描述任何可以被遠程解釋爲散列表的東西。 – Jared 2014-12-13 07:56:39

回答

0

您提到了字符串的'容錯'。爲什麼不建立在散列函數本身的「寬容」中,從而避免了迭代的需要呢?

+0

是的,這就是我所做的。我已經用細節更新了這個問題。其實我不需要遍歷整個數組。但想知道如何加快速度。 – 2014-12-14 09:19:42

0

你可以去class LinkedHashMap這個類,它通過使它成爲一個雙向鏈接列表。

中的條目是具有指向前一個和下一個條目的鍵 - 值對。HashMap中本身所具有的大的陣列以及鏈接列表的頭部。

插入/缺失是恆定時間對於這兩種數據結構來說,搜索都是通過哈希映射完成的,並通過鏈表重複進行。

+0

謝謝。我想到了這一點,但如何刪除插入是恆定的時間?對於地圖來說只會是不變的。您還必須在鏈接列表中搜索要插入/刪除的密鑰。 (當條目數很少時,這不會成爲問題) – 2014-12-14 09:24:12