查找——圖文翔解HashTree(哈希樹)

注:本文爲轉載文章,原文地址爲:http://blog.csdn.net/yang_yulei/article/details/46337405

感謝原博主整理分享!


在各種數據結構(線性表、樹等)中,記錄在結構中的相對位置是隨機的。因此在機構中查找記錄的時需要進行一系列和關鍵字的比較。這一類的查找方法建立在“比較”的基礎上。查找的效率依賴於查找過程中所進行的比較次數

之前我們介紹的各種基於比較的樹查找算法,這些查找算法的效率都將隨着數據記錄數的增長而下降。僅僅是有的比較慢(時間複雜度爲O(n)),有的比較快(時間複雜度是O(logn))而已。這些查找算法的平均查找長度是在一種比較理想的情況下獲得的。在實際應用當中,對數據結構中數據的頻繁增加和刪除將不斷地改變着數據的結構。這些操作將可能導致某些數據結構退化爲鏈表結構,那麼其性能必然將下降。爲了避免出現這種情況而採取的調整措施,又不可避免的增加了程序的複雜程度以及操作的額外時間。

哈希表

理想的情況是希望不經過任何比較,一次存取便能得到所查的記錄,那就必須在記的存儲位置和它的關鍵字之間建立一個確定的對應關係f,使每個關鍵字和一個唯一的存儲位置相對應。因而在查找時,只要根據這個對應關係f找到給定值K的像f(K)。由此,不需要進行比較便可直接取得所查記錄。在此,我們稱這個對應關係爲哈希(Hash)函數,按這個思想建立的表爲哈希表

在哈希表中對於不同的關鍵字可能得到同一哈希地址,這種現象稱做衝突。在一般情況下,衝突只能儘可能地減少,而不能完全避免。因爲哈希函數是從關鍵字集合到地址集合的映像。通常關鍵字的集合比較大,它的元素包括所有可能的關鍵字,而地址集合的元素僅爲哈希表中的地址值。在一般情況下,哈希函數是一個壓縮映像函數,這就不可避免的要產生衝突。

哈希樹(HashTree)算法就是要提供一種在理論上和實際應用中均能有效地處理衝突的方法。一般的哈希(Hash)算法都是O(1)的,而且基本是以空間換時間。這很容易導致對存儲空間無限制的需求。本文中哈希樹(HashTree)算法在實際操作中使用了一些技巧使得對空間的需求控制在一定範圍內。即空間需求僅和所需要存儲的對象個數有關,不會無限制地“膨脹”下去。


哈希樹的理論基礎


