1. 概念
哈希表(hash table),又稱散列表,是根據鍵key直接訪問內存存儲位置的數據結構。關鍵字經過散列函數,得到鍵key。
給定一對(關鍵字,值),關鍵字經過散列函數轉換,得到存儲位置,該存儲位置存儲(關鍵字,值)。
2. 常見的散列函數
散列函數的性質:
如果兩個散列值是不相同的(根據同一函數),那麼這兩個散列值的原始輸入也是不相同的。
6種散列函數:
- 直接定址法:
取關鍵字的某個線性函數值爲散列地址。即 .
- 數字分析法:
假設關鍵字是以r爲基的數,並且哈希表中可能出現的關鍵字都是事先知道的,則可取關鍵字的若干數位組成哈希地址。
- 平方取中法:
取關鍵字平方後的中間幾位爲哈希地址。
通常在選定哈希函數時不一定能知道關鍵字的全部情況,取其中的哪幾位也不一定合適,而一個數平方後的中間幾位數和數的每一位都相關,由此使隨機分佈的關鍵字得到的哈希地址也是隨機的。取的位數由表長決定。
- 摺疊法:
將關鍵字分割成位數相同的幾部分(最後一部分的位數可以不同),然後取這幾部分的疊加和(捨去進位)作爲哈希地址。
- 隨機數法
選擇一隨機函數,取關鍵字的隨機值作爲散列地址,通常用於關鍵字長度不同的場合。
- 除留餘數法:
取關鍵字被某個不大於散列表表長m的數p除後所得的餘數爲散列地址。即。不僅可以對關鍵字直接取模,也可在摺疊法、平方取中法等運算之後取模。對p的選擇很重要,一般取素數或m,若p選擇不好,容易產生衝突。
3. 處理衝突幾個方法
介紹三種方法:
- 開放定址法
,其中 爲散列函數, 爲散列表長, 爲增量序列, i爲已發生衝突的次數。
增量序列可以是,線性函數,平方函數,隨機函數的序列。
- 再散列
對散列後的衝突值,再次進行散列。
- 鏈表法
關鍵字經過散列函數後的值相同,用鏈表來存儲該內存位置的所有值。一般在某個內存位置,衝突的值較多時,比如,大於8個,用紅黑樹來存儲。
4. 哈希表實現
用python實現一個簡單的哈希表,在python中,哈希表對應的是字典。
整個哈希表,除留餘數法作爲散列函數,同時,遇到衝突使用開放定址法解決。同時,動態擴容哈希表,當存儲的數量達到容量的設定閾值時,進行擴容。
class Dict():
def __init__(self):
self.capacity = 11
self.load_factor = 0.75
self.size = 0
self.mod = self.capacity
self.hash_table = [(None,None) for i in range(self.capacity)]
def mod_function(self, x):
return x%self.mod
def hash(self,x):
return self.mod_function(x)
def resize(self):
new_capacity = int(self.capacity*1.5)
new_hash_table = [(None,None) for _ in range(new_capacity)]
for (key,value) in self.hash_table:
if key:
hash_k = self.hash(key)
if new_hash_table[hash_k][0] is None:
new_hash_table[hash_k] = (key,value)
else:
for i in range(1,new_capacity):
hash_k = self.hash(key+i)
if new_hash_table[hash_k][0] is None:
new_hash_table[hash_k] = (key,value)
self.capacity = new_capacity
del self.hash_table
self.hash_table = new_hash_table
def put(self, key, value):
hash_k = self.hash(key)
# 存儲數據與容量比大於載荷因子,擴容
if self.size/self.capacity > self.load_factor:
self.resize()
# 衝突處理
if self.hash_table[hash_k][0] is None:
self.hash_table[hash_k] = (key,value)
else:
for i in range(self.capacity):
hash_k = self.hash(key+i)
if self.hash_table[hash_k][0] is None:
self.hash_table[hash_k] = (key,value)
self.size += 1
def get(self, key):
hash_k = self.hash(key)
# 判斷是否衝突
h_key, value = self.hash_table[hash_k]
if h_key == key:
return value
else:
for i in range(self.capacity):
hash_k = self.hash(key+i)
h_key, value = self.hash_table[hash_k]
if h_key == key:
return value
return -1
dict = Dict()
for i in range(11):
dict.put(i,"hello{}".format(i))
print(dict.capacity)
print(dict.get(2))
運行結果:
16
hello2
參考: