散列表——Hash Table

前文對數組和鏈表進行了分析,介紹了各自在插入、遍歷、刪除方面的優勢和劣勢,時間複雜度如下表示。

  數組 鏈表
讀取 O(1) O(n)
插入 O(n) O(1)
刪除 O(n) O(1)

既然數組和鏈表都存在一定的不足,那麼有沒有什麼方法能在快速讀取的同時,可以快速的將數據插入呢?答案就是散列表!


場景

假設你開了一家超市,但還沒有采購收銀機器,於是在顧客結賬的時候只能到價格列表上一個個查找。如果這個列表有序且商品數量不多,你只需要進行O(nlogn)次查找即可。可一旦顧客買了十幾甚至幾十種商品時,這就比較費勁了。一旦慢下來,後面那麼長的隊伍還在等着你呢,那麼有沒有什麼辦法能更快的做查詢呢?一個好辦法就是找一個熟記價格的營業員,對n件商品只需O(n)時間即可。那麼問題又來了,怎麼找到這麼優秀的僱員呢?!接下來就要看散列表出場了!


定義&原理

定義:對於給定輸入,能穩定的給出相應的輸出(不能每次都不一樣)。

那散列表又是如何存儲的呢?爲了達到理想的O(1)查找時間,能滿足這個要求的非數組莫屬了。但是數組是按照index進行索引,那又如何將輸入的數字或字符串(如“book”、“mouse”等)轉換爲索引呢,即f(input)=index,且一個input對應一個唯一的index。這就是散列函數所要乾的事情,有關的見SHA算法。

衝突

但是對於再優秀的散列函數,也可能存在有缺陷的地方,即存在多個給定輸入,產生同一個輸出的情況。之前MD5、SHA算法都已先後被攻破(新聞)。

缺陷歸缺陷,當出現衝突的時候,是不是又可以用上其他方法來解決呢?常見的衝突解決方法有開放定址法,鏈地址法,建立公共溢出區等。實際的哈希表實現中,使用最多的是鏈地址法,如下。

在這種情況下的時間複雜度如下(差了可不是一點半點):

  平均情況 最壞情況
讀取 O(1) O(n)
插入 O(1) O(n)
刪除 O(1) O(n)

 

裝填因子

因哈希表的存儲介質是數組,而一個固定了大小的數組不可能存儲無限多的數據,因此需要考慮什麼時候對數組進行擴容。裝填因子乾的就是這個事情,告訴散列表什麼時候預先進行擴容。


實際應用

DNS解析:域名->IP轉換(上億個網頁的域名解析,不用散列表,有其他更高效的方法嗎?)

數據緩存——對於要頻繁訪問的數據,從磁盤載入到內存的成本高昂,何嘗不直接就放在內存裏呢?


總結

散列表和數組、鏈表一樣,都有其優劣之處。各取其長,來解決更大的問題。

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