總結性歸納:哈希值
什么是 Hash
Hash(哈希),又稱“散列”。
散列(hash)英文原意是“混雜”、“拼湊”、“重新表述”的意思。
在某種程度上,散列是與排序相反的一種操作,排序是將集合中的元素按照某種方式比如字典順序排列在一起,而散列通過計算哈希值,打破元素之間原有的關系,使集合中的元素按照散列函數的分類進行排列。
在介紹一些集合時,我們總強調需要重寫某個類的 equlas() 方法和 hashCode() 方法,確保唯一性。這里的 hashCode() 表示的是對當前對象的唯一標示。計算 hashCode 的過程就稱作 哈希。
為什么要有 Hash
我們通常使用數組或者鏈表來存儲元素,一旦存儲的內容數量特別多,需要占用很大的空間,而且在查找某個元素是否存在的過程中,數組和鏈表都需要挨個循環比較,而通過 哈希 計算,可以大大減少比較次數。
幾種常見的哈希函數(散列函數)構造方法
- 直接定址法
- 取關鍵字或關鍵字的某個線性函數值為散列地址。
- 即 H(key) = key 或 H(key) = a*key + b,其中a和b為常數。
- 除留余數法
- 取關鍵字被某個不大於散列表長度 m 的數 p 求余,得到的作為散列地址。
- 即 H(key) = key % p, p < m。
- 數字分析法
- 當關鍵字的位數大於地址的位數,對關鍵字的各位分布進行分析,選出分布均勻的任意幾位作為散列地址。
- 僅適用於所有關鍵字都已知的情況下,根據實際應用確定要選取的部分,盡量避免發生沖突。
- 平方取中法
- 先計算出關鍵字值的平方,然后取平方值中間幾位作為散列地址。
- 隨機分布的關鍵字,得到的散列地址也是隨機分布的。
- 折疊法(疊加法)
- 將關鍵字分為位數相同的幾部分,然后取這幾部分的疊加和(舍去進位)作為散列地址。
- 用於關鍵字位數較多,並且關鍵字中每一位上數字分布大致均勻。
- 隨機數法
- 選擇一個隨機函數,把關鍵字的隨機函數值作為它的哈希值。
- 通常當關鍵字的長度不等時用這種方法。
構造哈希函數的方法很多,實際工作中要根據不同的情況選擇合適的方法,總的原則是盡可能少的產生沖突。
通常考慮的因素有關鍵字的長度和分布情況、哈希值的范圍等。
如:當關鍵字是整數類型時就可以用除留余數法;如果關鍵字是小數類型,選擇隨機數法會比較好。
哈希沖突的解決
選用哈希函數計算哈希值時,可能不同的 key 會得到相同的結果,一個地址怎么存放多個數據呢?這就是沖突。
常用的主要有兩種方法解決沖突:
1.鏈接法(拉鏈法)
拉鏈法解決沖突的做法是:
將所有關鍵字為同義詞的結點鏈接在同一個單鏈表中。
若選定的散列表長度為 m,則可將散列表定義為一個由 m 個頭指針組成的指針數組 T[0..m-1] 。
凡是散列地址為 i 的結點,均插入到以 T[i] 為頭指針的單鏈表中。
T 中各分量的初值均應為空指針。
在拉鏈法中,裝填因子 α 可以大於 1,但一般均取 α ≤ 1。
2.開放定址法
用開放定址法解決沖突的做法是:
用開放定址法解決沖突的做法是:當沖突發生時,使用某種探測技術在散列表中形成一個探測序列。沿此序列逐個單元地查找,直到找到給定的關鍵字,或者碰到一個開放的地址(即該地址單元為空)為止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查找時探測到開放的地址則表明表中無待查的關鍵字,即查找失敗。
簡單的說:當沖突發生時,使用某種探查(亦稱探測)技術在散列表中尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到。
按照形成探查序列的方法不同,可將開放定址法區分為線性探查法、二次探查法、雙重散列法等。
a.線性探查法
hi=(h(key)+i) % m ,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],然后依次探查 T[d+1],…,直到 T[m-1],此后又循環到 T[0],T[1],…,直到探查到 有空余地址 或者到 T[d-1]為止。
b.二次探查法
hi=(h(key)+i*i) % m,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],然后依次探查 T[d+1^2],T[d+2^2],T[d+3^2],…,等,直到探查到 有空余地址 或者到 T[d-1]為止。
缺點是無法探查到整個散列空間。
c.雙重散列法
hi=(h(key)+i*h1(key)) % m,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],然后依次探查 T[d+h1(d)], T[d + 2*h1(d)],…,等。
該方法使用了兩個散列函數 h(key) 和 h1(key),故也稱為雙散列函數探查法。
定義 h1(key) 的方法較多,但無論采用什么方法定義,都必須使 h1(key) 的值和 m 互素,才能使發生沖突的同義詞地址均勻地分布在整個表中,否則可能造成同義詞地址的循環計算。
該方法是開放定址法中最好的方法之一。