數據結構-hash-碰撞解決方法

Hash是一種校驗方法,其中應用最廣爲人知的就是 HashMap。當然Hash算法並不完美,有可能兩個不同的原始值在經過哈希運算後得到同樣的結果,這樣就是哈希碰撞。

開放地址法

開放地執法有一個公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)
其中,m爲哈希表的表長。di 是產生衝突的時候的增量序列。如果di值可能爲1,2,3,…m-1,稱線性探測再散列。
如果di取1,則每次衝突之後,向後移動1個位置.如果di取值可能爲1,-1,2,-2,4,-4,9,-9,16,-16,…kk,-kk(k<=m/2),稱二次探測再散列。
如果di取值可能爲僞隨機數列。稱僞隨機探測再散列。
基本思想:當發生地址衝突時,按照某種方法繼續探測哈希表中的其他存儲單元,直到找到空位置爲止。

當發生地址衝突時,按照某種方法繼續探測哈希表中的其他存儲單元,直到找到空位置爲止。用方程來表達的話是這樣子,
H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1))
m 是哈希表的長度。舉一個實際的例子,一個哈希函數是 H ( key ) = key mod 7 ,哈希表長度爲 7,關鍵字序列( 32 , 13 , 49 , 55 , 22 , 38 , 21 )如果以線性探測再散列來生成哈希表的話,過程是這樣的
32 % 7 = 4 ; 13 % 7 = 6 ; 49 % 7 = 0 ; 55 % 7 = 6 發生衝突,下一個存儲地址( 6 + 1 )% 7 = 0 ,仍然發生衝突,再下一個存儲地址:( 6 + 2 )% 7 = 1 未發生衝突,可以存入。 22 % 7 = 1 發生衝突,下一個存儲地址是:( 1 + 1 )% 7 = 2 未發生衝突; 38 % 7 = 3 ; 21 % 7 = 0 發生衝突,按照上面方法繼續探測直至空間 5 ,不發生衝突
所得到的哈希表對應存儲位置:
下標: 0 1 2 3 4 5 6 49 55 22 38 32 21 13

對於利用開放地址法處理衝突所產生的哈希表中刪除一個元素時需要謹慎,不能直接地刪除,因爲這樣將會截斷其他具有相同哈希地址的元素的查找地址,所以,通常採用設定一個特殊的標誌以示該元素已被刪除。

再哈希法

使用第二個或第三個…計算地址,知道無衝突。比如:按首字母進行hash衝突了,則按照首字母第二位,進行hash尋址。
當發生衝突時,使用第二個、第三個、哈希函數計算地址,直到無衝突時。缺點:計算時間增加。
比如上面第一次按照姓首字母進行哈希,如果產生衝突可以按照姓字母首字母第二位進行哈希,再衝突,第三位,直到不衝突爲止

鏈地址法(拉鍊法)

將所有關鍵字爲同義詞的記錄存儲在同一線性鏈表中
創建一個鏈表數組,數組中每一格就是一個鏈表。若遇到哈希衝突,則將衝突的值加到鏈表中即可。
java hashmap使用的就是拉鍊法解決hash碰撞。

優點:
①拉鍊法處理衝突簡單,且無堆積現象,即非同義詞決不會發生衝突,因此平均查找長度較短;
②由於拉鍊法中各鏈表上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況;
③開放定址法爲減少衝突,要求裝填因子α較小,故當結點規模較大時會浪費很多空間。而拉鍊法中可取α≥1,且結點較大時,拉鍊法中增加的指針域可忽略不計,因此節省空間;
④在用拉鍊法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去鏈表上相應的結點即可。而對開放地址法構造的散列表,刪除結點不能簡單地將被刪結 點的空間置爲空,否則將截斷在它之後填人散列表的同義詞結點的查找路徑。這是因爲各種開放地址法中,空地址單元(即開放地址)都是查找失敗的條件。因此在 用開放地址法處理衝突的散列表上執行刪除操作,只能在被刪結點上做刪除標記,而不能真正刪除結點。

缺點:
指針需要額外的空間,故當結點規模較小時,開放定址法較爲節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的衝突,從而提高平均查找速度。

公共溢出區

假設哈希函數的值域爲[0,m-1],則設向量HashTable[0…m-1]爲基本表,另外設立存儲空間向量OverTable[0…v]用以存儲發生衝突的記錄。

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