數據結構 哈希

構造哈希函數的方法

構造哈希函數的原則是: 1. 函數本身便於計算;2. 計算出來的地址分佈均勻,即對任一關鍵字k,f(k) 對應不同地址的概率相等。

  1. 直接定址法
    取關鍵字或者關鍵字的某個線性函數爲Hash地址,即Hash(key)=a*key+b
  2. 除留餘數法:
    假設哈希表長爲m,p爲小於等於m的最大素數,則哈希函數爲h(k) = k % p。
  3. 平方取中法:
    先求出關鍵字的平方值,然後按需取平方值的中間幾位作爲哈希地址。這是因爲:平方後中間幾位和關鍵字中每一位都相關,故不同關鍵字會以較高的概率產生不同的哈希地址。

解決哈希衝突的方法

開放定址法

當一個關鍵字和另一個關鍵字發生衝突時,使用某種探測技術在Hash表中形成一個探測序列,然後沿着這個探測序列依次查找下去,當碰到一個空的單元時,則插入其中。基本公式爲:hash(key) = (hash(key) + di) mod TableSize。其中di爲增量序列,TableSize爲表長。根據 di 的不同我們又可以分爲線性探測,平方(二次)探測,雙散列探測。

1)線性探測以增量序列 1,2,……,(TableSize -1)循環試探下一個存儲地址,即di = i。如果table[index+di]爲空則進行插入,反之試探下一個增量。但是線性探測也有弊端,就是會造成元素聚集現象,降低查找效率。

2)平方(二次)探測以增量序列1,-1,4,-4…且 q ≤ TableSize/2 循環試探下一個存儲地址。

3)雙散列探測:略

4)僞隨機探測:略

特別對於開放定址法的刪除操作,不能簡單的進行物理刪除,因爲對於同義詞來說,這個地址可能在其查找路徑上,若物理刪除的話,會中斷查找路徑,故只能設置刪除標誌

鏈地址法

又稱拉鍊法、開散列,首先將關鍵碼根據哈希函數來計算出哈希地址,對相同的哈希地址放在某一子集合中,每個子集合叫做一個桶,每個通放的都是哈希衝突元素,每個桶中的元素通過單鏈表連接,每個鏈表的頭節點存放在哈希表中,這種結構叫做哈希桶

HashMap,HashSet 其實都是採用的拉鍊法來解決哈希衝突的,就是在每個位桶實現的時候,我們採用鏈表(jdk1.8之後採用鏈表+紅黑樹)的數據結構來去存取發生哈希衝突的輸入域的關鍵字(也就是被哈希函數映射到同一個位桶上的關鍵字)。

使用拉鍊法解決哈希衝突的幾個操作:

①插入操作:在發生哈希衝突的時候,輸入域的關鍵字去映射到位桶(實際上是實現位桶的這個數據結構,鏈表或者紅黑樹)中去的時候,我們先檢查帶插入元素 x 是否出現在表中,很明顯,這個查找所用的次數不會超過裝載因子(n / m,n爲輸入域的關鍵字個數,m爲位桶的數目),它是個常數,所以插入操作的最壞時間複雜度爲O(1)。

②查詢操作:和①一樣,在發生哈希衝突的時候,檢索的時間複雜度不會超過裝載因子,檢索數據的時間複雜度也是O(1)。

③刪除操作:如果在拉鍊法中我們想要使用鏈表這種數據結構來實現位桶,那麼這個鏈表一定是雙向鏈表,因爲在刪除一個元素x的時候,需要更改 x 的前驅元素的 next 指針的屬性,把 x 從鏈表中刪除。這個操作的時間複雜度也是O(1)。

優點

與開放定址法相比,拉鍊法有如下幾個優點:

①拉鍊法處理衝突簡單,且無堆積現象,即非同義詞決不會發生衝突,因此平均查找長度較短;

②由於拉鍊法中各鏈表上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況;

③開放定址法爲減少衝突,要求裝填因子 α 較小,故當結點規模較大時會浪費很多空間。而拉鍊法中可取α ≥ 1,且結點較大時,拉鍊法中增加的指針域可忽略不計,因此節省空間;

④在用拉鍊法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去鏈表上相應的結點即可。

缺點

指針需要額外的空間,故當結點規模較小時,開放定址法較爲節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的衝突,從而提高平均查找速度。

再散列(雙重散列,多重散列)

同時構造多個不同的哈希函數,當發生衝突時,使用第二個、第三個哈希函數計算地址,直到無衝突。

缺點:計算時間增加。

建立一個公共溢出區

將哈希表分爲基本表和溢出表兩部分,凡是和基本表發生衝突的元素,一律填入溢出表。

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