1. 程式人生 > >記錄一下雜湊表底層原理

記錄一下雜湊表底層原理

理解HashMap底層,首先應該理解Hash函式

從解決一個問題入手:大量的資料要儲存查詢,構造雜湊表來解決

初步想法

借鑑陣列下標訪問的思路來做,只需知道起始位置和下標值,

不管陣列中有多少個元素,都可以一次訪問到,

將元素和元素位置建立一種一一對應的關係

Hash函式的出現

輸入的元素的範圍可能很大甚至無窮,而我們的記憶體有限,

所以說我們需要一種函式對映關係,將這些無限的元素對映到我們有限的記憶體地址上。

Hash函式代表著一類函式,即把任意範圍的元素可以通過對映關係壓縮成固定範圍的元素。

Hash函式的選擇

如果是正整數,我們可以用這個正整數數除以某個數,取其餘數,即我們常用的 k % m,k為正整數,m 為除數;

這樣一來,範圍就縮小了很多,比如說 15%10=5,26%10=6,...,所有的正整數經過運算,都變成了 0-9 範圍之間的數了,

這樣範圍就縮小了很多

m 的選擇

這種做法,m 的選擇就非常重要了,如果 k 值分佈均勻還無所謂,如果 k 值具有某些特徵

比如說 k 的個位基本上不變,而高位分佈均勻,如 15,25,45,65,85,95,155,就遭遇大沖突了,

必須要使得經過Hash函式後關鍵字的分佈均勻,儘量減少衝突

鏈地址法

為了解決衝突,引出鏈地址法

在儲存的時候,如果多個元素被Hash到同一位置,那麼就加入到該位置所指向的連結串列中,

如果該位置沒有元素,則為null(指向空)”

由於新加入的元素很可能被再次訪問到,使用“頭插”

rehash

這樣解決衝突固然好,但是也有瓶頸

當我們實際存入的值越來越多的時候,這個連結串列也勢必越來越長,

那當我們進行查詢的時候,勢必就會遍歷連結串列,效率也就越來越慢。

因此,我們要選取一個相關的新的Hash函式(比如之前使用 key % m,現在只改變一下m的值)

將舊Hash表中所有的元素通過新的Hash函式計算出新的Hash值,並將其插入到新表中(仍然使用連結串列),這就叫rehash

 這裡的陣列就擴大了近兩倍,由於要大小要選素數,那就選原陣列大小兩倍後的第一個素數7,舊Hash表和新Hash表採用了不同的Hash函式,但相關,只是m的取值變了

裝載因子 α 

 我們可以定義這樣一個變數 α = 所有元素個數/陣列的大小,

它代表著我們的Hash表(也就是陣列)的裝滿程度,在這裡也代表連結串列的平均長度

 這個裝載因子代表了Hash表的裝滿程度,這裡也可以代表連結串列的平均長度,那麼也就可以代表查詢時的時間長短了。

 

參考資料:神速雜湊上神速雜湊下