哈希函數,哈希表理解

學習轉自:https://zhuanlan.zhihu.com/p/95156642,感謝作者,本文爲個人學習記錄!!!

百度百科:

散列表(Hash table,也叫哈希表),是根據鍵(Key)而直接訪問在內存存儲位置的數據結構。也就是說,它通過計算一個關於鍵值的函數,
將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。這個映射函數稱做散列函數,存放記錄的數組稱做散列表

哈希表是一種數據結構,它的特點是:可以根據一個key值來直接訪問數據,因此查找速度快

哈希表的本質是數組,它的底層實現是用到了數組,在數組的基礎上加工加工,變得更加有特色了,然後人家就自立門戶,叫哈希表。

實現hash表的可以採用的兩種方法:

1、數組+鏈表

2、數組+二叉樹

數組中一般就是存放的單一的數據,而哈希表中存放的是一個鍵值對,這是個區別吧

1.散列函數

例:現在給個電話本,上面記錄的有姓名和對應的手機號,查找王二的手機號怎麼做?

1.暴力解決:枚舉查找,找到爲止

2.如果這個王二是在最後幾頁,那前面幾頁都白找了,是不是可以按照人名給分個類,比如按照首字母來排序,就abcd那樣的順序,這樣根據王二我就知道去找w這些,這樣就快很多。

按照人名的首字母作出如下表格:

a    
b    
...    
w 王二 18896321123

以上表格中,王二用w標記,找王二不是直接找而是先去找W,這裏通過人名,加工後拿到首字母,這個方法或者這個函數在哈希表中就叫散列函數,其中規定的一些操作就叫函數法則。

2.關鍵值key

傳給散列函數加工的值就是關鍵值key。

index=Hash(key)

index是哈希表中,數據存放的位置。

3.哈希衝突

w後面同時有王三,王四,而w已經被王二佔了。

哈希表

 

哈希表中存放的是鍵值對,在哈希表中,是通過哈希函數將一個值映射到另一個值的,x映射到y,x就是key,y就是x的哈希值;

在jdk中鍵值對,就叫Entry;

哈希表如何存數據

看上面的圖,我們已經知道了哈希表本質是個數組,所以這裏有個數組,長度是7,現在要做的是把學生信息存放到哈希表中,也就是這個數組中去,需要考慮怎麼去存放

這裏的學號是個key,哈希表就是根據key值來通過哈希函數計算得到一個值,這個值就是用來確定這個Entry要存放在哈希表中的位置的,實際上這個值就是一個下標值,來確定放在數組的哪個位置上。

比如這裏的學號是1010,經過哈希函數的計算之後得到了1,這個1就是告訴我們應該把這個Entry放到數組裏的確切位置的下標,也就是需要放在數組中下標爲1的位置。

哈希衝突

上面李四的key經過哈希函數處理後,也得到哈希值1,但是下標1的位置已經被張三佔用了,這就叫哈希衝突或哈希碰撞,然後要想辦法安置李四。

處理哈希衝突

關於哈希衝突的解決辦法有好幾種,我學習的這片文章只介紹了兩種主要的,所以我就先學習這兩種,一個是開放尋址法,一個是拉鍊法

開發尋址法:這裏所說的開放尋址法其實簡單來說就是,既然位置被佔了,那就另外再找個位置不就得了,怎麼找其他的位置呢?這裏其實也有很多的實現,我們說個最基本的就是既然當前位置被佔用了,我們就看看該位置的後一個位置是否可用,也就是1的位置被佔用了,我們就看看2的位置,如果沒有被佔用,那就放到這裏唄,當然,也有可能2的位置也被佔用了,那咱就繼續往下找,看看3的位置,一次類推,直到找到空位置。

不會一直找不到地址,佔滿的話,會擴容

拉鍊法:

 拉鍊法也是比較常用的,HashMap就是使用了這種方法

 

 拉鍊法還是在該位置,這裏採用的是鏈表,就像圖中所示,現在張三和李四都要放在1找個位置上,但是張三先來的,已經佔了這個位置,待在了這個位置上了,那李四呢?解決辦法就是鏈表,這時候這個1的位置存放的不單單是之前的那個Entry了,此時的Entry還額外的保存了一個next指針這個指針指向數組外的另外一個位置,將李四安排在這裏,然後張三那個Entry中的next指針就指向李四的這個位置,也就是保存的這個位置的內存地址,如果還有衝突,那就把又衝突的那個Entry放在一個新位置上,然後李四的Entry中的next指向它,這樣就形成了一個鏈表

如果衝突很多,鏈表很長,鏈表會轉換結構比如,java集合類中的HashMap,如果這裏的鏈表長度大於等於8的話,鏈表就會轉換成樹結構,當然如果長度小於等於6的話,就會還原鏈表。以此來解決鏈表過長導致的性能。

 哈希表擴容

有一個很重要的原因就是當哈希表被佔的位置比較多的時候,出現哈希衝突的概率也就變高了,所以很有必要進行擴容。

那麼這個擴容是怎麼擴的呢?這裏一般會有一個增長因子的概念,也叫作負載因子,簡單點說就是已經被佔的位置與總位置的一個百分比,比如一共十個位置,現在已經佔了七個位置,就觸發了擴容機制,因爲它的增長因子是0.7,也就是達到了總位置的百分之七十就需要擴容。

而且這個擴容也不是簡單的把數組擴大,而是新創建一個數組是原來的2倍,然後把原數組的所有Entry都重新Hash一遍放到新的數組。因爲數組擴大了,所以一般哈希函數也會有變化,這裏的Hash也就是把之前的數據通過新的哈希函數計算出新的位置來存放。

哈希表如何讀取數據

 比如我們現在要通過學號1011來查找學生的姓名,怎麼操作呢?我們首先通過學號利用哈希函數得出位置1,然後我們就去位置1拿數據啊,拿到這個Entry之後我們得看看這個Entry的key是不是我們的學號1011,一看是1010,什麼鬼,一邊去,這不是我們要的key啊,然後根據這個Entry的next知道下一給位置,在比較key,好成功找到李四。

哈希函數是核心

在哈希表中,哈希函數的設計很重要,一個好的哈希函數可以極大的提升性能,而且如果你的哈希函數設計的比較簡單粗陋,那很容易被那些不懷好意的人搗亂,比如知道了你哈希函數的規則,故意製造容易衝突的key值,那就有意思了,你的哈希表就會一直撞啊,一直撞啊

設計哈希函數的方法直接定址法,數字分析法,摺疊法,隨機數法和除留餘數法等等

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章