1. 程式人生 > >《資料結構與演算法之美》專欄閱讀筆記5——散列表和雜湊函式

《資料結構與演算法之美》專欄閱讀筆記5——散列表和雜湊函式

這應該是看完最呆(沒有想到的那種呆~)的一個小章節了,給作者鼓掌,講的好好。果然抽象能力才是王道

文章目錄

1、散列表

核心:散列表用的是陣列支援按照下標隨機訪問資料的特性。
這個例子舉的好好~不抄了,粘原文,重點是下面的亮條條(廣告

跟著學了幾個排序演算法後,此時此刻看到散列表想到的是計數排序呢,因為都是想著法兒地給元素和陣列下標搞關係。

1.1、小概念
  • 鍵:也叫關鍵字,就是最終放到資料結構中的元素啦
  • 雜湊函式:也叫雜湊函式。算命先生,告訴應該去陣列的哪個坑裡蹲著
  • 雜湊值:也叫雜湊值。蹲的那個坑
1.2、雜湊函式

如果把元素都對應到了陣列中,查詢的時間複雜度就是O(1),看著很酷呢~
雜湊函式需要滿足三點基本要求:

  • 雜湊函式計算得到的雜湊值是一個非負整數
  • 如果key1 = key2,那麼hash(key1) = hash(key2)
  • 如果key1 != key2,那麼hash(key1) != hash(key2)
    (補充一條:簡單不燒腦更好)
1.3、雜湊衝突

實際中,比較難滿足第三點要求,當存在key1 != key2,hash(key1) != hash(key2)時稱作雜湊衝突。解決雜湊衝突常見的兩種辦法:

  • 開放定址法
    思路:出現衝突就重新探測空閒可用的位置來儲存資料。一種簡單的線性探測方法如下。

刪除操作:因為使用開放定址法的時候,key相同的資料儲存在同一個位置(但是我們不曉得是有幾個相同的資料儲存在這一份資料中),所以刪除的時候不能直接刪,而是標記位deleted,避免被定址覆蓋了。(會有很多的空間浪費吧~不環保,差評!)

【效能分析】因為算完還需要找合適的位置,最壞的情況可能需要挨個兒找一遍,O(n)啦

【更好的辦法】
二次探測:探測步長變成n^2。
雙重雜湊:使用一組雜湊函式,挨個算,知道有一個函式算出來沒被佔用的位置為止(這組雜湊函式應該很不容易吧)

  • 連結串列法
    思路:將雜湊值相同的元素用連結串列存起來,數組裡儲存的是這個連結串列的頭的資訊。
1.4、裝載因子

裝載因子可以用來表示陣列中空位的多少,裝在因子越大,說明空閒位置越少,衝突越多,散列表效能會下降。

load factor = 填入表中的元素的個數 / 散列表長度

2、實際應用中的散列表注意事項

2.1、雜湊函式的設計原則
  • 不能太複雜,避免消耗太多的計算時間
  • 生成的雜湊值要儘可能隨機並且均勻分佈
2.2、裝載因子過大

動態擴容。

散列表的擴容需要重新計算雜湊位置,搬移資料。裝載因子特別小時,如果對空間消耗敏感,還可以動態縮容。

2.3、如何避免低效地擴容

避免一次性擴容,將新資料插入新的散列表的過程中搬移舊資料到新的散列表

2.4、解決衝突的方案選擇

【開放定址法】

  • 優點
    資料儲存在陣列中,可以利用CPU快取加快查詢速度。
    序列化相對簡單
  • 缺點
    刪除資料比較麻煩,更浪費記憶體空間。
  • 適用場景
    資料量比較小,裝載因子小

【連結串列法】

  • 優點
    記憶體利用率高
    對大裝載因子的容忍度更高
  • 缺點
    消耗記憶體
    非連續儲存,對CPU快取不友好。可以通過使用其他資料結構來替代連結串列優化效率
  • 適用場景
    儲存大物件、大資料量的散列表
2.5、設計一個工業級的雜湊函式

像這種要考慮很多方面的問題,大致都只能給個方針啥的,作者給的,我抄過來啦~遇到的時候還能回來翻翻看:

一個工業級的散列表需要滿足以下要求:

  • 支援快速的查詢、插入、刪除操作
  • 記憶體佔用合理,不能浪費過多的記憶體空間
  • 效能穩定,極端情況下,散列表的效能也不會退化到無法接受的情況

如何實現:

  • 設計一個合適的雜湊函式
  • 定義裝載因子閾值,並且設計動態擴容策略
  • 選擇合適的雜湊衝突解決辦法

舉栗子的時候給了個HashMap的雜湊值計算的方法,看完頭皮發麻。評論區有小牛角給了分析呢,好好看完,就抄過來啦

	static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

找了一下位運算技巧:面試常用位運算技巧

位運算的這些沒有找到可以總結的辦法,所以記不住啦~回頭找機會再瞄一瞄吧。

**TODO:**LRU實現

3、雜湊演算法

原理:將任意長度的二進位制值串對映為固定長度的二進位制值串的規則。
一個優秀的雜湊演算法要滿足的幾點要求:

  • 從雜湊值不能反向推導處原始資料
  • 對輸入資料非常敏感,哪怕原始資料只修改了一個Bit,雜湊值也大不相同
  • 雜湊衝突概率很小
  • 執行效率高
應用
  • 安全加密
  • 唯一標識
  • 資料校驗
  • 雜湊函式
  • 負載均衡
  • 資料分片
  • 分散式儲存
    一致性雜湊