哈希函數又叫散列函數,一個哈希函數的輸入域可以是非常大的范圍,但是他的輸出域是一個固定的范圍
哈希函數的性質:
- 典型的哈希函數都擁有無限的輸入值域
- 輸入值相同的時候,輸出值也一樣
- 輸入值不一樣時,輸出值可能一樣,也可能不一樣
-
不同的輸入值得到的哈希值,整體均勻的分布在輸出域上
其實哈希函數有點神奇,他可以把任意輸入的一個個字符串變成一個個相同長度的字符串等等……,並可以得到類似於數組的一種結構,哈希表(散列表),對於存在他中的元素的查找操作是非常方便的可以是復雜度為O(1)(想想數組的查找操作)
以上前三點是哈希函數的基本特點,第4條是判斷一個哈希函數優劣的關鍵,也就是說一組數據經過好的哈希函數進行計算之后,他的每個輸出值可能都不會重復,並且均勻的分布在一個范圍中,但是在實際應用中,很少能做到這些。不同的輸入值經過哈希函數計算后得到了相同的輸出值。
這里說一下抽屜原理(又叫鴿巢原理)鴿巢原理的簡單形式:如果n+1個物體被放進n個盒子,那么至少有一個盒子包含兩個或者更多的物體。也就是在進行哈希計算后,一定有重復的輸出值,造成了沖突。既然造成了沖突,就要解決沖突
解決沖突的兩種方法:開放定址法,鏈地址法
1.開放定址法:
只要經過哈希計算后又沖突,就去尋找下一個空的散列地址,只要這個散列表足夠大,就一定可以找到一個合適的地址。但是散列表不能定義太大,會帶來內存上的巨大開銷,所以散列表大小要設計合理。
Hi(key) = (H(key)+di) MOD m (di=1,2,3,......,m-1), 其中:H(key)為哈希函數;m為哈希表表長;di為增量序列
大致說一下原理:給定一組數字,首先選取合適的哈希表的長度,盡量選擇合適的長度。之后依次進行計算,每得到一個值,去哈希表中看是否該數字對應的地址為空,如果為空此次地址尋找完畢,探測次數為一,如果不為空,原數據加一再進行計算,每次加一,直到找到地址為空的地方,探測次數為找到空的地址判斷了多少次。
例子:已知一組關鍵字為(30,11,41,26,15,9),用除余法構造散列函數,用線性探查法解決沖突構造這組關鍵字的散列表。
已知數據個數為6個,不如取m=7,構建一個從0~6的一個哈希表(散列表),散列函數為:h(key)=key%7
步驟:1.每個數對7取余計算,30%7=2,地址為2的位置為空,把30放入2位置,比較次數為1次;1%7=4,4位置為空,放入,比較次數1次;41%7=6,6位置為空放入;26%7=5,位置為空放入;15%7=1,位置為空放入;9%7=2,地址2位置已經放入了30,此地址不為空,就接着向下找,(9+1)%7=3,3的位置為空放入。如果得到的地址還不為空,就接着向下找,直到找到空的地址放進去。

平均查找長度就是比較次數相加除以元素個數(1+1+2+1+1+1)/6=7/6
這里是一組其他數據的演示動畫點擊打開鏈接
二次探測散列法:
如果有一個數正好得到1的位置,可是1的后面沒有位置,但是他的前面有位置,這樣他需要把表中的每個地方都判斷一遍,效率很差。
因此我們可以改進di = 12, -12, 22, -22,……, q2, -q2 (q <= m/2),這樣就等於是可以雙向尋找到空位置,可以避免前面的需要判斷整個表的情況。
我們稱這種方法為二次探測法。
開放定址法只要在散列表未填滿時,總是可以找到不發生沖突的地址。
2.鏈地址法(拉鏈法)
和開放定址法的求法類似,只不過當沖突的時候,不再像后查找,而是在當前位置生成一條鏈,是單鏈表的結構,按照順序依次計算就可以。
還是上面的例子:已知一組關鍵字為(30,11,41,26,15,9),我們用前面同樣的7為除數,進行除留余數法:
下面的每個元素的存儲空間大小都應該是相同的

與開放定址法相比,鏈地址法有如下幾個優點:
- 鏈地址法處理沖突簡單,且無堆積現象,他的數據不會非常密集的出現在一塊區域,因此平均查找長度較短;
- 由於鏈地址法中是一個鏈表的結構,擁有鏈表的所有特點,各鏈表上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況,減少對不必要內存的開銷;
- 在用鏈地址法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去鏈表上相應的結點即可。而對開放地址法構造的散列表,刪除結點不能簡單地將被刪結 點的空間置為空,否則將截斷在它之后填入散列表的同義詞結點的查找路徑。這是因為各種開放地址法中,空地址單元(即開放地址)都是查找失敗的條件。因此在用開放地址法處理沖突的散列表上執行刪除操作,只能在被刪結點上做刪除標記,而不能真正刪除結點。
拉鏈法的缺點:指針需要額外的空間,所以當結點規模較小時,開放定址法較為節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子(a=表中填入的記錄數 / 哈希表的長度)變小,這又減少了開放定址法中的沖突,從而提高平均查找速度。
鏈地址法相比於開放定址法,鏈地址法一定可以找到一個放入元素的位置,就如開放定址法的表滿的時候不能放入元素,而鏈地址法只是在后面動態申請節點就可以。
參考:
http://blog.csdn.net/ywok526/article/details/38540885
http://www.nowamagic.net/academy/detail/3008060g#
