打造最快的Hash表

一個簡單的問題:有一個龐大的字符串數組,然後給你一個單獨的字符串,讓你從這個數組中查找是否有這個字符串並找到它,你會怎麼做?有一個方法最簡單,老老 實實從頭查到尾,一個一個比較,直到找到爲止,我想只要學過程序設計的人都能把這樣一個程序作出來,但要是有程序員把這樣的程序交給用戶,我只能用無語來 評價,或許它真的能工作,但 ... 也只能如此了。

       最合適的算法自然是使用 HashTable (哈希表),先介紹介紹其中的基本知識,所謂 Hash ,一般是一個整數,通過某種算法,可以把一個字符串 " 壓縮 " 成一個整數。當然,無論如何,一個 32 位整數是無法對應回一個字符串的,但在程序中,兩個字符串計算出的 Hash 值相等的可能非常小,下面看看在 MPQ 中的 Hash 算法:

                  

   

               

 

     Blizzard 的這個算法是非常高效的,被稱爲 "One-Way Hash"( A one-way hash is a an algorithm that is constructed in such a way that deriving the original string (set of strings, actually) is virtually impossible) 。舉個例子,字符串 "unitneutralacritter.grp" 通過這個算法得到的結果是 0xA26067F3

    是不是把第一個算法改進一下,改成逐個比較字符串的 Hash 值就可以了呢,答案是,遠遠不夠,要想得到最快的算法,就不能進行逐個的比較,通常是構造一個哈希表 (Hash Table) 來解決問題,哈希表是一個大數組,這個數組的容量根據程序的要求來定義,例如 1024 ,每一個 Hash 值通過取模運算 (mod) 對應到數組中的一個位置,這樣,只要比較這個字符串的哈希值對應的位置又沒有被佔用,就可以得到最後的結果了,想想這是什麼速度?是的,是最快的 O(1) ,現在仔細看看這個算法吧:

                  

     看到此,我想大家都在想一個很嚴重的問題:“如果兩個字符串在哈希表中對應的位置相同怎麼辦?” , 畢竟一個數組容量是有限的,這種可能性很大。解決該問題的方法很多,我首先想到的就是用“鏈表” , 感謝大學裏學的數據結構教會了這個百試百靈的法寶,我遇到的很多算法都可以轉化成鏈表來解決,只要在哈希表的每個入口掛一個鏈表,保存所有對應的字符串就 OK 了。事情到此似乎有了完美的結局,如果是把問題獨自交給我解決,此時我可能就要開始定義數據結構然後寫代碼了。然而 Blizzard 的程序員使用的方法則是更精妙的方法。基本原理就是:他們在哈希表中不是用一個哈希值而是用三個哈希值來校驗字符串。

    MPQ 使用文件名哈希表來跟蹤內部的所有文件。但是這個表的格式與正常的哈希表有一些不同。首先,它沒有使用哈希作爲下標,把實際的文件名存儲在表中用於驗證,實際上它根本就沒有存儲文件名。而是使用了 3 種不同的哈希:一個用於哈希表的下標,兩個用於驗證。這兩個驗證哈希替代了實際文件名。

    當然了,這樣仍然會出現 2 個不同的文件名哈希到 3 個同樣的哈希。但是這種情況發生的概率平均是 1:18889465931478580854784 ,這個概率對於任何人來說應該都是足夠小的 。現在再回到數據結構上, Blizzard 使用的哈希表沒有使用鏈表,而採用 " 順延 " 的方式來解決問題,看看這個算法:

             

     1.      計算出字符串的三個哈希值(一個用來確定位置,另外兩個用來校驗 )
       2. 
察看哈希表中的這個位置

       3. 
哈希表中這個位置爲空嗎?如果爲空,則肯定該字符串不存在,返回
       4. 
如果存在,則檢查其他兩個哈希值是否也匹配,如果匹配,則表示找到了該字符串,返回
       5. 
移到下一個位置,如果已經移到了表的末尾,則反繞到表的開始位置起繼續查詢 
       6. 
看看是不是又回到了原來的位置,如果是,則返回沒找到
       7. 
回到 3

 

發佈了0 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章