咱們都知道,二叉樹、紅黑樹等數據結構,它們的查找都是先從根節點開始查找,從節點取出數據或者索引與查找值進行比較。數組
那麼,有沒有一種函數H,根據這個函數和查找關鍵字 Key,能夠直接肯定查找值所在的位置,而不須要一個個比較。這樣叫「預先知道」 Key 所在的位置,直接找到數據,提高效率。即:數據結構
地址index = H(key)dom
說白了,hash函數功能就是根據 key 計算出應該存儲地址的位置,而哈希表是基於哈希函數創建的一種查找表。函數
根據前人經驗,統計出集中經常使用 hash 函數構造方法設計
哈希函數爲關鍵字的線性函數,如 H(key) = a * key + b,這種構造方法比較簡單,均勻。可是有很大限制,僅限與地址大小 = 關鍵字集合的狀況。指針
假設關鍵字集合中的每一個關鍵字 key 都是由s位數字組成(k1,k2,……,kn),分析 key 中的全體數據,並從中提取分佈均勻的若干位或他們的組合構成全體。code
使用舉例:假設要保存的集合中 key 由 5 位不一樣的數字構成。而且這些數字分佈均勻,咱們能夠提取其中一位,表明數據保存的地址。H(key) = key % 100000。索引
此種方法一般用於數字位數較長的狀況,要求數字存在必定的規律,其次必須知道數字的分佈狀況。hash
若是關鍵字的每一位都有某些數字重複出現頻率很高的現象,能夠先求關鍵字的平方值,經過平方擴大差別,然後取中間數位做爲最終存儲地址。table
使用舉例:
好比:key = 1234 1234^2 = 1522756 取 227 做hash地址
好比:key = 4321 4321^2 = 18671041 取 671 做hash地址
這種方法適合事先不知道數據而且數據長度較小的狀況。
若是數字的位數不少,能夠將數字分割爲幾個部分,取他們的疊加和做爲hash地址。
使用舉例:
好比 key=123 456 789
咱們能夠取 1368(123+456+789=1368) 做爲 hash 地址。
該方法適用於數字位數較多且事先不知道數據分佈的狀況。
H(key)=key MOD p (p<=m m爲表長) 很明顯,如何選取p是個關鍵問題。
使用舉例
好比咱們存儲3 6 9,那麼p就不能取3
由於 3 MOD 3 == 6 MOD 3 == 9 MOD 3
p應爲不大於m的質數或是不含20如下的質因子的合數,這樣能夠減小地址的重複(衝突)
好比key = 7,39,18,24,33,21 時取表長m爲9 p爲7 那麼存儲以下:
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
Key | 7 | 21(衝突後移) | 24 | 39 | 18(衝突後移) | 33(衝突後移) |
H(key)= random (key) 取關鍵字的隨機函數爲它們的散列地址。
上面已經提到,不一樣 key 經過 hash 函數產生相同的地址,即 H(key1) == H (key2),此時,key1 和 key2 就發生了 hash 衝突。
無論 hash 函數設計的如何巧妙,總會有特殊的 key 致使 hash 衝突, 特別是對童泰查找表來講。
首先有一個H(key)的哈希函數。
若是H(key1)= H(keyi),那麼 keyi 存儲位置 Hi = (H(key)+di) MOD m。其中 m 是表長。
di 有三種取法:
產生hash衝突後在存儲數據後面加一個指針,指向後面衝突的數據。
創建一個特殊存儲空間,專門存放衝突的數據。此種方法適用於數據和衝突較少的狀況。
準備若干個hash函數,若是使用第一個hash函數發生了衝突,就使用第二個hash函數,第二個也衝突,使用第三個……
查找過程和造表過程一致,假設採用開放定址法處理衝突,則查找過程爲:
對於給定的 key,計算 hash 地址 index = H(key)。
若是數組 arr[index] == null, 則查找不成功;
若是數組 arr[index] == key,則查找成功;
若是 (arr[index] != null && arr[index] != key),使用 hash 衝突解決方法求下一個地址,直到(arr[index] == null || arr[index] == key)。
決定 hash 表查找的 ASL 因素: