Java數(shù)據(jù)結(jié)構(gòu)與算法(九)-哈希表

1. 什么是哈希表

散列表(Hash table舍肠,也叫哈希表)括堤,是根據(jù)關(guān)鍵碼值(Key value)而直接進(jìn)行訪問的數(shù)據(jù)結(jié)構(gòu).也就是說羡宙,它通過把關(guān)鍵碼值映射到表中一個位置來訪問記錄烁登,以加快查找的速度贫导。這個映射函數(shù)叫做散列函數(shù)抱完,存放記錄的數(shù)組叫做散列表贼陶。
也是基于數(shù)組來實(shí)現(xiàn)。

Hash表也稱散列表,也有直接譯作哈希表每界,Hash表是一種特殊的數(shù)據(jù)結(jié)構(gòu)捅僵,它同數(shù)組、鏈表以及二叉排序樹等相比較有很明顯的區(qū)別眨层,它能夠快速定位到想要查找的記錄庙楚,而不是與表中存在的記錄的關(guān)鍵字進(jìn)行比較來進(jìn)行查找。這個源于Hash表設(shè)計(jì)的特殊性趴樱,它采用了函數(shù)映射的思想將記錄的存儲位置與記錄的關(guān)鍵字關(guān)聯(lián)起來馒闷,從而能夠很快速地進(jìn)行查找。

1.Hash表的設(shè)計(jì)思想
  對于一般的線性表叁征,比如鏈表纳账,如果要存儲聯(lián)系人信息: 
張三 13980593357
李四 15828662334
王五 13409821234
張帥 13890583472
  那么可能會設(shè)計(jì)一個結(jié)構(gòu)體包含姓名,手機(jī)號碼這些信息捺疼,然后把4個聯(lián)系人的信息存到一張鏈表中疏虫。當(dāng)要查找”李四 15828662334“這條記錄是否在這張鏈表中或者想要得到李四的手機(jī)號碼時,可能會從鏈表的頭結(jié)點(diǎn)開始遍歷啤呼,依次將每個結(jié)點(diǎn)中的姓名同”李四“進(jìn)行比較卧秘,直到查找成功或者失敗為止,這種做法的時間復(fù)雜度為O(n)官扣。即使采用二叉排序樹進(jìn)行存儲翅敌,也最多為O(logn)。假設(shè)能夠通過”李四“這個信息直接獲取到該記錄在表中的存儲位置惕蹄,就能省掉中間關(guān)鍵字比較的這個環(huán)節(jié)蚯涮,復(fù)雜度直接降到O(1)。Hash表就能夠達(dá)到這樣的效果卖陵。

Hash表采用一個映射函數(shù) f : key —> address 將關(guān)鍵字映射到該記錄在表中的存儲位置遭顶,從而在想要查找該記錄時,可以直接根據(jù)關(guān)鍵字和映射關(guān)系計(jì)算出該記錄在表中的存儲位置泪蔫,通常情況下棒旗,這種映射關(guān)系稱作為Hash函數(shù),而通過Hash函數(shù)和關(guān)鍵字計(jì)算出來的存儲位置(注意這里的存儲位置只是表中的存儲位置鸥滨,并不是實(shí)際的物理地址)稱作為Hash地址嗦哆。比如上述例子中,假如聯(lián)系人信息采用Hash表存儲婿滓,則當(dāng)想要找到“李四”的信息時老速,直接根據(jù)“李四”和Hash函數(shù)計(jì)算出Hash地址即可。下面討論一下Hash表設(shè)計(jì)中的幾個關(guān)鍵問題凸主。

1.1. Hash函數(shù)的設(shè)計(jì)

Hash函數(shù)設(shè)計(jì)的好壞直接影響到對Hash表的操作效率橘券。下面舉例說明:

假如對上述的聯(lián)系人信息進(jìn)行存儲時,采用的Hash函數(shù)為:姓名的每個字的拼音開頭大寫字母的ASCII碼之和。

因此address(張三)=ASCII(Z)+ASCII(S)=90+83=173;

address(李四)=ASCII(L)+ASCII(S)=76+83=159;

address(王五)=ASCII(W)+ASCII(W)=87+87=174;

address(張帥)=ASCII(Z)+ASCII(S)=90+83=173;

假如只有這4個聯(lián)系人信息需要進(jìn)行存儲旁舰,這個Hash函數(shù)設(shè)計(jì)的很糟糕锋华。首先,它浪費(fèi)了大量的存儲空間箭窜,假如采用char型數(shù)組存儲聯(lián)系人信息的話毯焕,則至少需要開辟174*12字節(jié)的空間,空間利用率只有4/174磺樱,不到5%纳猫;另外,根據(jù)Hash函數(shù)計(jì)算結(jié)果之后竹捉,address(張三)和address(李四)具有相同的地址芜辕,這種現(xiàn)象稱作沖突,對于174個存儲空間中只需要存儲4條記錄就發(fā)生了沖突块差,這樣的Hash函數(shù)設(shè)計(jì)是很不合理的侵续。所以在構(gòu)造Hash函數(shù)時應(yīng)盡量考慮關(guān)鍵字的分布特點(diǎn)來設(shè)計(jì)函數(shù)使得Hash地址隨機(jī)均勻地分布在整個地址空間當(dāng)中。通常有以下幾種構(gòu)造Hash函數(shù)的方法:

