STL源碼剖析之哈希表 hashtable【2013.12.06】

STL源碼剖析之哈希表 hashtable【2013.12.06】

歡迎加入我們的QQ羣,無論你是否工作,學生,只要有c / vc / c++ 編程經驗,就來吧!158427611 



哈希表,一種鍵值對用的關係。具有關聯性。

也稱散列表,主要是通過特殊算法建立值和鍵的關係,然後根據鍵值對應存儲和搜索。

因爲是鍵值對應的關係,所以再鍵的存儲上面,會出現一種【碰撞】問題,即根據算法計算兩個不同的值對應了同一個鍵,因爲導致鍵的存儲衝突。

哈希表根據解決【碰撞】的方案不同而產生了幾種存儲表現,不同的存儲表現有不同的存儲搜索效率。

線性探測,二次探測,開鏈。

【一】線性探測

在哈希表的連續空間上,當發生衝突的時候,向後存儲。



這種探測方法產生的問題:

需要一個很大的空間,而且依賴於數據的獨立性(儘量使數據對應獨立的鍵)。

【二次探測】

二次探測其實是對 線性探測的一種優化,即當第一次探測出現碰撞問題的時候,再進行算法探測,的算法探測就是一種二次方算法:



二次探測雖然表上解決了線性探測的問題,

但是他其實值是推遲了線性探測的問題,也以來數據獨立性。

【開鏈】

開鏈的方法,就是在對應鍵下面維護一個鏈表,鏈表維護鍵對應下的所有值。




SGI STL中 hashtable容器的內部實現就是使用的【開鏈】方法。

其中鍵值序列,使用的是vector,而vector的大小,取的是一個質數集合,一共28個常數,大於53,且相鄰質數的關係爲:2的倍數中最接近的質數。

如果傳入的鍵大小爲50,那麼hashtable建立的時候,就會取出__stl_prime_list的53,並建立vector。


SGI STL 中hashtable的鍵確定方法:爲用值對大小(就是上面的這28個常數)取模。

而對應鍵的值序列用的單向的List,List是時時增長大小的,所以不用像鍵序列一樣需要確定大小

由於值序列的List是單向的,所以hashtable只能 ++ 不能 --;

而且值List的最後一個指向的是下一個鍵節點。這樣能夠保證順序遍歷的高效。

同樣,由於hashtable的存儲方式,不能像紅黑樹一樣做排序。即遍歷出來的元素不是排序的,這和紅黑樹是不同的。

【注意】當hashtable的鍵vector大小重新分配的時候,hashtable原鍵的值List會重新分配,因爲vector重建了,相當於鍵增加,那麼原來的值對應的鍵可能就不同與原來分配的鍵,這樣的情況就需要重新確定值的鍵。主要觸發方法爲hashtable<T>::resize(size_t ) 

如下:buchets爲原鍵vector,tmp爲新的。


原來的hashtable大小爲53,所以【55】【2】【108】這些元素 % 53都等於2

那麼這些元素都存在鍵序列中值爲2的節點下的List中。

當hashtable進行resize變成97的大小後,【52】%97 = 55 【2】%97 = 2【108】= 2 很明顯取模97的值不全是2,

52元素要移到鍵序列值爲 55 下的List中,所以要重整鍵序列下的List。

如圖:



SGI STL爲hashtable提供了hash function 在<stl_hash_fun.h>頭文件中

全都是仿函數。

SGI STL中 hashtable的鍵確定方式就是取模,所以這些仿函數都是將各種類型做類型轉換並返回原值(char . int . long 等)

唯獨 不同的是 string char* 等字符串類型;做了不一樣的轉換:

如string


代碼很清楚,就是遍歷字符串字符,並運算,再返回無符號long。

至於字符指針char * 和const char *也是調用的string方法。


其他可以缺省轉爲整數的類型就不上代碼了。

所以hashtable的鍵 其實都是要聲明成爲整形數據的,不然默認的方法是無法使用(因爲默認的鍵確定方法是對值與28個常數取模,如果鍵聲明爲string,取出的模無法轉成string,自然會編譯錯誤的)的,當然你也可以自定義自己的鍵確定方法,並加到hashtable的聲明中去,就可以正常使用了

string hashtable的錯誤使用方法如下:


如上聲明方法,已經將key聲明成string,但是沒有指定符合要求的hash_func(確定key的仿函數)依舊是用的默認的。所以當插入數據的時候就無法生成正確的代碼副本,當然就會編譯錯誤。

上述代碼,只要將hashtable的key聲明爲int 就可以了,其他不用變。

有興趣的可以悄悄代碼就知道了。


【hashtable正確運用範例】





由最後的遍歷輸出,可以看出,遍歷hashtable得到的結果並不是排序的,這是和紅黑樹不同的一點。

原因就是變量是對鍵序列下值List的遍歷。可能很大的值對應的鍵卻很小。













歡迎加入我們的QQ羣,無論你是否工作,學生,只要有c / vc / c++ 編程經驗,就來吧!158427611 

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