哈希表:即散列存儲結構。
散列法存儲的基本思想:建立記錄關鍵碼字與其存儲位置的對應關系,或者說,由關鍵碼的值決定數據的存儲地址。
這樣,不經過比較,一次存取就能得到所查元素的查找方法
優點:查找速度極快(O(1)),查找效率與元素個數n無關!
哈希方法(雜湊法)
選取某個函數,依該函數按關鍵字計算元素的存儲位置并按此存放;查找時也由同一個函數對給定值k計算地址,將k與地址中內容進行比較,確定查找是否成功。
哈希函數(雜湊函數)
哈希方法中使用的轉換函數稱為哈希函數(雜湊函數).在記錄的關鍵碼與記錄的存儲地址之間建立的一種對應關系
例:
有數據元素序列(14,23,39,9,25,11),若規定每個元素k的存儲地址H(k)=k , H(k)稱為散列函數,畫出存儲結構圖。
根據散列函數H(k)=k ,可知元素14應當存入地址為14的單元,元素23應當存入地址為23的單元,……,
如何進行散列查找?
根據存儲時用到的散列函數H(k)表達式,迅即可查到結果!
例如,查找key=9,則訪問H(9)=9號地址,若內容為9則成功;
若查不到,應當設法返回一個特殊值,例如空指針或空記錄。
很顯然這種搜索方式空間效率過低。
哈希函數可寫成:addr(ai)=H(ki)
選取某個函數,依該函數按關鍵字計算元素的存儲位置并按此存放;查找時也由同一個函數對給定值k計算地址,將k與地址中內容進行比較,確定查找是否成功。哈希方法中使用的轉換函數稱為哈希函數(雜湊函數).在記錄的關鍵碼與記錄的存儲地址之間建立的一種對應關系。
可能導致的沖突
通常關鍵碼的集合比哈希地址集合大得多,因而經過哈希函數變換后,可能將不同的關鍵碼映射到同一個哈希地址上,這種現象稱為沖突。
例.
有6個元素的關鍵碼分別為:(14,23,39,9,25,11)。
選取關鍵碼與元素位置間的函數為H(k)=k mod 7
根據哈希函數算出來發現同一個地址放了多個關鍵碼,也就是沖突了。
在哈希查找方法中,沖突是不可能避免的,只能盡可能減少。
所以,哈希方法必須解決以下兩個問題:
1)構造好的哈希函數
(a)所選函數盡可能簡單,以便提高轉換速度;
(b)所選函數對關鍵碼計算出的地址,應在哈希地址內集中并大致均勻分布,以減少空間浪費。
2)制定一個好的解決沖突的方案
查找時,如果從哈希函數計算出的地址中查不到關鍵碼,則應當依據解決沖突的規則,有規律地查詢其它相關單元。
從上面兩個例子可以得出如下結論:
哈希函數只是一種映象,所以哈希函數的設定很靈活,只要使任何關鍵碼的哈希函數值都落在表長允許的范圍之內即可
沖突:key1≠key2,但H(key1)=H(key2)
同義詞:具有相同函數值的兩個關鍵碼
哈希函數沖突不可避免,只能盡量減少。所以,哈希方法解決兩個問題:
構造好的哈希函數;
制定解決沖突基本要求:
要求一:n個數據原僅占用n個地址,雖然散列查找是以空間換時間,但仍希望散列的地址空間盡量小。
要求二:無論用什么方法存儲,目的都是盡量均勻地存放元素,以避免沖突。
常用的哈希函數構造方法有:
- 直接定址法
- 除留余數法
- 乘余取整法
- 數字分析法
- 平方取中法
- 折疊法
- 隨機數法
1、直接定址法
Hash(key) = a·key + b (a、b為常數)
優點:以關鍵碼key的某個線性函數值為哈希地址,不會產生沖突.
缺點:要占用連續地址空間,空間效率低。
例.關鍵碼集合為{100,300,500,700,800,900},
選取哈希函數為Hash(key)=key/100,
則存儲結構(哈希表)如下:
2、除留余數法
Hash(key)=key mod p (p是一個整數)
特點:以關鍵碼除以p的余數作為哈希地址。
關鍵:如何選取合適的p?p選的不好,容易產生同義詞
技巧:若設計的哈希表長為m,則一般取p≤m且為質數
(也可以是合數,但不能包含小于20的質因子)。
3、乘余取整法
Hash(key)= ? B( Akey mod 1 ) ?
(A、B均為常數,且0<A<1,B為整數)
特點:以關鍵碼key乘以A,取其小數部分,然后再放大B倍并取整,作為哈希地址。
例:欲以學號最后兩位作為地址,則哈希函數應為:
H(k)=100(0.01k % 1 )
其實也可以用法2實現: H(k)=k % 100
4、數字分析法
特點:選用關鍵字的某幾位組合成哈希地址。選用原則應當是:各種符號在該位上出現的頻率大致相同。
例:有一組(例如80個)關鍵碼,其樣式如下:
討論:
① 第1、2位均是“3和4”,第3位也只有“ 7、8、9”,因此,這幾位不能用,余下四位分布較均勻,可作為哈希地址選用。
② 若哈希地址取兩位(因元素僅80個),則可取這四位中的任意兩位組合成哈希地址,也可以取其中兩位與其它兩位疊加求和后,取低兩位作哈希地址。
5、平方取中法
特點:對關鍵碼平方后,按哈希表大小,取中間的若干位作為哈希地址。(適于不知道全部關鍵碼情況)
理由:因為中間幾位與數據的每一位都相關。
例:2589的平方值為6702921,可以取中間的029為地址。
6、折疊法
特點:將關鍵碼自左到右分成位數相等的幾部分(最后一部分位數可以短些),然后將這幾部分疊加求和,并按哈希表表長,取后幾位作為哈希地址。
適用于:關鍵碼位數很多,且每一位上各符號出現概率大致相同的情況。
法1:移位法 ── 將各部分的最后一位對齊相加。
法2:間界疊加法──從一端向另一端沿分割界來回折疊后,最后一位對齊相加。
例:元素42751896,
用法1: 427+518+96=1041
用法2: 427 518 96—> 724+518+69 =1311
7、隨機數法
Hash(key) = random ( key ) (random為偽隨機函數)
適用于:關鍵字長度不等的情況。造表和查找都很方便。
小結:構造哈希函數的原則:
① 執行速度(即計算哈希函數所需時間);
② 關鍵字的長度;
③ 哈希表的大??;
④ 關鍵字的分布情況;
⑤ 查找頻率。
三、沖突處理方法
- 開放定址法(開地址法)
- 鏈地址法(拉鏈法)
- 再哈希法(雙哈希函數法)
- 建立一個公共溢出區
1、開放定址法(開地址法)
設計思路:有沖突時就去尋找下一個空的哈希地址,只要哈希表足夠大,空的哈希地址總能找到,并將數據元素存入。
1)線性探測法
Hi=(Hash(key)+di) mod m ( 1≤i < m )
其中:
Hash(key)為哈希函數
m為哈希表長度
di 為增量序列 1,2,…m-1,且di=i
關鍵碼集為 {47,7,29,11,16,92,22,8,3},
設:哈希表表長為m=11;
哈希函數為Hash(key)=key mod 11;
擬用線性探測法處理沖突。建哈希表如下:
解釋:
① 47、7是由哈希函數得到的沒有沖突的哈希地址;
② Hash(29)=7,哈希地址有沖突,需尋找下一個空的哈希地址:由H1=(Hash(29)+1) mod 11=8,哈希地址8為空,因此將29存入。
③ 另外,22、8、3同樣在哈希地址上有沖突,也是由H1找到空的哈希地址的。
其中3 還連續移動了(二次聚集)
線性探測法的優點:只要哈希表未被填滿,保證能找到一個空地址單元存放有沖突的元素;
線性探測法的缺點:可能使第i個哈希地址的同義詞存入第i+1個哈希地址,這樣本應存入第i+1個哈希地址的元素變成了第i+2個哈希地址的同義詞,……,
因此,可能出現很多元素在相鄰的哈希地址上“堆積”起來,大大降低了查找效率。
解決方案:可采用二次探測法或偽隨機探測法,以改善“堆積”問題。
2) 二次探測法
仍舉上例,改用二次探測法處理沖突,建表如下:
Hi=(Hash(key)±di) mod m
其中:Hash(key)為哈希函數
m為哈希表長度,m要求是某個4k+3的質數;
di為增量序列 1^2,-1 ^2,2 ^2,-2 ^2,…,q ^2
注:只有3這個關鍵碼的沖突處理與上例不同,
Hash(3)=3,哈希地址上沖突,由
H1=(Hash(3)+1 ^2) mod 11=4,仍然沖突;
H2=(Hash(3)-1 ^2) mod 11=2,找到空的哈希地址,存入。
3) 若di=偽隨機序列,就稱為偽隨機探測法
2、鏈地址法(拉鏈法)
基本思想:將具有相同哈希地址的記錄(所有關鍵碼為同義詞)鏈成一個單鏈表,m個哈希地址就設m個單鏈表,然后用一個數組將m個單鏈表的表頭指針存儲起來,形成一個動態的結構。
設{ 47, 7, 29, 11, 16, 92, 22, 8, 3, 50, 37, 89 }的哈希函數為:
Hash(key)=key mod 11,
用拉鏈法處理沖突,則建表如圖所示。
3、再哈希法(雙哈希函數法)
Hi=RHi(key) i=1, 2, …,k
RHi均是不同的哈希函數,當產生沖突時就計算另一個哈希函數,直到沖突不再發生。
優點:不易產生聚集;
缺點:增加了計算時間。
4. 建立一個公共溢出區
思路:除設立哈?;颈硗?,另設立一個溢出向量表。
所有關鍵字和基本表中關鍵字為同義詞的記錄,不管它們由哈希函數得到的地址是什么,一旦發生沖突,都填入溢出表。
哈希表的查找及分析
明確:散列函數沒有“萬能”通式(雜湊法),要根據元素集合的特性而分別構造。
討論:哈希查找的速度是否為真正的O(1)?
不是。由于沖突的產生,使得哈希表的查找過程仍然要進行比較,仍然要以平均查找長度ASL來衡量。
一般地,ASL依賴于哈希表的裝填因子α,它標志著哈希表的裝滿程度。
0≤α≤1
α 越大,表中記錄數越多,說明表裝得越滿,發生沖突的可能性就越大,查找時比較次數就越多。
例 已知一組關鍵字(19,14,23,1,68,20,84,27,55,11,10,79)
哈希函數為:H(key)=key MOD 13, 哈希表長為m=16,
設每個記錄的查找概率相等
(1) 用線性探測再散列處理沖突,即Hi=(H(key)+di) MOD m
H(19)=6
H(14)=1
H(23)=10
H(1)=1 沖突,H1=(1+1) MOD16=2
H(68)=3
H(20)=7
H(84)=6 沖突,H1=(6+1)MOD16=7
沖突,H2=(6+2)MOD16=8
H(27)=1 沖突,H1=(1+1)MOD16=2
沖突,H2=(1+2)MOD16=3
沖突,H3=(1+3)MOD16=4
H(55)=3 沖突,H1=(3+1)MOD16=4
沖突,H2=(3+2)MOD16=5
H(11)=11
H(10)=10 沖突,H1=(10+1)MOD16=11
沖突,H2=(10+2)MOD16=12
H(79)=1 沖突,H1=(1+1)MOD16=2
沖突,H2=(1+2)MOD16=3
沖突,H3=(1+3)MOD16=4
沖突,H4=(1+4)MOD16=5
沖突,H5=(1+5)MOD16=6
沖突,H6=(1+6)MOD16=7
沖突,H7=(1+7)MOD16=8
沖突,H8=(1+8)MOD16=9
ASL=(1*6+2+3*3+4*1+9*1)/12=2.5
(2) 用二次探測再散列處理沖突,即Hi=(H(key)+di) MOD m
H(19)=6
H(14)=1
H(23)=10
H(1)=1沖突, H1=(1+12) MOD16=2
H(68)=3
H(20)=7
H(84)=6 沖突,H1=(6+12)MOD16=7
沖突,H2=(6﹣12)MOD16=5
H(27)=1 沖突,H1=(1+12 )MOD16=2
沖突,H2=(1 -12 )MOD16=0
H(55)=3 沖突,H1=(3+12)MOD16=4
H(11)=11
H(10)=10 沖突,H1=(10+12)MOD16=11
沖突,H2=(10 ﹣12 )MOD16=9
H(79)=1 沖突,H1=(1 +12 )MOD16=2
沖突,H2=(1﹣12)MOD16=0
沖突,H3=(1+ 22)MOD16=5
沖突,H4=(1﹣22)MOD16=13
ASL=(1*6+2*2+3*3+5*1)/12=2
(3) 用鏈地址法處理沖突
ASL=(16+24+31+41)/12=1.75
討論:
1) 散列存儲的查找效率到底是多少?
答:ASL與裝填因子α有關!既不是嚴格的O(1),也不是O(n)
2)“沖突”是不是特別討厭?
答:不一定!正因為有沖突,使得文件加密后無法破譯?。▎蜗蛏⒘泻瘮挡豢赡?,常用于數字簽名和間接加密)。
利用了哈希表性質:源文件稍稍改動,會導致哈希表變動很大。