本文根據《大話數據結構》一書,實現了Java版的一個簡單的散列表(哈希表)。java
對關鍵字key,將其值存放在f(key)的存儲位置上。由此,在查找時不需比較,只需計算出f(key)即可直接取得所查記錄。這個函數 f() 就叫作散列函數,按這個思想創建的表稱爲散列表。數據結構
散列技術便是一種存儲方法,又是一種查找方法:ide
存儲過程:根據關鍵字key,算出f(key),將記錄存放在f(key)的位置上;函數
查找過程:根據關鍵字key,算出f(key),該位置上的值即爲要找的記錄。this
直接定址法spa
直接取關鍵字的線性函數爲散列地址:f(key)=a×key+b(a,b爲常數)設計
如:對下表的記錄,關鍵字key取爲出生年份,令f(key)=key-1980便可。指針
數字分析法code
分析一組數據,找出其規律,儘量利用這些數據來構造衝突概率較低的散列地址blog
如:以員工的手機號碼做爲關鍵字,前7位數字基本相同,能夠選擇後面四位數字做爲散列地址。
平方取中法
當沒法肯定關鍵字中哪幾位分佈較均勻時,能夠先求出關鍵字的平方值,而後按須要取平方值的中間幾位做爲散列地址。
摺疊法
將關鍵字分割成位數相同的幾部分,最後一部分位數能夠不一樣,而後取這幾部分的疊加和(去除進位)做爲散列地址。
除留餘數法
最爲經常使用的方法,取關鍵字被某個不大於散列表表長m的數p除後所得的餘數爲散列地址。
f(key) = key MOD p,p<=m。
隨機數法
選擇一隨機函數(僞隨機),取關鍵字的隨機值做爲散列地址,一般用於關鍵字長度不一樣的場合。
當兩個關鍵字key1和key2不一樣時,有f(key1)=f(key2),這種現象稱爲衝突。通常狀況下,咱們會盡可能設計恰當的散列函數減小衝突,但沒法徹底避免,這就須要對衝突進行處理。
開放尋址法
一旦發生衝突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到,並將記錄存入。根據下一個位置的不一樣,又可分爲如下三種:
①線性探測法:
②二次探測法
③隨機探測法
再散列函數法
在同義詞產生地址衝突時計算另外一個散列函數地址,直到衝突再也不發生,這種方法不易產生「彙集」,但增長了計算時間。以下圖所示(RHi表明不一樣的散列函數):
鏈地址法
相同地址的記錄存放在一個單鏈表中,散列表值存儲全部同義詞子表的頭指針。以下圖所示:
公共溢出區法
爲全部衝突的關鍵字創建一個公共的溢出區來存放。
接下來創建一個簡單的散列表,其散列函數採用上述的除留餘數法,處理衝突的方法採用開放定址法下的線性探測法。
Java代碼以下:
package HashTable; /** * 散列表 * @author Yongh * */ public class HashTable { int[] elem; int count; private static final int Nullkey = -32768; public HashTable(int count) { this.count = count; elem = new int[count]; for (int i = 0; i < count; i++) { elem[i] = Nullkey; // 表明位置爲空 } } /* * 散列函數 */ public int hash(int key) { return key % count; // 除留餘數法 } /* * 插入操做 */ public void insert(int key) { int addr = hash(key); // 求散列地址 while (elem[addr] != Nullkey) { // 位置非空,有衝突 addr = (addr + 1) % count; // 開放地址法的線性探測 } elem[addr] = key; } /* * 查找操做 */ public boolean search(int key) { int addr = hash(key); // 求散列地址 while (elem[addr] != key) { addr = (addr + 1) % count; // 開放地址法的線性探測 if (addr == hash(key) || elem[addr] == Nullkey) { // 循環回到原點或者到了空地址 System.out.println("要查找的記錄不存在!"); return false; } } System.out.println("存在記錄:" + key + ",位置爲:" + addr); return true; } public static void main(String[] args) { int[] arr = { 12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34 }; HashTable aTable = new HashTable(arr.length); for (int a : arr) { aTable.insert(a); } for (int a : arr) { aTable.search(a); } } }
存在記錄:12,位置爲:0 存在記錄:67,位置爲:7 存在記錄:56,位置爲:8 存在記錄:16,位置爲:4 存在記錄:25,位置爲:1 存在記錄:37,位置爲:2 存在記錄:22,位置爲:10 存在記錄:29,位置爲:5 存在記錄:15,位置爲:3 存在記錄:47,位置爲:11 存在記錄:48,位置爲:6 存在記錄:34,位置爲:9
代碼中重點能夠看:插入操做是如何處理衝突 以及查找操做是如何判斷記錄是否存在的。