數據結構 - 解決Hash沖突(碰撞)四種常用方法


一、預備知識

1、非哈希表的特點:關鍵字在表中的位置和它之間不存在一個確定的關系,查找的過程為給定值一次和各個關鍵字進行比較,查找的效率取決於和給定值進行比較的次數。
2、哈希表的特點:關鍵字在表中位置和它之間存在一種確定的關系。
3、哈希函數:一般情況下,需要在關鍵字與它在表中的存儲位置之間建立一個函數關系,以f(key)作為關鍵字為key的記錄在表中的位置,通常稱這個函數f(key)為哈希函數。
4、hash:翻譯為“散列”,把任意長度的輸入通過hash算法變換成固定長度的輸出,這個輸出就是Hash值。這種轉換是一種壓縮映射,哈希值的空間遠小於輸入的空間,所以可能會發生“哈希碰撞”,即兩個不同的輸入,產生了同一個輸出,所以不可能從散列值來唯一的確定輸入值。簡單來說就是,一種將任意長度的消息壓縮到莫伊固定長度的消息摘要的函數,Hash算法常用於消息摘要的場景 MD5、SHA都屬於Hash算法的實現。

5、hash沖突:根據key即經過一個函數f(key)得到的結果的作為地址去存放當前的key value鍵值對(這個是hashmap的存值方式),但發現與其他的對象計算值一樣。

例如:當我們對某個元素進行哈希運算,得到一個存儲地址,要插入時發現已被其他元素占用。

二、解決方案

1、 開放定址法(再散列法):

  當關鍵字key的哈希地址p出現沖突時,以p為基礎,產生另一個哈希地址p1,如果p1仍然沖突,再以p為基礎,產生另一個哈希地址,直到找出一個不沖突的哈希地址pi ,將相應元素存入其中。

  一旦發生了沖突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到,並將記錄存入公式為:
  fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1) 
  ※ 做法是:當沖突發生時,使用某種探測技術在散列表中形成一個探測序列。沿此序列逐個單元地查找,直到找到給定的關鍵字,或者碰到一個開放的地址(即該地址單元為空)為止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查找時探測到開放的地址則表明表中無待查的關鍵字,即查找失敗。 

  比如說,我們的關鍵字集合為{12,67,56,16,25,37,22,29,15,47,48,34},表長為12。

  我們用散列函數f(key) = key mod l2 
  當計算前S個數{12,67,56,16,25}時,都是沒有沖突的散列地址,直接存入: 

  這里寫圖片描述 
  計算key = 37時,發現f(37) = 1,此時就與25所在的位置沖突。 
  我們應用上面的公式f(37) = (f(37)+1) mod 12 = 2。

  於是將37存入下標為2的位置: 
  這里寫圖片描述

2、再哈希法(雙哈希法): 

  在發生沖突時,再用第二個,第三個...哈希函數算出哈希值,直到算出的哈希值不同為止。雖然不易發生聚集,但增加了計算時間。

3、鏈地址法(拉鏈法): 

  把同一個散列槽(數組的每一個槽)中的所有元素放到一個鏈表中。

  每個哈希表節點都有一個next指針,多個哈希表節點可以用next指針構成一個單向鏈表,被分配到同一個索引上的多個節點可以用這個單向鏈表連接起來,如:
  鍵值對k2, v2與鍵值對k1, v1通過計算后的索引值都為2,此時產生沖突,但是可以通道next指針將k2, k1所在的節點連接起來,這樣就解決了哈希的沖突問題。  
  這里寫圖片描述

4、建立公共溢出區:

  將哈希表分為基本表和溢出表兩部分,凡是和基本表發生沖突的元素,一律填入溢出表。建立一個公共溢出區域,把沖突的都放在另一個地方,不在表里面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM