數(shù)據(jù)結(jié)構(查找-散列表(哈希表)的查找)

1. 散列表的基本概念

元素的存儲位置和其關鍵字之間建立某種直接關系,這就是散列查找法。

(1) 散列函數(shù)和散列地址:在記錄的存儲位置p和其關鍵字key之間建立一個確定的對應關系H,使p=H(key),稱這對應關系H為散列函數(shù),p為散列地址。
(2) 散列表:一個有限連續(xù)的地址空間,用以存儲按散列函數(shù)計算得到相應散列地址的數(shù)據(jù)記錄。通常散列表是一個一維數(shù)組,散列地址是數(shù)組的下標。
(3) 沖突和同義詞:對不同的關鍵字可能得帶統(tǒng)一散列地址,即key1 != key2,而H(key1) = H(key2),這種現(xiàn)象稱為沖突。具有相同函數(shù)值得關鍵字對該散列函數(shù)來說稱作同義詞,key1 和key2互稱為同義詞。

2. 散列函數(shù)的構造方法

選取散列函數(shù)的參考:

  1. 計算散列地址所需的時間;
  2. 關鍵字長度;
  3. 散列表大小;
  4. 關鍵字的分布情況;
  5. 查找記錄的頻率。

(1) 直接定址法

其實就是直接通過取關鍵的字的某個線性值作為散列地址:f(key)=a*key+b,(a,b為常數(shù))

(2) 數(shù)字分析法

假設某公司的員工登記表以員工的手機號作為關鍵字。手機號一共11位。前3位是接入號,對應不同運營商 的子品牌;中間4位表示歸屬地;最后4位是用戶號。不同手機號前7位相同的可能性很大,所以可以選擇后4 位作為散列地址,或者對后4位反轉(zhuǎn)(1234 -> 4321)、循環(huán)右移(1234 -> 4123)、循環(huán)左移等等之后作為散列地址。

數(shù)字分析法通常適合處理關鍵字位數(shù)比較大的情況,如果事先知道關鍵字的分布且關鍵字的若干位分布比較 均勻,就可以考慮這個方法。

(3) 平方取中法

假設關鍵字是1234,平方之后是1522756,再抽取中間3位227,用作散列地址。平方取中法比較適合于不 知道關鍵字的分布,而位數(shù)又不是很大的情況。

(4) 折疊法

將關鍵字從左到右分割成位數(shù)相等的幾部分,最后一部分位數(shù)不夠時可以短些,然后將這幾部分疊加求和, 并按散列表表長,取后幾位作為散列地址。

比如關鍵字是9876543210,散列表表長是3位,將其分為四組,然后疊加求和:987 + 654 + 321 + 0 = 1962,取后3位962作為散列地址。

折疊法事先不需要知道關鍵字的分布,適合關鍵字位數(shù)較多的情況。

(5) 除留取余數(shù)法

f(key) = key mod p (p≤m),m為散列表長。
這種方法不僅可以對關鍵字直接取模,也可在折疊、平方取中
后再取模。根據(jù)經(jīng)驗,若散列表表長為m,通常p為小于或等于表長(最好接近m)的最小質(zhì)數(shù),可以更好的 減小沖突。

(6) 隨機數(shù)法

f(key) = random(key),這里random是隨機函數(shù)。
當關鍵字的長度不等時,采用這個方法構造散列函數(shù)是比較合適的。

3. 處理沖突的方法

(1) 開放地址法

開放地址就是一旦發(fā)生沖突,就去尋找下一個空的散列地址,只有散列表足夠大,空的散列地址總能找到,并且記錄它。
至于如何尋找下一個空的散列地址,有三種方法

1. 線性探測法

f(key)=(f(key)+d)%m ,其中d取(0,1,2,3,4.....,m-1),m為散列表的長度


如上圖所示,散列表的長度為12,而且我們現(xiàn)在已經(jīng)插入了部分數(shù)據(jù)了,下面我們繼續(xù)插入37,。然后,我們使用散列函數(shù)計算37的散列地址:
f(37)=f(37)%12=1
但是我們發(fā)現(xiàn)1這個位置已經(jīng)存放了25,那么我們就繼續(xù)尋找下一個空的散列地址。
f(37)=(f(37)+1)%12=2
發(fā)現(xiàn)2這個地址沒有內(nèi)容,所以把37插入到這個位置,得如下圖的結(jié)果:



線性探測來解決沖突問題,會造成沖突堆積。所謂的沖突堆積就是比如說剛才的37,它本來是屬于下標1的元素,現(xiàn)在卻占用了下標為2的空間,這會造成待會我們需要存放本來要放在下標為2的元素時,再次發(fā)生沖突,這個沖突會一直傳播下去,造成查找和插入效率都大大減低。

2. 二次探測法

f(key)=(f(key)+d)%m, ,其中d取(0^2,1^2,-1^2,2^2,-2^2,3^2,-3^2,4^2,-4^2...,q^2,-q^2),q<=m/2,m為散列表的長度

其實,這個是對線性探測的一個優(yōu)化,增加了平方可以不讓關鍵字聚集在某一塊區(qū)域。



例如,我們對剛才的那個散列表,插入一個元素:7,通過二次探測的散列函數(shù)計算得到的散列地址為:
f(7)=f(7)%12=7
但是,我發(fā)現(xiàn)下標為7的位置已經(jīng)存放了元素:67,所以我需要尋找下一個存儲地址:
f(7)=(f(7)+1^2)%12=8
突然發(fā)現(xiàn)下標為8的地址也存放了56這個元素,所以我們只能繼續(xù)往下尋找下一個存儲地址:
f(7)=(f(7)+(-1^2))%12=6
發(fā)現(xiàn)下標為6的這個地址空間還是空的,所以我就把7插入到這個位置,得到如下結(jié)果:


3. 隨機探測法

f(key)=(f(key)+d)%m, d為隨機數(shù)列,而m為表長
在實際程序中應預先用隨機數(shù)發(fā)生器產(chǎn)生一個隨機序列,將此序列作為依次探測的步長。這樣就能使不同的關鍵字具有不同的探測次序,從而可以避 免或減少堆聚。

(2) 鏈地址法

所謂的鏈地址法,其實就是當發(fā)生沖突時,我還是把它存放在當前的位置,只是每個位置都是使用鏈表來存放同義詞,這個思路和圖的鄰接表存儲方式很相似。如下圖所示:


4. 散列表的查找

開放地址散列表存儲表示

#define m 20 //散列表的長度
typedef struct{
    KeyType key;
    InfoType otherinfo;
}HashTable;
  1. 給定待查找的關鍵字key,根據(jù)造表時設定的散列函數(shù)計算H0=H(key)。
  2. 若單元H0為空,則所查找元素不存在。
  3. 若單元H0中元素關鍵字為key,則查找成功。
  4. 否則重復下述解決沖突的過程:
  • 按處理沖突的方法,計算下一個散列地址Hi。
  • 若單元Hi為空,則所查找元素不存在。
  • 若單元Hi中元素的關鍵字為key,則查找成功。
#define NULLKEY 0
int SearchHash(HashTable HT,KeyType key)
{
    H0 = H(key);//根據(jù)散列函數(shù)H(key)計算散列地址
    if(HT[H0].key == NULLKEY) return -1;
    else if(HT[H0].key == key ) return H0;
    else
    {
        for(i=1;i<m;i++)
        {
              Hi = (H0+i)%m;//按照線性探測法計算下一個散列地址Hi
              if(HT[Hi].key == NULLKEY) return -1;
              else if(HT[Hi].key == key ) return Hi;
        }
        return -1;
    }
}

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372