1)直接定址法

取關(guān)鍵字或者關(guān)鍵字的某個線性函數(shù)為Hash地址憨闰,即address(key)=a*key+b;如知道學(xué)生的學(xué)號從2000開始状蜗,最大為4000,則可以將address(key)=key-2000作為Hash地址起趾。

2)平方取中法

對關(guān)鍵字進(jìn)行平方運(yùn)算诗舰,然后取結(jié)果的中間幾位作為Hash地址警儒。假如有以下關(guān)鍵字序列{421训裆,423,436}蜀铲,平方之后的結(jié)果為{177241边琉,178929,190096}记劝,那么可以取{72变姨,89,00}作為Hash地址厌丑。

3)折疊法

將關(guān)鍵字拆分成幾部分定欧,然后將這幾部分組合在一起,以特定的方式進(jìn)行轉(zhuǎn)化形成Hash地址怒竿。假如知道圖書的ISBN號為8903-241-23砍鸠,可以將address(key)=89+03+24+12+3作為Hash地址。

4)除留取余法

如果知道Hash表的最大長度為m耕驰,可以取不大于m的最大質(zhì)數(shù)p爷辱,然后對關(guān)鍵字進(jìn)行取余運(yùn)算,address(key)=key%p。

在這里p的選取非常關(guān)鍵饭弓,p選擇的好的話双饥,能夠最大程度地減少沖突,p一般取不大于m的最大質(zhì)數(shù)弟断。

2.Hash表大小的確定

Hash表大小的確定也非常關(guān)鍵咏花,如果Hash表的空間遠(yuǎn)遠(yuǎn)大于最后實(shí)際存儲的記錄個數(shù),則造成了很大的空間浪費(fèi)阀趴,如果選取小了的話迟螺,則容易造成沖突。在實(shí)際情況中舍咖,一般需要根據(jù)最終記錄存儲個數(shù)和關(guān)鍵字的分布特點(diǎn)來確定Hash表的大小矩父。還有一種情況時可能事先不知道最終需要存儲的記錄個數(shù),則需要動態(tài)維護(hù)Hash表的容量排霉,此時可能需要重新計(jì)算Hash地址窍株。

3.沖突的解決

在上述例子中,發(fā)生了沖突現(xiàn)象攻柠,因此需要辦法來解決球订,否則記錄無法進(jìn)行正確的存儲。通常情況下有2種解決辦法:

1)開放定址法

即當(dāng)一個關(guān)鍵字和另一個關(guān)鍵字發(fā)生沖突時瑰钮,使用某種探測技術(shù)在Hash表中形成一個探測序列冒滩,然后沿著這個探測序列依次查找下去,當(dāng)碰到一個空的單元時浪谴,則插入其中开睡。比較常用的探測方法有線性探測法,比如有一組關(guān)鍵字{12苟耻,13篇恒,25,23凶杖,38胁艰,34,6智蝠,84腾么,91},Hash表長為14杈湾,Hash函數(shù)為address(key)=key%11解虱,當(dāng)插入12,13毛秘,25時可以直接插入饭寺,而當(dāng)插入23時阻课,地址1被占用了,因此沿著地址1依次往下探測(探測步長可以根據(jù)情況而定)艰匙,直到探測到地址4限煞,發(fā)現(xiàn)為空,則將23插入其中员凝。

2)鏈地址法

采用數(shù)組和鏈表相結(jié)合的辦法署驻,將Hash地址相同的記錄存儲在一張線性表中,而每張表的表頭的序號即為計(jì)算得到的Hash地址健霹。如上述例子中旺上,采用鏈地址法形成的Hash表存儲表示為:

image.png

雖然能夠采用一些辦法去減少沖突,但是沖突是無法完全避免的糖埋。因此需要根據(jù)實(shí)際情況選取解決沖突的辦法宣吱。

4.Hash表的平均查找長度

Hash表的平均查找長度包括查找成功時的平均查找長度和查找失敗時的平均查找長度。

查找成功時的平均查找長度=表中每個元素查找成功時的比較次數(shù)之和/表中元素個數(shù)瞳别;

查找不成功時的平均查找長度相當(dāng)于在表中查找元素不成功時的平均比較次數(shù)征候,可以理解為向表中插入某個元素,該元素在每個位置都有可能祟敛,然后計(jì)算出在每個位置能夠插入時需要比較的次數(shù)疤坝,再除以表長即為查找不成功時的平均查找長度。

