哈希之開散列和閉散列

原文鏈接:https://blog.csdn.net/Yinghuhu333333/article/details/81364739

目的:實現一種結構,不經過任何比較,一次直接得到想要的元素。通過某種函數使元素的存儲位置與它的關鍵碼之間建立一種一一映射的關係。那麼就可以在查找時快速的找到需要的元素。


哈希概念

哈希之散列方法:

  • 插入元素時:根據需要插入元素的值,通過某種計算得出元素的存儲位置,將該元素插入到其對應的位置。
  • 查找元素時:根據需要查找的元素進行某種計算得到其存儲位置,將該位置的元素與查找的元素進行比較,若相同則查找成功。

例如:數據集合爲{180,750,460,430,800,600,541}
哈希函數:Hash(key) = key%m;(m爲內存單元的個數)
假設在該例子中,m爲12;
Hash(180) = 0;
Hash(750) = 6;
Hash(460) = 4;
Hash(430) = 10;
Hash(800) = 8;
Hash(603) = 3;
Hash(541) = 1;
所以這些數據集合在內存中的存儲爲:
這裏寫圖片描述
可是如果數據有衝突 了應該怎麼辦???

哈希衝突

哈希衝突:對於值不相同的元素但是卻有相同的哈希值。
例如:對於兩個元素a,b並且a!=b
但是Hash(a) == Hash(b);
不同的元素通過相同的哈希函數得到相同的哈希地址。


引起哈希衝突的原因:哈希函數設計的不夠合理。
哈希函數的設計原則:

  • 如果哈希表允許容納的元素個數爲m,那麼元素的值域爲0~m-1。
  • 哈希函數計算出來的地址儘量均勻的分佈整個空間之中。

常見的哈希函數

  • 直接定製法:
    即取元素的某個線性函數爲散列地址:Hash(key) = A*key +B;
    例如:找出字符串中只出現一次的字符。時間複雜度爲O(N),空間複雜度爲:O(1);(就可以使用該方法,開闢一個256個元素的數組,進行統計每個元素出現的次數)
    優點:簡單,均勻
    適合於查找比較小而且連續的情況。
  • 除留取餘法:(比較常用的方法)
    Hash(key) = key % p;(p <= m && p 質數),m爲散列表中允許的地址個數。
  • 平方取中法:
    對數據進行平方,然後取數據的中間3位爲哈希地址。
    適合於:不知道數據的分佈情況,但是數字又不是很大的情況

若哈希函數設計的非常合理,那麼產生哈希衝突的概率就非常低,但是哈希衝突是無法避免的。


哈希衝突的處理:
方法一:
閉散列(即開放地址法):當發生哈希衝突時,如果該哈希表還沒有被填滿,那麼就把該元素放到哈希表的下一個空閒的位置。
線性探測法查找下一個位置:
例如:關鍵碼集合爲:{37,25,14,36,49,57,11},設表的長度爲12,Hash(key) = key%p(p = 11);
Hash(37) = 4;
Hash(25) = 3;
Hash(14) = 3;
Hash(36) = 3;
Hash(49) = 5;
Hash(57) = 2;
Hash(11) = 0;
很明顯:這組數據的哈希地址有衝突。
在插入時,如果該位置已經有元素了,就從該位置起向後找,找到一個空閒的位置就進行插入。
如下圖所示:
線性探測法
優點:簡單 易懂
缺點:一旦發生了哈希衝突,所有的衝突連接在一起,很容易產生數據”堆積”。即不同的數據佔用可以利用的位置,就使得尋找其餘數據的位置需要進行多次比較,就會導致查找的效率降低。


負載因子
散列表的負載因子的值爲:α = 填入表中的元素個數 / 散列表的長度。
分析:由於表長是定值,那麼α就與”填入表中的元素個數”成正比。所以,α越大,就說明填入表中的元素個數越多,那麼產生衝突的可能性就越大;反之,α越小,就說明填入表中的越少,產生的衝突就越小,但是可能浪費的空間就越多。
對於開放地址法:負載因子特別重要,應該限制在07-0.8之內。若超過0.8,就可能產生衝突的概率非常大,那麼CPU緩存不命中率也就越高。


二次探測法:
就是當有哈希衝突時,尋找下一個空閒位置時,首先在該位置處加1的平方,若加1的平方的位置處依然有元素,那就加2的平方,知道找到一個空閒的位置爲止。


方法2:開散列法(哈希桶):又名鏈地址法,先用哈希函數計算每個數據的散列地址,把具有相同地址的元素歸於同一個集合之中,把該集合處理爲一個鏈表,鏈表的頭節點存儲於哈希表之中。
例如:還是上面閉散列中的例子,當使用開散列的方法後,其每個元素的存儲爲下圖所示:
哈希桶

由此可見:開散列法有效的解決了數據溢出,不過需要增設鏈接指針,增加了存儲的開銷。但是,總體而言,效率還是快的多。


總結

開散列表:運用單鏈表存儲方式,不產生堆積現象,但因爲附加了指針域而增加了空間開銷。

閉散列表:運用順序表存儲,存儲效率較高,但容易產生堆積,查找不易實現,需要用到二次再查找。


版權聲明:本文爲CSDN博主「MISSyingying」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Yinghuhu333333/article/details/81364739

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