Hash學習(3)-衝突的解決

         爲提高hash表查找性能,除了考慮選擇合適的hash表表長和完美的hash函數外,還必須考慮hash表處理衝突的能力。當hash函數對兩個不同的數據項產生了相同的hash值時,衝突就產生了。對於衝突的處理,通常採用的方法可以分爲三類:

(1)線性再散列法,簡單的按順序遍歷hash表,尋找下一個可用的槽;

(2)非線性再散列法,計算一個新的hash值;

(3)外部拉鍊法,將hash表中的每個槽當作具有相同hash值的數據項所組成鏈表的頭部,hash表將發生衝突的項添加到同一個鏈表中。

下面對這三種方法分別介紹。

1.線性再散列法

       線性再散列法是形式最簡單的處理衝突的方法。插入元素時,如果發生衝突,算法會簡單的遍歷hash表,直到找到表中的下一個空槽,並將該元素放入該槽中。查找元素時,首先散列值所指向的槽,如果沒有找到匹配,則繼續遍歷hash表,直到:(1)找到相應的元素;(2)找到一個空槽(指示查找的元素不存在);(3)整個hash表遍歷完畢(指示該元素不存在並且hash表是滿的)。下表顯示了以線性再散列法將{89,18,49,58,69}5個元素插入hash表的過程。(hash函數爲:hash(X)=X mod 10;hash表長一般用素數,這裏爲了說明方便取表長爲10)


         第一次衝突發生在插入關鍵字49時,它被放在下一個空閒地址,即地址0。關鍵字58依次和18,89,49發生衝突,試選三次之後才找到一個空單元。對69的衝突用類似的方法處理。從以上過程可以看出,只要表中有空閒單元,總可以找到,但這裏選擇步長爲1,將會在hash表中產生聚集,即:即使hash表相對較空,還是會在某些區域形成一些區塊,這些區塊中的任何活動都將設計更大的步長。但如果以5或更大的值作爲步長,可以迅速地從擁擠區域移開,從而減少聚集現象的發生。事實上,只要hash表長和檢查槽的步長是互質的,那麼表中的每個槽都會被檢查到。

       線性再散列法有兩個缺點:第一,不能從表中刪除元素,因爲相應的單元可能已經引起過沖突,元素繞過它存到了別處,例如,如果我們刪除了18,那麼其他的元素都會找不到。如果確實需要刪除,可以採用懶惰刪除的方法。第二,當表被填滿時性能下降明顯。

2.非線性再散列法

        線性再散列法是從衝突位置開始,採用一個步長以順序方式遍歷hash表,來查找一個可用的槽,從上面的討論可以看出,它容易產生聚集現象。非線性再散列法可以避免遍歷散列表,它會計算一個新的hash值,並通過它跳轉到表中一個完全不同的部分。它的思想就是:通過跳轉到表中不同的部分,從而避免相似值的聚集,如果再散列函數跳轉到的槽已經被佔用了,則繼續執行新一輪的再散列和跳轉。

    例如,還是上面的例子,如果再散列函數是hash(X)=R-(X mod R),其中R爲小於hash表長的素數,如果我們選擇R=7,則下表顯示了插入與前面相同的關鍵字的結果。


        第一個衝突發生在49被插入的時候, hash(49)=7-0=7,故49被插入到位置6。Hash(58)=7-2=5,於是58被插入到位置3。最後69產生衝突,從而被插入到距離爲hash(69)=7-6=1的地方。

        非線性再散列法也有不能從表中刪除元素的缺點。

        無論是使用線性再散列法還是非線性再散列法,只有在散列表不會接近填滿的情況下,才能使用再散列。當散列表的負載因子增大時,再散列所花費的時間也會顯著增加。通過以上討論可以看出,再散列方法適用於表負載較低並且不太可能執行刪除操作的情況。

3.外部拉鍊法

        外部拉鍊法是將hash表看作是一個鏈表數組,表中的每個槽要不爲空,要不指向hash到該槽的表項的鏈表。可以通過把元素添加到鏈表中來解決衝突。同樣,可以通過從鏈表中刪除元素來執行刪除操作。因此,解決衝突的代價不會超過向鏈表中添加一個節點,不需要執行再散列。在再散列中,表項的最大數量是由表中槽的原始數量確定的,與之不同的是,外部拉鍊法可以容納的元素於將在內存中存放的元素一樣多。

        外部拉鍊法的原則是:hash表的大小一般與預料的元素個數差不多。

        假設有一個表長爲10的hash表,給出10個關鍵字爲前10個自然數的平方,hash函數爲hash(X)=X mod 10,下圖就是對應的外部拉鍊法的hash表。


          外部拉鍊法的平均查找時間是對鏈表的查找時間加上1,這個1是最初的定位hash表槽。外部拉鍊法的缺點是:它需要稍微多一些的空間來實現,因爲添加任何元素都需要添加指向節點的指針,並且每次探查也要花費稍微多一點的時間,因爲它需要間接引用指針,而不是直接訪問元素。由於今天的內存成本很低並且可以使用非常快的CPU,所以這些缺點都是微不足道的。因此,實際使用hash表時,一般都是使用拉鍊法來解決hash衝突。



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