【Java】 大話數據結構(13) 查找算法(4) (散列表(哈希表))

本文根據《大話數據結構》一書,實現了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
HashTable

  代碼中重點能夠看:插入操做是如何處理衝突 以及查找操做是如何判斷記錄是否存在的。

相關文章
相關標籤/搜索