由於在工作中碰到了用哈希桶算法的地方,因此,在這里科普mark一下,有不足之處請指正。
通常大家所說的哈希函數也可以稱為散列函數,哈希函數的功能只是將你的目標key通過一種映射方法,也可以說是一種函數運算f,最后得到你目標的hashValue = f(key),這里的函數f就稱為哈希函數/散列函數。
可以看到哈希函數的選擇是一個很關鍵的步驟。為了引進哈希桶算法,必然要介紹一下哈希沖突,因為哈希桶就是為了解決哈希沖突的。舉個例子,有一組序列為[1,2,3,4,5,6,7,8,9],使用的哈希函數為f(key) = key mod 5,那么依次得到的hasvalue就是[1,2,3,4,0,1,2,3,4],顯然在key值為1,6的時候得到的哈希值都為1,如下所示:
| key |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
| f(key) |
1 |
2 |
3 |
4 |
0 |
1 |
2 |
3 |
4 |
這個時候就產生了沖突了,也就是不同的key通過映射后得到了同樣值的hashvalue。而所謂的哈希桶算法其實就是鏈地址解決沖突的方法,如上面的例子所示,就可以設置桶的個數為5,也就是f(key)集合的個數,而這樣的話,hashvalue就可以作為桶的索引,將1,2,3,4,5分別通過f(key)得到1,2,3,4,0,則可將這幾個key放入桶1,2,3,4,0的首地址所指的內存中,然后處理值為6的key,得到hashvalue值為1,這個時候需要放入桶1中,但桶1的首地址已經有了元素1,怎么辦?那么就可以為每個桶開辟一片內存,內存中存放所有hashvalue相同的key,沖突的key之間用單向鏈表進行存儲,這樣就解決了哈希沖突,在查找對應key的時候,只需要通過key索引到對應的桶,然后從桶的首地址對應的節點開始查找,就是鏈表順序找到,對比key的值,直到找到對應key的信息,所以,在沖突的時候,特別是沖突率比較高的時候,桶內的鏈表就會很長,使得查找效率比較低,而在最壞的情況下,所有的key都對應同一個hashvalue,當然這種情況不會出現,這樣的哈希函數選取的也沒有意義了,假設這種情況出現,那么哈希表就退化成了單鏈表,其他桶內存浪費,且將查找效率從O(1)直接降到了O(N),所以上面才說,哈希函數的選擇也是很關鍵的。
