雜湊表、Java中HashMap
雜湊演算法,是一類「演算法」。
雜湊表(Hash Table),是一種「資料結構」。
雜湊函式,是支撐雜湊表的一類「函式」。
Map
是對映/地圖的意思,在Java中Map
表示一種把K
對映到V
的「資料型別」。HashMap
,是Java中用雜湊表實現的一種「Map
」。
一、Hash演算法
1. 是什麼?
查下詞典:
hash 英 [hæʃ] 美 [hæʃ]
n. 剁碎的食物;混雜,拼湊;重新表述
vt. 搞糟,把…弄亂;切碎;推敲
n. (Hash)人名;(阿拉伯、保、英)哈什;(西)阿什
「hash」一詞我覺得叫「切碎」比較合適,但正式上會被稱為「雜湊」
接下來我們先給出定義,Hash演算法 是這樣一類演算法:
這類演算法接受「任意長度的二進位制輸入值」,對輸入值做換算(切碎),最終給出「固定長度的二進位制輸出值」。
以更好理解的方式來說,Hash演算法 是摘要演算法 :它從不同的輸入中,通過一些計算摘取 出來一段輸出資料,且這個值可以用以區分輸入資料。
所以,MD5 可能是最著名的一種Hash演算法 了。
回顧一下:Hash演算法 不是某個固定的演算法,它代表的是一類演算法。
2. 有什麼用?
那麼,具體來說Hash/摘要/雜湊/切碎演算法
「資訊保安」領域
Hash演算法 可用作加密演算法。
如檔案校驗:通過對檔案摘要,可以得到檔案的「數字指紋」。你從網路上下載的任何副本的「數字指紋」只要和官方給出的「數字指紋」一致,那麼就可以知道這是未經篡改的檔案。例如著名的MD5 。
「資料結構」領域
Hash演算法 通常還可用作快速查詢。
這是今天我想說的部分,根據Hash函式 我們可以實現一種叫做雜湊表(Hash Table) 的資料結構。這種結構可以使得我們可以實現對資料進行快速的「存」和「取」。
以上我們瞭解了Hash演算法 有什麼用,接下來我們就來具體看看Hash演算法 的重要的應用場景 —「資料結構-雜湊表」。
二、雜湊表
1. 什麼是雜湊表?
首先想一個問題:我們之前是如何在「資料結構」中做「查詢」的呢?
「線性表、樹」: 線上性表、樹 這些結構中,記錄 在結構 中的相對位置是隨機的,和記錄的關鍵字之間不存在確定關係。因此,在結構中查詢時需要進行一系列和關鍵字的「比較」,即這一類查詢方法建立在「比較」的基礎上。在順序查詢時,比較的結果為"="
與"≠"
2種可能;在折半查詢、二叉排序樹查詢和B-樹查詢時,比較的結果為"<", "=", ">"
3種可能。此時,查詢的效率依賴於查詢過程中所進行的「比較次數」。
「引出Hash表」:理想的情況是希望不經過任何比較,一次存取便能得到所查記錄,那就必須在記錄的儲存位置和它的關鍵字之間建立一個確定的關係,使每個關鍵字和結構中一個唯一的儲存位置相對應。因而在查詢時,只要根據這個對應關係找到給定值的像。若結構中存在關鍵字和相等的記錄,則必定在的儲存位置上,反之在這個位置上沒有記錄。由此,「不需要比較」便可直接取得所查記錄。在此,我們稱這個對應關係為:雜湊(Hash)函式,按這個思想建立的對映關係表為:雜湊表。
(插播:記得「理想情況」這幾個字~~ 這會在後文給出解釋)
這是《資料結構(C語言版)》[1]中引出雜湊表的一段描述,通俗易懂。至此,我們知道了什麼是雜湊函式和雜湊表,下面再繼續擴充描述如下:
「雜湊函式」的特點:
靈活
雜湊函式是一個映像,因此雜湊函式的設定很靈活,只要使得任何關鍵字由此所得的雜湊函式值都落在表長允許的範圍之內即可。
衝突
對不同的關鍵字可能得到同一雜湊地址,即,而 ,這種現象稱為「衝突(collision)」。
衝突只能儘量地少,而不能完全避免。因為,雜湊函式是從關鍵字集合到地址集合的映像。而通常關鍵字集合比較大,它的元素包括所有可能的關鍵字,而地址集合的元素僅為雜湊表中的地址值。因此,在實現雜湊表這種資料結構的時候不僅要設定一個“好”的雜湊函式,而且要設定一種“處理衝突的方法”。
「雜湊表」的正式定義:
根據設定的Hash函式 - 和處理衝突的方法,將一組關鍵字映象 到一個有限的連續的地址集(區間)上,並以關鍵字在地址集中的象 作為記錄在表中的儲存位置,這樣的對映表便稱為Hash表。
2. 雜湊函式詳細描述
上面我們已經引出了並解釋了雜湊函式,即「雜湊函式」是支撐「雜湊表」的「一類函式」。實際工作中,需要視不同的情況採用不同的Hash函式,通常要考慮的因素有:
- Hash函式 執行的時間。
- 關鍵字 的長度。
- Hash表 的大小。
- 關鍵字 的分佈情況。
- 記錄 的查詢頻率。
有如下一些常用的Hash函式 構造方法:
直接定址法:
或者
取k 或k 的某個線性函式為Hash地址 。
特點:由於直接地址法相當於有多少個關鍵字就必須有多少個相應地址去對應,所以不會產生衝突,也正因為此,所以實際中很少使用這種構造方法。
數字分析法:
首先分析待存的一組關鍵字,比如是一個班級學生的出生年月日,我們發現他們的出生年 大體相同,那麼我們肯定不能用他們的年 來作為儲存地址 ,這樣出現衝突 的機率很大;但是,我們發現月日 的具體數字差別很大,如果我們用月日 來作為Hash地址,則會明顯降低衝突機率。因此,數字分析法就是找出關鍵字 的規律,儘可能用差異資料來構造Hash地址 ;
特點:需要提前知道所有可能的關鍵字,才能分析運用此種方法,所以不太常用。
平方取中法:
先求出關鍵字的平方值,然後按需要取平方值的中間幾位作為雜湊地址。這是因為:平方後中間幾位和關鍵字中每一位都相關,故不同關鍵字會以較高的概率產生不同的雜湊地址。
例:我們把英文字母在字母表中的位置序號作為該英文字母的內部編碼。例如K的內部編碼為11,E的內部編碼為05,Y的內部編碼為25,A的內部編碼為01, B的內部編碼為02。由此組成關鍵字“KEYA”的內部程式碼為11052501,同理我們可以得到關鍵字“KYAB”、“AKEY”、“BKEY”的內部編碼。之後對關鍵字進行平方運算後,取出第7到第9位作為該關鍵字雜湊地址,如下圖所示:
關鍵字 內部編碼 內部編碼的平方值 H(k)關鍵字的雜湊地址 KEYA 11050201 122157778355001 778 KYAB 11250102 126564795010404 795 AKEY 01110525 001233265775625 265 BKEY 02110525 004454315775625 315 [2]
特點:較常用。
摺疊法:
將關鍵字分割成位數相同的幾部分(最後一部分位數可以不同),然後取這幾部分的疊加和(去除進位)作為雜湊地址。數位疊加可以有移位疊加和間界疊加兩種方法。移位疊加是將分割後的每一部分的最低位對齊,然後相加;間界疊加是從一端向另一端沿分割界來回摺疊,然後對齊相加。
隨機數法:
選擇一個隨機函式,取關鍵字的隨機函式值作為Hash地址,通常用於關鍵字長度不同的場合。即
特點:通常,關鍵字長度不相等時,採用此法構建Hash函式 較為合適。
除留取餘法:
,
取關鍵字被某個不大於Hash表 長m 的數p 除後所得的餘數為Hash地址 。
特點:這是最簡單也是最常用的Hash函式構造方法。可以直接取模,也可以在平法法、摺疊法之後再取模。
值得注意的是,在使用除留取餘法 時,對p 的選擇很重要,如果p 選的不好會容易產生同義詞。由經驗得知:p 最好選擇不大於表長m 的一個質數、或者不包含小於20的質因數的合數。
3. 處理衝突
如何處理衝突是雜湊造表不可缺少的一個方面。現在描述一下處理衝突:
假設雜湊表的地址集為 : ,那麼「衝突」是指 : 由關鍵字得到的雜湊地址為的位置上已存有記錄,而「處理衝突」: 就是為該關鍵字的記錄找到另一個「空的雜湊地址」。
在處理衝突的過程中可能得到一個地址序列 。處理時,若得到的另一個雜湊地址 仍然發生衝突,則再求下一個地址,若 仍然衝突,再求,依次類推,直至 不發生衝突為止,則 為記錄在表中的地址。(注意,此定義不適合鏈地址法)
衝突處理通常有以下4種方法:
開放定址法:
為雜湊函式; 為雜湊表表長;
為增量序列,有3種取法: