1. 程式人生 > >雜湊表:Hash table

雜湊表:Hash table

雜湊表

1、什麼是雜湊表

      對於陣列,我們可以通過下標直接進行訪問,這種訪問的速度很快,例如通過關鍵字key(1,2,...m)來定位其對應的元素data[1,2,...m]。假設可以用的記憶體很大而且關鍵字的全集(1,2,3,...m,...,2m)又不是很大,那麼可以為每個元素分配一個記憶體空間,也就可以利用這種直接定址的方法快速的訪問元素。在直接定址方式下,具有關鍵字key的元素被直接存放在索引為key的記憶體中。       但在平常的使用過程中,常用的關鍵字(1,2,3,...m)只是其全集(1,2,3,...,m,...)的一部分且全集有時候非常大,如果為關鍵字全集賦予一個空間這大大浪費了資源甚至有時候是不可能的。當所使用的關鍵字集合K比起關鍵字域U要小很多,那麼可以設計一種方式,將很”零散“分佈在整個關鍵字域中通過一個轉換,使其變得”緊緻“一些。這個過程有點類似於空間變換,假想在一個矩形上的點沿著一邊做投影,(假設投影不重合)只需要在邊上搜搜就能找到對應的點而不是在整個平面中去找。或者可以想象用凸透鏡聚光,聚光前和聚光後,光線所佔的面積是不是變小了?! 因此,這種對映關係就變得很關鍵,它能將本來需要很多空間來構成的關鍵字表大大減小,這樣經過對映之後的表稱之為雜湊表或者散列表。這種對映稱之為雜湊函式或者雜湊函式。

2、雜湊函式

雜湊函式可以表示為: h:U----〉{0,1,...,m-1}。其中我們所希望的關鍵字儘可能的在這個區間[0,m-1]上分佈均勻,而且不論關鍵字是何種形式(數字或者字元)都希望對映後的值是自然數這樣就能和散列表中陣列的索引進行對應了。

1)直接地址法 取關鍵詞的某個線性函式值為雜湊地址 h(key) = a*key+b
2)除法雜湊法
h(key) = key mod m
此種方法對m的選擇是有要求的,一個不好的m很容易影響雜湊效果,一般不選擇2的n次方,取素數。

3) 乘法雜湊法
h(key) = floor(m(key*A mod 1)) (0<A<1)
此種方法對m的選擇沒什麼要求,一般選擇2的n次方。

4)數字分析法
分析數字關鍵字在各個位上的變化情況,取比較隨機的位作為雜湊地址。 ......

3、碰撞問題

在之前的解釋中,因為對映關係的存在,使得關鍵字變得”緊緻“,但是如果兩個不同的關鍵字經過對映後可能重疊,如矩形上的點沿著邊投影很容易發生重疊,這就是所謂的碰撞。一個設計好的對映法則應該使得碰撞問題發生的情況儘可能少,但是關鍵字域所組成的表大於對映後的散列表,因此肯定有關鍵字經過對映後重疊(但這些關鍵字不一定會使用,即不是我們使用的關鍵字集),我們需要避免的是所使用的關鍵字集對映後發生碰撞。 1)一種解決碰撞的方案是使用連結串列,就是在關鍵字所對應的陣列元素記憶體中儲存一個連結串列的頭指標。將擁有同一個對映值卻不同關鍵字對應的元素儲存於這個連結串列中。這時的散列表變為了一個一維陣列,陣列元素儲存的指向一個連結串列的指標。 2)還有一種方式是開放定址法(opening address)。其實就是尋找另一個地址存放物件,通過設計一個規則來計算新的索引,直至獲得新的未被使用的空間存放地址。新的規則可以設計為:若發生h第i次衝突則hi(key)=(h(key)+di) mod m (0<i<m)。根據di的取值方式不同,這些新索引計算方法大致有:線性探索(di = i)、平方探索(di = + i^2 or -i^2)、雙雜湊(di = i*h2(key),h2(key)= p - (key mod p))等。 線性探測會產生聚集效應,在衝突位置之後很容易再次產生衝突。平方探測有時候會找不到表中空閒的空間,但聚集效應不會太嚴重,如果m=4A+3(A為整數)形式的素數時平方探測法可以查詢到整個散列表空間。  再雜湊(rehashing):當雜湊元素太多時,裝填因子太高使得查詢困難,這是可以擴大雜湊表,但需要將原先表中的元素重新雜湊。