資料結構 - 雜湊表,初探

IT规划师發表於2024-10-27

今天我們繼續學習新的資料結構-雜湊表。

01、定義

我們先來了解一些常見概念名詞解釋。

雜湊:雜湊表的實現叫做雜湊,是一種實現以常數級時間複雜度執行查詢、插入和刪除的技術;

雜湊值:透過雜湊函式對輸入值(key)計算出來的結果,用來表示key的唯一性,它通常是一個長度固定的值,即無論key多大,雜湊值的長度都是固定的;

雜湊地址:透過雜湊值計算出來的儲存位置,即儲存value的位置,通常使用雜湊值和雜湊表的大小取模得到雜湊地址。

碰撞:也叫衝突,指兩個不同的key,經過雜湊函式計算後得到相同的雜湊值;

負載係數:也叫負載因子,用來衡量雜湊表填充程度,透過雜湊表已儲存的元素個數除以雜湊表的大小計算可得;

雜湊函式:也叫雜湊函式,指將key對映為雜湊值的函式;

雜湊表又稱雜湊表,是以「key-value」形式儲存資料的資料結構。可以理解為任意的key都可以唯一對應到記憶體中的某個地址,而這個地址就存放著value,透過key快速查詢到value。也可以把雜湊表理解為一種高階的陣列,其中key就相當於陣列下標,此陣列下標不但可以是整數,還可以是浮點數、字串、結構體等,其中value就相當於陣列元素值。

透過前面的資料結構學習,我們知道陣列是查詢容易、插入和刪除困難;連結串列是查詢困難、插入和刪除容易;而雜湊表是集兩者之大成,做大查詢、插入、刪除都容易的一種資料結構。

本質上來說雜湊表就是一個陣列。雖然上面把key比作陣列下標,但是key並不真的是陣列下標。因此這中間就需要一個轉換工具把key轉換為陣列下標,而這個工具就是雜湊函式。

如下圖,展示了在雜湊表中如何通用key獲取到value的過程。輸入key4透過雜湊函式計算得到陣列索引3,最後透過陣列下標取出value4。

02、雜湊函式

雜湊技術核心難點之一就是如何設計一個準確、快速、均勻、抗碰撞的雜湊函式。而一個好的雜湊函式對雜湊表是至關重要的,這影響雜湊表的儲存和檢索效率。

1、四大特性

(1)確定性:指相同的輸入key總能輸出相同的結果;

(2)快速計算:指計算雜湊值的速度儘可能的快;

(3)均勻分佈:指儘量將輸入key均勻的對映到雜湊表中,以減少碰撞;

(4)抗碰撞性:指儘量減少不同的輸入key生成出相同的雜湊值機率;

2、常見雜湊函式演算法

(1)取模演算法

該演算法是一種最簡單且常用的雜湊函式構造方法。

原理:透過將key取模雜湊表的大小(通常是質數)來計算雜湊值;

公式:hash(key) = key % m;

優點:簡單易實現,計算速度快;

缺點:如果m不是質數,會增加碰撞機率,因此m最好選用質數來減少碰撞機率。

示例:假設我們有個雜湊表,大小為10,分別雜湊以下key:11,23,4,19

hash(11) = 11 % 10 = 1

hash(23) = 23 % 10 = 3

hash(4) = 4 % 10 = 4

hash(17) = 17 % 10 = 7

則最終生成的雜湊值分別為1、3、4、7,而這些值就可以作為雜湊表的索引。

(2)乘法演算法

該演算法是透過乘法來降低碰撞機率,以使雜湊值均勻分佈;

原理:透過將key與一個固定常數 A(通常是黃金比例的倒數)相乘,取其乘積的小數部分,再乘以雜湊表的大小(通常是質數),所得結果的整數部分即為雜湊值;

公式:hash(key) = ⌊m * (key * A % 1)⌋;

優點:簡單易實現,雜湊值分佈更均勻;

缺點:常數A的選擇對計算結果雜湊值影響很大,選擇不好很容易增加碰撞機率;

示例:假設我們有個雜湊表,大小為10,常數A為0.618,分別雜湊以下key:11,23,4,19

hash(11) = ⌊10 * (11 * 0.618 % 1)⌋ = 7

hash(23) = ⌊10 * (23 * 0.618 % 1)⌋ = 2

hash(4) = ⌊10 * (4 * 0.618 % 1)⌋ = 4

hash(17) = ⌊10 * (17 * 0.618 % 1)⌋ = 5

則最終生成的雜湊值分別為7、2、4、5,而這些值就可以作為雜湊表的索引。

(3)DJB2演算法

DJB2 是一種使用廣泛的字串雜湊演算法,它簡單而高效,由 Daniel和J. Bernstein 提出。其核心公式:hash = hash * 33 + c,其中 33 是常用的基數。

原理:使用一個初始雜湊值(通常是5381),然後透過對key的每一個字元的ASCII值進行逐步加權處理,最終得到雜湊值;

公式:hash(key) = {

hash = 5381

for character c in string:

hash = ((hash << 5) + hash) + c

return hash & 0xFFFFFFFF

}

優點:簡單易懂,速度快,底碰撞;

缺點:基數(33)的選擇對雜湊效能有一定影響,雖然普遍表現良好,但並不是最佳選擇;雜湊值的輸出通常需要進行掩碼操作(如 & 0xFFFFFFFF),這可能導致部分資訊丟失,尤其在處理較大資料時。

上面的公式中沒有使用hash * 33 + c,而是使用((hash << 5) + hash) + c是因為位運算效率更高。

(4)其他演算法

除了以上三種演算法還有很多其他演算法,比如和DJB2演算法類似的BKDR演算法、PJW演算法,比如還有諸如:直接定演算法、數字分析演算法、平方取中演算法、摺疊演算法、隨機數演算法等等,這裡就不一一細說了,有興趣的可以自己研究研究。

:測試方法程式碼以及示例原始碼都已經上傳至程式碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

相關文章