您好,登錄后才能下訂單哦!
在Python中,字典是通過散列表或說哈希表實現的。字典也被稱為關聯數組,還稱為哈希數組等。也就是說,字典也是一個數組,但數組的索引是鍵經過哈希函數處理后得到的散列值。哈希函數的目的是使鍵均勻地分布在數組中,并且可以在內存中以O(1)的時間復雜度進行尋址,從而實現快速查找和修改。哈希表中哈希函數的設計困難在于將數據均勻分布在哈希表中,從而盡量減少哈希碰撞和沖突。由于不同的鍵可能具有相同的哈希值,即可能出現沖突,高級的哈希函數能夠使沖突數目最小化。Python中并不包含這樣高級的哈希函數,幾個重要(用于處理字符串和整數)的哈希函數是常見的幾個類型。
通常情況下建立哈希表的具體過程如下:
數據添加:把key通過哈希函數轉換成一個整型數字,然后就將該數字對數組長度進行取余,取余結果就當作數組的下標,將value存儲在以該數字為下標的數組空間里。
數據查詢:再次使用哈希函數將key轉換為對應的數組下標,并定位到數組的位置獲取value。
哈希函數就是一個映射,因此哈希函數的設定很靈活,只要使得任何關鍵字由此所得的哈希函數值都落在表長允許的范圍之內即可。本質上看哈希函數不可能做成一個一對一的映射關系,其本質是一個多對一的映射,這也就引出了下面一個概念–哈希沖突或者說哈希碰撞。哈希碰撞是不可避免的,但是一個好的哈希函數的設計需要盡量避免哈希碰撞。
Python2中使用使用開放地址法解決沖突。
CPython使用偽隨機探測(pseudo-random probing)的散列表(hash table)作為字典的底層數據結構。由于這個實現細節,只有可哈希的對象才能作為字典的鍵。字典的三個基本操作(添加元素,獲取元素和刪除元素)的平均事件復雜度為O(1)。
Python中所有不可變的內置類型都是可哈希的。
可變類型(如列表,字典和集合)就是不可哈希的,因此不能作為字典的鍵。
常見的哈希碰撞解決方法:
1 開放尋址法(open addressing)
開放尋址法中,所有的元素都存放在散列表里,當產生哈希沖突時,通過一個探測函數計算出下一個候選位置,如果下一個獲選位置還是有沖突,那么不斷通過探測函數往下找,直到找個一個空槽來存放待插入元素。
開放地址的意思是除了哈希函數得出的地址可用,當出現沖突的時候其他的地址也一樣可用,常見的開放地址思想的方法有線性探測再散列,二次探測再散列等,這些方法都是在第一選擇被占用的情況下的解決方法。
2 再哈希法
這個方法是按順序規定多個哈希函數,每次查詢的時候按順序調用哈希函數,調用到第一個為空的時候返回不存在,調用到此鍵的時候返回其值。
3 鏈地址法
將所有關鍵字哈希值相同的記錄都存在同一線性鏈表中,這樣不需要占用其他的哈希地址,相同的哈希值在一條鏈表上,按順序遍歷就可以找到。
4 公共溢出區
其基本思想是:所有關鍵字和基本表中關鍵字為相同哈希值的記錄,不管他們由哈希函數得到的哈希地址是什么,一旦發生沖突,都填入溢出表。
5 裝填因子α
一般情況下,處理沖突方法相同的哈希表,其平均查找長度依賴于哈希表的裝填因子。哈希表的裝填因子定義為表中填入的記錄數和哈希表長度的比值,也就是標志著哈希表的裝滿程度。直觀看來,α越小,發生沖突的可能性就越小,反之越大。一般0.75比較合適,涉及數學推導。
在python中一個key-value是一個entry,
entry有三種狀態。
Unused: me_key == me_value == NULL
Unused是entry的初始狀態,key和value都為NULL。插入元素時,Unused狀態轉換成Active狀態。這是me_key為NULL的唯一情況。
Active: me_key != NULL and me_key != dummy 且 me_value != NULL
插入元素后,entry就成了Active狀態,這是me_value唯一不為NULL的情況,刪除元素時Active狀態刻轉換成Dummy狀態。
Dummy: me_key == dummy 且 me_value == NULL
此處的dummy對象實際上一個PyStringObject對象,僅作為指示標志。Dummy狀態的元素可以在插入元素的時候將它變成Active狀態,但它不可能再變成Unused狀態。
為什么entry有Dummy狀態呢?這是因為采用開放尋址法中,遇到哈希沖突時會找到下一個合適的位置,例如某元素經過哈希計算應該插入到A處,但是此時A處有元素的,通過探測函數計算得到下一個位置B,仍然有元素,直到找到位置C為止,此時ABC構成了探測鏈,查找元素時如果hash值相同,那么也是順著這條探測鏈不斷往后找,當刪除探測鏈中的某個元素時,比如B,如果直接把B從哈希表中移除,即變成Unused狀態,那么C就不可能再找到了,因為AC之間出現了斷裂的現象,正是如此才出現了第三種狀態---Dummy,Dummy是一種類似的偽刪除方式,保證探測鏈的連續性。
提示
一般情況下普通的順序表數組存儲結構也可以認為是簡單的哈希表,雖然沒有采用哈希函數(取余),但同樣可以在O(1)時間內進行查找和修改。但是這種方法存在兩個問題:擴展性不強;浪費空間。
set集合和dict一樣也是基于散列表的,只是他的表元只包含鍵的引用,而沒有對值的引用,其他的和dict基本上是一致的,所以在此就不再多說了。并且dict要求鍵必須是能被哈希的不可變對象,因此普通的set無法作為dict的鍵,必須選擇被“凍結”的不可變集合類:frozenset。顧名思義,一旦初始化,集合內數據不可修改。
以上這篇Python字典底層實現原理詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。