1. 程式人生 > >解決hash衝突的四種方法

解決hash衝突的四種方法

通過構造效能良好的雜湊函式,可以減少衝突,但一般不可能完全避免衝突,因此解決衝突是雜湊法的另一個關鍵問題。建立雜湊表和查詢雜湊表都會遇到衝突,兩種情況下解決衝突的方法應該一致。下面以建立雜湊表為例,說明解決衝突的方法。常用的解決衝突方法有以下四種:

一、開放定址法(再雜湊法)

這種方法也稱再雜湊法,其基本思想是:當關鍵字key的雜湊地址p=H(key)出現衝突時,以p為基礎,產生另一個雜湊地址p1,如果p1仍然衝突,再以p為基礎,產生另一個雜湊地址p2,…,直到找出一個不衝突的雜湊地址pi ,將相應元素存入其中。這種方法有一個通用的再雜湊函式形式:
Hi=(H(key)+di)% m i=1,2,…,n
其中H(key)為雜湊函式,m 為表長,di稱為增量序列。增量序列的取值方式不同,相應的再雜湊方式也不同。主要有以下三種:
線性探測再雜湊


dii=1,2,3,…,m-1
這種方法的特點是:衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表。
平方探測再雜湊
di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )
這種方法的特點是:衝突發生時,在表的左右進行跳躍式探測,比較靈活。
隨機探測再雜湊
di=偽隨機數序列。
優點:
①記錄更容易進行序列化(serialize)操作(由於不存在指標)
②如果記錄總數可以預知,可以建立完美雜湊函式,此時處理資料的效率是非常高的。
缺點:
①擴容成本飆升。儲存記錄的數目不能超過桶陣列的長度,如果超過就需要擴容,而擴容會導致某次操作的時間成本飆升,這在實時或者互動式應用中可能會是一個嚴重的缺陷
②加大計算成本。使用探測序列,有可能其計算的時間成本過高,導致雜湊表的處理效能降低
③有空槽導致記憶體浪費。由於記錄是存放在桶陣列中的,而桶陣列必然存在空槽,所以當記錄本身尺寸(size)很大並且記錄總數規模很大時,空槽佔用的空間會導致明顯的記憶體浪費
④刪除記錄時,比較麻煩。比如需要刪除記錄a,記錄b是在a之後插入桶陣列的,但是和記錄a有衝突,是通過探測序列再次跳轉找到的地址,所以如果直接刪除a,a的位置變為空槽,而空槽是查詢記錄失敗的終止條件,這樣會導致記錄b在a的位置重新插入資料前不可見,所以不能直接刪除a,而是設定刪除標記。這就需要額外的空間和操作。

二、拉鍊法(如:HashMap)

這種方法的基本思想是將所有雜湊地址為i的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指標存在雜湊表的第i個單元中,因而查詢、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。
優點:
①對於記錄總數頻繁可變的情況,處理的比較好(也就是避免了動態調整的開銷)
②由於記錄儲存在結點中,而結點是動態分配,不會造成記憶體的浪費,所以尤其適合那種記錄本身尺寸(size)很大的情況,因為此時指標的開銷可以忽略不計了
③刪除記錄時,比較方便,直接通過指標操作即可

缺點:
①儲存的記錄是隨機分佈在記憶體中的,這樣在查詢記錄時,相比結構緊湊的資料型別(比如陣列),雜湊表的跳轉訪問會帶來額外的時間開銷
③由於使用指標,記錄不容易進行序列化(serialize)操作

三、再雜湊法

這種方法是同時構造多個不同的雜湊函式:
Hi=RH1(key) i=1,2,…,k
當雜湊地址Hi=RH1(key)發生衝突時,再計算Hi=RH2(key)……,直到衝突不再產生。這種方法不易產生聚集,但增加了計算時間。

四、建立公共溢位區

這種方法的基本思想是:將雜湊表分為基本表和溢位表兩部分,凡是和基本表發生衝突的元素,一律填入溢位表。