雜湊表的原理

funtrin發表於2022-03-26

雜湊表的原理

簡介

雜湊表是一種根據關鍵字key來訪問值value的一種資料結構。

雜湊表的基本原理

雜湊表的本質是陣列雜湊函式陣列不難理解,那什麼是雜湊函式

在雜湊表中,它的作用就是將雜湊表的某個key作為輸入,然後經過一系列的運算後,得到陣列的某

image

個索引。一種很樸素的思路是,先用key計算出一個很大的數,然後對陣列長度取模,從而得到索引,這只是眾多方法中的一種,其他的比如:直接定址法,平方取中法等。

得到索引後就可以通過索引對陣列執行插入或查詢的操作,因為本質上是通過索引來訪問陣列,所以雜湊表的插入和查詢的效率非常高,時間複雜度都是O(1)

雜湊衝突

我們不難發現雜湊函式是整個雜湊表的關鍵。所以為了更好的效能,我們希望在儘可能短的時間內,相同的key經過雜湊函式的計算,可以得到相同的索引,不同的key經過雜湊函式的計算,可以得到不同的索引,但在實際中往往事與願違,不同的key小概率會計算出相同的索引,這就是雜湊衝突(collision),幾乎所有的雜湊函式都存在這個問題。

這裡介紹幾個常見的解決雜湊衝突的方法:

開放定址法

開放定址是一種思想,如果通過雜湊函式計算出的索引所對應的空間已經被佔用了,就再找一個還沒被佔用的空間將資料存進去。

常見的體現開放定址思想的方法:

  • 線性探測法:簡單來說就是從當前被佔用的空間的索引開始,向下遍歷整個陣列,直到找到空閒空間為止。如下圖所示:

image

  • 雙重雜湊法:使用多個雜湊函式來計算索引,如果第一個雜湊函式計算得到的索引所對應的空間已被佔用,就用第二個,第二個被佔用就用第三個,以此類推,直到計數出沒被佔用的空間對應的索引。

連結串列法

連結串列法是一種更加常見的解決雜湊衝突的方法,Java中的HashMap就是採用這種方法。在這種方法中,陣列索引對應的空間並不直接儲存資料,而是儲存一個連結串列的地址,而資料存在連結串列中。如下圖所示:

image

這樣發生衝突時,就可將衝突的key對應的資料存在同一個連結串列上,當需要取資料時,就先找到key對應的連結串列,然後遍歷連結串列。

陣列擴容

上面說的方法只能在一定程度上解決雜湊衝突,因為畢竟陣列的容量有限,當頻繁插入資料時,因為陣列的容量有限,所以就會使雜湊衝突加劇,進而使連結串列的長度增加,連結串列的長度增加,就會使得查詢的效能降低,這不是我們想看到的結果,所以要對陣列擴容。

image

那什麼時候給陣列擴容呢?裝載因子(已插入元素的數量除以陣列容量)超過某一閾值時就進行擴容,Java中HashMap的裝載因子是0.75,當然,也可以是別的值。因為之前插入的元素都是按照原陣列的長度來計算索引的,所以一旦陣列擴容後,長度改變,就要重新進行計算,然後將已插入的元素移動到新的位置上,所以陣列擴容不僅僅只是將容量增大而已。

相關文章