18-散列表(上):Word文檔中的單詞拼寫檢查功能是如何實現(xiàn)的魁蒜?

散列表的英文叫“Hash Table”,我們平時也叫它“哈希表”或者“Hash 表”吩翻。

散列表用的是數(shù)組支持按照下標隨機訪問數(shù)據(jù)的特性兜看,所以散列表其實就是數(shù)組的一種擴展,由數(shù)組演化而來狭瞎∠敢疲可以說,如果沒有數(shù)組熊锭,就沒有散列表弧轧。

鍵、散列函數(shù)碗殷、散列值的關(guān)系

散列函數(shù)

散列函數(shù)精绎,我們可以把它定義成hash(key),其中 key 表示元素的鍵值亿扁,hash(key) 的值表示經(jīng)過散列函數(shù)計算得到的散列值捺典。

三點散列函數(shù)設(shè)計的基本要求

  • 散列函數(shù)計算得到的散列值是一個非負整數(shù);
  • 如果 key1 = key2从祝,那 hash(key1) == hash(key2)襟己;
  • 如果 key1 ≠ key2引谜,那 hash(key1) ≠ hash(key2);

第三點理解起來可能會有問題擎浴,我著重說一下员咽。這個要求看起來合情合理,但是在真實的情況下贮预,要想找到一個不同的 key 對應的散列值都不一樣的散列函數(shù)贝室,幾乎是不可能的。即便像業(yè)界著名的MD5仿吞、SHA滑频、CRC等哈希算法,也無法完全避免這種散列沖突唤冈。而且峡迷,因為數(shù)組的存儲空間有限,也會加大散列沖突的概率你虹。

所以我們幾乎無法找到一個完美的無沖突的散列函數(shù)绘搞,即便能找到,計算成本也是很大的傅物,所以針對散列沖突問題夯辖,我們需要通過其他途徑來解決。

散列沖突

再好的散列函數(shù)也無法避免散列沖突董饰。那究竟該如何解決散列沖突問題呢蒿褂?我們常用的散列沖突解決方法有兩類,開放尋址法(open addressing)和鏈表法(chaining)尖阔。

1. 開放尋址法

它的核心思想是如果出現(xiàn)了散列沖突贮缅,我們就重新探測一個空閑位置榨咐,將其插入介却。

那如何重新探測新的位置呢?我先講一個比較簡單的探測方法块茁,線性探測(Linear Probing)齿坷。

當我們往散列表中插入數(shù)據(jù)時,如果某個數(shù)據(jù)經(jīng)過散列函數(shù)散列之后数焊,存儲位置已經(jīng)被占用了永淌,我們就從當前位置開始,依次往后查找佩耳,看是否有空閑位置遂蛀,直到找到為止。

舉一個例子具體給你說明一下干厚。這里面黃色的色塊表示空閑位置李滴,橙色的色塊表示已經(jīng)存儲了數(shù)據(jù)螃宙。

在散列表中查找元素的過程有點兒類似插入過程。我們通過散列函數(shù)求出要查找元素的鍵值對應的散列值所坯,然后比較數(shù)組中下標為散列值的元素和要查找的元素谆扎。如果相等,則說明就是我們要找的元素芹助;否則就順序往后依次查找堂湖。如果遍歷到數(shù)組中的空閑位置,還沒有找到状土,就說明要查找的元素并沒有在散列表中无蜂。

散列表跟數(shù)組一樣,不僅支持插入蒙谓、查找操作酱讶,還支持刪除操作。對于使用線性探測法解決沖突的散列表彼乌,刪除操作稍微有些特別泻肯。我們不能單純地把要刪除的元素設(shè)置為空。

還記得我們剛講的查找操作嗎慰照?在查找的時候灶挟,一旦我們通過線性探測方法,找到一個空閑位置毒租,我們就可以認定散列表中不存在這個數(shù)據(jù)稚铣。但是,如果這個空閑位置是我們后來刪除的墅垮,就會導致原來的查找算法失效惕医。本來存在的數(shù)據(jù),會被認定為不存在算色。這個問題如何解決呢抬伺?

我們可以將刪除的元素,特殊標記為 deleted灾梦。當線性探測查找的時候峡钓,遇到標記為 deleted 的空間,并不是停下來若河,而是繼續(xù)往下探測能岩。

