教你幾招HASH表查找的方法


摘要:根據設定的哈希函數 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. 執行速度(即計算哈希函數所需時間)
  2. 關鍵字的長度
  3. 哈希表的大小
  4. 關鍵字的分布情況
  5. 查找頻率

采用何種構造哈希函數的方法取決於建表的關鍵字集合的情況
原則是使產生沖突的可能性降到盡可能地小

處理沖突的方法

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—表的長度)

α 越大,表中記錄數越多,說明表裝得越滿,發生沖突的可能性就越大,查找時比較次數就越多

  1. 對哈希表技術具有很好的平均性能,優於一些傳統的技術
  2. 鏈地址法優於開地址法
  3. 除留余數法作哈希函數優於其它類型函數

哈希表應用舉例

編譯器對標識符的管理多是采用哈希表

  • 構造哈希函數的方法
    • 將標識符中的每個字符轉換為一個非負整數
    • 將得到的各個整數組合成一個整數(可以將第一個、中間的和最后一個字符值加在一起,也可以將所有字符的值加起來)
    • 將結果數調整到0~M-1范圍內,可以利用取模的方法,Ki%M(M為素數)

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM