python dict底层实现_dict实现原理和哈希表
dict底層實(shí)現(xiàn)
在Python中,字典是依靠散列表或說哈希表(Hash Table)進(jìn)行實(shí)現(xiàn)的,使用開放地址法解決沖突。所以其查找的時(shí)間復(fù)雜度會(huì)是O(1),下文會(huì)具體講解哈希表的工作原理和解決沖突時(shí)的具體方法。
也就是說,字典也是一個(gè)數(shù)組,但數(shù)組的索引是鍵經(jīng)過哈希函數(shù)處理后得到的散列值。哈希函數(shù)的目的是使鍵均勻地分布在數(shù)組中,并且可以在內(nèi)存中以O(shè)(1)的時(shí)間復(fù)雜度進(jìn)行尋址,從而實(shí)現(xiàn)快速查找和修改。哈希表中哈希函數(shù)的設(shè)計(jì)困難在于將數(shù)據(jù)均勻分布在哈希表中,從而盡量減少哈希碰撞和沖突。由于不同的鍵可能具有相同的哈希值,即可能出現(xiàn)沖突,高級(jí)的哈希函數(shù)能夠使沖突數(shù)目最小化。
通常情況下建立哈希表的具體過程如下:
數(shù)據(jù)添加:把key通過哈希函數(shù)轉(zhuǎn)換成一個(gè)整型數(shù)字,然后就將該數(shù)字對(duì)數(shù)組長(zhǎng)度進(jìn)行取余,取余結(jié)果就當(dāng)作數(shù)組的下標(biāo),將value存儲(chǔ)在以該數(shù)字為下標(biāo)的數(shù)組空間里。
數(shù)據(jù)查詢:再次使用哈希函數(shù)將key轉(zhuǎn)換為對(duì)應(yīng)的數(shù)組下標(biāo),并定位到數(shù)組的位置獲取value
哈希函數(shù)就是一個(gè)映射,因此哈希函數(shù)的設(shè)定很靈活,只要使得任何關(guān)鍵字由此所得的哈希函數(shù)值都落在表長(zhǎng)允許的范圍之內(nèi)即可。本質(zhì)上看哈希函數(shù)不可能做成一個(gè)一對(duì)一的映射關(guān)系,其本質(zhì)是一個(gè)多對(duì)一的映射,這也就引出了下面一個(gè)概念–哈希沖突或者說哈希碰撞。哈希碰撞是不可避免的,但是一個(gè)好的哈希函數(shù)的設(shè)計(jì)需要盡量避免哈希碰撞。
Python2中使用使用開放地址法解決沖突。
CPython使用偽隨機(jī)探測(cè)(pseudo-random probing)的散列表(hash table)作為字典的底層數(shù)據(jù)結(jié)構(gòu)。由于這個(gè)實(shí)現(xiàn)細(xì)節(jié),只有可哈希的對(duì)象才能作為字典的鍵
哈希表
哈希表是key-value類型的數(shù)據(jù)結(jié)構(gòu),通過關(guān)鍵碼值直接進(jìn)行訪問。通過散列函數(shù)進(jìn)行鍵和數(shù)組的下標(biāo)映射從而決定該鍵值應(yīng)該放在哪個(gè)位置,哈希表可以理解為一個(gè)鍵值需要按一定規(guī)則存放的數(shù)組,而哈希函數(shù)就是這個(gè)規(guī)則。此處提出幾個(gè)專業(yè)名詞后面會(huì)一一進(jìn)行介紹。
哈希函數(shù)
裝填因子
沖突
1.哈希表產(chǎn)生的原因假設(shè)我們存在一個(gè)簡(jiǎn)單的鍵值對(duì)結(jié)構(gòu),鍵-員工號(hào),值-是否在崗。現(xiàn)在需要這樣一個(gè)功能,輸入員工號(hào),返回該員工是否在崗,理想的方法是創(chuàng)建一個(gè)長(zhǎng)度為Max(員工號(hào))的數(shù)組,數(shù)組下標(biāo)就是員工號(hào),數(shù)組中的值用0和1對(duì)是否在崗進(jìn)行區(qū)分,這樣只需要O(1)的時(shí)間復(fù)雜度就可以完成操作,但是擴(kuò)展性不強(qiáng),存在以下問題。
假設(shè)新進(jìn)員工的員工號(hào)比Max(員工號(hào))還要大,這就需要重新申請(qǐng)數(shù)組進(jìn)行遷移操作。
假設(shè)一種極端的情況,存在兩個(gè)員工,員工號(hào)分別是1和100000000001,這樣子的話按照先前的設(shè)計(jì)思路,是會(huì)浪費(fèi)很大的存儲(chǔ)空間的。
上面兩點(diǎn),第一點(diǎn)是因?yàn)閿?shù)組的固定申請(qǐng)大小的屬性所決定,而第二點(diǎn)就是引入哈希表的原因,會(huì)不會(huì)存在一個(gè)方法,讓一個(gè)大員工號(hào)變小而而且沒有標(biāo)記,哈希函數(shù)便產(chǎn)生,假設(shè)此處的哈希規(guī)則是除3取模,則員工1得到的哈希值是1,員工100000000001得到的哈希值是
這樣的話按照設(shè)計(jì)思路,只需要一個(gè)大小為2的數(shù)組便可以覆蓋了,這就是哈希思想。
算法中時(shí)間和空間是不能兼得的,哈希表就是一種用合理的時(shí)間消耗去減少大量空間消耗的操作,這取決于具體的功能要求。
2. 哈希函數(shù)
上面的例子中哈希函數(shù)的設(shè)計(jì)很隨意,但是從這個(gè)例子中我們也可以得到信息:
哈希函數(shù)就是一個(gè)映射,因此哈希函數(shù)的設(shè)定很靈活,只要使得任何關(guān)鍵字由此所得的哈希函數(shù)值都落在表長(zhǎng)允許的范圍之內(nèi)即可;
并不是所有的輸入都只對(duì)應(yīng)唯一一個(gè)輸出,也就是哈希函數(shù)不可能做成一個(gè)一對(duì)一的映射關(guān)系,其本質(zhì)是一個(gè)多對(duì)一的映射,這也就引出了下面一個(gè)概念–沖突。
3. 沖突
只要不是一對(duì)一的映射關(guān)系,沖突就必然會(huì)發(fā)生,還是上面的極端例子,這時(shí)新加了一個(gè)員工號(hào)為2的員工,先不考慮我們的數(shù)組大小已經(jīng)定為2了,按照之前的哈希函數(shù),工號(hào)為2的員工哈希值也是2,這與100000000001的員工一樣了,這就是一個(gè)沖突,針對(duì)不同的解決思路,提出三個(gè)不同的解決方法。
4.沖突解決方法
4.1 開放地址
開放地址的意思是除了哈希函數(shù)得出的地址可用,當(dāng)出現(xiàn)沖突的時(shí)候其他的地址也一樣可用,常見的開放地址思想的方法有線性探測(cè)再散列,二次探測(cè)再散列,這些方法都是在第一選擇被占用的情況下的解決方法。
4.2 再哈希法
這個(gè)方法是按順序規(guī)定多個(gè)哈希函數(shù),每次查詢的時(shí)候按順序調(diào)用哈希函數(shù),調(diào)用到第一個(gè)為空的時(shí)候返回不存在,調(diào)用到此鍵的時(shí)候返回其值。
4.3 鏈地址法
將所有關(guān)鍵字哈希值相同的記錄都存在同一線性鏈表中,這樣不需要占用其他的哈希地址,相同的哈希值在一條鏈表上,按順序遍歷就可以找到。
4.4公共溢出區(qū)
其基本思想是:所有關(guān)鍵字和基本表中關(guān)鍵字為相同哈希值的記錄,不管他們由哈希函數(shù)得到的哈希地址是什么,一旦發(fā)生沖突,都填入溢出表。
5.裝填因子α
一般情況下,處理沖突方法相同的哈希表,其平均查找長(zhǎng)度依賴于哈希表的裝填因子。哈希表的裝填因子定義為表中填入的記錄數(shù)和哈希表長(zhǎng)度的壁紙,也就是標(biāo)志著哈希表的裝滿程度。直觀看來(lái),α越小,發(fā)生沖突的可能性就越小,反之越大。一般0.75比較合適,涉及數(shù)學(xué)推導(dǎo)
原文:https://blog.csdn.net/shouting3901/article/details/80468735
總結(jié)
以上是生活随笔為你收集整理的python dict底层实现_dict实现原理和哈希表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java mediainfo.dll_M
- 下一篇: ppython_Python pcom包