你可能已經(jīng)發(fā)現(xiàn)了,線性探測法其實存在很大問題萧福。當散列表中插入的數(shù)據(jù)越來越多時拉鹃,散列沖突發(fā)生的可能性就會越來越大,空閑位置會越來越少,線性探測的時間就會越來越久膏燕。極端情況下炭庙,我們可能需要探測整個散列表,所以最壞情況下的時間復雜度為 O(n)煌寇。同理焕蹄,在刪除和查找時,也有可能會線性探測整張散列表阀溶,才能找到要查找或者刪除的數(shù)據(jù)腻脏。

對于開放尋址沖突解決方法,除了線性探測方法之外银锻,還有另外兩種比較經(jīng)典的探測方法永品,二次探測(Quadratic probing)和雙重散列(Double hashing)。

所謂二次探測击纬,跟線性探測很像鼎姐,線性探測每次探測的步長是 1,那它探測的下標序列就是 hash(key)+0更振,hash(key)+1炕桨,hash(key)+2……而二次探測探測的步長就變成了原來的“二次方”,也就是說肯腕,它探測的下標序列就是 hash(key)+0献宫,hash(key)+1^2,hash(key)+2^2……

所謂雙重散列实撒,意思就是不僅要使用一個散列函數(shù)姊途。我們使用一組散列函數(shù) hash1(key),hash2(key)知态,hash3(key)……我們先用第一個散列函數(shù)捷兰,如果計算得到的存儲位置已經(jīng)被占用,再用第二個散列函數(shù)负敏,依次類推贡茅,直到找到空閑的存儲位置。

不管采用哪種探測方法原在,當散列表中空閑位置不多的時候友扰,散列沖突的概率就會大大提高彤叉。為了盡可能保證散列表的操作效率庶柿,一般情況下,我們會盡可能保證散列表中有一定比例的空閑槽位秽浇。我們用裝載因子(load factor)來表示空位的多少浮庐。

裝載因子的計算公式是:

散列表的裝載因子 = 填入表中的元素個數(shù) / 散列表的長度

裝載因子越大,說明空閑位置越少泌霍,沖突越多更啄,散列表的性能會下降晾嘶。

2. 鏈表法

鏈表法是一種更加常用的散列沖突解決辦法南片,相比開放尋址法诈胜,它要簡單很多遮精。我們來看這個圖啰扛,在散列表中疙描,每個“桶(bucket)”或者“槽(slot)”會對應一條鏈表璧坟,所有散列值相同的元素我們都放到相同槽位對應的鏈表中既穆。

當插入的時候,我們只需要通過散列函數(shù)計算出對應的散列槽位雀鹃,將其插入到對應鏈表中即可幻工,所以插入的時間復雜度是 O(1)。

當查找黎茎、刪除一個元素時囊颅,我們同樣通過散列函數(shù)計算出對應的槽,然后遍歷鏈表查找或者刪除傅瞻。那查找或刪除操作的時間復雜度是多少呢踢代?

實際上,這兩個操作的時間復雜度跟鏈表的長度 k 成正比嗅骄,也就是 O(k)奸鬓。對于散列比較均勻的散列函數(shù)來說,理論上講掸读,k=n/m串远,其中 n 表示散列中數(shù)據(jù)的個數(shù),m 表示散列表中“槽”的個數(shù)儿惫。

開篇解答

Word 文檔中單詞拼寫檢查功能是如何實現(xiàn)的澡罚?

常用的英文單詞有 20 萬個左右,假設(shè)單詞的平均長度是 10 個字母肾请,平均一個單詞占用 10 個字節(jié)的內(nèi)存空間留搔,那 20 萬英文單詞大約占 2MB 的存儲空間,就算放大 10 倍也就是 20MB铛铁。對于現(xiàn)在的計算機來說隔显,這個大小完全可以放在內(nèi)存里面。所以我們可以用散列表來存儲整個英文單詞詞典饵逐。

當用戶輸入某個英文單詞時括眠,我們拿用戶輸入的單詞去散列表中查找。如果查到倍权,則說明拼寫正確掷豺;如果沒有查到,則說明拼寫可能有誤,給予提示当船。借助散列表這種數(shù)據(jù)結(jié)構(gòu)题画,我們就可以輕松實現(xiàn)快速判斷是否存在拼寫錯誤。

內(nèi)容小結(jié)

今天我講了一些比較基礎(chǔ)德频、比較偏理論的散列表知識苍息,包括散列表的由來、散列函數(shù)壹置、散列沖突的解決方法档叔。

散列表來源于數(shù)組,它借助散列函數(shù)對數(shù)組這種數(shù)據(jù)結(jié)構(gòu)進行擴展蒸绩,利用的是數(shù)組支持按照下標隨機訪問元素的特性衙四。散列表兩個核心問題是散列函數(shù)設(shè)計散列沖突解決。散列沖突有兩種常用的解決方法患亿,開放尋址法鏈表法传蹈。散列函數(shù)設(shè)計的好壞決定了散列沖突的概率,也就決定散列表的性能步藕。

針對散列函數(shù)和散列沖突惦界,今天我只講了一些基礎(chǔ)的概念、方法咙冗,下一節(jié)我會更貼近實戰(zhàn)沾歪、更加深入探討這兩個問題。

課后思考

  • 假設(shè)我們有 10 萬條 URL 訪問日志雾消,如何按照訪問次數(shù)給 URL 排序灾搏?

  • 有兩個字符串數(shù)組,每個數(shù)組大約有 10 萬條字符串立润,如何快速找出兩個數(shù)組中相同的字符串狂窑?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市桑腮,隨后出現(xiàn)的幾起案子泉哈,更是在濱河造成了極大的恐慌,老刑警劉巖破讨,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丛晦,死亡現(xiàn)場離奇詭異,居然都是意外死亡提陶,警方通過查閱死者的電腦和手機烫沙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搁骑,“玉大人斧吐,你說我怎么就攤上這事又固≈倨鳎” “怎么了煤率?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乏冀。 經(jīng)常有香客問我蝶糯,道長,這世上最難降的妖魔是什么辆沦? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任昼捍,我火速辦了婚禮,結(jié)果婚禮上肢扯,老公的妹妹穿的比我還像新娘妒茬。我一直安慰自己,他們只是感情好蔚晨,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布乍钻。 她就那樣靜靜地躺著,像睡著了一般铭腕。 火紅的嫁衣襯著肌膚如雪银择。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天累舷,我揣著相機與錄音浩考,去河邊找鬼。 笑死被盈,一個胖子當著我的面吹牛析孽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播只怎,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼绿淋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尝盼?” 一聲冷哼從身側(cè)響起吞滞,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盾沫,沒想到半個月后裁赠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡赴精,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年佩捞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕾哟。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡一忱,死狀恐怖莲蜘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情帘营,我是刑警寧澤票渠,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站芬迄,受9級特大地震影響问顷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禀梳,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一杜窄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧算途,春花似錦塞耕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纱注,卻和暖如春畏浆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狞贱。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工刻获, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞎嬉。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓蝎毡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氧枣。 傳聞我的和親對象是個殘疾皇子沐兵,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

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

  • Word文檔編輯器大家應該經(jīng)常使用吧扎谎,大家有沒有留意到它編輯功能,當我們輸入一個錯誤的單詞時烧董,單詞單面就會標紅提示...
    仙花斗影閱讀 779評論 0 0
  • 本文主要介紹散列表(Hash Table)這一常見數(shù)據(jù)結(jié)構(gòu)的原理與實現(xiàn)毁靶。由于個人水平有限,文章中難免存在不準確或是...
    absfree閱讀 16,282評論 2 35
  • 說明:該系列博客整理自《算法導論(原書第二版)》逊移,但更偏重于實用预吆,所以晦澀偏理論的內(nèi)容未整理,請見諒胳泉。另外本人能力...
    黑夜0411閱讀 1,458評論 0 2
  • 實質(zhì)性的春天來了拐叉,萬物復蘇岩遗。 漫步在園子里,望著柳枝上的嫩芽凤瘦,路邊開得正艷的迎春花宿礁,心情卻很平靜,比往年少了些心動...
    天堂里的魚閱讀 256評論 2 2
  • 今天穿上了那條粉紅裙子廷粒,就是在上個禮拜的昨天窘拯,買的红且。在我們學校的小后街坝茎,那里掛著各式各樣的衣服,尤其是裙子暇番,在這個...
    小鹿風閱讀 274評論 0 0