說道查找,我想大家並不陌生,所謂查找,即為根據某個給定的值,在查找表中確定一個其關鍵字等於給定值的記錄或數據元素。
像比較常見的,有循序查找,針對有序表的有比較高效的二分(折半)查找,同時還有通過樹來優化的靜態樹查找與動態樹查找,但是這些無一不是在查找過程中要進行一系列的和關鍵字之間的比較。
那么,我們是否可以不需要比較就找到了目標呢? 當然,如果我們把相應查找表的存儲位置(index)和其關鍵字之間構建一個確定的聯系,那么在每一查找時,我們就可以直接通過關鍵字獲得查找對象的存儲位置,然后就可以拿到相應都目標對象了。
這就是哈希表所做的事兒!!!
下面舉個栗子:
現需要建立一張全國30個地區的各名族人口統計表,每個地區有一個記錄,記錄的數據如下:
顯然,使用數組存儲,數組長度為30,每一個數組元保存一個地區的信息,一般情況下,我們所做的就是把地區一個一個的添加到數組里面去,最多也就是按照第一個漢字的拼音的開頭字母進行一下排序,最終我們要去找某個元素的時候,雖然大概知道我們要找的元素大概在那個位置,但是是不是還是要進行相應的關鍵字比對,即使只有那么一兩次(優秀的查找算法加上有序的查找表),哈希表所做的就是,我的關鍵字就是數組下標,(什么? –_– 你是不是瘋了,我的關鍵字是中文,是字符串,怎么就和數組下標,不對 ),當然,關鍵字不能做下標,但是我們可以找到一種對應關系,是得每一個關鍵字都盡量對應唯一個下標(數值),這就是我們所說的哈希函數。
還是上面的例子:現列出三種關鍵字與存儲位置(index)之間的對應關系:
第二種哈希函數:先求關鍵字的第一個和最后一個字母在字母表中的序號之和,和值若比30大,則減去30,
第三種哈希函數:先求每個漢字的第一個拼音字母的ASCII嗎之和的八進制形式,然后將這個八進制數看成十進制數在除以30取余,若余數為零則加上30使其成為哈希函數值。
當然!從上面的表格中我們可以看到,不管值哪一種哈希函數,都存在通關關鍵字求出來的數組相同的情況,我們稱之為沖突,從哈希函數的原理以及目的來分析,顯然這是不可以的,如何解決,這里先買一個官子,先來談談如何構建哈希表,如何找到一個合適和哈希函數,何為合適,
如對於關鍵字集合的任意一個關鍵字,經哈希函數地址結合中任意一個地址的概率是相等的,則成此類哈希函數為均勻的哈希函數,即關鍵字的哈希地址均勻分布在整個地址空間中,從而減少沖突。
下面列出常用的構建哈希函數的方法:
1:直接定址法(取關鍵字和關鍵字的某個線性函數值為哈希地址)。
如:一個從1歲到100歲的人口數字統計表,其中,年齡作為關鍵字,哈希函數就可以取關鍵字本身,滿足哈希表的要求。
2:數字分析法(通過關鍵字的整體情況來判斷去關鍵字的某一段或者某一種形式):
原則:盡量選擇關鍵字出現隨機且相互獨立的區段。
3:平方取中法(取關鍵字平方后的中間幾位為哈希地址)
原理:一個數平方后的中間幾位數和數的每一位都相關。
4:折疊法(將關鍵字分割為位數相同的幾部分,然后取這幾部分的疊加和,針對於關鍵字比較多的時候)
栗子:國際標准圖書編號,0-442-20586-4 可以通過如下過程求得哈希函數值
5:除留余數法,取關鍵字 被 p(某個不大於哈希表表長m的的數) 除后所得余數的為哈希地址。
H(key) = key MOD p, ( p < m);
一般情況下,選擇的p應應為質數(只能被1和其自身整除的數)。
6:隨機數法
上面談到在通過哈希函數求哈希值時,難免會遇見兩個不同的關鍵字求得的哈希地址相同的情況,如何處理沖突?
處理沖突:為沖突的關鍵字記錄找到另一個’空’的地址
1:開放定址法:
1):線性探測再散列: 即從沖突的地址開始往后找,直至找到‘空’的哈希地址。
2):二次探測再散列:一次取1的二次方,-1的二次方,2的二次方,-2的二次方……讓當前沖突的哈希地址與這些數字一次相加知道找到‘空地址’,其實我們可以看做在沖突的地方兩邊波動並逐漸遠離。
3):偽隨機數序列(這個就顧名思義了)。
二次聚集:兩個第一個哈希地址不同的記錄爭奪同一個后繼哈希地址。 上圖為例,我們已經求出了60,17,29的哈希地址,當我們就后面的關鍵字時,就有可能會求得其哈希地址為5,與關鍵字為60的有沖突,此時,若使用線性探測在散列,哈希地址將會變為8,這時,如果有一個關鍵字的算出來的哈希地址是6,與17沖突,這個新算出來的為6的關鍵字是不是本來也應該取8為其哈希地址的(根據線性探測再散列),此時五門就說這兩個關鍵字(最新算出來的哈希地址與60相同的和最新算出來的哈希地址和17相同的)聚集。
2:再哈希法: H = R(H(key)):即在同義詞產生地址沖突時通過另一個哈希函數計算哈希地址。
3:鏈地址法:創建另外一個鏈表,將所有關鍵字為同義詞的記錄存儲在此線性表中。
4:建立公共溢出區:建立向量OverTable[0…v]:所有關鍵字和基本表中關鍵字為同義詞的記錄,不管其有哈希函數的到的哈希地址是什么,只要沖突發生都填入溢出表。