【C++自學筆記】哈希結構淺識(如何解決哈希衝突—閉散列、開散列)待更新~

一、哈希概念

順序結構以及平衡樹中,元素的關鍵碼與其存儲位置之間沒有對應關係,因此在查找一個元素的時候,必須要經過關鍵碼的多次比較。順序查找時間複雜度爲O(N),平衡樹中爲數的高度,即O(log2N),搜索的效率取決於搜索過程中元素的比較次數;

理想的搜索方法:可以不經過任何比較,一次直接從表中得到要搜索的元素。如果構造一種存儲結構,通過某種函數(hashFunc)使元素的存儲位置與它的關鍵碼之間能夠建立一一映射的關係,那麼在查找時通過該函數可以很快找到該元素。

當向該結構中:

插入元素

  • 根據待插入元素的關鍵碼,以此函數計算出該元素的存儲位置並按此位置進行存放;

搜索元素

  • 對元素的關鍵碼進行同樣的計算,把求得的函數值當做元素的存儲位置,在結構中按此位置取元素比較,若關鍵碼相等,則搜索成功;

這種方式即爲哈希(散列)方法,哈希方法中是喲個的轉換函數稱爲哈希(散列)函數,構造出來的結構稱爲哈希表(Hast Table)

例如:數據集合{ 1, 7, 6, 4, 5, 9};

哈希函數設置爲:hash(key) = key % capacity;capacity 爲存儲元素底層空間的總大小;

用該方法進行搜索不必進行多次關鍵的比較,依次搜索的速度比較快。

二、常見的哈希函數

常見的哈希函數:
1、直接定製法(常用)

取關鍵字的某個線性函數爲散列地址:Hash(key) = A*key + B,優點:簡單、均勻;缺點:需要事先直到關鍵字的分佈情況;

使用場景:適合查找比較小且連續的數;

2、除留餘數法(常用)

設散列表中允許的地址數爲 m ,取一個不大於 m,但是最接近或者等於 m的質數 p作爲除數;

按照哈希函數:Hash(key) = key % p(p <= m),將關鍵碼轉換成哈希地址;

3、平方取中法

將關鍵字平方後,取中間的值作爲哈希地址;

該方法比較適用於不知道關鍵字的分佈,而位數又不是很大的情況;

4、摺疊法

將關鍵字從左到右分爲位數相等的幾部分(最後一部分可以短一些),然後將這幾部分疊加求和,並按散列表表長,取後幾位作爲散列地址;

摺疊法適合事先不需要知道關鍵字的分佈,適合關鍵字比價多的情況

5、隨機數法

選擇一個隨機函數,取關鍵字的隨機函數值爲它的哈希地址,及H(key) = random(key),其中random爲隨機數函數;

通常適用於關鍵字長度不等時;

注意:哈希函數設計的越精妙,產生哈希衝突的可能性就越低,但是無法避免!

三、哈希衝突及其解決方式

1、哈希衝突的概念

對於兩個數據元素的關鍵字 ki 和 ki != kj,但有 Hash(ki) == Has(kj),即:不同關鍵字通過相同的哈希函數計算出相同的哈希地址,該種現象稱爲哈希衝突或者哈希碰撞。

把具有不同關鍵碼而具有相同哈希地址的數據元素稱爲“同義詞”。

產生哈希衝突的一個原因可能是:哈希函數設計不夠合理。

哈希函數的設計原則:

  • 哈希函數的定義域必須包括需要存儲的全部關鍵碼,而如果散列表允許有m個地址時,其值域必須在0到m-1之間;
  • 哈希函數計算出來的地址能均有分佈在整個空間中;
  • 哈希函數應該比較簡單;

2、哈希衝突的解決方式

解決哈希衝突的兩種常見方式是:閉散列 和 開散列

1、閉散列

閉散列:也叫開放定址法,當發生哈希衝突的時候,如果哈希表未被裝滿,說明在哈希表中必然還有空位,那麼可以把key 存到衝突位置中的“下一個”空位置中去。

如何尋找空位置:

1、線性探測

線性探測:從發生衝突的位置開始,依次向後探測,知道尋找到下一個空位置爲止。

插入:

  • 通過哈希函數獲取帶插入元素在哈希表中的位置;
  • 如果找到該位置中沒有元素則直接插入新元素,如果該位置中有元素髮生哈希衝突,使用線性探測找到下一個空位置,插入新元素。

                              

刪除:

  • 採用閉散列處理哈希衝突時,不能隨便物理刪除哈希表中已有的元素,若直接刪除元素會影響其他元素的搜索。因此用線性探測採用標記的僞刪除發來刪除一個元素。
//哈希表每個空間都給個標記
//EMPTY此位置空,EXIST此位置已有元素,DELETE元素已經刪除
enum State{ EMPTY, EXIST, DELETE};

線性探測的實現

線性探測的優缺點

  • 優點:實現非常簡單
  • 缺點:一旦發生哈希衝突,所有的衝突連在一起,容易產生數據“堆積”,即:不同關鍵碼佔據了可利用的空位置,使得尋找某關鍵碼的位置需要許多次比較,導致搜索效率降低,解決辦法->二次探測;

2、二次探測

線性探測的缺陷時產生衝突的數據堆積在一起,這與其找下一個空位置有關係,因爲找空位置的方式就是挨着往後找,因此二次探測爲了避免這個問題: 找下一個空位值的方法爲: 或者 。(其中:i = 1,2,3……),H0時通過散列函數Hash(X)對元素的關鍵碼 key 進行計算得到的位置,m 時表的大小。

                       

當表的長度爲質數且表裝載因子a不超過0.5時,新的表項一定能夠插入,而且任何一個位置都不會被探測兩次。因此只要表中有一半的空位置,就不會在表滿的問題。在搜索時可以不考慮表裝滿的情況,但在插入時必須確保表的裝載因子a 不超過0.5,如果超出就必須要考慮擴容。

因此:閉散列最大的缺陷就是空間利用率比較低,這也是哈希的缺陷;

2、開散列

1、開散列概念

開散列法又叫作鏈地址法(開鏈法),首先對關鍵碼集合用散列函數計算散列地址,具有相同的地址關鍵碼歸於同一子集和,每一個子集合稱爲一個桶,每個桶中的元素通過一個單鏈錶鏈接起來,各鏈表的頭結點存儲在哈希表中。

                                              

從上圖可以看出,開散列中每個桶中放的都是發生哈希衝突的元素;

2、開散列的實現

 

四、哈希的應用

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