Python字典的實現原理


一、字典的實現原理

python中的字典底層依靠哈希表(hash table)實現, 使用開放尋址法解決沖突,
哈希表是key-value類型的數據結構, 可以理解為一個鍵值需要按照一定規則存放的數組, 而哈希函數就是這個規則

字典本質上是一個散列表(總有空白元素的數組, python至少保證1/3的數組是空的), 字典中的每個鍵都占用一個單元, 一個單元分為兩部分, 分別是對鍵的引用和對值的引用, 使用hash函數獲得鍵的散列值, 散列值對數組長度取余, 取得的值就是存放位置的索引
哈希沖突(數組的索引相同), 使用開放尋址法解決
這也是python中要求字典的key必須可hash的原因

數組中1/3的位置為空, 增加元素可能會導致擴容, 引發新的散列沖突, 導致新的散列表中鍵的次序發生變化, 這也是字典遍歷時不能添加和刪除的原因

字典在內存中開銷很大, 實際上是以空間換時間

二、hash算法與哈希沖突

  • 哈希算法

根據設定的哈希函數H(key)和處理沖突方法將一組關鍵字映象到一個有限的地址區間上的算法。也稱為散列算法、雜湊算法。

  • 哈希表

數據經過哈希算法之后得到的集合。這樣關鍵字和數據在集合中的位置存在一定的關系,可以根據這種關系快速查詢。

  • 非哈希表

與哈希表相對應,集合中的 數據和其存放位置沒任何關聯關系的集合。
由此可見,哈希算法是一種特殊的算法,能將任意數據散列后映射到有限的空間上,通常計算機軟件中用作快速查找或加密使用。

  • 哈希沖突

由於哈希算法被計算的數據是無限的,而計算后的結果范圍有限,因此總會存在不同的數據經過計算后得到的值相同,這就是哈希沖突。

三、解決哈希沖突的方法

解決哈希沖突的方法一般有:開放定址法、鏈地址法(拉鏈法)、再哈希法、建立公共溢出區等方法。

1 開放定址法

從發生沖突的那個單元起,按照一定的次序,從哈希表中找到一個空閑的單元。然后把發生沖突的元素存入到該單元的一種方法。開放定址法需要的表長度要大於等於所需要存放的元素。
在開放定址法中解決沖突的方法有:線行探查法、平方探查法、雙散列函數探查法。
開放定址法的缺點在於刪除元素的時候不能真的刪除,否則會引起查找錯誤,只能做一個特殊標記。只到有下個元素插入才能真正刪除該元素。

  • 線行探查法
    線行探查法是開放定址法中最簡單的沖突處理方法,它從發生沖突的單元起,依次判斷下一個單元是否為空,當達到最后一個單元時,再從表首依次判斷。直到碰到空閑的單元或者探查完全部單元為止。
  • 平方探查法
    平方探查法即是發生沖突時,用發生沖突的單元d[i], 加上 1²、 2²等。即d[i] + 1²,d[i] + 2², d[i] + 3²...直到找到空閑單元。
    在實際操作中,平方探查法不能探查到全部剩余的單元。不過在實際應用中,能探查到一半單元也就可以了。若探查到一半單元仍找不到一個空閑單元,表明此散列表太滿,應該重新建立。
  • 雙散列函數探查法
    這種方法使用兩個散列函數hl和h2。其中hl和前面的h一樣,以關鍵字為自變量,產生一個0至m—l之間的數作為散列地址;h2也以關鍵字為自變量,產生一個l至m—1之間的、並和m互素的數(即m不能被該數整除)作為探查序列的地址增量(即步長),探查序列的步長值是固定值l;對於平方探查法,探查序列的步長值是探查次數i的兩倍減l;對於雙散列函數探查法,其探查序列的步長值是同一關鍵字的另一散列函數的值。

2 鏈地址法(拉鏈法)

鏈接地址法的思路是將哈希值相同的元素構成一個同義詞的單鏈表,並將單鏈表的頭指針存放在哈希表的第i個單元中,查找、插入和刪除主要在同義詞鏈表中進行。鏈表法適用於經常進行插入和刪除的情況。
如下一組數字,(32、40、36、53、16、46、71、27、42、24、49、64)哈希表長度為13,哈希函數為H(key)=key%13,則鏈表法結果如下:

0       
1  -> 40 -> 27 -> 53 
2
3  -> 16 -> 42
4
5
6  -> 32 -> 71
7  -> 46
8
9
10 -> 36 -> 49
11 -> 24
12 -> 64

注:在java中,鏈接地址法也是HashMap解決哈希沖突的方法之一,jdk1.7完全采用單鏈表來存儲同義詞,jdk1.8則采用了一種混合模式,對於鏈表長度大於8的,會轉換為紅黑樹存儲。

3 再哈希法

就是同時構造多個不同的哈希函數:
Hi = RHi(key) i= 1,2,3 ... k;
當H1 = RH1(key) 發生沖突時,再用H2 = RH2(key) 進行計算,直到沖突不再產生,這種方法不易產生聚集,但是增加了計算時間。

4 建立公共溢出區

將哈希表分為公共表和溢出表,當溢出發生時,將所有溢出數據統一放到溢出區。


免責聲明!

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



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