python 下的數據結構與算法---8:哈希一下【dict與set的實現】


少年,不知道你好記不記得第三篇文章講python內建數據結構的方法及其時間復雜度時里面關於dict與set的時間復雜度[為何訪問元素為O(1)]原理我說后面講嗎?其實就是這篇文章講啦。

目錄:

  一:Hash的定義

  二:dict與set的實現原理

  三:常用構造hash函數的方法

  四:hash碰撞及其解決方法

  五:dict的實現

 

一:Hash的定義

  Hash,一般翻譯做“散列”,就是把任意長度的輸入,通過散列算法,變換成固定長度的輸出,該輸出就是散列值。【不同的輸入可能會散列成相同的輸出,所以不可能從散列值來唯一的確定輸入值】

 

二:dict與set的實現原理

  dict與set實現原理是一樣的,都是將實際的值放到list中。唯一不同的在於hash函數操作的對象,對於dict,hash函數操作的是其key,而對於set是直接操作的它的元素,假設操作內容為x,其作為因變量,放入hash函數,通過運算后取list的余數,轉化為一個list的下標,此下標位置對於set而言用來放其本身,而對於dict則是創建了兩個list,一個list該下表放此key,另一個list中該下標方對應的value。

  其中,我們把實現set的方式叫做Hash Set,實現dict的方式叫做Hash Map/Table(注:map指的就是通過key來尋找value的過程)

 

三:常用構造hash函數的方法

  1:折疊法

    將每個元素分為相等的幾部分后相加后再除以list長度,e.g:如果項目是436-555-4601, 以2為分組,分成了 (43, 65, 55, 46, 01). 全部加起來:43 + 65 + 55 + 46 + 01 = 210. 假設list有11個元素, 則210%11 =1, 所以將436-555-4601放到list下標為1的地方。

  

  2:取中法:

    如元素44平方后得1936取中93再取list的余

    

  注:對於string其所對應的數字可用其ASCII碼來代替(還可與位數結合,見圖5.7)ord('a')可返回'a'的ASCII碼

    

  注:此地就是為什么dict與set訪問元素時間復雜度為O(1)的原因了,通過對元素的hash函數運算后能夠直接知道其下標,所以為O(1)

 

四:hash碰撞及其解決方法

  定義里面講到過不同的輸入可能會散列成相同的輸出,所以就可能出現名為“哈希碰撞”的情況,也就是說兩個不同的元素算出來的下標值一樣,此時就有兩種解決方法:

  1:向后探測

      架設一個元素算出來下標為5,另一個元素算出來下標也為5,從開頭開始探測第0第1位是否為空,當看到為空的就放入,不過這樣相鄰探測的不好之處在於容易發生聚集,所以最好是跳躍着進行探測,定義一個skip的值,比如3,用方程rehash(pos) = (pos + skip)%sizeoftable,即使查看0,3,6這樣跳躍着來

  2:鏈式存儲

    原理圖如下,其實就是將發生有沖突的元素放到同一位置,然后通過“指針“來串聯起來

    

 

五:HashTable

  下面將寫一個hashTable,而實際中的dict就是由hashTable擴展而來的

 

 1 class HashTable:
 2     def __init__(self):
 3         self.size = 11
 4         self.slots = [None] * self.size
 5         self.data = [None] * self.size
 6 
 7     def hash_function(self, key, size):
 8         return key % size
 9     def rehash(self, old_hash, size):
10         return (old_hash + 1) % size
11     def __getitem__(self, key):
12         return self.get(key)
13     def __setitem__(self, key, data):
14         self.put(key, data)
15 
16     def put(self, key, data):
17         hash_value = self.hash_function(key,len(self.slots))
18         if self.slots[hash_value] == None:
19             self.slots[hash_value] = key
20             self.data[hash_value] = data
21         elif self.slots[hash_value] == key:
22             self.data[hash_value] = data        # replace
23         else:
24             next_slot = self.rehash(hash_value, len(self.slots))
25             while self.slots[next_slot] != None and self.slots[next_slot] != key:
26                 next_slot = self.rehash(next_slot, len(self.slots))
27                 if self.slots[next_slot] == None:
28                     self.slots[next_slot] = key
29                     self.data[next_slot] = data
30                 else:
31                     self.data[next_slot] = data     #replace
32 
33     def get(self, key):
34         start_slot = self.hash_function(key, len(self.slots))
35         data = None
36         stop = False
37         found = False
38         position = start_slot
39         while self.slots[position] != None and not found and not stop:
40             if self.slots[position] == key:
41                 found = True
42                 data = self.data[position]
43             else:
44                 position=self.rehash(position, len(self.slots))
45                 if position == start_slot:
46                     stop = True
47         return data
HashTable

 


免責聲明!

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



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