淺談搜索結構之哈希

哈希之閉散列(線性探測/二次探測)
哈希開散列

什麼是哈希

如果構造一種結構,通過某種函數(hashFunc)使元素的存儲位置與它的關鍵碼之間能夠建立一一映射的關係,那麼查找時通過該函數可以很快找到該元素。

  • 插入元素時:根據待插入元素的關鍵碼,以此函數計算出該元素的位置的存儲位置並按此位置進行存放。
  • 搜索元素時:對元素的關鍵碼進行同樣的計算,把求得的函數值當做元素的存儲位置,在結構中按此位置元素比較,若關鍵碼相等,則搜索成功。
    這種方式即爲哈希方法,哈希方法中使用的轉換函數稱爲哈希函數,構造出來的結構稱爲哈希表
    這裏寫圖片描述

哈希衝突

不同的關健字通過相同哈希函數計算出相同的哈希地址,該現象稱爲哈希衝突或哈希碰撞,把具有不同關鍵碼而具有相同哈希地址的數據元素成爲“同義詞”。
如上圖,在往哈希表裏放個14就會產生衝突,因爲該位置已經有數字6了。

哈希函數

引起哈希衝突的一個可能原因就是哈希函數設置的不合理。
哈希函數設計原則:

  • 哈希函數的定義域必須包括需要存儲的全部關鍵碼,而如果散列表允許有m個地址,其值域必須在0到m-1之間
  • 哈希函數計算出來的地址能夠均勻分佈在整個函數空間中
  • 哈希函數應該比較簡單。
常見的哈希函數
  • 直接定址法
    取關鍵字的某個線性函數爲散列地址:Hash(key) = A*key+B
    優點:均勻、簡單
    缺點:需要實現直到關鍵字的分佈情況
    適合查找比較小且連續的情況
  • 除留餘數法
    設散列表中允許的地址數爲m,取一個不大於m,但最接近或者等於m的質數作爲除數,按照哈希函數:Hash(key)= key%p(p<=m),將關鍵碼轉換爲哈希地址。
  • 平方取中法
    假設關鍵字爲1234,對它平方就是1522756,抽取中間的3位227作爲哈希地址;
    再比如關鍵字爲4321,對它平方就是18671041,抽取中間的3位671(或710)作爲哈希地址
    平方取中法比較適合:不知道關鍵字的分佈,而位數又不是很大的情況
  • 摺疊法
    摺疊法是將關鍵字從左到右分割成位數相等的幾部分(最後一部分位數可以短些),然後將這幾部分疊加求和,並按散列表表
    長,取後幾位作爲散列地址
    摺疊法適合事先不需要知道關鍵字的分佈,適合關鍵字位數比較多的情況
  • 隨機數法
    選擇一個隨機函數,取關鍵字的隨機函數值爲它的哈希地址,即H(key) = random(key),其中random爲隨機數函數
    通常應用於關鍵字長度不等時採用此法
  • 數學分析法
    設有n個d位數,每一位可能有r種不同的符號,這r種不同的符號在各位上出現的頻率不一定相同,可能在某些位上分佈比較均
    勻,每種符號出現的機會均等,在某些位上分佈不均勻只有某幾種符號經常出現。可根據散列表的大小,選擇其中各種符號分
    布均勻的若干位作爲散列地址。
    數字分析法通常適合處理關鍵字位數比較大的情況,如果事先知道關鍵字的分佈且關鍵字的若干位分佈較均勻的情況

處理哈希衝突

閉散列

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

  • 線性探測

一旦衝突必須要找出下一個空餘位置,線性探測找的處理爲:從發生衝突的位置開始,依次繼續向後探測,直到找到空位置爲止。

採用閉散列處理哈希衝突時,不能隨便物理刪除哈希表中已有的元素,若直接刪除元素會影響其他元素的搜索。

採用線性探測,實現起來非常簡單,缺陷是:
一旦發生哈希衝突,所有的衝突連在一起,容易產生數據“堆積”,即:不同關鍵碼佔據了可利用的空位置,使得尋找某關鍵碼的位置需要許多次比較,導致搜索效率降低。

  • 二次探測
    發生哈希衝突時,二次探查法在表中尋找“下一個”空位置的公式爲:
    = ( + ) % m, = ( - ) % m, i = 1,2,3…, 是通過散列函數Hash(x)對元素的關鍵碼 key 進行計算得到的位置,m是表的大小。

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

開散列

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

擴展

素數表
// 使用素數表對齊做哈希表的容量,降低哈希衝突
const int _PrimeSize = 28;
static const unsigned long _PrimeList [_PrimeSize] =
{
    53ul, 97ul, 193ul, 389ul, 769ul,
    1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
    49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
    1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
    50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
    1610612741ul, 3221225473ul, 4294967291ul
};
字符串哈希算法

http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html

發佈了82 篇原創文章 · 獲贊 30 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章