摘要:根據設定的雜湊函式 H(key) 和所選中的處理衝突的方法,將一組關鍵字映象到一個有限的、地址連續的地址集 (區間) 上,並以關鍵字在地址集中的“象”作為相應記錄在表中的儲存位置,如此構造所得的查詢表稱之為“雜湊表”。
本文分享自華為雲社群《查詢——HASH》,原文作者:ruochen。
對於頻繁使用的查詢表,希望 ASL = 0
記錄在表中位置和其關鍵字之間存在一種確定的關係
HASH
定義
根據設定的雜湊函式 H(key) 和所選中的處理衝突的方法,將一組關鍵字映象到一個有限的、地址連續的地址集 (區間) 上,並以關鍵字在地址集中的“象”作為相應記錄在表中的儲存位置,如此構造所得的查詢表稱之為“雜湊表”
HASH函式的構造
- 構造原則
- 函式本身便於計算
- 計算出來的地址分佈均勻,即對任一關鍵字k,f(k) 對應不同地址的概率相等,目的是儘可能減少衝突
直接定址法
- 雜湊函式為關鍵字的線性函式
- H(key) = key
- H(key) = a * key + b
- 此法僅適合於:
地址集合的大小 = = 關鍵字集合的大小 - 優點:以關鍵碼key的某個線性函式值為雜湊地址,不會產生衝突
- 缺點:要佔用連續地址空間,空間效率低
數字分析法
- 假設關鍵字集合中的每個關鍵字都是由 s 位數字組成 (u1, u2, …, us),分析關鍵字集中的全體, 並從中提取分佈均勻的若干位或它們的組合作為地址
- 此方法僅適合於:
能預先估計出全體關鍵字的每一位上各種數字出現的頻度
平方取中法
- 以關鍵字的平方值的中間幾位作為儲存地址。求“關鍵字的平方值” 的目的是“擴大差別” ,同時平方值的中間各位又能受到整個關鍵字中各位的影響
- 此方法適合於:
關鍵字中的每一位都有某些數字重複出現頻度很高的現象
摺疊法
- 將關鍵字分割成若干部分,然後取它們的疊加和為雜湊地址。有兩種疊加處理的方法:移位疊加和間界疊加
- 此方法適合於:
關鍵字的數字位數特別多
除留餘數法
- Hash(key)=key mod p (p是一個整數)
- p≤m (表長)
- p 應為小於等於 m 的最大素數
為什麼要對 p 加限制?
給定一組關鍵字為: 12, 39, 18, 24, 33, 21若取 p=9, 則他們對應的雜湊函式值將為:
3, 3, 0, 6, 6, 3
可見,若 p 中含質因子 3, 則所有含質因子 3 的關鍵字均對映到“3 的倍數”的地址上,從而增加了“衝突”的可能
隨機數法
- H(key) = Random(key) (Random 為偽隨機函式)
- 此方法用於對長度不等的關鍵字構造雜湊函式
考慮因素
- 執行速度(即計算雜湊函式所需時間)
- 關鍵字的長度
- 雜湊表的大小
- 關鍵字的分佈情況
- 查詢頻率
採用何種構造雜湊函式的方法取決於建表的關鍵字集合的情況
原則是使產生衝突的可能性降到儘可能地小
處理衝突的方法
1. 開放定址法
基本思想
- 有衝突時就去尋找下一個空的雜湊地址,只要雜湊表足夠大,空的雜湊地址總能找到,並將資料元素存入
線性探測法
- Hi=(Hash(key)+di) mod m ( 1≤i < m )
其中:m為雜湊表長度
di 為增量序列 1,2,…m-1,且di=i
一旦衝突,就找下一個空地址存入
- 優點:只要雜湊表未被填滿,保證能找到一個空地址單元存放有衝突的元素
- 缺點:能使第i個雜湊地址的同義詞存入第i+1個地址,這樣本應存入第i+1個雜湊地址的元素變成了第i+2個雜湊地址的同義詞,……,產生“聚集”現象,降低查詢效率
二次探測法
di = 12, -12, 22, -22, …±k2
偽隨機探測法
Hi=(Hash(key)+di) mod m ( 1≤i < m )
其中:m為雜湊表長度
di 為隨機數
開放定址法建立雜湊表步驟
- 取資料元素的關鍵字key,計算其雜湊函式值(地址)。若該地址對應的儲存 空間還沒有被佔用,則將該元素存入;否則執行step2解決衝突
- 根據選擇的衝突處理方法,計算關鍵字key的下一個儲存地址。若下一個儲存地址仍被佔用,則繼續執行step2,直到找 到能用的儲存地址為止
開放定址雜湊表的儲存結構
/* ------------- 開放定址雜湊表的儲存結構 ------------- */ int hashsize[] = {997, ...}; typedef struct{ ElemType* elem; int count; // 當前資料元素個數 int sizeindex; // hashsize[sizeindex]為當前容量 } HashTable; #define SUCCESS 1 #define UNSUCCESS 0 #define DUPLICATE -1 Status SearchHash(HashTable H, KeyType K, int &p, int &c){ // 在開放定址雜湊表H中查詢關鍵碼為K的記錄 p = Hash(K); // 求得雜湊地址 while(H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key)) collisiion(p, ++c); // 求得下一探測地址p if(EQ(K, H.elem[p].key)) return SUCCESS; // 查詢成功,返回待查資料元素位置 p else return UNSUCCESS; // 查詢不成功 }
2. 再HASH法
- H2(key) 是另設定的一個雜湊函式,它的函式值應和 m 互質
3. 鏈地址法
基本思想
- 相同雜湊地址的記錄鏈成一單連結串列,m個雜湊地址就設m個單連結串列,然後用用一個陣列將m個單連結串列的表頭指標儲存起來,形成一個動態的結構
優點:
- 非同義詞不會衝突,無“聚集”現象
- 連結串列上結點空間動態申請,更適合於表長不確定的情況
雜湊表的查詢
對於給定值 K,計算雜湊地址 i = H(K)
- 若 r[i] = NULL 則查詢不成功
- 若 r[i].key = K 則查詢成功, 否則 “求下一地址 Hi” ,直至r[Hi] = NULL (查詢不成功) 或r[Hi].key = K (查詢成功) 為止
案例v01
- 線性探測法解決衝突
案例v02
- 鏈地址法處理衝突
雜湊表查詢的分析
從查詢過程得知,雜湊表查詢的平均查詢長度實際上並不等於零
決定雜湊表查詢的ASL的因素
- 選用的雜湊函式
- 選用的處理衝突的方法
- 雜湊表飽和的程度,裝載因子 α=n/m 值的大小(n—記錄數,m—表的長度)
α 越大,表中記錄數越多,說明表裝得越滿,發生衝突的可能性就越大,查詢時比較次數就越多
- 對雜湊表技術具有很好的平均效能,優於一些傳統的技術
- 鏈地址法優於開地址法
- 除留餘數法作雜湊函式優於其它型別函式
雜湊表應用舉例
編譯器對識別符號的管理多是採用雜湊表
- 構造雜湊函式的方法
- 將識別符號中的每個字元轉換為一個非負整數
- 將得到的各個整陣列合成一個整數(可以將第一個、中間的和最後一個字元值加在一起,也可以將所有字元的值加起來)
- 將結果數調整到0~M-1範圍內,可以利用取模的方法,Ki%M(M為素數)