哈希查找定義:
哈希查找是通過計算數據元素的存儲地址進行查找的一種方法。O(1)的查找,即所謂的秒殺。哈希查找的本質是先將數據映射成它的哈希值。哈希查找的核心是構造一個哈希函數,它將原來直觀、整潔的數據映射爲看上去似乎是隨機的一些整數。
哈希查找的操作步驟:
1) 用給定的哈希函數構造哈希表;
2) 根據選擇的衝突處理方法解決地址衝突;
3) 在哈希表的基礎上執行哈希查找。
建立哈希表操作步驟:
1) step1 取數據元素的關鍵字key,計算其哈希函數值(地址)。若該地址對應的存儲空間還沒有被佔用,則將該元素存入;否則執行step2解決衝突。
2) step2 根據選擇的衝突處理方法,計算關鍵字key的下一個存儲地址。若下一個存儲地址仍被佔用,則繼續執行step2,直到找到能用的存儲地址爲止。
哈希查找步驟爲:
1) Step1 對給定key值,計算哈希地址 Di=Hash(key);若HST爲空,則查找失敗;若HST=key,則查找成功;否則,執行step2(處理衝突)。
2) Step2 重複計算處理衝突的下一個存儲地址 Dk=R(Dk-1),直到HST[Dk]爲空,或HST[Dk]=key爲止。若HST[Dk]=key,則查找成功,否則查找失敗。
比如說:”5“是一個要保存的數,然後我丟給哈希函數,哈希函數給我返回一個”2",那麼此時的”5“和“2”就建立一種對應關係,這種關係就是所謂的“哈希關係”,在實際應用中也就形成了”2“是key,”5“是value。
那麼有的朋友就會問如何做哈希,首先做哈希必須要遵守兩點原則:
①: key儘可能的分散,也就是我丟一個“6”和“5”給你,你都返回一個“2”,那麼這樣的哈希函數不盡完美。
②:哈希函數儘可能的簡單,也就是說丟一個“6”給你,你哈希函數要搞1小時才能給我,這樣也是不好的。
其實常用的做哈希的方法有“五種”:
第一種:”直接定址法“。
很容易理解,key=Value+C;這個“C"是常量。Value+C其實就是一個簡單的哈希函數。
第二種:“除法取餘法”。
很容易理解, key=value%C;解釋同上。
第三種:“數字分析法”。
這種蠻有意思,比如有一組value1=112233,value2=112633,value3=119033,
針對這樣的數我們分析數中間兩個數比較波動,其他數不變。那麼我們取key的值就可以是
key1=22,key2=26,key3=90。
第四種:“平方取中法”。此處忽略,見名識意。
第五種:“摺疊法”。
這種蠻有意思,比如value=135790,要求key是2位數的散列值。那麼我們將value變爲13+57+90=160,然後去掉高位“1”,此時key=60,哈哈,這就是他們的哈希關係,這樣做的目的就是key與每一位value都相關,來做到“散列地址”儘可能分散的目地。
影響哈希查找效率的一個重要因素是哈希函數本身。當兩個不同的數據元素的哈希值相同時,就會發生衝突。爲減少發生衝突的可能性,哈希函數應該將數據儘可能分散地映射到哈希表的每一個表項中。
常用的解決衝突的方法有以下兩種:
(1) 開放地址法
如果兩個數據元素的哈希值相同,則在哈希表中爲後插入的數據元素另外選擇一個表項。當程序查找哈希表時,如果沒有在第一個對應的哈希表項中找到符合查找要求的數據元素,程序就會繼續往後查找,直到找到一個符合查找要求的數據元素,或者遇到一個空的表項。
(2) 鏈地址法
將哈希值相同的數據元素存放在一個鏈表中,在查找哈希表的過程中,當查找到這個鏈表時,必須採用線性查找方法。
實現哈希函數爲“除法取餘法”,解決衝突爲“開放地址線性探測法”,代碼如下:
public class HashSearch {
public static void main(String[] args) {
//“除法取餘法”
int hashLength = 13;
int [] array = { 13, 29, 27, 28, 26, 30, 38 };
//哈希表長度
int[] hash = new int[hashLength];
//創建hash
for (int i = 0; i < array.length; i++)
{
insertHash(hash, hashLength, array[i]);
}
int result = searchHash(hash,hashLength, 29);
if (result != -1)
System.out.println("已經在數組中找到,索引位置爲:" + result);
else
System.out.println("沒有此原始");
}
/****
* Hash表檢索數據
*
* @param hash
* @param hashLength
* @param key
* @return
*/
public static int searchHash(int[] hash, int hashLength, int key) {
// 哈希函數
int hashAddress = key % hashLength;
// 指定hashAdrress對應值存在但不是關鍵值,則用開放尋址法解決
while (hash[hashAddress] != 0 && hash[hashAddress] != key) {
hashAddress = (++hashAddress) % hashLength;
}
// 查找到了開放單元,表示查找失敗
if (hash[hashAddress] == 0)
return -1;
return hashAddress;
}
/***
* 數據插入Hash表
*
* @param hash 哈希表
* @param hashLength
* @param data
*/
public static void insertHash(int[] hash, int hashLength, int data) {
// 哈希函數
int hashAddress = data % hashLength;
// 如果key存在,則說明已經被別人佔用,此時必須解決衝突
while (hash[hashAddress] != 0) {
// 用開放尋址法找到
hashAddress = (++hashAddress) % hashLength;
}
// 將data存入字典中
hash[hashAddress] = data;
}
}
原文:https://blog.csdn.net/xiaoping8411/article/details/7706376