前言
很多的應用都需要一些動態集合結構,這些動態集合結構都支持INSERT, SEARCH, DELETE的字典操作。對於普通的數組進行尋址我們需要。但是我們實際存儲的關鍵字數比全部的關鍵字要小很多,我們使用hash替代普通的數組,在合理假設的情況下,我們做上述操作的時候,也只需要。即使hash函數在最糟糕的情況下,也就是hash都碰撞到同一個槽,這樣所需要
本文首先介紹了hash table是個什麼東西,以及內部實現的原理。然後講解了hash函數,和開放尋址法來處理hash碰撞的問題。最後講解了完全hash,一種在最壞的情況下,也能在的時間完成search操作。文中有些證明過程就省略了。
hash是什麼
我們先考慮一下map獲取其中元素的方法,就是通過key值來獲取的。這裏的hash表也是一樣的道理。如圖所示,我們對輸入的key值,使用設計的hash函數來進行計算,得到相應的尋址地址,然後就可以獲取到key對應的value。
在討論hash函數之前,先介紹一下hash碰撞。
假設有其中=,則就會在hash table裏面尋址到同一個位置,這樣就會產生hash碰撞。解決hash碰撞的方法就是將slot擴展爲鏈表的結構,slot裏面儲存這指向鏈表的頭部。這也解釋了爲什麼,在最壞的情況下hash的時間複雜度爲n
我們讓n個key映射到table上,table有m個slot,在每一個key映射到每一個slot上的概率都是獨立和相等的情況下,hash表的承載因子爲
因此搜索一個存在的記錄,其給定的時間爲。
hash函數
hash函數的好壞與否,主要是看能否把keys均勻的分佈到table的solt裏面。由於我們不知道keys的分佈,或者keys可能在一些局部分佈會比較密集。因此很難找到一種統一的hash函數的方法。
division method
m不應爲,一個不太接近2的整數冪的素數是一個很好的選擇。
Multiplication method
全域hash
首先需要明確的一點是對於單一的hash函數,我們總能設計相應的key集合,讓每一個key都映射到一個solt裏面,因此就有了隨機選擇hash函數的方法也就是全域hash。
我們先定義一個有限的hash函數集合,hash table的槽位,對於。在獨立隨機分佈的情況下,使得的hash函數有個。這是一個概率問題,對於k,l在隨機選擇一個hash函數的情況下,他們發生碰撞的概率爲,因此想要使得k,l發生碰撞的函數個數按概率來說需要。
定理:Let h be a hash function chosen (uniformly) at random from a universal set H of hash function. Suppose h is used to hash n arbitrary keys into the m slots of a table T. Then, for a given key x, we have
上面的定律話句話說就是,從H中隨機選取的一個h,使得x發生碰撞的概率不會大於(證明就省略了,也是indicate function)
構造一個全域函數集
構造一個全域hash函數集有很多種方法,現在介紹一種使用點積的構造方法(dot product)。
Let m be prime, Decompose key k into r+1 digits, each with value in the set {0, 1, …, m-1}. That is , let k = , where ,
randomized strategy:
Pick a = {}, where each ai is chosen randomly from {0, 1, … , m-1}
we define
所以
要證明全域hash,即使要證明造成hash碰撞的可能性非常低,低到可以忽略不記。k分解爲恰恰方便證明全域hash函數,
證明過程就不給出了,結論是這樣的,
要想造成一次hash碰撞需要試次也就是上述說的
開放尋址(open address)
開放尋址和鏈接法的差別是,它的slot裏面沒有存放指針,如果hash到一個slot之後,這個槽以及被佔有,就使用探查probe方法(線性探查,二次探查,雙重探查)進行下一個slot的檢索。
至於使用怎樣的一個序列來探查slot,我們將這個探查序列表示爲是的一個序列。
我們來看看insert
hashInsert(T, k)
i=0
repeat
j = h(k,i)
if T[j]==nil
T[i}=k
return i
else i++
until i==m
error "hash table overflow"
0到m-1有m!種組合,因此探查序列應該是從這m!個序列中選擇一個。
線性探查
爲什麼mod m這樣可以實現循環的探查,但是這個循環只實現一次。
二次探查
不做評論,和線性探查差不多,都有m種可能,由初始位置決定探查序列
雙重探查
這兩個函數的設計有很多講究的,會產生種序列,是這三種方法中最好的
完全hash(prefect hash)
首先要明確一點就是完全hash函數是針對靜態的key集合,我們可以通過嘗試多組hash函數來確定最終的函數完成key到slot的映射。最後實現在的時間和的空間的hash函數。
我們有定理,隨機從全域hash函數中選擇一個h,對於個槽下映射,發生碰撞的概率不大於1\2。我們可以試幾次,就可以得到一個不發生碰撞的函數函數。 但是對於n的平方的空間消耗還是太大了,因此就引入了二級hash的方式,在兩級hash裏面都使用全域hash函數。第一級的slot的總數爲m=n,第二級就有點講究了爲。我們可以證明其期望的消耗的空間是小於2n的。所以多試幾次就好了,穩住