1. 程式人生 > >雜湊表(散列表)及雜湊表處理衝突的方法

雜湊表(散列表)及雜湊表處理衝突的方法

前面介紹了靜態查詢表以及動態查詢表中的一些查詢方法,其查詢的過程都無法避免同查詢表中的資料進行比較,查詢演算法的效率很大程度取決於同表中資料的查詢次數。

而本節所介紹的雜湊表可以通過關鍵字直接找到資料的儲存位置,不需要進行任何的比較,其查詢的效率相較於前面所介紹的查詢演算法是更高的。

雜湊表的構建

在初中的數學課本中學習過函式的相關知識,給定一個 x,通過一個數學公式,只需要將 x 的值帶入公式就可以求出一個新的值 y。

雜湊表的建立同函式類似,把函式中的 x 用查詢記錄時使用的關鍵字來代替,然後將關鍵字的值帶入一個精心設計的公式中,就可以求出一個值,用這個值來表示記錄儲存的雜湊地址。即:
資料的雜湊地址=f(關鍵字的值)

雜湊地址只是表示在查詢表中的儲存位置,而不是實際的物理儲存位置。f()是一個函式,通過這個函式可以快速求出該關鍵字對應的的資料的雜湊地址,稱之為

“雜湊函式”

例如,這裡有一個電話簿(查詢表),電話簿中有 4 個人的聯絡方式: 張三 13912345678
李四 15823457890
王五 13409872338
趙六 13805834722 假如想查詢李四的電話號碼,對於一般的查詢方式最先想到的是從頭遍歷,一一比較。而如果將電話簿構建成一張雜湊表,可以直接通過名字“李四”直接找到電話號碼在表中的位置。

在構建雜湊表時,最重要的是雜湊函式的設計。例如設計電話簿案例中的雜湊函式為:每個名字的姓的首字母的 ASCII 值即為對應的電話號碼的儲存位置。這時會發現,張三和趙六兩個關鍵字的姓的首字母都是 Z ,最終求出的電話號碼的儲存位置相同,這種現象稱為衝突
。在設計雜湊函式時,要儘量地避免衝突現象的發生。

對於雜湊表而言,衝突只能儘可能地少,無法完全避免。

雜湊函式的構造

常用的雜湊函式的構造方法有 6 種:直接定址法數字分析法平方取中法摺疊法除留餘數法隨機數法

直接定址法:其雜湊函式為一次函式,即以下兩種形式: H(key)= key 或者 H(key)=a * key + b 其中 H(key)表示關鍵字為 key 對應的雜湊地址,a 和 b 都為常數。

例如有一個從 1 歲到 100 歲的人口數字統計表,如表 1 所示:
表 1 人口統計表 假設其雜湊函式為第一種形式,其關鍵字的值表示最終的儲存位置。若需要查詢年齡為 25 歲的人口數量,將年齡 25 帶入雜湊函式中,直接求得其對應的雜湊地址為 25(求得的雜湊地址表示該記錄的位置在查詢表的第 25 位)。

數字分析法:
如果關鍵字由多位字元或者數字組成,就可以考慮抽取其中的 2 位或者多位作為該關鍵字對應的雜湊地址,在取法上儘量選擇變化較多的位,避免衝突發生。

例如表 2 中列舉的是一部分關鍵字,每個關鍵字都是有 8 位十進位制陣列成:
表 2 通過分析關鍵字的構成,很明顯可以看到關鍵字的第 1 位和第 2 位都是固定不變的,而第 3 位不是數字 3 就是 4,最後一位只可能取 2、7 和 5,只有中間的 4 位其取值近似隨機,所以為了避免衝突,可以從 4 位中任意選取 2 位作為其雜湊地址。

平方取中法是對關鍵字做平方操作,取中間得幾位作為雜湊地址。此方法也是比較常用的構造雜湊函式的方法。

例如關鍵字序列為{421,423,436},對各個關鍵字進行平方後的結果為{177241,178929,190096},則可以取中間的兩位{72,89,00}作為其雜湊地址。

摺疊法是將關鍵字分割成位數相同的幾部分(最後一部分的位數可以不同),然後取這幾部分的疊加和(捨去進位)作為雜湊地址。此方法適合關鍵字位數較多的情況。

例如,在圖書館中圖書都是以一個 10 位的十進位制數字為關鍵字進行編號的,若對其查詢表建立雜湊表時,就可以使用摺疊法。

若某書的編號為:0-442-20586-4,分割方式如圖 1 中所示,在對其進行摺疊時有兩種方式:一種是移位摺疊,另一種是間界摺疊
  • 移位摺疊是將分割後的每一小部分,按照其最低位進行對齊,然後相加,如圖 1(a);
  • 間界摺疊是從一端向另一端沿分割線來回摺疊,如圖 1(b)。

圖 1 移位摺疊和間界摺疊 除留餘數法:若已知整個雜湊表的最大長度 m,可以取一個不大於 m 的數 p,然後對該關鍵字 key 做取餘運算,即:H(key)= key % p

在此方法中,對於 p 的取值非常重要,由經驗得知 p 可以為不大於 m 的質數或者不包含小於 20 的質因數的合數。

隨機數法:是取關鍵字的一個隨機函式值作為它的雜湊地址,即:H(key)=random(key)此方法適用於關鍵字長度不等的情況。
注意:這裡的隨機函式其實是偽隨機函式,隨機函式是即使每次給定的 key 相同,但是 H(key)都是不同;而偽隨機函式正好相反,每個 key 都對應的是固定的 H(key)。 如此多的構建雜湊函式的方法,在選擇的時候,需要根據實際的查詢表的情況採取適當的方法。通常考慮的因素有以下幾方面:
  • 關鍵字的長度。如果長度不等,就選用隨機數法。如果關鍵字位數較多,就選用摺疊法或者數字分析法;反之如果位數較短,可以考慮平方取中法;
  • 雜湊表的大小。如果大小已知,可以選用除留餘數法;
  • 關鍵字的分佈情況;
  • 查詢表的查詢頻率;
  • 計算雜湊函式所需的時間(包括硬體指令的因素)

處理衝突的方法

對於雜湊表的建立,需要選取合適的雜湊函式,但是對於無法避免的衝突,需要採取適當的措施去處理。

通常用的處理衝突的方法有以下幾種:

  • 開放定址法
H(key)=(H(key)+ d)MOD m(其中 m 為雜湊表的表長,d 為一個增量) 當得出的雜湊地址產生衝突時,選取以下 3 種方法中的一種獲取 d 的值,然後繼續計算,直到計算出的雜湊地址不在衝突為止,這 3 種方法為:
  • 線性探測法:d=1,2,3,…,m-1
  • 二次探測法:d=12,-12,22,-22,32,…
  • 偽隨機數探測法:d=偽隨機數
例如,在長度為 11 的雜湊表中已填寫好 17、60 和 29 這 3 個數據(如圖 2(a) 所示),其中採用的雜湊函式為:H(key)=key MOD 11,現有第 4 個數據 38 ,當通過雜湊函式求得的雜湊地址為 5,與 60 衝突,則分別採用以上 3 種方式求得插入位置如圖 2(b)所示: 圖 2 開放定址法 註釋:線上性探測法中,當遇到衝突時,從發生衝突位置起,每次 +1,向右探測,直到有空閒的位置為止;二次探測法中,從發生衝突的位置起,按照 +12,-12,+22,…如此探測,直到有空閒的位置;偽隨機探測,每次加上一個隨機數,直到探測到空閒位置結束。
  • 再雜湊法
當通過雜湊函式求得的雜湊地址同其他關鍵字產生衝突時,使用另一個雜湊函式計算,直到衝突不再發生。
  • 鏈地址法
將所有產生衝突的關鍵字所對應的資料全部儲存在同一個線性連結串列中。例如有一組關鍵字為{19,14,23,01,68,20,84,27,55,11,10,79},其雜湊函式為:H(key)=key MOD 13,使用鏈地址法所構建的雜湊表如圖 3 所示:
圖 3 鏈地址法構建的雜湊表
  • 建立一個公共溢位區
建立兩張表,一張為基本表,另一張為溢位表。基本表儲存沒有發生衝突的資料,當關鍵字由雜湊函式生成的雜湊地址產生衝突時,就將資料填入溢位表。

總結

本節主要介紹了雜湊表的構造及其在構造過程中對產生的衝突進行處理的方法。在選擇具體使用哪種方法時,要根據查詢表的實際情況具體問題具體分析。

雜湊表實現查詢操作的具體實現下節有詳細介紹。