下面舉個例子:

有一組關(guān)鍵字{23馆铁,12跑揉,14,2埠巨,3历谍,5},表長為14乖订,Hash函數(shù)為key%11扮饶,則關(guān)鍵字在表中的存儲如下:

地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13

關(guān)鍵字 23 12 14 2 3 5

比較次數(shù) 1 2 1 3 3 2

因此查找成功時的平均查找長度為(1+2+1+3+3+2)/6=11/6具练;

查找失敗時的平均查找長度為(1+7+6+5+4+3+2+1+1+1+1+1+1+1)/14=38/14乍构;

這里有一個概念裝填因子=表中的記錄數(shù)/哈希表的長度,如果裝填因子越小扛点,表明表中還有很多的空單元哥遮,則發(fā)生沖突的可能性越小陵究;而裝填因子越大眠饮,則發(fā)生沖突的可能性就越大,在查找時所耗費(fèi)的時間就越多铜邮。因此仪召,Hash表的平均查找長度和裝填因子有關(guān)寨蹋。有相關(guān)文獻(xiàn)證明當(dāng)裝填因子在0.5左右的時候,Hash的性能能夠達(dá)到最優(yōu)扔茅。因此已旧,一般情況下,裝填因子取經(jīng)驗(yàn)值0.5召娜。

5.Hash表的優(yōu)缺點(diǎn)

Hash表存在的優(yōu)點(diǎn)顯而易見运褪,能夠在常數(shù)級的時間復(fù)雜度上進(jìn)行查找,并且插入數(shù)據(jù)和刪除數(shù)據(jù)比較容易玖瘸。但是它也有某些缺點(diǎn)秸讹,比如不支持排序,一般比用線性表存儲需要更多的空間雅倒,并且記錄的關(guān)鍵字不能重復(fù)璃诀。

借鑒博客:海子

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蔑匣,隨后出現(xiàn)的幾起案子文虏,更是在濱河造成了極大的恐慌,老刑警劉巖殖演,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氧秘,死亡現(xiàn)場離奇詭異,居然都是意外死亡趴久,警方通過查閱死者的電腦和手機(jī)丸相,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彼棍,“玉大人灭忠,你說我怎么就攤上這事∽叮” “怎么了弛作?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長华匾。 經(jīng)常有香客問我映琳,道長,這世上最難降的妖魔是什么蜘拉? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任萨西,我火速辦了婚禮,結(jié)果婚禮上旭旭,老公的妹妹穿的比我還像新娘谎脯。我一直安慰自己纳本,他們只是感情好蒋纬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布菲茬。 她就那樣靜靜地躺著缎谷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪废麻。 梳的紋絲不亂的頭發(fā)上矢否,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音脑溢,去河邊找鬼僵朗。 笑死,一個胖子當(dāng)著我的面吹牛屑彻,可吹牛的內(nèi)容都是我干的验庙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼社牲,長吁一口氣:“原來是場噩夢啊……” “哼粪薛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起搏恤,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤违寿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后熟空,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藤巢,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年息罗,在試婚紗的時候發(fā)現(xiàn)自己被綠了掂咒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡迈喉,死狀恐怖绍刮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挨摸,我是刑警寧澤孩革,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站得运,受9級特大地震影響膝蜈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澈圈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一彬檀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞬女,春花似錦、人聲如沸努潘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至报慕,卻和暖如春深浮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背眠冈。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工飞苇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜗顽。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓布卡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雇盖。 傳聞我的和親對象是個殘疾皇子忿等,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載http://www.cnblogs.com/dolphin0520/archive/2012/09/28/2...
    一臉傲嬌的喵嗚喵閱讀 641評論 0 0
  • 前言 哈希(Hash)或者說散列表,它是一種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)崔挖。Hash 表是一種特殊的數(shù)據(jù)結(jié)構(gòu)贸街,它同數(shù)組、鏈表以及二叉...
    ZhengYaWei閱讀 17,143評論 13 107
  • 凌晨五點(diǎn)狸相,清潔工和晨練的人都上了馬路薛匪,與他們一同早起的還有各早餐鋪的攤主們,偌大的高郵城就在這樣一個不太喧囂的環(huán)境...
    月明飛錫閱讀 1,111評論 5 9
  • 你好脓鹃,我來了蛋辈。你好,我走了将谊。 嗨冷溶,我來,哈羅尊浓,拜拜逞频。 噯,在呢栋齿。嗯苗胀,走了。 哦瓦堵,到基协。好,再見菇用。 再見澜驮。
    趙蘊(yùn)蘊(yùn)閱讀 72評論 0 0
  • 最近有一部電影上映,值得關(guān)注惋鸥≡忧睿《攻殼機(jī)動隊(duì)》首部動畫電影是押井守1995年執(zhí)導(dǎo)的版本悍缠。《攻殼機(jī)動隊(duì) 》真人版在本月...
    cnight閱讀 350評論 1 2