質數分辨定理
簡單地說就是:n個不同的質數可以“分辨”的連續整數的個數和他們的乘積相等。“分辨”就是指這些連續的整數不可能有完全相同的餘數序列。
(這個定理的證明詳見:http://wenku.baidu.com/view/16b2c7abd1f34693daef3e58.html

例如:
從2起的連續質數,連續10個質數就可以分辨大約M(10) =2*3*5*7*11*13*17*19*23*29= 6464693230 個數,已經超過計算機中常用整數(32bit)的表達範圍。連續100個質數就可以分辨大約M(100) = 4.711930 乘以10的219次方。
而按照目前的CPU水平,100次取餘的整數除法操作幾乎不算什麼難事。在實際應用中,整體的操作速度往往取決於節點將關鍵字裝載內存的次數和時間。一般來說,裝載的時間是由關鍵字的大小和硬件來決定的;在相同類型關鍵字和相同硬件條件下,實際的整體操作時間就主要取決於裝載的次數。他們之間是一個成正比的關係。


插入

我們選擇質數分辨算法來建立一棵哈希樹。
選擇從2開始的連續質數來建立一個十層的哈希樹。第一層結點爲根結點,根結點下有2個結點;第二層的每個結點下有3個結點;依此類推,即每層結點的子節點數目爲連續的質數。到第十層,每個結點下有29個結點。
同一結點中的子結點,從左到右代表不同的餘數結果。
例如:第二層結點下有三個子節點。那麼從左到右分別代表:除3餘0,除3餘1,除3餘2.
對質數進行取餘操作得到的餘數決定了處理的路徑

結點結構:結點的關鍵字(在整個樹中是唯一的),結點的數據對象,結點是否被佔據的標誌位(標誌位爲真時,關鍵字才被認爲是有效的),和結點的子結點數組。
哈希樹的節點結構

  1. struct Node  
  2. {  
  3.     keyType  key ;  
  4.     ValueType value ;  
  5.     bool   occupied ; //用occupied來表示節點是否被佔據。如果節點的關鍵字(key)有效,那麼occupied應該設置位true,否則設置爲false。  
  6.  struct Node* subNodes[1] ; //我們用subNodes[i]來表示節點的第i個子節點的地址。(此技術在跳躍表中有介紹,可翻看前面博客)  
  7. } ;  
<span style="font-size:14px;">struct Node
{
    keyType      key ;
    ValueType    value ;
    bool         occupied ;    //用occupied來表示節點是否被佔據。如果節點的關鍵字(key)有效,那麼occupied應該設置位true,否則設置爲false。
    struct Node* subNodes[1] ; //我們用subNodes[i]來表示節點的第i個子節點的地址。(此技術在跳躍表中有介紹,可翻看前面博客)
} ;</span>
(如果在建立當初就建立所有的節點,那麼所消耗的計算時間和磁盤空間是巨大的。在實際使用當中,只需要初始化根節點就可以開始工作。子節點的建立是在有更多的數據進入到哈希樹中的時候建立的。因此可以說哈希樹和其他樹一樣是一個動態結構。)


下面我們以隨機的10個數的插入爲例,來圖解HashTree的插入過程,這個史上最清晰的圖解,你一定能看的明白^_^


有讀者可能有疑問,如果一直衝突下去怎麼辦?首先,若關鍵字是整型,我們的10層哈希樹完全可以分辨出來它們,這是質數分辨算法決定的。

(我們其實也可以把所有的鍵-值節點放在哈希樹的第10層葉節點處,這第10層的滿節點數就包含了所有的整數個數,但是如果這樣處理的話,所有的非葉子節點作爲鍵-值節點的索引,這樣使樹結構龐大,浪費空間)

【這裏沒有說的太清楚,此圖是以2開始的連續質數創建的,即:從上到下的層級中的每個節點中的子樹個數爲2、3、5、7、11、13、17、19、23、29。第一層中的每個節點的子樹個數爲2,第二層中的每個節點子樹個數爲5.。。。。

上圖中的子樹上的數字,是其父節點的子樹指針數組的索引值】


查找 

哈希樹的節點查找過程和節點插入過程類似,就是對關鍵字用質數序列取餘,根據餘數確定下一節點的分叉路徑,直到找到目標節點
如上圖,最小”哈希樹(HashTree)在從4G個對象中找出所匹配的對象,比較次數不超過10次。也就是說:最多屬於O(10)。在實際應用中,調整了質數的範圍,使得比較次數一般不超過5次。也就是說:最多屬於O(5)。因此可以根據自身需要在時間和空間上尋求一個平衡點。

刪除 

哈希樹的節點刪除過程也很簡單,哈希樹在刪除的時候,並不做任何結構調整。
只是先查到到要刪除的節點,然後把此節點的“佔位標記”置爲false即可(即表示此節點爲空節點,但並不進行物理刪除)。

優點

1、結構簡單

從哈希樹的結構來說,非常的簡單。每層節點的子節點個數爲連續的質數。子節點可以隨時創建。因此哈希樹的結構是動態的,也不像某些哈希算法那樣需要長時間的初始化過程。哈希樹也沒有必要爲不存在的關鍵字提前分配空間。
需要注意的是哈希樹是一個單向增加的結構,即隨着所需要存儲的數據量增加而增大。即使數據量減少到原來的數量,但是哈希樹的總節點數不會減少。這樣做的目的是爲了避免結構的調整帶來的額外消耗。

2、查找迅速

從算法過程我們可以看出,對於整數,哈希樹層級最多能增加到10。因此最多只需要十次取餘和比較操作,就可以知道這個對象是否存在。這個在算法邏輯上決定了哈希樹的優越性。
一般的樹狀結構,往往隨着層次和層次中節點數的增加而導致更多的比較操作。操作次數可以說無法準確確定上限。而哈希樹的查找次數和元素個數沒有關係。如果元素的連續關鍵字總個數在計算機的整數(32bit)所能表達的最大範圍內,那麼比較次數就最多不會超過10次,通常低於這個數值。 

3、結構不變

從刪除算法中可以看出,哈希樹在刪除的時候,並不做任何結構調整。這個也是它的一個非常好的優點。常規樹結構在增加元素和刪除元素的時候都要做一定的結構調整,否則他們將可能退化爲鏈表結構,而導致查找效率的降低。哈希樹採取的是一種“見縫插針”的算法,從來不用擔心退化的問題,也不必爲優化結構而採取額外的操作,因此大大節約了操作時間。

缺點

1、非排序性

哈希樹不支持排序,沒有順序特性。如果在此基礎上不做任何改進的話並試圖通過遍歷來實現排序,那麼操作效率將遠遠低於其他類型的數據結構。



關於超長字符串的問題


如果是超長字符串的關鍵字,該如何處理?若把它們按26進制每一位都轉換爲數字,則得到的結果太大。
我們可以用MD5等消息壓縮算法來生成定長的整數。

關於MD5

維基鏈接:http://zh.wikipedia.org/wiki/MD5
MD5(Message Digest Algorithm 消息摘要算法第五版)
一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

MD5算法具有以下特點:
1、壓縮性:任意長度的數據,算出的MD5值長度都是固定的
2、容易計算:從原數據計算出MD5值很容易。
3、抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。
4、弱抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即僞造數據)是非常困難的。
5、強抗碰撞:想找到兩個不同的數據,使它們具有相同的MD5值,是非常困難的。
(1996年後被證實存在弱點,可以被加以破解,對於需要高度安全性的數據,專家一般建議改用其他算法,如SHA-1)

對於超長字符串,我們可以用MD5算法生成一個128bit的整數,然後用RadixTree(翻看前面博客)來存儲這個大整數,或者使用哈希樹來存儲,對於這樣的大整數,我們不能簡單地使用計算機的整數來做除法,而是使用程序模擬人工的除法方式來做除法並獲得餘數。
這樣,使用MD5和選用更大的質數相結合的辦法。這樣就可以使得通過層次比較少的哈希樹來獲得對關鍵字區間的完整覆蓋。這樣就減少了比較操作的次數,並提高整體的工作效率。


應用

哈希樹可以廣泛應用於那些需要對大容量數據進行快速匹配操作的地方。例如:數據庫索引系統、短信息中的收條匹配、大量號碼路由匹配、信息過濾匹配。哈希樹不需要額外的平衡和防止退化的操作,效率十分理想。


【參考】

http://baike.baidu.com/view/10403049.htm
http://wenku.baidu.com/view/16b2c7abd1f34693daef3e58.html

----------------------------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章